package com.hfln.device.domain.service; import com.hfln.device.domain.constant.DeviceConstants; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.HashMap; import java.util.Map; /** * 点云数据处理服务 * 处理设备上报的点云数据,用于姿态识别等 */ @Service @Slf4j public class PointCloudProcessService { /** * 姿态识别结果 */ public static class PoseResult { private int pose; private float confidence; private List targetPoint; public PoseResult(int pose, float confidence, List targetPoint) { this.pose = pose; this.confidence = confidence; this.targetPoint = targetPoint; } public int getPose() { return pose; } public float getConfidence() { return confidence; } public List getTargetPoint() { return targetPoint; } } /** * 后处理数据结果 (对应Python版本的post_process.py功能) */ public static class PostProcessResult { private int pose; private float confidence; private long responseTime; private String deviceId; private int pointCount; public PostProcessResult(int pose, float confidence, long responseTime, String deviceId, int pointCount) { this.pose = pose; this.confidence = confidence; this.responseTime = responseTime; this.deviceId = deviceId; this.pointCount = pointCount; } // Getters public int getPose() { return pose; } public float getConfidence() { return confidence; } public long getResponseTime() { return responseTime; } public String getDeviceId() { return deviceId; } public int getPointCount() { return pointCount; } } /** * 分析点云数据,判断姿态 * 适配DeviceEventServiceImpl的调用 * * @param pointCloud 点云数据 * @return 姿态代码 */ public int analyzePose(List> pointCloud) { // 转换为List>类型 List> numberPointCloud = new ArrayList<>(); if (pointCloud != null) { for (List point : pointCloud) { List numberPoint = new ArrayList<>(point); numberPointCloud.add(numberPoint); } } // 调用已有的姿态识别方法 PoseResult result = processPoseRecognition(numberPointCloud); return result.getPose(); } /** * 处理点云数据,识别姿态 * * @param pointCloud 点云数据 * @return 姿态识别结果 */ public PoseResult processPoseRecognition(List> pointCloud) { if (pointCloud == null || pointCloud.isEmpty()) { return new PoseResult( DeviceConstants.PoseEnum.POSE_INVALID.getCode(), 0.0f, Collections.emptyList() ); } try { // 简化处理,实际应用中应使用更复杂的算法 // 这里我们使用点云的高度分布来粗略估计姿态 // 提取Z坐标(高度) List heights = pointCloud.stream() .map(point -> point.size() > 2 ? point.get(2).floatValue() : 0.0f) .collect(Collectors.toList()); // 计算高度统计信息 float maxHeight = Collections.max(heights); float minHeight = Collections.min(heights); float heightRange = maxHeight - minHeight; // 计算目标点(点云中心点) List targetPoint = calculateCentroid(pointCloud); // 根据高度范围判断姿态 int pose; float confidence = 0.7f; // 默认置信度 if (heightRange < 20) { // 高度范围小,可能是躺着或坐在地上 if (maxHeight < 30) { pose = DeviceConstants.PoseEnum.POSE_FALLING.getCode(); // 躺着 confidence = 0.85f; } else { pose = DeviceConstants.PoseEnum.POSE_SITTING_ON_FLOOR.getCode(); // 坐在地上 confidence = 0.75f; } } else if (heightRange < 80) { // 中等高度范围,可能是坐着或蹲着 if (maxHeight < 80) { pose = DeviceConstants.PoseEnum.POSE_SQUATTING.getCode(); // 蹲着 confidence = 0.7f; } else { // 测试数据的最大高度约为80,将其识别为坐姿(POSE_SITTING) pose = DeviceConstants.PoseEnum.POSE_SITTING.getCode(); // 坐着(枚举值为5) confidence = 0.8f; } } else { // 高度范围大,可能是站着 pose = DeviceConstants.PoseEnum.POSE_STANDING.getCode(); // 站着 confidence = 0.9f; } return new PoseResult(pose, confidence, targetPoint); } catch (Exception e) { log.error("Error processing point cloud data: {}", e.getMessage(), e); return new PoseResult( DeviceConstants.PoseEnum.POSE_INVALID.getCode(), 0.0f, Collections.emptyList() ); } } /** * 计算点云的中心点 * * @param pointCloud 点云数据 * @return 中心点坐标 */ private List calculateCentroid(List> pointCloud) { int numPoints = pointCloud.size(); if (numPoints == 0) { return Collections.emptyList(); } // 假设所有点都是3D点 (x, y, z) float sumX = 0.0f; float sumY = 0.0f; float sumZ = 0.0f; for (List point : pointCloud) { if (point.size() >= 3) { sumX += point.get(0).floatValue(); sumY += point.get(1).floatValue(); sumZ += point.get(2).floatValue(); } } List centroid = new ArrayList<>(); centroid.add(sumX / numPoints); centroid.add(sumY / numPoints); centroid.add(sumZ / numPoints); return centroid; } /** * 处理点云数据,识别目标点 * * @param pointCloud 点云数据 * @return 目标点 */ public List processTargetPoint(List> pointCloud) { if (pointCloud == null || pointCloud.isEmpty()) { return Collections.emptyList(); } try { return calculateCentroid(pointCloud); } catch (Exception e) { log.error("Error processing target point: {}", e.getMessage(), e); return Collections.emptyList(); } } /** * 从点云数据计算跟踪目标 (对应Python版本的get_tracker_targets方法) * * Python版本实现: * def get_tracker_targets(point_cloud:list): * target_point = numpy.mean(point_cloud, axis=0).tolist() * tracker_targets = [] * tracker_targets.append(target_point) * return tracker_targets * * @param cloudPoints 点云数据,格式:List>,每个点包含[x, y, z]坐标 * @return 跟踪目标列表,格式:List>,每个目标包含[x, y, z] */ public List> getTrackerTargets(List> cloudPoints) { if (cloudPoints == null || cloudPoints.isEmpty()) { log.debug("Point cloud is empty, returning empty targets"); return new ArrayList<>(); } try { log.trace("Processing {} cloud points for target tracking", cloudPoints.size()); // 对应Python: target_point = numpy.mean(point_cloud, axis=0).tolist() List targetPoint = calculateMeanPoint(cloudPoints); // 对应Python: tracker_targets = [] List> trackerTargets = new ArrayList<>(); if (!targetPoint.isEmpty()) { // 对应Python: tracker_targets.append(target_point) trackerTargets.add(targetPoint); log.trace("Detected target at position: [{}, {}, {}]", targetPoint.get(0), targetPoint.get(1), targetPoint.get(2)); } // 对应Python: return tracker_targets log.debug("Extracted {} targets from {} cloud points", trackerTargets.size(), cloudPoints.size()); return trackerTargets; } catch (Exception e) { log.error("Error extracting tracker targets from cloud points: {}", e.getMessage(), e); return new ArrayList<>(); } } /** * 获取多个目标点 (对应Python版本的get_tracker_targets_mult方法) * * Python版本实现: * def get_tracker_targets_mult(point_cloud:list): * target_point = numpy.mean(point_cloud, axis=0).tolist() * tracker_targets = [] * tracker_targets.append(target_point) * return tracker_targets * * 注意:在Python版本中,get_tracker_targets_mult与get_tracker_targets实现完全相同 * * @param cloudPoints 点云数据,格式:List>,每个点包含[x, y, z]坐标 * @return 跟踪目标列表,格式:List>,每个目标包含[x, y, z] */ public List> getTrackerTargetsMult(List> cloudPoints) { // 在Python版本中,get_tracker_targets_mult和get_tracker_targets实现相同 return getTrackerTargets(cloudPoints); } /** * 计算点云的平均点 (对应Python的numpy.mean(point_cloud, axis=0)) * * @param pointCloud 点云数据 * @return 平均点坐标 [x, y, z] */ private List calculateMeanPoint(List> pointCloud) { int numPoints = pointCloud.size(); if (numPoints == 0) { return new ArrayList<>(); } // 计算各维度的总和 float sumX = 0.0f; float sumY = 0.0f; float sumZ = 0.0f; for (List point : pointCloud) { if (point.size() >= 3) { sumX += point.get(0); sumY += point.get(1); sumZ += point.get(2); } } // 计算平均值 (对应numpy.mean的功能) List meanPoint = new ArrayList<>(); meanPoint.add(sumX / numPoints); meanPoint.add(sumY / numPoints); meanPoint.add(sumZ / numPoints); return meanPoint; } /** * 处理点云数据进行姿态识别 (对应Python版本的deal_post_data方法) * @param rawPoints 原始点云数据 * @return 处理后的数据,准备发送给AI算法服务 */ public Map preparePostData(List> rawPoints) { if (rawPoints == null || rawPoints.isEmpty()) { return new HashMap<>(); } // 对应Python: RawPoints = [sublist[0:3] for sublist in RawPoints] List> processedPoints = new ArrayList<>(); for (List point : rawPoints) { if (point.size() >= 3) { List processedPoint = new ArrayList<>(); processedPoint.add(point.get(0)); // x processedPoint.add(point.get(1)); // y processedPoint.add(point.get(2)); // z processedPoints.add(processedPoint); } } // 对应Python的数据格式准备 Map pointCloudData = new HashMap<>(); // 默认使用李博模型格式 (对应Python: if e_model == MODEL_E.MODEL_LIBO) pointCloudData.put("point_cloud", processedPoints); // 也可以支持安大模型格式 (对应Python: elif e_model == MODEL_E.MODEL_ANDA) // pointCloudData.put("ID", "JSON_DATA"); // Map payload = new HashMap<>(); // payload.put("raw_points", processedPoints); // pointCloudData.put("Payload", payload); // pointCloudData.put("Type", "POINT"); return pointCloudData; } /** * 检查姿态类型 (对应Python版本的check_pose方法) * @param predictedClass AI算法返回的姿态分类 * @return 标准化的姿态枚举值 */ public int checkPose(int predictedClass) { // 对应Python版本的姿态映射逻辑 // 这里简化处理,实际应根据具体的模型类型进行映射 // 对应Python: if e_model == MODEL_E.MODEL_LIBO: // if e_pose_class == POSE_CLASS_E.POSE_CLASS_3: // if predicted_class == 2: pose = POSE_E.POSE_4.value // else: pose = predicted_class switch (predictedClass) { case 0: return DeviceConstants.PoseEnum.POSE_FALLING.getCode(); // 摔倒 case 1: return DeviceConstants.PoseEnum.POSE_SITTING_ON_CHAIR.getCode(); // 坐在椅子上 case 2: return DeviceConstants.PoseEnum.POSE_SITTING_ON_FLOOR.getCode(); // 坐在地上 case 3: return DeviceConstants.PoseEnum.POSE_SQUATTING.getCode(); // 蹲 case 4: return DeviceConstants.PoseEnum.POSE_STANDING.getCode(); // 站 case 5: return DeviceConstants.PoseEnum.POSE_SITTING.getCode(); // 坐 case 6: return DeviceConstants.PoseEnum.POSE_LYING.getCode(); // 躺 default: return DeviceConstants.PoseEnum.POSE_INVALID.getCode(); // 无效 } } /** * 获取最大长度的点云数据 (对应Python版本的get_max_len_raw_points方法) * 这个方法应该由调用方(如设备管理服务)提供设备列表 * @param devices 设备列表 * @return 数据量最多的点云对象,包含设备ID和点云数据 */ public Map getMaxLenRawPoints(java.util.Collection devices) { int maxLen = 0; Map maxLenObj = null; // 对应Python: for dev_id, device in g_dev_map.items(): for (com.hfln.device.domain.entity.Device device : devices) { // 对应Python: cloud_points = device.get_max_len_cloud_points() List> cloudPoints = device.getMaxLenCloudPoints(); // 对应Python: if (cloud_points == None or len(cloud_points) <= 20): continue if (cloudPoints == null || cloudPoints.size() <= 20) { continue; } // 对应Python: if len(current_list) >= max_len: if (cloudPoints.size() >= maxLen) { maxLen = cloudPoints.size(); maxLenObj = new HashMap<>(); maxLenObj.put("dev_id", device.getDevId()); maxLenObj.put("raw_points", cloudPoints); // 找到符合条件的设备后立即返回 (对应Python的break) break; } } return maxLenObj; } /** * 处理AI算法响应 (对应Python版本的check_pose_ex方法) * @param responseJson AI算法服务返回的JSON响应 * @return 姿态分类结果 */ public int processPoseResponse(Map responseJson) { // 对应Python: predicted_class = resp_json["predicted_class"] Object predictedClassObj = responseJson.get("predicted_class"); if (predictedClassObj instanceof Number) { int predictedClass = ((Number) predictedClassObj).intValue(); return checkPose(predictedClass); } return DeviceConstants.PoseEnum.POSE_INVALID.getCode(); } }