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

7.7 KiB
Raw Blame History

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 msmax=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 透传期间同步轮询关节角。

数据流

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.yamlright_arm_rm75.yamldual_arm_rm75.yaml 管理,launch 不再声明或覆盖:

  • robot_iprobot_port
  • avoid_singularity
  • control_rate_hz
  • followcanfd_trajectory_modecanfd_radio
  • configure_safety_limits
  • move_to_initial_pose_on_connect
  • enable_tool_controlenable_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_hostudp_portudp_timer_hz
  • launch 根据安装路径和左右臂生成的 robot_urdf_pathperipheral_config_fileperipheral_armtool_command_topic

launcher_ui.py 和 README 中的启动命令同步删除机械臂 IP、初始化移动等已 移交 YAML 的 launch 参数,只保留 armuse_mock 和 PICO 输入覆盖。

控制模式保持分阶段范围:

  • 三份 YAML 均使用 follow: falsecanfd_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 参数。

随后运行:

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 mstotal max < 8 ms
  • feedback_interval mean 接近 5 ms
  • feedback_age mean < 5 ms,且最大值不触发 120 ms 安全停止。