Files
acRealman_xr/docs/superpowers/plans/2026-07-30-rm75-joint-command-braking.md
T

310 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`
- [x] **步骤 1:在现有首周期加速度测试后增加固定目标测试**
增加以下测试:
```python
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)
```
该测试同时覆盖正负方向、不同目标距离、最大速度、最大加速度、禁止越过固定目标
和最终停止。
- [x] **步骤 2:运行新增测试并确认失败**
运行:
```bash
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`
- [x] **步骤 1:用离散制动逻辑替换现有限幅计算**
保留方法签名和现有长度、参数校验,将
`desired_velocity = np.clip(...)` 到返回值的部分替换为:
```python
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`
- [x] **步骤 2:运行新增测试并确认通过**
运行:
```bash
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`
- [x] **步骤 3:运行关节控制测试文件**
运行:
```bash
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` 继续通过,证明首周期
加速度行为没有回归。
### 任务三:完整验证
**文件:**
- 不修改文件。
- [x] **步骤 1:运行 `xr_rm_teleop` 全部测试**
运行:
```bash
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 回归测试必须执行,不能因缺少模块而跳过。
- [x] **步骤 2:单独运行姿态控制测试**
运行:
```bash
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
```
预期:全部通过。
- [x] **步骤 3:构建工作空间**
运行:
```bash
source /opt/ros/humble/setup.bash
colcon build --symlink-install
```
预期:`xr_rm_input``xr_rm_interfaces``xr_rm_teleop``xr_rm_bringup`
全部构建成功。
- [x] **步骤 4:使用 mock 启动右臂统一 launch**
运行:
```bash
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 错误。
- [x] **步骤 5:检查最终差异**
运行:
```bash
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 操作真机:
1. 保持低跟随,从安全姿态和小于 5 mm 的上下位移开始;
2. 手柄停止后观察机械臂是否立即减振并稳定;
3. 确认无持续 QP、UDP、CANFD 或故障锁存错误后,再测试 10 mm;
4. 若不再振荡但仍有不可接受的整臂大幅构型变化,停止扩大位移,转入独立的奇异点
处理设计。