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.
11 KiB
11 KiB
箱子数量计算接口使用说明
概述
本系统提供了基于点云数据的箱子数量计算接口,可以根据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
请求体:
{
"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层
响应示例:
{
"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
示例:
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
响应示例:
{
"floorHeight": 0.0,
"boxLength": 200.0,
"boxWidth": 140.0,
"boxHeight": 250.0,
"stackType": "1w",
"maxBoxesPerLayer": 10,
"baseTolerance": 50.0,
"additionalTolerancePerLevel": 20.0
}
Java调用示例
示例1: 基本调用
@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: 使用默认配置
public void calculateBoxesDefault() {
BoxCountRequest request = BoxCountRequest.createDefault();
request.setPcdFilePath("D:/output/test.pcd");
BoxCountResult result = lxCameraService.countBoxes(request);
System.out.println(result.getSummary());
}
示例3: 自定义冗余和边界
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: 拍照后立即计算
// 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: 批量处理历史数据
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: 定时任务自动计算
@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. 边界裁剪
设置合理的边界可以:
- 提高计算速度
- 排除噪声点
- 提高计算准确性
示例:
// 根据相机视野设置边界
request.setMinBounds(new double[]{-600, -600, -300});
request.setMaxBounds(new double[]{600, 600, 200});
3. 最小点数阈值
默认值:1000点
如果点云密度较低或箱面较小,可以调小此值:
// 在BoxCountCalculator.java中修改
private static final int MIN_POINTS_PER_LAYER = 500; // 调小到500
常见问题
Q1: 计算结果不准确?
可能原因:
- 地板高度设置错误
- 箱子尺寸与实际不符
- 冗余参数设置不合理
- 点云质量差(点数少)
解决方法:
- 检查floorHeight是否正确
- 测量实际箱子尺寸并更新参数
- 调整baseTolerance和additionalTolerancePerLevel
- 提高点云拍摄质量
Q2: 某层没有被识别?
可能原因:
- 该层点数少于阈值(默认1000)
- 高度计算偏差超出冗余范围
解决方法:
- 降低最小点数阈值
- 增加冗余参数
- 检查该层点云质量
Q3: 计算速度慢?
优化方法:
- 使用边界裁剪减少点数
- 提高PCD文件读取效率
- 优化凸包计算算法
Q4: 如何处理多个堆叠区域?
方法1: 设置多个边界,分别计算
// 区域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: 修改代码支持多区域计算
注意事项
- PCD文件格式: 确保PCD文件包含XYZ坐标
- 单位统一: 所有参数使用毫米(mm)作为单位
- 路径格式: Windows路径使用正斜杠或双反斜杠
- 内存管理: 处理大文件时注意内存使用
- 并发安全: 本实现是线程安全的,可以并发调用
性能指标
基于测试数据:
| PCD文件大小 | 点云点数 | 计算用时 |
|---|---|---|
| ~10MB | ~100,000 | ~500ms |
| ~20MB | ~200,000 | ~1000ms |
| ~30MB | ~300,000 | ~1500ms |
相关文档
PATH_STRUCTURE.md- 文件保存路径说明LX_CAMERA_USAGE.md- 相机服务使用说明