10 KiB
RM75 关节命令提前制动实施计划
供智能体执行者: 必须使用
superpowers:subagent-driven-development(推荐)或superpowers:executing-plans逐项实施;所有步骤使用复选框跟踪。
目标: 修复 90 Hz 关节命令在稳定目标附近反复越界的问题,使 RM75 在保留 现有速度、加速度限制和低跟随模式的前提下提前制动并稳定停止。
架构: 保留当前 QP、反馈和故障恢复链路,只替换
SingleArmVelocityTeleop._limit_joint_command_step() 内部的关节命令生成规则。
每个关节根据离散制动距离决定继续加速或开始减速,最终命令仍由现有
_send_joint_target() 发送。
技术栈: Python 3.10、ROS2 Humble、NumPy、pytest、ament/colcon。
执行约束
- 设计文档:
docs/superpowers/specs/2026-07-30-rm75-joint-command-braking-design.md。 - 构建、测试和启动命令在
/home/robot/WS_xr执行,并先运行source /opt/ros/humble/setup.bash。 - 不连接真机,不发送真实 CANFD,不移动机械臂,不操作夹爪。
- 启动验证只使用
xr_rm_bringup/launch/arm_debug.launch.py arm:=right use_mock:=true。 - 不修改 QP、YAML、RealMan 适配器、launch、UI、依赖和公开 API。
- 保留工作空间、圆柱、TCP 速度、姿态速度、关节速度、关节加速度、超时保持、 CANFD 恢复、Grip 重新使能和安全停止逻辑。
- 用户未要求 Git 提交,因此本计划不执行
git commit或git push。 - 保留工作区中已有的其他修改,不回退、不覆盖:
placo_ik_solver.py、test_placo_transforms.py及现有 Superpowers 文档。
文件范围
- 修改
xr_rm_teleop/test/test_joint_control.py- 增加固定目标提前制动回归测试。
- 修改
xr_rm_teleop/xr_rm_teleop/single_arm_velocity_teleop.py- 在现有关节限幅入口实现离散制动距离判断。
- 不创建新的运行时代码文件或配置项。
任务一:增加持续振荡回归测试
文件:
-
修改:
xr_rm_teleop/test/test_joint_control.py:206 -
测试:
xr_rm_teleop/test/test_joint_control.py -
步骤 1:在现有首周期加速度测试后增加固定目标测试
增加以下测试:
def test_joint_command_step_brakes_before_fixed_target_without_overshoot() -> None:
dt = 1.0 / 90.0
max_speed = math.radians(180.0)
max_acceleration = math.radians(300.0)
target = np.radians(
[10.0, -10.0, 3.0, -3.0, 1.0, -1.0, 0.1]
).tolist()
command = [0.0] * 7
velocity = [0.0] * 7
for _ in range(180):
previous_velocity = list(velocity)
command, velocity = (
SingleArmVelocityTeleop._limit_joint_command_step(
target=target,
previous_target=command,
previous_velocity=velocity,
max_speed=max_speed,
max_acceleration=max_acceleration,
dt=dt,
)
)
for index in range(7):
assert min(0.0, target[index]) - 1e-12 <= command[index]
assert command[index] <= max(0.0, target[index]) + 1e-12
assert abs(velocity[index]) <= max_speed + 1e-12
assert (
abs(velocity[index] - previous_velocity[index])
<= max_acceleration * dt + 1e-12
)
assert command == pytest.approx(target, abs=1e-12)
assert velocity == pytest.approx([0.0] * 7, abs=1e-12)
该测试同时覆盖正负方向、不同目标距离、最大速度、最大加速度、禁止越过固定目标 和最终停止。
- 步骤 2:运行新增测试并确认失败
运行:
source /opt/ros/humble/setup.bash
PYTHONPATH="/home/robot/WS_xr/src/xr_rm_teleop:${PYTHONPATH:-}" \
python3 -m pytest \
src/xr_rm_teleop/test/test_joint_control.py::test_joint_command_step_brakes_before_fixed_target_without_overshoot \
-v
预期:FAIL,现有实现会让至少一个关节命令越过固定目标。失败原因必须来自新增
越界断言,不能是导入或环境错误。
任务二:实现离散提前制动
文件:
-
修改:
xr_rm_teleop/xr_rm_teleop/single_arm_velocity_teleop.py:1232-1263 -
测试:
xr_rm_teleop/test/test_joint_control.py -
步骤 1:用离散制动逻辑替换现有限幅计算
保留方法签名和现有长度、参数校验,将
desired_velocity = np.clip(...) 到返回值的部分替换为:
values = np.asarray(
[target, previous_target, previous_velocity],
dtype=float,
)
if not np.isfinite(values).all():
raise ValueError("joint command contains NaN/Inf")
velocity_step = max_acceleration * dt
arrival_distance = velocity_step * dt
limited_target = []
limited_velocity = []
for desired_target, last_target, last_velocity in zip(
target,
previous_target,
previous_velocity,
):
error = desired_target - last_target
if (
abs(last_velocity) <= 1e-12
and abs(error) <= arrival_distance
):
velocity = error / dt
position = desired_target
else:
direction = (
math.copysign(1.0, error)
if abs(error) > 1e-12
else 0.0
)
accelerated_speed = min(
abs(last_velocity) + velocity_step,
max_speed,
)
braking_steps = max(
0,
math.ceil(accelerated_speed / velocity_step) - 1,
)
braking_distance = accelerated_speed * dt + dt * (
braking_steps * accelerated_speed
- velocity_step
* braking_steps
* (braking_steps + 1)
/ 2.0
)
desired_velocity = direction * max_speed
if (
last_velocity * error > 0.0
and abs(error) <= braking_distance
):
desired_velocity = 0.0
velocity = _clamp(
desired_velocity,
last_velocity - velocity_step,
last_velocity + velocity_step,
)
velocity = _clamp(velocity, -max_speed, max_speed)
position = last_target + velocity * dt
limited_target.append(position)
limited_velocity.append(velocity)
if not np.isfinite(limited_target).all():
raise ValueError("joint command contains NaN/Inf")
return limited_target, limited_velocity
不要增加 ROS 参数或辅助类。制动距离直接使用当前方法已有的
max_acceleration、max_speed 和 dt。
- 步骤 2:运行新增测试并确认通过
运行:
source /opt/ros/humble/setup.bash
PYTHONPATH="/home/robot/WS_xr/src/xr_rm_teleop:${PYTHONPATH:-}" \
python3 -m pytest \
src/xr_rm_teleop/test/test_joint_control.py::test_joint_command_step_brakes_before_fixed_target_without_overshoot \
-v
预期:PASS。
- 步骤 3:运行关节控制测试文件
运行:
source /opt/ros/humble/setup.bash
PYTHONPATH="/home/robot/WS_xr/src/xr_rm_teleop:${PYTHONPATH:-}" \
python3 -m pytest src/xr_rm_teleop/test/test_joint_control.py -v
预期:全部通过;现有
test_joint_command_step_limits_acceleration_from_rest 继续通过,证明首周期
加速度行为没有回归。
任务三:完整验证
文件:
-
不修改文件。
-
步骤 1:运行
xr_rm_teleop全部测试
运行:
source /opt/ros/humble/setup.bash
export RM75_TEST_PYTHONPATH="/home/robot/WS_xr/src/xr_rm_teleop:/home/robot/miniconda3/envs/xr/lib/python3.10/site-packages:/home/robot/miniconda3/envs/xr/lib/python3.10/site-packages/cmeel.prefix/lib/python3.10/site-packages"
PYTHONPATH="${RM75_TEST_PYTHONPATH}:${PYTHONPATH:-}" \
python3 -m pytest \
src/xr_rm_teleop/test -v
预期:全部测试通过,无失败;真实 Placo 回归测试必须执行,不能因缺少模块而跳过。
- 步骤 2:单独运行姿态控制测试
运行:
source /opt/ros/humble/setup.bash
PYTHONPATH="/home/robot/WS_xr/src/xr_rm_teleop:${PYTHONPATH:-}" \
python3 -m pytest \
src/xr_rm_teleop/test/test_orientation_control.py -v
预期:全部通过。
- 步骤 3:构建工作空间
运行:
source /opt/ros/humble/setup.bash
colcon build --symlink-install
预期:xr_rm_input、xr_rm_interfaces、xr_rm_teleop 和 xr_rm_bringup
全部构建成功。
- 步骤 4:使用 mock 启动右臂统一 launch
运行:
source /opt/ros/humble/setup.bash
source install/setup.bash
timeout 10s ros2 launch xr_rm_bringup arm_debug.launch.py \
arm:=right use_mock:=true
预期:
-
节点日志显示控制周期约
dt=0.0111s; -
日志显示
follow=False; -
不连接厂商 SDK,不发送真实 CANFD;
-
除
timeout主动结束产生的退出状态外,没有 Python 异常或 ROS 错误。 -
步骤 5:检查最终差异
运行:
git -C /home/robot/WS_xr/src diff --check
git -C /home/robot/WS_xr/src status --short
git -C /home/robot/WS_xr/src diff -- \
xr_rm_teleop/xr_rm_teleop/single_arm_velocity_teleop.py \
xr_rm_teleop/test/test_joint_control.py
预期:
diff --check无输出;- 本次运行时代码改动只涉及上述两个文件;
- 原有工作区修改仍保留;
- 不存在提交或远程推送。
用户真机验证边界
自动验证完成后,只提供手动验证步骤,不由 Codex 操作真机:
- 保持低跟随,从安全姿态和小于 5 mm 的上下位移开始;
- 手柄停止后观察机械臂是否立即减振并稳定;
- 确认无持续 QP、UDP、CANFD 或故障锁存错误后,再测试 10 mm;
- 若不再振荡但仍有不可接受的整臂大幅构型变化,停止扩大位移,转入独立的奇异点 处理设计。