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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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