You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lxCameraApi/BOX_COUNT_USAGE.md

431 lines
11 KiB
Markdown

2 weeks ago
# 箱子数量计算接口使用说明
## 概述
本系统提供了基于点云数据的箱子数量计算接口可以根据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` - 相机服务使用说明