话题清单

章节

节点 / 包

包路径(工作区 src/ 下)

说明

§2

ti5_master_arm

ti5_controller/

主臂:外骨骼 → 关节/扳机/摇杆等话题

§3

ti5_follow_arm

ti5_controller/

从臂:跟随主臂 CAN 驱动

§4

ti5_aoyihand_node

ti5_aoyihand/

傲意灵巧手(单独 launch)

§5

dex1_gripper_node

dex1_gripper_ros2/

宇树 Dex1 夹爪(单独 launch)

§6

lanxin_chassis_controller_node

lanxin_chassis_controller/

蓝芯 VMR 底盘摇杆(单独 launch)

备注

说明:下文路径均相对于工作区根目录(如 ti5-remote_control/)。修改 YAML 后需重启对应 launch;ti5_controller 内 C++ 节点改参一般无需 colcon build,改 launch/Python 或新增包才需重新编译。


1. 命名约定

  • 主臂分话题前缀:/{master_device_id}/...,默认 MASTER_001(仓库配置示例常用 ti5_master_001)。

  • 从臂分话题前缀:/{slave_device_id}/...,默认 SLAVE_001(仓库配置示例常用 ti5_slave_001)。

  • 傲意灵巧手(ti5_aoyihand_node,包 src/ti5_aoyihand/):控制输入(订阅)在主臂命名空间 /{master_device_id}/...;末端状态(与 Dex1 相同话题名)发布在 /{device_id}/...。master_device_id / device_id 须在 src/ti5_aoyihand/config/ti5_aoyihand_node.yaml(或启动时参数覆盖)与主臂 paired_device_id、从臂 device_id 自行对齐;

  • 宇树 Dex1 夹爪(dex1_gripper_node,包 src/dex1_gripper_ros2/)订阅主臂 trigger_cmd,状态话题发布在夹爪节点的 device_id 下(默认与从臂同为 ti5_slave_001),即用 /<SLAVE_ID>/..._gripper_states。请单独 launch,与从臂 launch 独立。

  • 主臂、从臂(包 src/ti5_controller/)、灵巧手、Dex1、底盘等节点各自独立 launch,运行时通过话题与参数约定配合;底盘见 §6(src/lanxin_chassis_controller/)。

  • 本文的 <MASTER_ID>、<SLAVE_ID> 为占位符。


2. 主臂节点(ti5_master_arm)

包位置:src/ti5_controller/
启动:ros2 launch ti5_controller ti5_master.launch.py(launch 文件:src/ti5_controller/launch/ti5_master.launch.py)
主要配置(src/ti5_controller/config/):

文件

用途

ti5_master_config.yaml

主臂节点 ROS 参数(device_id、串口、按键映射、腿控等)

retargeting_T170C.yaml

外骨骼→机器人关节名与限位(须与从臂 retargeting_config_file 一致)

ti5_arm_solver_170c.yaml

臂/腿 solver 参数(须与从臂 arm_solver_config_file 一致)

2.1 发布话题

  • /<MASTER_ID>/right_arm_joint_cmd (sensor_msgs/msg/JointState)

    • name: 右臂关节名(优先来自 retargeting)

    • position: 右臂各关节目标角(弧度)

    • header.frame_id: master_arm

  • /<MASTER_ID>/left_arm_joint_cmd (sensor_msgs/msg/JointState)

    • 同上,左臂

  • /<MASTER_ID>/head_joint_cmd (sensor_msgs/msg/JointState)

    • name: 头部 R/P/Y 关节名

    • position: [roll, pitch, yaw](弧度)

    • header.frame_id: master_arm

  • /<MASTER_ID>/waist_joint_cmd (sensor_msgs/msg/JointState)

    • name: 腰部关节名(支持单轴或多轴)

    • position: 腰部目标角(弧度)

    • header.frame_id: master_arm

  • /<MASTER_ID>/leg_joint_cmd (sensor_msgs/msg/JointState)(仅 leg_teleop_enabled 且 enable_leg_joint_cmd_pub=true)

    • name: 腿部关节名(retargeting 的 leg)

    • position: 腿电机目标角(弧度)

    • header.frame_id: master_arm

    • 当前为“遥操态按键腿控”输出:会话开始前先请求一次 leg_states 刷新,锁存当前末端姿态后仅改末端位置

  • /<MASTER_ID>/trigger_cmd (sensor_msgs/msg/JointState)

    • name: ["right_trigger","left_trigger"] [右扳机, 左扳机]

    • position: 范围约 [0,1]

    • header.frame_id: trigger_cmd

  • /<MASTER_ID>/thumb_side_cmd (sensor_msgs/msg/JointState)

    • name: ["right_thumb_side","left_thumb_side"]

    • position: 两侧拇指侧向量,范围约 [0,1]

    • header.frame_id: thumb_side_cmd

  • /<MASTER_ID>/rocker_cmd (sensor_msgs/msg/JointState)

    • name: ["left_joy_x","left_joy_y","right_joy_x","right_joy_y"]

    • position: 四轴摇杆值

    • header.frame_id: rocker_cmd

  • /<MASTER_ID>/control_cmd (sensor_msgs/msg/JointState)

    • name[0]:命令字符串(如 enter_remote_mode、arm_follow_pause_left);position 多为占位。

    • 主臂按档位/按键自动发布;从臂如何处理见 §3.1 控制命令。

  • /<MASTER_ID>/bottom_cmd (sensor_msgs/msg/JointState)

    • 用途:按键记录(供录包/数据分析);不驱动从臂,功能映射走 control_cmd。

    • 发布时机:某键按住期间,按按键解析频率持续发布;松手即停止。BOTH_3F 下不发布。

    • 命名:name[0] = {模式前缀}_{left|right}_{键名};position[0] 固定 0.0;header.frame_id: bottom_cmd。

    • 模式前缀:BOTH_1F → 1F_(如 1F_left_blue);NONE → None_(如 None_right_joystick)。

    • 记录的键(左右手对称,共 20 路;未按住则不发布该条):

      键名

      含义

      black / white / blue / red

      四色面键(白→X、黑→Y、红→A、蓝→B)

      joystick

      摇杆轴心按下)

      ext_start / ext_mode / ext_back

      拓展键(须 enable_extended_buttons: true 且协议有按下)

      ext_rear_up / ext_rear_down

      拓展后侧上/下

    • 不在本话题:扳机 → trigger_cmd;拇指侧向 → thumb_side_cmd;摇杆轴值 → rocker_cmd。

2.2 订阅话题

  • /<SLAVE_ID>/leg_states (sensor_msgs/msg/JointState)(仅折腿遥操启用时)

    • 用于主臂折腿 IK 反馈输入

    • 优先按 name 对齐读取,回退按下标读取

    • 兼容 position.size()==4(标准)或 ==3(兼容路径)


3. 从臂节点(ti5_follow_arm)

包位置:src/ti5_controller/(与主臂同包,独立进程)
启动:ros2 launch ti5_controller ti5_follow.launch.py(launch 文件:src/ti5_controller/launch/ti5_follow.launch.py)
主要配置(src/ti5_controller/config/):

文件

用途

ti5_follow_config.yaml

从臂 ROS 参数(device_id、paired_device_id、腰/头/腿使能、CAN 等)

retargeting_T170C.yaml

与主臂同一份(retargeting_config_file)

ti5_arm_solver_170c.yaml

与主臂同一份(arm_solver_config_file)

3.1 订阅话题

下述话题是否创建受 ti5_follow_config.yaml 的 enable_*/include*OnCan() 影响。

  • 主从遥操输入(来自主臂):

    • /<MASTER_ID>/right_arm_joint_cmd (sensor_msgs/msg/JointState)

    • /<MASTER_ID>/left_arm_joint_cmd (sensor_msgs/msg/JointState)

    • (双臂话题从臂侧 QoS 为 KEEP_LAST depth=1,减轻暂停恢复后 DDS 堆积旧帧;主臂单侧恢复后会对该臂 pulse 瞬时多发一帧 并唤醒发布节拍,control_cmd 本身不再要求带 7 轴。)

    • /<MASTER_ID>/head_joint_cmd (sensor_msgs/msg/JointState)

    • /<MASTER_ID>/waist_joint_cmd (sensor_msgs/msg/JointState)

    • /<MASTER_ID>/leg_joint_cmd (sensor_msgs/msg/JointState)

  • 控制命令:

    • /<MASTER_ID>/control_cmd (sensor_msgs/msg/JointState)

    • 怎么读:看 name[0] 里的命令名;若带 :,冒号前是命令、后面是参数。position 多数情况只是占位,以命令名为准。

    • 进出遥操模式(主臂按档位自动发,从臂据此切换工作模式):

      命令

      主臂何时发布

      从臂收到后

      enter_chassis_control

      一切换到 BOTH_1F(两手 1F)

      底盘节点启用摇杆;从臂不处理此命令

      enter_remote_mode

      BOTH_1F 保持满 1 秒且尚未在遥操态

      进入遥操跟随(REMOTE_OPERATION):清跟随缓存、腰/头/臂开始跟主臂;首帧关节会做平滑衔接

      enter_default_mode

      ① 切到 BOTH_3F(两手 3F)立即发;② 在遥操态切到 NONE 且持续 ≥0.5s(须 none_state_exit_remote: true)

      退出遥操回待机:清跟随缓存、恢复默认速度限制;若配置了退出回默认位姿则做规划

      典型流程:两手 1F 并保持 1s → enter_chassis_control(进 1F 即发)+ enter_remote_mode(满 1s 发)→ 从臂跟随;两手 3F → enter_default_mode → 从臂待机、主臂停发关节。

    • 从臂直接忽略(留给录包/LeRobot 等其它节点):

      • record_bag_start / record_bag_stop

      • toggle_record、drop、quit、mark_failure

    • 单侧臂暂停 / 恢复(默认绑在红键 A,BOTH_1F 且已在遥操态;按住 ≥200ms 后松开触发):

      命令

      主臂何时发布

      从臂做什么

      arm_follow_pause_left / _right

      该侧第一次按红键松开

      该侧停止向电机发跟随;仍收 *_arm_joint_cmd 并更新缓存

      reset_first_point_left / _right

      该侧再次按红键松开(恢复)

      解除暂停;对该侧再做一次首帧平滑衔接(从当前电机位置插到主臂目标),然后恢复跟随

      恢复顺序:主臂先解除该侧轨迹冻结并刷新关节话题 → 等待约 50ms → 发 reset_first_point_*(position 一般为占位,目标角来自仍在发布的 *_arm_joint_cmd)。

    • 腿控与腰控(主臂腿 IK 会话):

      命令

      含义

      refresh_leg_states

      腿控开始前:请从臂立刻读腿位置并发布 leg_states

      leg_hold_disable

      开始腿控:解除腿部 hold,允许下发 leg_joint_cmd

      leg_hold_enable

      腿控结束:非共享腿轴进入 hold

      set_waist_teleop_paused

      1 暂停腰遥操 CAN;0 恢复(腿控期间避免腰/腿抢轴)

      set_leg_hold_disabled

      旧命令,仍兼容;请优先用 leg_hold_enable / leg_hold_disable

    • 其它:adjust_speed_slow/fast(及 _left/_right 分侧版)、aoyihand_toggle_*_mode 等,见 src/ti5_controller/config/ti5_master_config.yaml 按键映射注释。

3.2 发布话题

  • /<SLAVE_ID>/right_arm_states (sensor_msgs/msg/JointState)

    • name: 右臂关节名

    • position: 右臂电机角(弧度)

    • header.frame_id: <SLAVE_ID>

  • /<SLAVE_ID>/left_arm_states (sensor_msgs/msg/JointState)

    • 同上,左臂

  • /<SLAVE_ID>/head_states (sensor_msgs/msg/JointState)

    • name: 头部 R/P/Y

    • position: [roll,pitch,yaw](弧度)

    • header.frame_id: <SLAVE_ID>

  • /<SLAVE_ID>/waist_states (sensor_msgs/msg/JointState)

    • name: 腰部关节名(长度可为 1 或更多)

    • position: 腰部电机角(弧度)

    • header.frame_id: <SLAVE_ID>

  • /<SLAVE_ID>/leg_states (sensor_msgs/msg/JointState)

    • name: 腿关节名(retargeting leg 顺序)

    • position: 腿电机角(弧度;与 CAN/0x44 约定一致)

    • velocity/effort: 当前填 0 占位

    • header.frame_id: <SLAVE_ID>


4. 灵巧手节点(ti5_aoyihand_node,傲意 ROHand)

包位置:src/ti5_aoyihand/
启动:ros2 launch ti5_aoyihand ti5_aoyihand.launch.py(launch 文件:src/ti5_aoyihand/launch/ti5_aoyihand.launch.py)
主要配置:src/ti5_aoyihand/config/ti5_aoyihand_node.yaml(串口、device_id、master_device_id、控制模式等)
源码:src/ti5_aoyihand/src/ti5_aoyihand_node.cpp

备注

说明:与主臂同一网段侧运行。订阅挂在 master_device_id(主臂)。夹爪形态状态挂在 device_id(与从臂一致),仅由本节点在串口上完成「控制帧 → 延时 → 查询帧」后发布;ti5_follow_arm 不参与 Aoyi 指令下发或状态读取。

4.1 订阅话题

  • /<MASTER_ID>/trigger_cmd (sensor_msgs/msg/JointState)

    • 与主臂发布约定一致:name 中 right_trigger / left_trigger,或无名时 position[0]=右、position[1]=左,值约 [0,1]

    • 映射为各指弯曲标量(实现内缩放至 0~65000 下发串口);five_finger / three_finger / two_finger 模式下依次参与映射的指序号递减(拇指侧向由 thumb_side_cmd 单独驱动为第 6 路)

  • /<MASTER_ID>/thumb_side_cmd (sensor_msgs/msg/JointState)

    • name:right_thumb_side / left_thumb_side,或无名时 position[0]=右、position[1]=左

    • 映射到第 6 路(拇指侧向轴)

  • /<MASTER_ID>/control_cmd (sensor_msgs/msg/JointState)

    • 使用 name[0] 字符串;当收到 aoyihand_toggle_left_mode / aoyihand_toggle_right_mode 时,在防抖间隔后分别循环切换左/右手控制模式:five_finger → three_finger → two_finger → …

4.2 发布话题(与 Dex1 对齐,由本节点闭环产生)

每个控制周期内顺序为:对单侧下发 Set_Finger_PosAll → 等待 query_delay_ms(默认 3ms)→ 查询指位/力 → 按 state_publish_hz 分频发布。

  • /<SLAVE_ID>/right_gripper_states (sensor_msgs/msg/JointState)

  • /<SLAVE_ID>/left_gripper_states (sensor_msgs/msg/JointState)

<SLAVE_ID> = 参数 device_id(src/ti5_aoyihand/config/ti5_aoyihand_node.yaml)。话题名与 §5 Dex1 相同,字段含义不同;Dex1 字段说明见 §5.2。

4.3 可选:模式话题(主臂侧)

  • /<MASTER_ID>/aoyihand_mode (std_msgs/msg/String)(publish_mode_topic=true 时)

    • 启动及每次切换模式时发布

    • data 格式:left:<mode> 或 right:<mode>,<mode> 为 five_finger | three_finger | two_finger


5. 夹爪节点(dex1_gripper_node,宇树 Dex1-1)

包位置:src/dex1_gripper_ros2/
启动:ros2 launch dex1_gripper_ros2 dex1_gripper.launch.py(launch 文件:src/dex1_gripper_ros2/launch/dex1_gripper.launch.py)
主要配置:src/dex1_gripper_ros2/config/dex1_gripper.yaml(device_id、master_device_id、开合角标定、力矩保护等)
源码:src/dex1_gripper_ros2/src/dex1_gripper_node.cpp
标定/探测脚本(非 ROS 包内):串口can口权限配置/install_scripts/(如 calibrate_dex1_gripper.sh、detect_dex1_gripper.sh)

5.1 订阅话题

  • /<MASTER_ID>/trigger_cmd (sensor_msgs/msg/JointState)(subscribe_master_trigger=true 时)

    • 读取优先级:

      1. 按 name 匹配 right_trigger/left_trigger

      2. 回退 position[0]=右、position[1]=左

    • 将触发量按 trigger_open/trigger_close 线性映射到夹爪目标角 q_open_rad~q_close_rad

5.2 发布话题:*_gripper_states

话题

话题

消息类型

出现条件

/<SLAVE_ID>/right_gripper_states

sensor_msgs/msg/JointState

探测到 预设 id=0 的 M4010 电机

/<SLAVE_ID>/left_gripper_states

sensor_msgs/msg/JointState

探测到 预设 id=1 的 M4010 电机

<SLAVE_ID> = 参数 device_id(默认 ti5_slave_001,见 dex1_gripper.yaml)。左右侧由节点预设的固定电机 id 区分:id=0 → 右,id=1 → 左。

备注

说明:与 §4 灵巧手话题名相同;同机勿并行 launch dex1_gripper_node 与 ti5_aoyihand_node。

消息形态

  • 类型:sensor_msgs/msg/JointState(ROS2 标准关节状态)。

  • 每侧只描述 1 个夹爪关节:name、position、velocity、effort 四个数组长度均为 1(无 name 与数组长度不一致的情况)。

  • 未填充的 JointState 可选字段(如空 header 以外的扩展)本节点不使用。

备注

字段说明:发布代码见 dex1_gripper_node.cpp:state.q/dq/tau 为 Unitree SDK 电机侧反馈,再经减速比换算到 关节侧 写入话题。

字段

ROS 类型

数组下标

单位

取值 / 换算

含义

header.stamp

builtin_interfaces/Time

—

—

控制环内本电机 sendRecv 完成时刻

状态采样时间

header.frame_id

string

—

—

等于 device_id 字符串

非 TF 帧名,仅作设备标识

name[0]

string

0

—

right_gripper 或 left_gripper

与预设 id 对应(0=右,1=左)

position[0]

float64

0

rad

position[0] = state.q / gear_ratio

关节侧当前角。M4010 gear_ratio = 25。本机标定:q_close_rad=0(全闭)、q_open_rad=5.0(全开),数值越大夹爪越开

velocity[0]

float64

0

rad/s

velocity[0] = state.dq / gear_ratio

关节侧角速度(电机 dq 经减速比)

effort[0]

float64

0

与力矩保护同量纲

effort[0] = -state.tau × gear_ratio

电机反馈力矩链(tau 取反后乘减速比)。夹持越紧通常 绝对值越大;本机 effort_limit: 3.0,注释量级约 0~5

电机侧 ↔ 关节侧(gear_ratio = 25)

量

电机侧(SDK state.*)

话题(关节侧)

位置

state.q(rad)

position[0] = state.q / 25

速度

state.dq(rad/s)

velocity[0] = state.dq / 25

力矩

state.tau

effort[0] = -state.tau × 25

控制下发时节点做相反换算:cmd.q = q_cmd × gear_ratio(目标角 q_cmd 与 position[0] 同量纲,均在关节侧 rad)。

与配置参数的对应

  • position[0] 应与 q_open_rad / q_close_rad 同标度:录包时全闭接近 0,全开接近 5(本机标定值,改 YAML 后以新标定为准)。

  • 主臂 trigger_cmd 经 trigger_open / trigger_close 映射的目标角也在该 rad 标度上;不是扳机的 [0,1] 无量纲值。

  • effort_limit_enable=true 时,节点用 r = |effort[0]| / effort_limit 做单调钳位、kp 软化、超限回退等(详见 dex1_gripper.yaml 注释);订阅方若做力控显示,应直接使用 effort[0],勿再乘减速比。

发布频率

  • 控制周期:control_period_us(默认 2000 µs → 500 Hz 控制环)。

  • 状态发布:state_publish_hz(默认 100 Hz),在控制环内分频;header.stamp 为该次发布对应的控制周期时刻,非独立慢速采样。

  • 仅扫描登记成功的电机会创建 publisher;未接线一侧无对应话题。


6. 底盘节点(lanxin_chassis_controller_node,蓝芯 VMR)

包位置:src/lanxin_chassis_controller/
启动:ros2 launch lanxin_chassis_controller lanxin_chassis_launch.py(launch 文件:src/lanxin_chassis_controller/launch/lanxin_chassis_launch.py)
主要配置:src/lanxin_chassis_controller/config/lanxin_chassis_config.yaml(VMR IP、速度缩放、话题前缀、master_device_id 等)
源码:src/lanxin_chassis_controller/src/lanxin_chassis_controller_node.cpp

备注

说明:与主臂同网段即可;不随 ti5_follow.launch.py 启动。默认通过 完整话题名或 master_topic_prefix 订阅主臂侧 control_cmd、rocker_cmd(配置里常为 /ti5_master_001/...)。

6.1 订阅话题

  • /<MASTER_ID>/control_cmd (sensor_msgs/msg/JointState)

    • 取 name[0],并按 : 截断命令基名

    • enable_cmd(默认 enter_chassis_control)使能底盘摇杆

    • disable_cmds(默认 enter_default_mode)停用底盘摇杆控制

  • /<MASTER_ID>/rocker_cmd (sensor_msgs/msg/JointState)

    • 支持两种解析:

      1. 按 name 匹配 left_joy_x/y, right_joy_x/y

      2. 回退按下标 [0..3]

    • 映射关系:

      • 左摇杆左右 -> 角速度 wz

      • 右摇杆前后 -> 线速度 vx

      • 右摇杆左右 -> 线速度 vy

6.2 发布话题

  • 无 ROS 话题发布。

  • 控制输出通过 VMR SDK 直接下发底盘速度(非 ROS 话题)。