|
|
|
|
|
# 箱子数量计算接口使用说明
|
|
|
|
|
|
|
|
|
|
|
|
## 概述
|
|
|
|
|
|
|
|
|
|
|
|
本系统提供了基于点云数据的箱子数量计算接口,可以根据PCD文件、地板高度、箱子尺寸和堆叠类型自动计算箱子数量。
|
|
|
|
|
|
|
|
|
|
|
|
## 功能特点
|
|
|
|
|
|
|
|
|
|
|
|
- ✅ 支持PCD文件读取和解析
|
|
|
|
|
|
- ✅ 自动按高度分层点云
|
|
|
|
|
|
- ✅ 支持冗余配置(每层递增)
|
|
|
|
|
|
- ✅ 基于凸包面积计算箱子数量
|
|
|
|
|
|
- ✅ 支持多种堆叠类型配置
|
|
|
|
|
|
- ✅ 返回详细的层信息
|
|
|
|
|
|
|
|
|
|
|
|
## 计算原理
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 高度计算
|
|
|
|
|
|
```
|
|
|
|
|
|
高度 = 地板高度 - 点的Z轴坐标
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 层判断
|
|
|
|
|
|
```
|
|
|
|
|
|
层数 = round(高度 / 箱子高度)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 冗余范围
|
|
|
|
|
|
```
|
|
|
|
|
|
冗余 = 基础冗余(50mm) + 层数 × 每层增加冗余(20mm)
|
|
|
|
|
|
|
|
|
|
|
|
有效点判断: |高度 - 层数 × 箱子高度| ≤ 冗余
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4. 面积计算
|
|
|
|
|
|
- 将每层的点投影到XY平面
|
|
|
|
|
|
- 计算凸包面积
|
|
|
|
|
|
- 凸包面积即为该层堆叠区域面积
|
|
|
|
|
|
|
|
|
|
|
|
### 5. 箱子数量
|
|
|
|
|
|
```
|
|
|
|
|
|
每层箱子数 = min(round(凸包面积 / (箱子长 × 箱子宽)), 每层最大箱子数)
|
|
|
|
|
|
总箱子数 = Σ(每层箱子数)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## API接口
|
|
|
|
|
|
|
|
|
|
|
|
### 1. POST方式计算(推荐)
|
|
|
|
|
|
|
|
|
|
|
|
**请求URL:**
|
|
|
|
|
|
```
|
|
|
|
|
|
POST /api/boxcount/calculate
|
|
|
|
|
|
Content-Type: application/json
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**请求体:**
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"pcdFilePath": "D:/output/20250325/pointCloud/192.168.1.100_pointcloud_20250325_143022.pcd",
|
|
|
|
|
|
"floorHeight": 1000.0,
|
|
|
|
|
|
"boxLength": 200.0,
|
|
|
|
|
|
"boxWidth": 140.0,
|
|
|
|
|
|
"boxHeight": 250.0,
|
|
|
|
|
|
"stackType": "2w 3h",
|
|
|
|
|
|
"maxBoxesPerLayer": 10,
|
|
|
|
|
|
"baseTolerance": 50.0,
|
|
|
|
|
|
"additionalTolerancePerLevel": 20.0,
|
|
|
|
|
|
"minBounds": [-1000.0, -1000.0, -1000.0],
|
|
|
|
|
|
"maxBounds": [1000.0, 1000.0, 1000.0]
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**参数说明:**
|
|
|
|
|
|
|
|
|
|
|
|
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
|
|
|
|
|
|--------|------|------|--------|------|
|
|
|
|
|
|
| pcdFilePath | String | 是 | - | PCD文件路径 |
|
|
|
|
|
|
| floorHeight | Double | 否 | 0.0 | 地板高度(mm) |
|
|
|
|
|
|
| boxLength | Double | 否 | 200.0 | 箱子长度(mm) |
|
|
|
|
|
|
| boxWidth | Double | 否 | 140.0 | 箱子宽度(mm) |
|
|
|
|
|
|
| boxHeight | Double | 否 | 250.0 | 箱子高度(mm) |
|
|
|
|
|
|
| stackType | String | 否 | "1w" | 堆叠类型(如"2w 3h") |
|
|
|
|
|
|
| maxBoxesPerLayer | Integer | 否 | 根据stackType计算 | 每层最大箱子数 |
|
|
|
|
|
|
| baseTolerance | Double | 否 | 50.0 | 基础冗余(mm) |
|
|
|
|
|
|
| additionalTolerancePerLevel | Double | 否 | 20.0 | 每层增加冗余(mm) |
|
|
|
|
|
|
| minBounds | double[] | 否 | [-∞, -∞, -∞] | 裁剪最小边界 |
|
|
|
|
|
|
| maxBounds | double[] | 否 | [+∞, +∞, +∞] | 裁剪最大边界 |
|
|
|
|
|
|
|
|
|
|
|
|
**stackType格式说明:**
|
|
|
|
|
|
- `1w`: 1层宽排列
|
|
|
|
|
|
- `2w`: 2层宽排列
|
|
|
|
|
|
- `1h`: 1层高排列
|
|
|
|
|
|
- `3h`: 3层高排列
|
|
|
|
|
|
- `2w 3h`: 宽排列2层,高排列3层
|
|
|
|
|
|
|
|
|
|
|
|
**响应示例:**
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"totalBoxCount": 25,
|
|
|
|
|
|
"boxesPerLayer": {
|
|
|
|
|
|
"1": 10,
|
|
|
|
|
|
"2": 8,
|
|
|
|
|
|
"3": 7
|
|
|
|
|
|
},
|
|
|
|
|
|
"pointsPerLayer": {
|
|
|
|
|
|
"1": 15678,
|
|
|
|
|
|
"2": 12450,
|
|
|
|
|
|
"3": 9876
|
|
|
|
|
|
},
|
|
|
|
|
|
"areaPerLayer": {
|
|
|
|
|
|
"1": 280000.0,
|
|
|
|
|
|
"2": 224000.0,
|
|
|
|
|
|
"3": 196000.0
|
|
|
|
|
|
},
|
|
|
|
|
|
"layerCount": 3,
|
|
|
|
|
|
"maxLayer": 3,
|
|
|
|
|
|
"pcdFilePath": "D:/output/20250325/pointCloud/192.168.1.100_pointcloud_20250325_143022.pcd",
|
|
|
|
|
|
"calculationTime": 1234,
|
|
|
|
|
|
"floorHeight": 1000.0,
|
|
|
|
|
|
"boxDimensions": [200.0, 140.0, 250.0],
|
|
|
|
|
|
"stackType": "2w 3h"
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**响应字段说明:**
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| success | Boolean | 是否成功 |
|
|
|
|
|
|
| totalBoxCount | Integer | 总箱子数 |
|
|
|
|
|
|
| boxesPerLayer | Map | 各层箱子数(key: 层数,value: 箱子数) |
|
|
|
|
|
|
| pointsPerLayer | Map | 各层有效点数 |
|
|
|
|
|
|
| areaPerLayer | Map | 各层凸包面积(mm²) |
|
|
|
|
|
|
| layerCount | Integer | 有效层数 |
|
|
|
|
|
|
| maxLayer | Integer | 最高层 |
|
|
|
|
|
|
| pcdFilePath | String | PCD文件路径 |
|
|
|
|
|
|
| calculationTime | Long | 计算用时(毫秒) |
|
|
|
|
|
|
| floorHeight | Double | 地板高度 |
|
|
|
|
|
|
| boxDimensions | Double[] | 箱子尺寸 [长, 宽, 高] |
|
|
|
|
|
|
| stackType | String | 堆叠类型 |
|
|
|
|
|
|
| errorMessage | String | 错误消息(失败时) |
|
|
|
|
|
|
|
|
|
|
|
|
### 2. GET方式计算
|
|
|
|
|
|
|
|
|
|
|
|
**请求URL:**
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /api/boxcount/calculate?pcdFilePath=xxx&floorHeight=xxx&boxLength=xxx&boxWidth=xxx&boxHeight=xxx&stackType=xxx
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**示例:**
|
|
|
|
|
|
```bash
|
|
|
|
|
|
curl "http://localhost:8080/api/boxcount/calculate?pcdFilePath=D:/output/test.pcd&floorHeight=1000&boxLength=200&boxWidth=140&boxHeight=250&stackType=2w"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 获取默认配置
|
|
|
|
|
|
|
|
|
|
|
|
**请求URL:**
|
|
|
|
|
|
```
|
|
|
|
|
|
GET /api/boxcount/default
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**响应示例:**
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"floorHeight": 0.0,
|
|
|
|
|
|
"boxLength": 200.0,
|
|
|
|
|
|
"boxWidth": 140.0,
|
|
|
|
|
|
"boxHeight": 250.0,
|
|
|
|
|
|
"stackType": "1w",
|
|
|
|
|
|
"maxBoxesPerLayer": 10,
|
|
|
|
|
|
"baseTolerance": 50.0,
|
|
|
|
|
|
"additionalTolerancePerLevel": 20.0
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Java调用示例
|
|
|
|
|
|
|
|
|
|
|
|
### 示例1: 基本调用
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
private LxCameraService lxCameraService;
|
|
|
|
|
|
|
|
|
|
|
|
public void calculateBoxes() {
|
|
|
|
|
|
BoxCountRequest request = new BoxCountRequest();
|
|
|
|
|
|
request.setPcdFilePath("D:/output/test.pcd");
|
|
|
|
|
|
request.setFloorHeight(1000.0);
|
|
|
|
|
|
request.setBoxLength(200.0);
|
|
|
|
|
|
request.setBoxWidth(140.0);
|
|
|
|
|
|
request.setBoxHeight(250.0);
|
|
|
|
|
|
request.setStackType("1w");
|
|
|
|
|
|
|
|
|
|
|
|
BoxCountResult result = lxCameraService.countBoxes(request);
|
|
|
|
|
|
|
|
|
|
|
|
if (result.isSuccess()) {
|
|
|
|
|
|
System.out.println("总箱子数: " + result.getTotalBoxCount());
|
|
|
|
|
|
System.out.println("层数: " + result.getLayerCount());
|
|
|
|
|
|
System.out.println("最高层: " + result.getMaxLayer());
|
|
|
|
|
|
System.out.println("用时: " + result.getCalculationTime() + "ms");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
System.out.println("计算失败: " + result.getErrorMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 示例2: 使用默认配置
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
public void calculateBoxesDefault() {
|
|
|
|
|
|
BoxCountRequest request = BoxCountRequest.createDefault();
|
|
|
|
|
|
request.setPcdFilePath("D:/output/test.pcd");
|
|
|
|
|
|
|
|
|
|
|
|
BoxCountResult result = lxCameraService.countBoxes(request);
|
|
|
|
|
|
|
|
|
|
|
|
System.out.println(result.getSummary());
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 示例3: 自定义冗余和边界
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
public void calculateBoxesCustom() {
|
|
|
|
|
|
BoxCountRequest request = BoxCountRequest.createDefault();
|
|
|
|
|
|
request.setPcdFilePath("D:/output/test.pcd");
|
|
|
|
|
|
request.setFloorHeight(1000.0);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置冗余
|
|
|
|
|
|
request.setBaseTolerance(60.0); // 基础冗余60mm
|
|
|
|
|
|
request.setAdditionalTolerancePerLevel(25.0); // 每层增加25mm
|
|
|
|
|
|
|
|
|
|
|
|
// 设置裁剪边界
|
|
|
|
|
|
request.setMinBounds(new double[]{-500.0, -500.0, -200.0});
|
|
|
|
|
|
request.setMaxBounds(new double[]{500.0, 500.0, 100.0});
|
|
|
|
|
|
|
|
|
|
|
|
BoxCountResult result = lxCameraService.countBoxes(request);
|
|
|
|
|
|
|
|
|
|
|
|
if (result.isSuccess()) {
|
|
|
|
|
|
result.getBoxesPerLayer().forEach((layer, count) -> {
|
|
|
|
|
|
int points = result.getPointsPerLayer().get(layer);
|
|
|
|
|
|
double area = result.getAreaPerLayer().get(layer);
|
|
|
|
|
|
System.out.printf("第%d层: %d个箱子, %d个点, 面积%.2f mm²%n",
|
|
|
|
|
|
layer, count, points, area);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 使用场景
|
|
|
|
|
|
|
|
|
|
|
|
### 场景1: 拍照后立即计算
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 1. 拍照
|
|
|
|
|
|
CaptureConfig config = CaptureConfig.createDefault();
|
|
|
|
|
|
CaptureResult captureResult = lxCameraService.capture("192.168.1.100", "D:/output", config);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 计算箱子数量
|
|
|
|
|
|
if (captureResult.isSuccess()) {
|
|
|
|
|
|
String pcdPath = captureResult.getFilePath("pointCloud");
|
|
|
|
|
|
|
|
|
|
|
|
BoxCountRequest request = new BoxCountRequest();
|
|
|
|
|
|
request.setPcdFilePath(pcdPath);
|
|
|
|
|
|
request.setFloorHeight(1000.0);
|
|
|
|
|
|
request.setBoxLength(200.0);
|
|
|
|
|
|
request.setBoxWidth(140.0);
|
|
|
|
|
|
request.setBoxHeight(250.0);
|
|
|
|
|
|
request.setStackType("2w 3h");
|
|
|
|
|
|
|
|
|
|
|
|
BoxCountResult boxResult = lxCameraService.countBoxes(request);
|
|
|
|
|
|
|
|
|
|
|
|
System.out.println("箱子总数: " + boxResult.getTotalBoxCount());
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 场景2: 批量处理历史数据
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
public void processHistoricalData(String directory) {
|
|
|
|
|
|
File dir = new File(directory);
|
|
|
|
|
|
File[] pcdFiles = dir.listFiles((d, name) -> name.endsWith(".pcd"));
|
|
|
|
|
|
|
|
|
|
|
|
for (File pcdFile : pcdFiles) {
|
|
|
|
|
|
BoxCountRequest request = BoxCountRequest.createDefault();
|
|
|
|
|
|
request.setPcdFilePath(pcdFile.getAbsolutePath());
|
|
|
|
|
|
|
|
|
|
|
|
BoxCountResult result = lxCameraService.countBoxes(request);
|
|
|
|
|
|
|
|
|
|
|
|
if (result.isSuccess()) {
|
|
|
|
|
|
System.out.printf("%s: %d个箱子%n",
|
|
|
|
|
|
pcdFile.getName(), result.getTotalBoxCount());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 场景3: 定时任务自动计算
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Component
|
|
|
|
|
|
public class BoxCountScheduler {
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
private LxCameraService lxCameraService;
|
|
|
|
|
|
|
|
|
|
|
|
@Scheduled(fixedRate = 60000) // 每分钟执行一次
|
|
|
|
|
|
public void autoCalculateBoxes() {
|
|
|
|
|
|
// 获取最新的PCD文件
|
|
|
|
|
|
String latestPcd = getLatestPcdFile("D:/output/20250325/pointCloud");
|
|
|
|
|
|
|
|
|
|
|
|
if (latestPcd != null) {
|
|
|
|
|
|
BoxCountRequest request = BoxCountRequest.createDefault();
|
|
|
|
|
|
request.setPcdFilePath(latestPcd);
|
|
|
|
|
|
|
|
|
|
|
|
BoxCountResult result = lxCameraService.countBoxes(request);
|
|
|
|
|
|
|
|
|
|
|
|
// 保存结果到数据库或日志
|
|
|
|
|
|
log.info("自动计算完成: {}", result.getSummary());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 参数调优建议
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 冗余参数
|
|
|
|
|
|
|
|
|
|
|
|
- **基础冗余**: 建议设置为箱子高度的10%-20%(25-50mm)
|
|
|
|
|
|
- **每层增加冗余**: 建议设置为每层2%-8%(5-20mm)
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 边界裁剪
|
|
|
|
|
|
|
|
|
|
|
|
设置合理的边界可以:
|
|
|
|
|
|
- 提高计算速度
|
|
|
|
|
|
- 排除噪声点
|
|
|
|
|
|
- 提高计算准确性
|
|
|
|
|
|
|
|
|
|
|
|
示例:
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 根据相机视野设置边界
|
|
|
|
|
|
request.setMinBounds(new double[]{-600, -600, -300});
|
|
|
|
|
|
request.setMaxBounds(new double[]{600, 600, 200});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 最小点数阈值
|
|
|
|
|
|
|
|
|
|
|
|
默认值:1000点
|
|
|
|
|
|
|
|
|
|
|
|
如果点云密度较低或箱面较小,可以调小此值:
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 在BoxCountCalculator.java中修改
|
|
|
|
|
|
private static final int MIN_POINTS_PER_LAYER = 500; // 调小到500
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 常见问题
|
|
|
|
|
|
|
|
|
|
|
|
### Q1: 计算结果不准确?
|
|
|
|
|
|
|
|
|
|
|
|
**可能原因:**
|
|
|
|
|
|
1. 地板高度设置错误
|
|
|
|
|
|
2. 箱子尺寸与实际不符
|
|
|
|
|
|
3. 冗余参数设置不合理
|
|
|
|
|
|
4. 点云质量差(点数少)
|
|
|
|
|
|
|
|
|
|
|
|
**解决方法:**
|
|
|
|
|
|
1. 检查floorHeight是否正确
|
|
|
|
|
|
2. 测量实际箱子尺寸并更新参数
|
|
|
|
|
|
3. 调整baseTolerance和additionalTolerancePerLevel
|
|
|
|
|
|
4. 提高点云拍摄质量
|
|
|
|
|
|
|
|
|
|
|
|
### Q2: 某层没有被识别?
|
|
|
|
|
|
|
|
|
|
|
|
**可能原因:**
|
|
|
|
|
|
1. 该层点数少于阈值(默认1000)
|
|
|
|
|
|
2. 高度计算偏差超出冗余范围
|
|
|
|
|
|
|
|
|
|
|
|
**解决方法:**
|
|
|
|
|
|
1. 降低最小点数阈值
|
|
|
|
|
|
2. 增加冗余参数
|
|
|
|
|
|
3. 检查该层点云质量
|
|
|
|
|
|
|
|
|
|
|
|
### Q3: 计算速度慢?
|
|
|
|
|
|
|
|
|
|
|
|
**优化方法:**
|
|
|
|
|
|
1. 使用边界裁剪减少点数
|
|
|
|
|
|
2. 提高PCD文件读取效率
|
|
|
|
|
|
3. 优化凸包计算算法
|
|
|
|
|
|
|
|
|
|
|
|
### Q4: 如何处理多个堆叠区域?
|
|
|
|
|
|
|
|
|
|
|
|
**方法1:** 设置多个边界,分别计算
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 区域1
|
|
|
|
|
|
request.setMinBounds(new double[]{-600, -600, -300});
|
|
|
|
|
|
request.setMaxBounds(new double[]{0, 600, 200});
|
|
|
|
|
|
BoxCountResult result1 = lxCameraService.countBoxes(request);
|
|
|
|
|
|
|
|
|
|
|
|
// 区域2
|
|
|
|
|
|
request.setMinBounds(new double[]{0, -600, -300});
|
|
|
|
|
|
request.setMaxBounds(new double[]{600, 600, 200});
|
|
|
|
|
|
BoxCountResult result2 = lxCameraService.countBoxes(request);
|
|
|
|
|
|
|
|
|
|
|
|
// 总计
|
|
|
|
|
|
int total = result1.getTotalBoxCount() + result2.getTotalBoxCount();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**方法2:** 修改代码支持多区域计算
|
|
|
|
|
|
|
|
|
|
|
|
## 注意事项
|
|
|
|
|
|
|
|
|
|
|
|
1. **PCD文件格式**: 确保PCD文件包含XYZ坐标
|
|
|
|
|
|
2. **单位统一**: 所有参数使用毫米(mm)作为单位
|
|
|
|
|
|
3. **路径格式**: Windows路径使用正斜杠或双反斜杠
|
|
|
|
|
|
4. **内存管理**: 处理大文件时注意内存使用
|
|
|
|
|
|
5. **并发安全**: 本实现是线程安全的,可以并发调用
|
|
|
|
|
|
|
|
|
|
|
|
## 性能指标
|
|
|
|
|
|
|
|
|
|
|
|
基于测试数据:
|
|
|
|
|
|
|
|
|
|
|
|
| PCD文件大小 | 点云点数 | 计算用时 |
|
|
|
|
|
|
|------------|---------|---------|
|
|
|
|
|
|
| ~10MB | ~100,000 | ~500ms |
|
|
|
|
|
|
| ~20MB | ~200,000 | ~1000ms |
|
|
|
|
|
|
| ~30MB | ~300,000 | ~1500ms |
|
|
|
|
|
|
|
|
|
|
|
|
## 相关文档
|
|
|
|
|
|
|
|
|
|
|
|
- `PATH_STRUCTURE.md` - 文件保存路径说明
|
|
|
|
|
|
- `LX_CAMERA_USAGE.md` - 相机服务使用说明
|