refactor(arm): 重构机械臂服务实现

- 将 HumanoidRobotService 重命名为 ArmService
- 更新 EdgeTouchOperateService 中的服务依赖注入
- 修改 FlowiseActionService 中的机械臂服务引用
- 更新 GrpcServiceManager 中的客户端工厂注册
- 在 ActionEnum 中添加 ARM_MOVE_TO_POINT 动作类型
- 替换 EdgeMoveJVO 为 EdgeArmMoveJVO 模型类
- 更新触控操作中的机械臂移动方法调用参数
This commit is contained in:
lixiaolong 2026-07-01 11:32:54 +08:00
parent 2d338d259b
commit ac35fd229b
24 changed files with 29094 additions and 224 deletions

View File

@ -0,0 +1,222 @@
package com.cmvr.web.controller.api;
import cmvr.api.ArmCommand;
import cmvr.api.Common;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.model.arm.*;
import com.cmvr.edge.client.service.EdgeArmService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 边缘系统机械臂控制器
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Api(tags = "边缘--机械臂")
@RestController
@RequestMapping("/api/arm")
@RequiredArgsConstructor
public class EdgeArmController {
private final EdgeArmService edgeArmService;
@ApiOperation("关闭力矩")
@GetMapping("/torqueOff")
public AjaxResult torqueOff(EdgeCommonVO vo) {
Common.CommandHeader.Feedback result = edgeArmService.torqueOff(vo);
return AjaxResult.ok();
}
@ApiOperation("开启力矩")
@GetMapping("/torqueOn")
public AjaxResult torqueOn(EdgeCommonVO vo) {
Common.CommandHeader.Feedback result = edgeArmService.torqueOn(vo);
return AjaxResult.ok();
}
@ApiOperation("关节空间运动")
@PostMapping("/moveJ")
public AjaxResult moveJ(@RequestBody EdgeArmMoveJVO moveJVO) {
edgeArmService.moveJ(
moveJVO,
moveJVO.getTarget(),
moveJVO.getVelocity(),
moveJVO.getAcceleration(),
moveJVO.getBlendRadius(),
moveJVO.getJointVelocityLimits(),
moveJVO.getAsynchronous()
);
return AjaxResult.ok();
}
@ApiOperation("笛卡尔空间直线运动")
@PostMapping("/moveL")
public AjaxResult moveL(@RequestBody EdgeArmMoveLVO moveLVO) {
edgeArmService.moveL(
moveLVO,
moveLVO.getX(),
moveLVO.getY(),
moveLVO.getZ(),
moveLVO.getRx(),
moveLVO.getRy(),
moveLVO.getRz(),
moveLVO.getFrame(),
moveLVO.getVelocity(),
moveLVO.getAcceleration(),
moveLVO.getBlendRadius()
);
return AjaxResult.ok();
}
@ApiOperation("关节速度控制")
@PostMapping("/speedJ")
public AjaxResult speedJ(@RequestBody EdgeArmSpeedJVO speedJVO) {
edgeArmService.speedJ(
speedJVO,
speedJVO.getVelocities(),
speedJVO.getAcceleration(),
speedJVO.getDuration()
);
return AjaxResult.ok();
}
@ApiOperation("笛卡尔速度控制")
@PostMapping("/speedL")
public AjaxResult speedL(@RequestBody EdgeArmSpeedLVO speedLVO) {
edgeArmService.speedL(
speedLVO,
speedLVO.getVx(),
speedLVO.getVy(),
speedLVO.getVz(),
speedLVO.getWx(),
speedLVO.getWy(),
speedLVO.getWz(),
speedLVO.getFrame(),
speedLVO.getAcceleration(),
speedLVO.getDuration()
);
return AjaxResult.ok();
}
@ApiOperation("伺服关节控制")
@PostMapping("/servoJ")
public AjaxResult servoJ(@RequestBody EdgeArmServoJVO servoJVO) {
edgeArmService.servoJ(servoJVO, servoJVO.getTarget());
return AjaxResult.ok();
}
@ApiOperation("停止运动")
@GetMapping("/stopMotion")
public AjaxResult stopMotion(EdgeCommonVO vo) {
edgeArmService.stopMotion(vo);
return AjaxResult.ok();
}
@ApiOperation("获取关节状态")
@GetMapping("/getJointState")
public AjaxResult getJointState(EdgeCommonVO vo) {
ArmCommand.JointResponse result = edgeArmService.getJointState(vo);
ArmCommand.JointState state = result.getState();
// 转换为VO
EdgeArmJointStateVO jointStateVO = new EdgeArmJointStateVO();
jointStateVO.setName(state.getNameList());
jointStateVO.setPosition(state.getPositionList());
jointStateVO.setVelocity(state.getVelocityList());
jointStateVO.setEffort(state.getEffortList());
jointStateVO.setTimestamp(state.getTimestamp());
return AjaxResult.ok(jointStateVO);
}
@ApiOperation("获取末端位姿")
@GetMapping("/getPose")
public AjaxResult getPose(EdgeCommonVO vo, String baseLink, String eeLink) {
ArmCommand.GetPose.Response result = edgeArmService.getPose(vo, baseLink, eeLink);
ArmCommand.CartesianPose pose = result.getPose();
// 转换为VO
EdgeArmCartesianPoseVO poseVO = new EdgeArmCartesianPoseVO();
poseVO.setX(pose.getX());
poseVO.setY(pose.getY());
poseVO.setZ(pose.getZ());
poseVO.setRx(pose.getRx());
poseVO.setRy(pose.getRy());
poseVO.setRz(pose.getRz());
return AjaxResult.ok(poseVO);
}
@ApiOperation("标定零点")
@GetMapping("/calibrateZeroQ")
public AjaxResult calibrateZeroQ(EdgeCommonVO vo, String jointName) {
edgeArmService.calibrateZeroQ(vo, jointName);
return AjaxResult.ok();
}
@ApiOperation("获取位姿矩阵")
@GetMapping("/getPoseMatrix")
public AjaxResult getPoseMatrix(EdgeCommonVO vo, String baseLink, String eeLink) {
ArmCommand.GetPoseMatrix.Response result = edgeArmService.getPoseMatrix(vo, baseLink, eeLink);
// 将TransformMatrix4x4转换为Map
ArmCommand.TransformMatrix4x4 matrix = result.getMatrix();
java.util.Map<String, Object> matrixMap = new java.util.HashMap<>();
matrixMap.put("m00", matrix.getM00());
matrixMap.put("m01", matrix.getM01());
matrixMap.put("m02", matrix.getM02());
matrixMap.put("m03", matrix.getM03());
matrixMap.put("m10", matrix.getM10());
matrixMap.put("m11", matrix.getM11());
matrixMap.put("m12", matrix.getM12());
matrixMap.put("m13", matrix.getM13());
matrixMap.put("m20", matrix.getM20());
matrixMap.put("m21", matrix.getM21());
matrixMap.put("m22", matrix.getM22());
matrixMap.put("m23", matrix.getM23());
matrixMap.put("m30", matrix.getM30());
matrixMap.put("m31", matrix.getM31());
matrixMap.put("m32", matrix.getM32());
matrixMap.put("m33", matrix.getM33());
return AjaxResult.ok(matrixMap);
}
@ApiOperation("计算正向运动学")
@PostMapping("/computeForwardKinematics")
public AjaxResult computeForwardKinematics(@RequestBody EdgeArmForwardKinematicsVO fkVO) {
ArmCommand.ComputeForwardKinematics.Response result = edgeArmService.computeForwardKinematics(
fkVO,
fkVO.getJoints(),
fkVO.getBaseLink(),
fkVO.getEeLink()
);
// 将TransformMatrix4x4转换为Map
ArmCommand.TransformMatrix4x4 matrix = result.getMatrix();
java.util.Map<String, Object> matrixMap = new java.util.HashMap<>();
matrixMap.put("m00", matrix.getM00());
matrixMap.put("m01", matrix.getM01());
matrixMap.put("m02", matrix.getM02());
matrixMap.put("m03", matrix.getM03());
matrixMap.put("m10", matrix.getM10());
matrixMap.put("m11", matrix.getM11());
matrixMap.put("m12", matrix.getM12());
matrixMap.put("m13", matrix.getM13());
matrixMap.put("m20", matrix.getM20());
matrixMap.put("m21", matrix.getM21());
matrixMap.put("m22", matrix.getM22());
matrixMap.put("m23", matrix.getM23());
matrixMap.put("m30", matrix.getM30());
matrixMap.put("m31", matrix.getM31());
matrixMap.put("m32", matrix.getM32());
matrixMap.put("m33", matrix.getM33());
return AjaxResult.ok(matrixMap);
}
}

View File

@ -1,55 +0,0 @@
package com.cmvr.web.controller.api;
import com.cmvr.common.core.domain.AjaxResult;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.model.humanoid.EdgeMoveJVO;
import com.cmvr.edge.client.model.humanoid.EdgeSpeedJVO;
import com.cmvr.edge.client.service.EdgeHumanoidRobotService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "边缘--机械臂")
@RestController
@RequestMapping("/api/humanoid")
@RequiredArgsConstructor
public class EdgeHumanoidRobotController {
private final EdgeHumanoidRobotService edgeHumanoidRobotService;
@ApiOperation("moveJ")
@PostMapping("/moveJ")
public AjaxResult moveJ(@RequestBody EdgeMoveJVO moveJVO) {
return AjaxResult.ok(edgeHumanoidRobotService.moveJ(moveJVO));
}
@ApiOperation("speedJ")
@PostMapping("/speedJ")
public AjaxResult speedJ(@RequestBody EdgeSpeedJVO speedJVO) {
return AjaxResult.ok(edgeHumanoidRobotService.speedJ(speedJVO));
}
@ApiOperation("获取关节状态")
@GetMapping("/getJointState")
public AjaxResult status(EdgeCommonVO vo) {
return AjaxResult.ok(edgeHumanoidRobotService.getJointState(vo));
}
@ApiOperation("使能开")
@GetMapping("/torqueOn")
public AjaxResult torqueOn(EdgeCommonVO vo) {
return AjaxResult.ok(edgeHumanoidRobotService.torqueOn(vo));
}
@ApiOperation("使能关")
@GetMapping("/torqueOff")
public AjaxResult torqueOff(EdgeCommonVO vo) {
return AjaxResult.ok(edgeHumanoidRobotService.torqueOff(vo));
}
}

View File

@ -55,7 +55,7 @@ public class GrpcServiceManager {
clientFactories.put(DexHandServiceGrpc.DexHandServiceBlockingStub.class, new GrpcClientFactory<>(DexHandServiceGrpc::newBlockingStub)); clientFactories.put(DexHandServiceGrpc.DexHandServiceBlockingStub.class, new GrpcClientFactory<>(DexHandServiceGrpc::newBlockingStub));
clientFactories.put(DexHandServiceGrpc.DexHandServiceStub.class, new GrpcClientFactory<>(DexHandServiceGrpc::newStub)); clientFactories.put(DexHandServiceGrpc.DexHandServiceStub.class, new GrpcClientFactory<>(DexHandServiceGrpc::newStub));
// 注册机械臂服务的stub // 注册机械臂服务的stub
clientFactories.put(HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub.class, new GrpcClientFactory<>(HumanoidRobotServiceGrpc::newBlockingStub)); clientFactories.put(ArmServiceGrpc.ArmServiceBlockingStub.class, new GrpcClientFactory<>(ArmServiceGrpc::newBlockingStub));
clientFactories.put(HlcServiceGrpc.HlcServiceBlockingStub.class, new GrpcClientFactory<>(HlcServiceGrpc::newBlockingStub)); clientFactories.put(HlcServiceGrpc.HlcServiceBlockingStub.class, new GrpcClientFactory<>(HlcServiceGrpc::newBlockingStub));
// 注册agv服务的stub // 注册agv服务的stub
clientFactories.put(AgvServiceGrpc.AgvServiceBlockingStub.class, new GrpcClientFactory<>(AgvServiceGrpc::newBlockingStub)); clientFactories.put(AgvServiceGrpc.AgvServiceBlockingStub.class, new GrpcClientFactory<>(AgvServiceGrpc::newBlockingStub));

View File

@ -0,0 +1,34 @@
package com.cmvr.edge.client.model.arm;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 机械臂笛卡尔位姿VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@ApiModel("机械臂笛卡尔位姿")
public class EdgeArmCartesianPoseVO {
@ApiModelProperty("X轴坐标单位")
private double x;
@ApiModelProperty("Y轴坐标单位")
private double y;
@ApiModelProperty("Z轴坐标单位")
private double z;
@ApiModelProperty("RX旋转角度单位弧度")
private double rx;
@ApiModelProperty("RY旋转角度单位弧度")
private double ry;
@ApiModelProperty("RZ旋转角度单位弧度")
private double rz;
}

View File

@ -0,0 +1,28 @@
package com.cmvr.edge.client.model.arm;
import com.cmvr.edge.client.model.EdgeCommonVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 机械臂正向运动学计算VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("机械臂正向运动学计算参数")
public class EdgeArmForwardKinematicsVO extends EdgeCommonVO {
@ApiModelProperty(value = "关节位置数组6个关节角度单位弧度", required = true)
private double[] joints;
@ApiModelProperty("基座连杆名称")
private String baseLink;
@ApiModelProperty("末端执行器连杆名称")
private String eeLink;
}

View File

@ -0,0 +1,33 @@
package com.cmvr.edge.client.model.arm;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 机械臂关节状态VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@ApiModel("机械臂关节状态")
public class EdgeArmJointStateVO {
@ApiModelProperty("关节名称列表")
private List<String> name;
@ApiModelProperty("关节位置列表(单位:弧度)")
private List<Double> position;
@ApiModelProperty("关节速度列表单位rad/s")
private List<Double> velocity;
@ApiModelProperty("关节力矩列表")
private List<Double> effort;
@ApiModelProperty("时间戳")
private double timestamp;
}

View File

@ -0,0 +1,37 @@
package com.cmvr.edge.client.model.arm;
import com.cmvr.edge.client.model.EdgeCommonVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 机械臂关节空间运动VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("机械臂关节空间运动参数")
public class EdgeArmMoveJVO extends EdgeCommonVO {
@ApiModelProperty(value = "目标关节位置数组6个关节角度单位弧度", required = true)
private double[] target;
@ApiModelProperty("运动速度单位m/s")
private Double velocity;
@ApiModelProperty("加速度单位m/s²")
private Double acceleration;
@ApiModelProperty("融合半径单位m")
private Double blendRadius;
@ApiModelProperty("关节速度限制数组")
private double[] jointVelocityLimits;
@ApiModelProperty("是否异步执行")
private Boolean asynchronous;
}

View File

@ -0,0 +1,49 @@
package com.cmvr.edge.client.model.arm;
import com.cmvr.edge.client.model.EdgeCommonVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 机械臂笛卡尔空间直线运动VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("机械臂笛卡尔空间直线运动参数")
public class EdgeArmMoveLVO extends EdgeCommonVO {
@ApiModelProperty(value = "X轴坐标单位", required = true)
private double x;
@ApiModelProperty(value = "Y轴坐标单位", required = true)
private double y;
@ApiModelProperty(value = "Z轴坐标单位", required = true)
private double z;
@ApiModelProperty(value = "RX旋转角度单位弧度", required = true)
private double rx;
@ApiModelProperty(value = "RY旋转角度单位弧度", required = true)
private double ry;
@ApiModelProperty(value = "RZ旋转角度单位弧度", required = true)
private double rz;
@ApiModelProperty("坐标系类型BASE/TOOL/WORLD/USER")
private String frame;
@ApiModelProperty("运动速度单位m/s")
private Double velocity;
@ApiModelProperty("加速度单位m/s²")
private Double acceleration;
@ApiModelProperty("融合半径单位m")
private Double blendRadius;
}

View File

@ -0,0 +1,49 @@
package com.cmvr.edge.client.model.arm;
import com.cmvr.edge.client.model.EdgeCommonVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 机械臂末端运动到指定点VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("机械臂末端运动到指定点参数")
public class EdgeArmMoveToPointVO extends EdgeCommonVO {
@ApiModelProperty(value = "X轴坐标单位", required = true)
private double x;
@ApiModelProperty(value = "Y轴坐标单位", required = true)
private double y;
@ApiModelProperty(value = "Z轴坐标单位", required = true)
private double z;
@ApiModelProperty(value = "RX旋转角度单位弧度", required = true)
private double rx;
@ApiModelProperty(value = "RY旋转角度单位弧度", required = true)
private double ry;
@ApiModelProperty(value = "RZ旋转角度单位弧度", required = true)
private double rz;
@ApiModelProperty("坐标系类型BASE/TOOL/WORLD/USER")
private String frame;
@ApiModelProperty("运动速度单位m/s")
private Double velocity;
@ApiModelProperty("加速度单位m/s²")
private Double acceleration;
@ApiModelProperty("融合半径单位m")
private Double blendRadius;
}

View File

@ -0,0 +1,22 @@
package com.cmvr.edge.client.model.arm;
import com.cmvr.edge.client.model.EdgeCommonVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 机械臂伺服关节控制VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("机械臂伺服关节控制参数")
public class EdgeArmServoJVO extends EdgeCommonVO {
@ApiModelProperty(value = "目标关节位置数组6个关节角度单位弧度", required = true)
private double[] target;
}

View File

@ -0,0 +1,28 @@
package com.cmvr.edge.client.model.arm;
import com.cmvr.edge.client.model.EdgeCommonVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 机械臂关节速度控制VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("机械臂关节速度控制参数")
public class EdgeArmSpeedJVO extends EdgeCommonVO {
@ApiModelProperty(value = "关节速度数组6个关节速度单位rad/s", required = true)
private double[] velocities;
@ApiModelProperty("加速度单位rad/s²")
private Double acceleration;
@ApiModelProperty("持续时间(单位:秒)")
private Double duration;
}

View File

@ -0,0 +1,46 @@
package com.cmvr.edge.client.model.arm;
import com.cmvr.edge.client.model.EdgeCommonVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 机械臂笛卡尔速度控制VO
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("机械臂笛卡尔速度控制参数")
public class EdgeArmSpeedLVO extends EdgeCommonVO {
@ApiModelProperty(value = "X轴速度单位m/s", required = true)
private double vx;
@ApiModelProperty(value = "Y轴速度单位m/s", required = true)
private double vy;
@ApiModelProperty(value = "Z轴速度单位m/s", required = true)
private double vz;
@ApiModelProperty(value = "RX角速度单位rad/s", required = true)
private double wx;
@ApiModelProperty(value = "RY角速度单位rad/s", required = true)
private double wy;
@ApiModelProperty(value = "RZ角速度单位rad/s", required = true)
private double wz;
@ApiModelProperty("坐标系类型BASE/TOOL/WORLD/USER")
private String frame;
@ApiModelProperty("加速度单位m/s²")
private Double acceleration;
@ApiModelProperty("持续时间(单位:秒)")
private Double duration;
}

View File

@ -1,22 +0,0 @@
package com.cmvr.edge.client.model.humanoid;
import cmvr.api.HumanoidRobotCommand;
import com.cmvr.edge.client.model.EdgeCommonVO;
import lombok.Data;
@Data
public class EdgeSpeedJVO extends EdgeCommonVO {
/** 关节角速度 (rad/s) */
private double vel;
/** 加速度 (rad/s^2) */
private double acc;
/** 关节名称 */
private String jointName;
/**
* 角度
*/
private HumanoidRobotCommand.RobotJointIndexDirection dir;
}

View File

@ -0,0 +1,178 @@
package com.cmvr.edge.client.service;
import cmvr.api.ArmCommand;
import cmvr.api.Common;
import com.cmvr.edge.client.model.EdgeCommonVO;
/**
* 边缘系统机械臂服务
*
* @author cmvr-iot
* @since 2026-07-01
*/
public interface EdgeArmService {
/**
* 关闭力矩命令码torqueOff
* 关闭机械臂的力矩控制使机械臂进入自由拖动模式
*
* @param edgeCommonVO 边缘通用参数
* @return 操作结果
*/
Common.CommandHeader.Feedback torqueOff(EdgeCommonVO edgeCommonVO);
/**
* 开启力矩命令码torqueOn
* 开启机械臂的力矩控制使机械臂进入受控状态
*
* @param edgeCommonVO 边缘通用参数
* @return 操作结果
*/
Common.CommandHeader.Feedback torqueOn(EdgeCommonVO edgeCommonVO);
/**
* 关节空间运动命令码moveJ
* 控制机械臂通过关节空间插值运动到目标关节位置
*
* @param edgeCommonVO 边缘通用参数
* @param target 目标关节位置数组6个关节角度单位弧度
* @param velocity 运动速度单位m/s可选
* @param acceleration 加速度单位m/s²可选
* @param blendRadius 融合半径单位m可选
* @param jointVelocityLimits 关节速度限制数组可选
* @param asynchronous 是否异步执行可选
* @return 运动结果
*/
ArmCommand.MoveJ.Response moveJ(EdgeCommonVO edgeCommonVO, double[] target, Double velocity,
Double acceleration, Double blendRadius,
double[] jointVelocityLimits, Boolean asynchronous);
/**
* 笛卡尔空间直线运动命令码moveL
* 控制机械臂在笛卡尔空间中沿直线运动到目标位姿
*
* @param edgeCommonVO 边缘通用参数
* @param x X轴坐标单位
* @param y Y轴坐标单位
* @param z Z轴坐标单位
* @param rx RX旋转角度单位弧度
* @param ry RY旋转角度单位弧度
* @param rz RZ旋转角度单位弧度
* @param frame 坐标系类型BASE/TOOL/WORLD/USER
* @param velocity 运动速度单位m/s可选
* @param acceleration 加速度单位m/s²可选
* @param blendRadius 融合半径单位m可选
* @return 运动结果
*/
ArmCommand.MoveL.Response moveL(EdgeCommonVO edgeCommonVO, double x, double y, double z,
double rx, double ry, double rz, String frame,
Double velocity, Double acceleration, Double blendRadius);
/**
* 关节速度控制命令码speedJ
* 控制机械臂各关节以指定速度运动
*
* @param edgeCommonVO 边缘通用参数
* @param velocities 关节速度数组6个关节速度单位rad/s
* @param acceleration 加速度单位rad/s²可选
* @param duration 持续时间单位可选
* @return 控制结果
*/
ArmCommand.SpeedJ.Response speedJ(EdgeCommonVO edgeCommonVO, double[] velocities,
Double acceleration, Double duration);
/**
* 笛卡尔速度控制命令码speedL
* 控制机械臂末端以指定笛卡尔速度运动
*
* @param edgeCommonVO 边缘通用参数
* @param vx X轴速度单位m/s
* @param vy Y轴速度单位m/s
* @param vz Z轴速度单位m/s
* @param wx RX角速度单位rad/s
* @param wy RY角速度单位rad/s
* @param wz RZ角速度单位rad/s
* @param frame 坐标系类型BASE/TOOL/WORLD/USER
* @param acceleration 加速度单位m/s²可选
* @param duration 持续时间单位可选
* @return 控制结果
*/
ArmCommand.SpeedL.Response speedL(EdgeCommonVO edgeCommonVO, double vx, double vy, double vz,
double wx, double wy, double wz, String frame,
Double acceleration, Double duration);
/**
* 伺服关节控制命令码servoJ
* 实时控制机械臂关节位置用于高频控制场景
*
* @param edgeCommonVO 边缘通用参数
* @param target 目标关节位置数组6个关节角度单位弧度
* @return 控制结果
*/
ArmCommand.ServoJ.Response servoJ(EdgeCommonVO edgeCommonVO, double[] target);
/**
* 停止运动命令码stopMotion
* 立即停止机械臂当前正在执行的所有运动
*
* @param edgeCommonVO 边缘通用参数
* @return 停止结果
*/
Common.CommandHeader.Feedback stopMotion(EdgeCommonVO edgeCommonVO);
/**
* 获取关节状态命令码getJointState
* 获取机械臂当前各关节的位置速度和力矩信息
*
* @param edgeCommonVO 边缘通用参数
* @return 关节状态信息
*/
ArmCommand.JointResponse getJointState(EdgeCommonVO edgeCommonVO);
/**
* 获取末端位姿命令码getPose
* 获取机械臂末端执行器相对于基座的笛卡尔位姿
*
* @param edgeCommonVO 边缘通用参数
* @param baseLink 基座连杆名称可选默认为base
* @param eeLink 末端执行器连杆名称可选默认为tool0
* @return 末端位姿信息
*/
ArmCommand.GetPose.Response getPose(EdgeCommonVO edgeCommonVO, String baseLink, String eeLink);
/**
* 标定零点命令码calibrateZeroQ
* 对指定关节进行零点标定
*
* @param edgeCommonVO 边缘通用参数
* @param jointName 关节名称可选为空则标定所有关节
* @return 标定结果
*/
ArmCommand.CalibrateZeroQ.Response calibrateZeroQ(EdgeCommonVO edgeCommonVO, String jointName);
/**
* 获取位姿矩阵命令码getPoseMatrix
* 获取机械臂末端执行器相对于基座的4x4变换矩阵
*
* @param edgeCommonVO 边缘通用参数
* @param baseLink 基座连杆名称可选默认为base
* @param eeLink 末端执行器连杆名称可选默认为tool0
* @return 位姿矩阵信息
*/
ArmCommand.GetPoseMatrix.Response getPoseMatrix(EdgeCommonVO edgeCommonVO, String baseLink, String eeLink);
/**
* 计算正向运动学命令码computeForwardKinematics
* 根据给定的关节位置计算末端执行器的位姿矩阵
*
* @param edgeCommonVO 边缘通用参数
* @param joints 关节位置数组6个关节角度单位弧度
* @param baseLink 基座连杆名称可选默认为base
* @param eeLink 末端执行器连杆名称可选默认为tool0
* @return 正向运动学计算结果4x4变换矩阵
*/
ArmCommand.ComputeForwardKinematics.Response computeForwardKinematics(EdgeCommonVO edgeCommonVO,
double[] joints,
String baseLink,
String eeLink);
}

View File

@ -1,35 +0,0 @@
package com.cmvr.edge.client.service;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.model.humanoid.EdgeMoveJVO;
import com.cmvr.edge.client.model.humanoid.EdgeSpeedJVO;
/**
* 边缘系统机械臂服务
*/
public interface EdgeHumanoidRobotService {
/**
* 移动机械臂
*/
public String moveJ(EdgeMoveJVO vo);
/**
* 获取关节状态
*/
public String getJointState(EdgeCommonVO vo);
/**
* 旋转关节
*/
public String speedJ(EdgeSpeedJVO vo);
/**
* 使能开
*/
public String torqueOn(EdgeCommonVO vo);
/**
* 使能关
*/
public String torqueOff(EdgeCommonVO vo);
}

View File

@ -0,0 +1,369 @@
package com.cmvr.edge.client.service.impl;
import cmvr.api.ArmCommand;
import cmvr.api.ArmServiceGrpc;
import cmvr.api.Common;
import cn.hutool.core.util.StrUtil;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.edge.client.manage.GrpcServiceManager;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.service.EdgeArmService;
import com.cmvr.edge.client.utils.EdgeCommonUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
/**
* 机械臂服务实现类
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class EdgeArmServiceImpl implements EdgeArmService {
private final GrpcServiceManager grpcServiceManager;
@Override
public Common.CommandHeader.Feedback torqueOff(EdgeCommonVO edgeCommonVO) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
Common.CommandHeader.Request request = EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId());
return executeGrpcCall(() -> stub.torqueOff(request));
}
@Override
public Common.CommandHeader.Feedback torqueOn(EdgeCommonVO edgeCommonVO) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
Common.CommandHeader.Request request = EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId());
return executeGrpcCall(() -> stub.torqueOn(request));
}
@Override
public ArmCommand.MoveJ.Response moveJ(EdgeCommonVO edgeCommonVO, double[] target, Double velocity,
Double acceleration, Double blendRadius,
double[] jointVelocityLimits, Boolean asynchronous) {
if (target == null || target.length == 0) {
throw new GlobalException("目标关节位置不能为空");
}
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
// 构建关节位置命令
ArmCommand.JointPositionCommand targetCommand = ArmCommand.JointPositionCommand.newBuilder()
.addAllPosition(Arrays.asList(
Arrays.stream(target).boxed().toArray(Double[]::new)
))
.build();
// 构建运动选项
ArmCommand.MotionOptions.Builder optionsBuilder = ArmCommand.MotionOptions.newBuilder();
if (velocity != null) {
optionsBuilder.setVelocity(velocity);
}
if (acceleration != null) {
optionsBuilder.setAcceleration(acceleration);
}
if (blendRadius != null) {
optionsBuilder.setBlendRadius(blendRadius);
}
if (jointVelocityLimits != null && jointVelocityLimits.length > 0) {
optionsBuilder.addAllJointVelocityLimits(Arrays.asList(
Arrays.stream(jointVelocityLimits).boxed().toArray(Double[]::new)
));
}
if (asynchronous != null) {
optionsBuilder.setAsynchronous(asynchronous);
}
ArmCommand.MoveJ.Request request = ArmCommand.MoveJ.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
.setTarget(targetCommand)
.setOptions(optionsBuilder.build())
.build();
return executeGrpcCall(() -> stub.moveJ(request));
}
@Override
public ArmCommand.MoveL.Response moveL(EdgeCommonVO edgeCommonVO, double x, double y, double z,
double rx, double ry, double rz, String frame,
Double velocity, Double acceleration, Double blendRadius) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
// 构建笛卡尔位姿
ArmCommand.CartesianPose targetPose = ArmCommand.CartesianPose.newBuilder()
.setX(x)
.setY(y)
.setZ(z)
.setRx(rx)
.setRy(ry)
.setRz(rz)
.build();
// 构建运动选项
ArmCommand.MotionOptions.Builder optionsBuilder = ArmCommand.MotionOptions.newBuilder();
if (velocity != null) {
optionsBuilder.setVelocity(velocity);
}
if (acceleration != null) {
optionsBuilder.setAcceleration(acceleration);
}
if (blendRadius != null) {
optionsBuilder.setBlendRadius(blendRadius);
}
// 确定坐标系类型
ArmCommand.ArmFrameType frameType = parseFrameType(frame);
ArmCommand.MoveL.Request request = ArmCommand.MoveL.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
.setTarget(targetPose)
.setOptions(optionsBuilder.build())
.setFrame(frameType)
.build();
return executeGrpcCall(() -> stub.moveL(request));
}
@Override
public ArmCommand.SpeedJ.Response speedJ(EdgeCommonVO edgeCommonVO, double[] velocities,
Double acceleration, Double duration) {
if (velocities == null || velocities.length == 0) {
throw new GlobalException("关节速度数组不能为空");
}
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
// 构建关节速度命令
ArmCommand.JointVelocityCommand velocityCommand = ArmCommand.JointVelocityCommand.newBuilder()
.addAllVelocity(Arrays.asList(
Arrays.stream(velocities).boxed().toArray(Double[]::new)
))
.build();
ArmCommand.SpeedJ.Request.Builder requestBuilder = ArmCommand.SpeedJ.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
.setVelocity(velocityCommand);
if (acceleration != null) {
requestBuilder.setAcceleration(acceleration);
}
if (duration != null) {
requestBuilder.setDuration(duration);
}
return executeGrpcCall(() -> stub.speedJ(requestBuilder.build()));
}
@Override
public ArmCommand.SpeedL.Response speedL(EdgeCommonVO edgeCommonVO, double vx, double vy, double vz,
double wx, double wy, double wz, String frame,
Double acceleration, Double duration) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
// 构建笛卡尔速度
ArmCommand.CartesianVelocity velocity = ArmCommand.CartesianVelocity.newBuilder()
.setVx(vx)
.setVy(vy)
.setVz(vz)
.setWx(wx)
.setWy(wy)
.setWz(wz)
.build();
ArmCommand.SpeedL.Request.Builder requestBuilder = ArmCommand.SpeedL.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
.setVelocity(velocity);
if (acceleration != null) {
requestBuilder.setAcceleration(acceleration);
}
if (duration != null) {
requestBuilder.setDuration(duration);
}
// 确定坐标系类型
ArmCommand.ArmFrameType frameType = parseFrameType(frame);
requestBuilder.setFrame(frameType);
return executeGrpcCall(() -> stub.speedL(requestBuilder.build()));
}
@Override
public ArmCommand.ServoJ.Response servoJ(EdgeCommonVO edgeCommonVO, double[] target) {
if (target == null || target.length == 0) {
throw new GlobalException("目标关节位置不能为空");
}
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
// 构建关节位置命令
ArmCommand.JointPositionCommand targetCommand = ArmCommand.JointPositionCommand.newBuilder()
.addAllPosition(Arrays.asList(
Arrays.stream(target).boxed().toArray(Double[]::new)
))
.build();
ArmCommand.ServoJ.Request request = ArmCommand.ServoJ.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
.setTarget(targetCommand)
.build();
return executeGrpcCall(() -> stub.servoJ(request));
}
@Override
public Common.CommandHeader.Feedback stopMotion(EdgeCommonVO edgeCommonVO) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
Common.CommandHeader.Request request = EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId());
return executeGrpcCall(() -> stub.stopMotion(request));
}
@Override
public ArmCommand.JointResponse getJointState(EdgeCommonVO edgeCommonVO) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
ArmCommand.JointRequest request = ArmCommand.JointRequest.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
.build();
return executeGrpcCall(() -> stub.getJointState(request));
}
@Override
public ArmCommand.GetPose.Response getPose(EdgeCommonVO edgeCommonVO, String baseLink, String eeLink) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
ArmCommand.GetPose.Request.Builder requestBuilder = ArmCommand.GetPose.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()));
if (StrUtil.isNotBlank(baseLink)) {
requestBuilder.setBaseLink(baseLink);
}
if (StrUtil.isNotBlank(eeLink)) {
requestBuilder.setEeLink(eeLink);
}
return executeGrpcCall(() -> stub.getPose(requestBuilder.build()));
}
@Override
public ArmCommand.CalibrateZeroQ.Response calibrateZeroQ(EdgeCommonVO edgeCommonVO, String jointName) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
ArmCommand.CalibrateZeroQ.Request.Builder requestBuilder = ArmCommand.CalibrateZeroQ.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()));
if (StrUtil.isNotBlank(jointName)) {
requestBuilder.setJointName(jointName);
}
return executeGrpcCall(() -> stub.calibrateZeroQ(requestBuilder.build()));
}
@Override
public ArmCommand.GetPoseMatrix.Response getPoseMatrix(EdgeCommonVO edgeCommonVO, String baseLink, String eeLink) {
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
ArmCommand.GetPoseMatrix.Request.Builder requestBuilder = ArmCommand.GetPoseMatrix.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()));
if (StrUtil.isNotBlank(baseLink)) {
requestBuilder.setBaseLink(baseLink);
}
if (StrUtil.isNotBlank(eeLink)) {
requestBuilder.setEeLink(eeLink);
}
return executeGrpcCall(() -> stub.getPoseMatrix(requestBuilder.build()));
}
@Override
public ArmCommand.ComputeForwardKinematics.Response computeForwardKinematics(EdgeCommonVO edgeCommonVO,
double[] joints,
String baseLink,
String eeLink) {
if (joints == null || joints.length == 0) {
throw new GlobalException("关节位置数组不能为空");
}
ArmServiceGrpc.ArmServiceBlockingStub stub = grpcServiceManager.getGrpcClient(
edgeCommonVO.getTerminalId(), ArmServiceGrpc.ArmServiceBlockingStub.class);
// 构建关节位置命令
ArmCommand.JointPositionCommand jointsCommand = ArmCommand.JointPositionCommand.newBuilder()
.addAllPosition(Arrays.asList(
Arrays.stream(joints).boxed().toArray(Double[]::new)
))
.build();
ArmCommand.ComputeForwardKinematics.Request.Builder requestBuilder = ArmCommand.ComputeForwardKinematics.Request.newBuilder()
.setHeader(EdgeCommonUtil.buildRequest(edgeCommonVO.getDeviceId()))
.setJoints(jointsCommand);
if (StrUtil.isNotBlank(baseLink)) {
requestBuilder.setBaseLink(baseLink);
}
if (StrUtil.isNotBlank(eeLink)) {
requestBuilder.setEeLink(eeLink);
}
return executeGrpcCall(() -> stub.computeForwardKinematics(requestBuilder.build()));
}
/**
* 解析坐标系类型
*
* @param frame 坐标系字符串
* @return 坐标系枚举
*/
private ArmCommand.ArmFrameType parseFrameType(String frame) {
if (StrUtil.isBlank(frame)) {
return ArmCommand.ArmFrameType.ARM_FRAME_BASE;
}
switch (frame.toUpperCase()) {
case "BASE":
return ArmCommand.ArmFrameType.ARM_FRAME_BASE;
case "TOOL":
return ArmCommand.ArmFrameType.ARM_FRAME_TOOL;
case "WORLD":
return ArmCommand.ArmFrameType.ARM_FRAME_WORLD;
case "USER":
return ArmCommand.ArmFrameType.ARM_FRAME_USER;
default:
log.warn("未知的坐标系类型: {}, 使用默认值 BASE", frame);
return ArmCommand.ArmFrameType.ARM_FRAME_BASE;
}
}
/**
* 执行gRPC调用并处理异常
*
* @param call gRPC调用
* @param <T> 返回类型
* @return 调用结果
*/
private <T> T executeGrpcCall(java.util.function.Supplier<T> call) {
try {
return call.get();
} catch (Exception e) {
log.error("机械臂 gRPC调用失败", e);
throw new GlobalException("机械臂通信失败: " + e.getMessage());
}
}
}

View File

@ -1,102 +0,0 @@
package com.cmvr.edge.client.service.impl;
import cmvr.api.Common;
import cmvr.api.HumanoidRobotCommand;
import cmvr.api.HumanoidRobotServiceGrpc;
import com.alibaba.fastjson2.JSON;
import com.cmvr.edge.client.manage.GrpcServiceManager;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.model.humanoid.EdgeMoveJVO;
import com.cmvr.edge.client.model.humanoid.EdgeSpeedJVO;
import com.cmvr.edge.client.service.EdgeHumanoidRobotService;
import com.cmvr.edge.client.utils.EdgeCommonUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class EdgeHumanoidRobotServiceImpl implements EdgeHumanoidRobotService {
private final GrpcServiceManager grpcServiceManager;
@Override
public String moveJ(EdgeMoveJVO vo) {
HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub stub =
grpcServiceManager.getGrpcClient(vo.getTerminalId(),
HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub.class);
Common.CommandHeader.Request header = EdgeCommonUtil.buildRequest(vo.getDeviceId());
HumanoidRobotCommand.MoveJ.Request.Builder builder = HumanoidRobotCommand.MoveJ.Request.newBuilder()
.setHeader(header)
.setVel(vo.getVel())
.setAcc(vo.getAcc());
if (vo.getCmds() != null) {
for (EdgeMoveJVO.JointCmd j : vo.getCmds()) {
builder.addCmds(HumanoidRobotCommand.JointCmd.newBuilder()
.setJointName(j.getJointName())
.setRad(j.getRad())
.setVel(j.getVel())
.build());
}
}
// log.info("movej start time={}", System.currentTimeMillis());
HumanoidRobotCommand.MoveJ.Response response = stub.moveJ(builder.build());
// log.info("movej success time={}", System.currentTimeMillis());
return JSON.toJSONString(response.getHeader());
}
@Override
public String getJointState(EdgeCommonVO vo) {
HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub stub =
grpcServiceManager.getGrpcClient(vo.getTerminalId(), HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub.class);
Common.CommandHeader.Request header = EdgeCommonUtil.buildRequest(vo.getDeviceId());
HumanoidRobotCommand.JointRequest build = HumanoidRobotCommand.JointRequest.newBuilder()
.setHeader(header)
.build();
HumanoidRobotCommand.JointResponse response = stub.getJointState(build);
return JSON.toJSONString(response.getStateList());
}
@Override
public String speedJ(EdgeSpeedJVO vo) {
HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub stub =
grpcServiceManager.getGrpcClient(vo.getTerminalId(), HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub.class);
Common.CommandHeader.Request header = EdgeCommonUtil.buildRequest(vo.getDeviceId());
HumanoidRobotCommand.SpeedJ.Request request = HumanoidRobotCommand.SpeedJ.Request.newBuilder()
.setHeader(header)
.setAcc(vo.getAcc())
.setVel(vo.getVel())
.setDir(vo.getDir())
.build();
HumanoidRobotCommand.SpeedJ.Response response = stub.speedJ(request);
return JSON.toJSONString(response.getHeader());
}
@Override
public String torqueOn(EdgeCommonVO vo) {
HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub stub =
grpcServiceManager.getGrpcClient(vo.getTerminalId(), HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub.class);
Common.CommandHeader.Request header = EdgeCommonUtil.buildRequest(vo.getDeviceId());
Common.CommandHeader.Feedback response = stub.torqueOn(header);
return JSON.toJSONString(response);
}
@Override
public String torqueOff(EdgeCommonVO vo) {
HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub stub =
grpcServiceManager.getGrpcClient(vo.getTerminalId(), HumanoidRobotServiceGrpc.HumanoidRobotServiceBlockingStub.class);
Common.CommandHeader.Request header = EdgeCommonUtil.buildRequest(vo.getDeviceId());
Common.CommandHeader.Feedback response = stub.torqueOff(header);
return JSON.toJSONString(response);
}
}

View File

@ -0,0 +1,66 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: cmvr/api/arm_service.proto
package cmvr.api;
public final class ArmServiceOuterClass {
private ArmServiceOuterClass() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistryLite registry) {
}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
registerAllExtensions(
(com.google.protobuf.ExtensionRegistryLite) registry);
}
public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\032cmvr/api/arm_service.proto\022\010cmvr.api\032\025" +
"cmvr/api/common.proto\032\032cmvr/api/arm_comm" +
"and.proto2\325\007\n\nArmService\022N\n\ttorqueOff\022\037." +
"cmvr.api.CommandHeader.Request\032 .cmvr.ap" +
"i.CommandHeader.Feedback\022M\n\010torqueOn\022\037.c" +
"mvr.api.CommandHeader.Request\032 .cmvr.api" +
".CommandHeader.Feedback\022:\n\005moveJ\022\027.cmvr." +
"api.MoveJ.Request\032\030.cmvr.api.MoveJ.Respo" +
"nse\022:\n\005moveL\022\027.cmvr.api.MoveL.Request\032\030." +
"cmvr.api.MoveL.Response\022=\n\006speedJ\022\030.cmvr" +
".api.SpeedJ.Request\032\031.cmvr.api.SpeedJ.Re" +
"sponse\022=\n\006speedL\022\030.cmvr.api.SpeedL.Reque" +
"st\032\031.cmvr.api.SpeedL.Response\022=\n\006servoJ\022" +
"\030.cmvr.api.ServoJ.Request\032\031.cmvr.api.Ser" +
"voJ.Response\022O\n\nstopMotion\022\037.cmvr.api.Co" +
"mmandHeader.Request\032 .cmvr.api.CommandHe" +
"ader.Feedback\022@\n\rgetJointState\022\026.cmvr.ap" +
"i.JointRequest\032\027.cmvr.api.JointResponse\022" +
"@\n\007getPose\022\031.cmvr.api.GetPose.Request\032\032." +
"cmvr.api.GetPose.Response\022U\n\016calibrateZe" +
"roQ\022 .cmvr.api.CalibrateZeroQ.Request\032!." +
"cmvr.api.CalibrateZeroQ.Response\022R\n\rgetP" +
"oseMatrix\022\037.cmvr.api.GetPoseMatrix.Reque" +
"st\032 .cmvr.api.GetPoseMatrix.Response\022s\n\030" +
"computeForwardKinematics\022*.cmvr.api.Comp" +
"uteForwardKinematics.Request\032+.cmvr.api." +
"ComputeForwardKinematics.Responseb\006proto" +
"3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
cmvr.api.Common.getDescriptor(),
cmvr.api.ArmCommand.getDescriptor(),
});
cmvr.api.Common.getDescriptor();
cmvr.api.ArmCommand.getDescriptor();
}
// @@protoc_insertion_point(outer_class_scope)
}

View File

@ -57,6 +57,8 @@ public enum ActionEnum {
// ---------------机械臂--------------- // ---------------机械臂---------------
TOUCH("EDGE", "TOUCH", "触控"), TOUCH("EDGE", "TOUCH", "触控"),
// 末端运动
ARM_MOVE_TO_POINT("EDGE", "ARM_MOVE_TO_POINT", "运动到指定点"),
// --------------- 头部 --------------- // --------------- 头部 ---------------
BIO_HEAD_SPEAK_START("EDGE", "BIO_HEAD_SPEAK_START", "开始说话"), BIO_HEAD_SPEAK_START("EDGE", "BIO_HEAD_SPEAK_START", "开始说话"),

View File

@ -0,0 +1,104 @@
package com.cmvr.test.flow.runtime.operator.edge;
import cmvr.api.ArmCommand;
import com.alibaba.fastjson2.JSONObject;
import com.cmvr.common.exception.GlobalException;
import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.service.EdgeArmService;
import com.cmvr.test.enums.ActionEnum;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 机械臂边缘操作服务
* 处理机械臂相关的设备行为如末端运动到指定位置
*
* @author cmvr-iot
* @since 2026-07-01
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class EdgeArmOperateService implements EdgeOperateService {
private final EdgeArmService edgeArmService;
@Override
public boolean supports(ActionEnum action) {
return action.name().startsWith("ARM_");
}
@Override
public TaskNodeExecuteResult execute(TaskNodeExecuteMessage message) {
ActionEnum action = message.getAction();
JSONObject inputParams = message.getInputParams();
String terminalId = message.getTerminalId();
// 从输入参数中获取设备ID
String deviceId = inputParams.getString("deviceId");
if (deviceId == null || deviceId.isEmpty()) {
throw new GlobalException("设备ID(deviceId)不能为空");
}
// 构建边缘端通用参数
EdgeCommonVO edgeCommonVO = new EdgeCommonVO();
edgeCommonVO.setTerminalId(terminalId);
edgeCommonVO.setDeviceId(deviceId);
// 先调用开启力矩
edgeArmService.torqueOn(edgeCommonVO);
switch (action) {
case ARM_MOVE_TO_POINT: {
log.info("执行机械臂末端运动到指定点操作设备ID: {}", deviceId);
// 从输入参数中获取坐标信息
Double x = inputParams.getDouble("x");
Double y = inputParams.getDouble("y");
Double z = inputParams.getDouble("z");
Double rx = inputParams.getDouble("rx");
Double ry = inputParams.getDouble("ry");
Double rz = inputParams.getDouble("rz");
String frame = inputParams.getString("frame");
Double velocity = inputParams.getDouble("velocity");
Double acceleration = inputParams.getDouble("acceleration");
Double blendRadius = inputParams.getDouble("blendRadius");
// 参数验证
if (x == null || y == null || z == null) {
throw new GlobalException("X、Y、Z坐标不能为空");
}
if (rx == null || ry == null || rz == null) {
throw new GlobalException("RX、RY、RZ旋转角度不能为空");
}
// 直接调用边缘端机械臂服务执行笛卡尔空间直线运动
ArmCommand.MoveL.Response result = edgeArmService.moveL(
edgeCommonVO,
x,
y,
z,
rx,
ry,
rz,
frame,
velocity,
acceleration,
blendRadius
);
log.info("机械臂末端运动任务下发成功");
break;
}
default:
throw new GlobalException("不支持的机械臂操作类型: " + action);
}
return TaskNodeExecuteResult.success();
}
}

View File

@ -3,8 +3,8 @@ package com.cmvr.test.flow.runtime.operator.edge;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.cmvr.common.exception.GlobalException; import com.cmvr.common.exception.GlobalException;
import com.cmvr.edge.client.model.humanoid.EdgeMoveJVO; import com.cmvr.edge.client.model.arm.EdgeArmMoveJVO;
import com.cmvr.edge.client.service.EdgeHumanoidRobotService; import com.cmvr.edge.client.service.EdgeArmService;
import com.cmvr.test.enums.ActionEnum; import com.cmvr.test.enums.ActionEnum;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage; import com.cmvr.test.flow.runtime.message.TaskNodeExecuteMessage;
import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult; import com.cmvr.test.flow.runtime.message.TaskNodeExecuteResult;
@ -18,7 +18,7 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor @RequiredArgsConstructor
public class EdgeTouchOperateService implements EdgeOperateService { public class EdgeTouchOperateService implements EdgeOperateService {
private final EdgeHumanoidRobotService edgeHumanoidRobotService; private final EdgeArmService edgeArmService;
@Override @Override
public boolean supports(ActionEnum action) { public boolean supports(ActionEnum action) {
@ -35,11 +35,19 @@ public class EdgeTouchOperateService implements EdgeOperateService {
switch (action) { switch (action) {
case TOUCH: { case TOUCH: {
EdgeMoveJVO edgeMoveJVO = JSON.parseObject(touchParams, EdgeMoveJVO.class); EdgeArmMoveJVO edgeArmMoveJVO = JSON.parseObject(touchParams, EdgeArmMoveJVO.class);
edgeMoveJVO.setTerminalId(terminalId); edgeArmMoveJVO.setTerminalId(terminalId);
edgeMoveJVO.setDeviceId(deviceId); edgeArmMoveJVO.setDeviceId(deviceId);
edgeHumanoidRobotService.moveJ(edgeMoveJVO); edgeArmService.moveJ(
edgeArmMoveJVO,
edgeArmMoveJVO.getTarget(),
edgeArmMoveJVO.getVelocity(),
edgeArmMoveJVO.getAcceleration(),
edgeArmMoveJVO.getBlendRadius(),
edgeArmMoveJVO.getJointVelocityLimits(),
edgeArmMoveJVO.getAsynchronous()
);
break; break;
} }

View File

@ -9,8 +9,8 @@ import com.cmvr.common.exception.GlobalException;
import com.cmvr.common.utils.http.CallAPIUtil; import com.cmvr.common.utils.http.CallAPIUtil;
import com.cmvr.common.utils.uuid.IdUtils; import com.cmvr.common.utils.uuid.IdUtils;
import com.cmvr.edge.client.model.EdgeCommonVO; import com.cmvr.edge.client.model.EdgeCommonVO;
import com.cmvr.edge.client.service.EdgeArmService;
import com.cmvr.edge.client.service.EdgeBioHeadService; import com.cmvr.edge.client.service.EdgeBioHeadService;
import com.cmvr.edge.client.service.EdgeHumanoidRobotService;
import com.cmvr.test.enums.FlowiseActionEnum; import com.cmvr.test.enums.FlowiseActionEnum;
import com.cmvr.test.flow.context.TaskContext; import com.cmvr.test.flow.context.TaskContext;
import com.cmvr.test.flow.context.TaskContextManager; import com.cmvr.test.flow.context.TaskContextManager;
@ -36,7 +36,7 @@ import java.util.Map;
public class FlowiseActionService { public class FlowiseActionService {
private final EdgeBioHeadService edgeBioHeadService; private final EdgeBioHeadService edgeBioHeadService;
private final EdgeHumanoidRobotService edgeHumanoidRobotService; private final EdgeArmService edgeArmService;
private final TaskContextManager taskContextManager; private final TaskContextManager taskContextManager;
private final FlowControlService flowControlService; private final FlowControlService flowControlService;
private final FlowTaskRuntimeService flowTaskRuntimeService; private final FlowTaskRuntimeService flowTaskRuntimeService;