简化理解:**上层负责“要去哪里、怎么平滑地过去”;控制器和硬件接口负责“按周期执行”;总线后端负责“把控制目标翻译成对应电机协议”;电机反馈再沿原路返回,供状态显示、下一周期控制和动作结果检查使用。
1. 目标入口:关节目标或末端位姿
项目支持两类目标:
- 关节目标:直接给每个关节一个目标角度,由
send_trajectory.py生成轨迹。 - 末端位姿目标:给出机械臂末端的位置和姿态,由
move_pose.py调用运动学求解,把它转换成关节目标,再进入同一条轨迹链路。
运动学参数、关节轴和工具参数来自配置,尽量让运动学计算和机器人模型使用同一套数据。配置没有真实标定时,系统不会把示例几何参数当成已经标定好的实机模型。
2. 轨迹规划:让关节平滑运动
轨迹规划发生在真正发给 ROS2 控制器之前。
单个目标使用三次贝塞尔轨迹,让关节从当前反馈位置平滑地移动到目标位置。多个连续路点使用五次贝塞尔轨迹,让相邻路段连接时位置、速度、加速度连续;如果某个关节在路点处要反向,连接速度会设为零。
轨迹规划还会检查关节位置、速度和配置了的加速度限制。多路点轨迹使用曲线控制点的保守范围来检查约束,而不是只抽几个时间点采样;这样能避免采样点之间的曲线超限。不过,这些是轨迹数学约束,不等同于真实机械臂的碰撞检测或动态响应保证。
3. ROS2 执行:把轨迹交给硬件接口
轨迹通过FollowJointTrajectory交给joint_trajectory_controller。它负责按时间执行轨迹,并通过controller_manager管理控制器和硬件接口。
ArmSystem是 ROS2 控制框架与底层总线之间的接口。它从控制器接收关节位置命令,并在控制周期中读取总线反馈、更新关节状态。激活时会等待新鲜有效的反馈,并以实际位置初始化命令,避免刚启动就沿用过期目标。
4. 总线与协议:根据配置选择电机控制路径
配置会决定使用哪个后端和协议。不同后端共享控制框架,但协议转换和电机控制逻辑不同。
MIT 七轴路径
MitBus按 MIT 协议编码位置和增益参数,并通过 CAN 发送。协议数据包含位置、速度、Kp、Kd、力矩等字段;当前实现里的速度和力矩前馈保持为零。反馈使用标准帧 ID 0,并根据首字节中的 master ID 分到对应关节。
激活时会清理旧反馈、发送准备指令并使能,然后等待各关节的新反馈;准备就绪后才从实测位置开始控制。失能时发送失能帧。项目明确不会自动发送写零点指令。
RoboMaster C620 路径
RoboMasterBus使用另一套逻辑:主机运行位置 P 控制和速度 PI 控制,再把结果转换成 C620 的电流指令。电流按电机组打包发送,反馈帧则用于更新编码器位置、速度、电流和温度等状态。
因此,MIT 路径里的 Kp/Kd 命令参数,和 RoboMaster 路径里的位置、速度环参数不是一回事,配置校验也会阻止两类控制参数混用。C620 后端也有独立的电流限制、反馈超时和控制看门狗逻辑。
5. 反馈与保护:判断动作实际执行情况
电机返回的 CAN 帧经过驱动收取和协议解析后,进入总线状态,再由ArmSystem转换为 ROS2 关节状态,发布到/joint_states。这些反馈既用于显示当前姿态,也用于后续控制周期和轨迹执行结果检查。
保护逻辑会关注反馈是否新鲜、温度是否超限、速度是否异常,以及通信或状态是否出现故障。故障会被锁存;恢复需要显式操作,避免故障刚消失就自动继续旧轨迹。动作结束时,程序还会检查最终反馈,不能仅凭“命令已经发出”就认定机械臂到位。
6. 怎么看项目的实现和验证边界
项目提供模拟后端,也包含 CAN 和不同电机协议的适配代码。离线测试和模拟闭环可以验证轨迹计算、协议编解码、反馈处理及部分故障逻辑,但不能替代在目标 ROS2 环境和真实硬件上的验收。
第一步:“按配置选择总线与协议”
这句话的意思是,程序启动时会读取 YAML 配置,根据配置决定使用哪个电机后端和通信协议。比如当前选 MIT,就走 MIT 协议的处理逻辑;选 RoboMaster,就走 RoboMaster 的控制逻辑。这是按配置选一条路径,不是几种协议同时控制一台机械臂。
第二步:目标是怎么来的
“运动目标:关节角度或末端位姿”。也就是说,上层可以直接给每个关节一个目标角度,也可以给机械臂末端的位置和姿态。
接下来经过“目标类型”判断:
- 如果已经是关节角度,就可以直接交给
send_trajectory.py规划。 - 如果是末端位姿,先进入
move_pose.py,通过逆运动学 IK 求出一组关节角度,再交给send_trajectory.py。
“系统支持关节空间目标和末端位姿目标,位姿目标会先做逆运动学,之后两种目标统一进入关节轨迹规划。”
第三步:send_trajectory.py负责规划轨迹
单个目标用三次贝塞尔轨迹,让关节从当前状态平滑地移动到目标位置。多路点用五次贝塞尔,让路点连接时的位置、速度和加速度尽量保持连续;如果关节在路点处需要反向,连接速度会设为零。
这里有个重要前提:轨迹起点要来自新鲜的关节反馈,而且机械臂需要满足静止起步条件。否则系统会拒绝发送,避免从过时或不可靠的状态开始规划。
第四步:轨迹要先过约束检查。
“轨迹约束检查”包括位置、速度、加速度和时长。系统会检查规划出的轨迹是否超出配置的关节范围、速度限制,以及配置了的加速度限制。
如果检查不通过,就拒绝发送,并提示调整目标或增加运动时间;如果通过,才进入下一步。你可以把它理解成一个发送前的安全门槛:不符合配置约束的轨迹不会交给控制器执行。
第五步:轨迹进入 ROS2 控制框架。
检查通过后,send_trajectory.py通过FollowJointTrajectory发送轨迹。joint_trajectory_controller根据轨迹和时间计算每个周期应该下发的关节命令;controller_manager负责管理控制器及其硬件接口。
图里这几个组件的位置画得比较分散,但它们的关系可以这样理解:轨迹 Action 交给轨迹控制器执行,控制器由 controller_manager 管理,并通过硬件接口连接电机。
第六步:到ArmSystem硬件接口。
ArmSystem是 ROS2 控制器与底层总线之间的连接层。上层控制器给它关节命令,它把命令交给选中的总线后端;同时,它周期性读取底层电机状态,再把状态更新给 ROS2。
因此,ArmSystem既参与下发,也参与读取,是整个软件控制链与电机通信链之间的接口。
第七步:按配置进入不同的电机后端。
MIT 分支进入MitBus,按 MIT 协议编码位置和 Kp、Kd 等控制参数,再发给电机。当前实现里的速度和力矩前馈为零。
RoboMaster 分支进入RoboMasterBus,控制方式不一样:主机运行位置 P 环和速度 PI 环,再把结果转换成 C620 的电流指令。
这两条分支的区别很关键。面试时不要笼统地说它们“都发送位置指令”:MIT 路径发送协议定义的控制参数;RoboMaster 路径由主机闭环计算,再发送电流命令。
第八步:驱动、总线和电机。
协议后端准备好帧之后,由底层驱动负责实际收发。项目中针对不同环境有不同驱动选择,例如 SocketCAN、ZLG 或模拟驱动;CAN 帧通过 CAN 总线到达电机。
电机执行命令后,会返回位置、速度、温度等反馈。接收端再解析这些帧,更新对应关节的状态。
第九步:反馈回来并检查状态。
电机反馈经过驱动收帧和协议解析后,进入 Bus 状态与反馈有效性检查。系统会关注反馈是否超时、状态是否有效,以及是否出现温度或速度异常等问题。
如果发现故障,就锁存故障并停止控制或发送失能指令;恢复需要显式操作。没有故障时,反馈通过ArmSystem更新到 ROS2 关节状态,例如/joint_states。
这些反馈还会用于控制过程和动作完成检查。也就是说,系统需要看实际反馈,不能只因为命令发出去了,就认定机械臂已经到位。
“系统启动后根据配置选择电机协议和总线后端。上层可以输入关节角度,也可以输入末端位姿;末端位姿先通过逆运动学转成关节目标。之后由
send_trajectory.py进行贝塞尔轨迹规划,并检查位置、速度、加速度和时长约束。通过检查的轨迹经FollowJointTrajectory交给 ROS2 轨迹控制器执行,再通过ArmSystem和对应的电机后端转换成协议帧,经底层驱动和总线发送给电机。电机反馈沿链路返回,更新关节状态;系统也会检查反馈和故障状态,异常时锁存并停止或失能。”