Files
acRealman_xr/docs/superpowers/specs/2026-07-29-rm75-canfd-udp-feedback-design.md
T

199 lines
7.7 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 CANFD UDP 主动反馈设计
> 真机验证修正:右臂高跟随首轮测试触发掉使能。离线复现发现静止目标存在
> `23.582°` 空空间漂移,第一周期约 `3315°/s²`。当前实现已移除
> manipulability 自运动、增加软件关节加速度限幅和故障 Grip 锁存,并将三份
> YAML 恢复为 `follow: false` 安全基线;高跟随须在基线验证后单独测试。
## 目标
按睿尔曼 MovejCANFD 示例,将真机关节反馈从同一 TCP 控制连接上的
`rm_get_joint_degree()` 周期轮询,替换为控制器 UDP 主动状态推送。
控制命令继续通过现有单个 `RoboticArm(RM_TRIPLE_MODE_E)` 句柄发送,不新增
RealMan 连接,不修改 Placo QP、工作空间/圆柱限位、速度限制、指令超时或安全
停止条件。
## 根因与证据
低跟随模式下,125 Hz CANFD 发送与绝对周期 TCP 反馈轮询能够同时工作:
- `feedback_interval mean=10.04110.389 ms`
- `feedback_age mean=5.7306.138 ms`
- 控制周期最大值不超过 `8.979 ms`
启用高跟随后,即使 `canfd_trajectory_mode=2`,控制发送仍正常:
- `period max=9.275 ms`
- `send max=0.235 ms`
但同步反馈退化为:
- `feedback_read mean=11.278 ms``max=80.320 ms`
- `feedback_age max=117.502 ms`
反馈年龄逼近现有 `command_timeout_sec=0.12 s`,触发“关节反馈缺失或过期”
安全停止,造成 Grip 按住期间控制反复退出和重新锁定。增大超时只会允许 QP
继续使用更旧的关节状态,不解决 TCP 反馈阻塞。
睿尔曼 MovejCANFD 示例使用三线程模式、`rm_set_realtime_push()`
`rm_realtime_arm_state_call_back()`,通过 UDP 回调获取关节状态,而不是在
CANFD 透传期间同步轮询关节角。
## 数据流
```text
PICO -> ROS 125 Hz 控制回调 -> Placo QP -> TCP rm_movej_canfd
RM75 控制器 -> UDP 5 ms 主动推送 -> SDK 第三线程回调
-> JointStateSnapshot 缓存 -> ROS 125 Hz 控制回调
```
TCP 仍承担 CANFD、慢停、安全配置和末端工具命令。UDP 只承担状态反馈,两条
传输路径共用同一个机械臂句柄。
## SDK 连接与反馈生命周期
`RealManAdapter.connect()` 保持三线程模式和单次 `rm_create_robot_arm()`
1. 创建并检查机械臂句柄。
2. 下发已有安全参数和可选初始位姿。
3. 创建并保存 `rm_realtime_arm_state_callback_ptr`,避免 Python 回调被垃圾
回收。
4. 使用 `rm_realtime_push_config_t` 配置 5 ms UDP 主动上报。
5. 注册 `rm_realtime_arm_state_call_back()`
6. 等待第一帧有效 UDP 反馈,最长 2 秒。
若配置接口返回非零,或 2 秒内没有有效反馈,连接初始化失败并删除已创建的
机械臂句柄;不静默回退到 TCP 轮询。
连接成功后不再创建反馈线程,也不再调用 `rm_get_joint_degree()`
`close()` 先停止接受回调更新,再执行现有慢停和句柄删除。控制器的 UDP 配置
由下一次启动重新覆盖,不额外增加关闭阶段配置命令。
## UDP 回调与缓存
回调只执行有界、非阻塞工作:
1. 检查回调对象、`errCode` 和来源机械臂 IP。
2. 读取 7 个 `joint_position`,检查数量、数值类型及 NaN/Inf。
3. 将厂商反馈的角度转换为弧度。
4. 使用 `time.monotonic()` 记录接收时刻,并计算与上一帧的更新间隔。
5. 在现有 `_joint_state_lock` 下替换 `JointStateSnapshot`
6. 第一帧有效数据唤醒连接初始化等待。
无效 UDP 帧不覆盖上一帧缓存。若后续持续丢包,现有 120 ms 新鲜度检查自然
触发安全停止。
UDP 回调不执行 QP、ROS 发布、停止命令或其他 SDK 调用,避免阻塞 SDK 接收
线程。
## 参数与三份 YAML
新增真机参数:
- `realtime_push_host_ip`:机械臂可直接访问的上位机地址;
- `realtime_push_port`:单臂 UDP 接收端口;
- `realtime_push_cycle_ms`:主动上报周期,默认并配置为 `5`
本次现场配置:
| 配置 | 节点 | host | port |
|---|---|---|---:|
| `right_arm_rm75.yaml` | 右臂 | `192.168.192.148` | 8090 |
| `left_arm_rm75.yaml` | 左臂 | `192.168.192.148` | 8089 |
| `dual_arm_rm75.yaml` | 左臂 | `192.168.192.148` | 8089 |
| `dual_arm_rm75.yaml` | 右臂 | `192.168.192.148` | 8090 |
左右臂端口必须不同。更换上位机或网络后,只需同步修改 YAML 中的
`realtime_push_host_ip`
Mock 模式不导入厂商 SDK,也不要求 UDP 参数有效。
## YAML 与 launch 参数所有权
此前 `arm_debug.launch.py` 会用 launch 默认值覆盖 YAML 中的机械臂参数,
导致 YAML 无法单独控制 `follow` 等行为。
用户选择由 YAML 作为机械臂行为和硬件参数的唯一默认来源。
以下参数只由 `left_arm_rm75.yaml``right_arm_rm75.yaml`
`dual_arm_rm75.yaml` 管理,launch 不再声明或覆盖:
- `robot_ip``robot_port`
- `avoid_singularity`
- `control_rate_hz`
- `follow``canfd_trajectory_mode``canfd_radio`
- `configure_safety_limits`
- `move_to_initial_pose_on_connect`
- `enable_tool_control``enable_trigger_gripper_control`
- `trigger_close_threshold`
- `configure_peripheral_on_connect`
- 本设计新增的 UDP 主动反馈参数。
三份 YAML 补齐工具控制参数;删除其中不再生效的 `use_mock`,避免出现两个
配置来源。
`arm_debug.launch.py` 只保留:
- `arm=left|right|both`,选择启动拓扑;
- `use_mock=true|false`,作为显式安全运行模式,默认仍为 `true`
- PICO 输入节点的 `udp_host``udp_port``udp_timer_hz`
- launch 根据安装路径和左右臂生成的 `robot_urdf_path`
`peripheral_config_file``peripheral_arm``tool_command_topic`
`launcher_ui.py` 和 README 中的启动命令同步删除机械臂 IP、初始化移动等已
移交 YAML 的 launch 参数,只保留 `arm``use_mock` 和 PICO 输入覆盖。
控制模式保持分阶段范围:
- 三份 YAML 均使用 `follow: false``canfd_trajectory_mode: 2` 完成安全基线;
- 基线验证通过前不启用高跟随或模式 0,不提高 `max_linear_speed`
## Timing 日志
保留:
- `period`
- `total`
- `qp`
- `send`
- `feedback_age`
- `feedback_interval`
`feedback_read` 表示同步 SDK 查询耗时;UDP 架构不存在该查询,因此该字段
不再产生样本,现有条件日志逻辑会自动省略它,不新增同义统计项。
## 测试
使用 FakeArm 和伪造 SDK 模块覆盖:
- 连接时使用正确 host、port、5 ms 周期配置 UDP 并注册回调;
- 第一帧有效回调转换 7 个关节角为弧度并解除启动等待;
- 连续回调正确记录 `feedback_interval`
- 错误码、来源 IP、长度或 NaN/Inf 无效帧不覆盖缓存;
- UDP 配置失败或首帧超时会清理句柄并抛出明确异常;
- 真机适配器不再启动轮询线程或调用 `rm_get_joint_degree()`
- Mock 模式无需厂商 SDK。
- launch 不再覆盖 YAML 的机械臂行为和硬件参数;
- `use_mock` 仍由 launch 默认设为 `true`
- `launcher_ui.py` 不再传递已经删除的 launch 参数。
随后运行:
```bash
cd /home/robot/WS_xr
source /opt/ros/humble/setup.bash
python3 -m pytest -q src/xr_rm_teleop/test
python3 -m pytest -q src/xr_rm_teleop/test/test_orientation_control.py
colcon build --symlink-install --executor sequential
```
Codex 不连接真机。用户在右臂小范围测试中确认:
- 启动日志显示收到 UDP 首帧;
- 按住 Grip 不再出现反馈过期或 SDK `-2`
- 连续四个 timing 窗口 `period max < 10 ms``total max < 8 ms`
- `feedback_interval mean` 接近 5 ms
- `feedback_age mean < 5 ms`,且最大值不触发 120 ms 安全停止。