ROS2_control控制器配置YAML
前一节学习了launch的一些基本操作接下来学习一下YAML它负责这些控制器怎么工作YAML是一种配置文件格式。ROS2常用它来写参数最基础结构节点名: ros__parameters: 参数名: 参数值例如我的remote_control.yamlvt13_chassis_remote_node: ros__parameters: joy_topic: /astra/remote/joy publish_rate: 50.0意思是给vt13_chassis_remote_node这个节点传入两个参数joy_topic/astra/remote/joypublish_rate50.0注意两个下划线ros__parametersYAML基本语法字符串base_frame_id: base_link数字update_rate: 1000布尔值enable_odom_tf: true列表joints: - arm_j6_joint - arm_j5_joint - arm_j4_joint嵌套对象kinematics: wheels_radius: 0.076 base_frame_offset: x: 0.0 y: 0.0 theta: 0.0YAML 最重要的是缩进。缩进错了配置含义就变了。接下来来看我的enginner_real.yamlcontroller_manager: ros__parameters: update_rate: 1000 joint_state_broadcaster: type: joint_state_broadcaster/JointStateBroadcaster chassis_controller: type: mecanum_drive_controller/MecanumDriveController arm_controller: type: joint_trajectory_controller/JointTrajectoryController gripper_controller: type: forward_command_controller/ForwardCommandController chassis_controller: ros__parameters: front_left_wheel_command_joint_name: front_left_wheel_joint front_right_wheel_command_joint_name: front_right_wheel_joint rear_right_wheel_command_joint_name: rear_right_wheel_joint rear_left_wheel_command_joint_name: rear_left_wheel_joint kinematics: wheels_radius: 0.076 sum_of_robot_center_projection_on_X_Y_axis: 0.51 base_frame_offset: x: 0.0 y: 0.0 theta: 0.0 base_frame_id: base_link odom_frame_id: odom enable_odom_tf: true use_stamped_vel: false reference_timeout: 0.5 arm_controller: ros__parameters: joints: - arm_j6_joint - arm_j5_joint - arm_j4_joint - arm_j3_joint - arm_j1_joint - arm_pitch1_joint command_interfaces: - position state_interfaces: - position - velocity allow_partial_joints_goal: true open_loop_control: false constraints: goal_time: 1.0 stopped_velocity_tolerance: 0.05 gripper_controller: ros__parameters: joints: - gripper_joint interface_name: position第一块告诉controller_manager有哪些控制器。controller_manager: ros__parameters: update_rate: 1000 joint_state_broadcaster: type: joint_state_broadcaster/JointStateBroadcaster chassis_controller: type: mecanum_drive_controller/MecanumDriveController arm_controller: type: joint_trajectory_controller/JointTrajectoryController gripper_controller: type: forward_command_controller/ForwardCommandController这部分不是控制参数而是“注册控制器类型”。可以理解成controller_manager我有四个 controller 1. joint_state_broadcaster 2. chassis_controller 3. arm_controller 4. gripper_controller 它们分别是什么类型请按 type 加载。第二块分别给每个控制器传参数。chassis_controller: ros__parameters: ...arm_controller: ros__parameters: ...gripper_controller: ros__parameters: ...所以结构是controller_manager: ros__parameters: 控制器名字: type: 控制器类型 控制器名字: ros__parameters: 这个控制器自己的参数这是ros2_control配置的核心模式。YAML 只是声明spawner 才是启动在 YAML 里写chassis_controller: type: mecanum_drive_controller/MecanumDriveController只是告诉系统有一个 controller 叫 chassis_controller但它还没启动。真正启动它的是_spawner(chassis_controller)完整流程是1. launch 启动 ros2_control_node 2. ros2_control_node 读取 engineer_real.yaml 3. controller_manager 知道有哪些 controller 4. spawner 请求 controller_manager 启动 controller所以记住YAML 负责配置 spawner 负责启动joint_state_broadcaster 是什么joint_state_broadcaster: type: joint_state_broadcaster/JointStateBroadcaster它的作用是读取所有关节状态然后发布/joint_states。比如arm_j6_joint 的 position arm_j6_joint 的 velocity front_left_wheel_joint 的 velocity这些状态会被 RViz、robot_state_publisher、上层控制逻辑使用。它通常必须启动否则机器人模型状态不会正常更新。chassis_controller 是什么底盘控制器chassis_controller: ros__parameters: front_left_wheel_command_joint_name: front_left_wheel_joint front_right_wheel_command_joint_name: front_right_wheel_joint rear_right_wheel_command_joint_name: rear_right_wheel_joint rear_left_wheel_command_joint_name: rear_left_wheel_joint这里告诉麦轮控制器四个轮子的 joint 分别叫什么这些名字必须和 xacro 里的关节名一致。比如我的enginner_real.xacro是?xml version1.0? robot xmlns:xacrohttp://www.ros.org/wiki/xacro nameengineer xacro:include filename$(find tide_robot_description)/urdf/base/engineer_description_base.urdf.xacro/ ros2_control nameTideHardwareInterface typesystem hardware plugintide_hw_interface/TideHardwareInterface/plugin param nameenable_virtual_controlfalse/param param nameneed_calibrationfalse/param param nametransportserial/param param nameserial_port/dev/ttySTM32/param param nameserial_baudrate115200/param param nameserial_command_period0.02/param /hardware joint namefront_left_wheel_joint param namecan_buscan0/param param nametx_id1/param param nameoffset0/param param namemotor_typeM3508/param command_interface namevelocity/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ /joint joint namefront_right_wheel_joint param namecan_buscan0/param param nametx_id2/param param nameoffset0/param param namemotor_typeM3508/param command_interface namevelocity/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ /joint joint namerear_left_wheel_joint param namecan_buscan0/param param nametx_id3/param param nameoffset0/param param namemotor_typeM3508/param command_interface namevelocity/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ /joint joint namerear_right_wheel_joint param namecan_buscan0/param param nametx_id4/param param nameoffset0/param param namemotor_typeM3508/param command_interface namevelocity/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ /joint joint namearm_j6_joint param namecan_buscan1/param param nametx_id1/param param nameoffset0/param param namemotor_typeDM3510/param param namekp40.0/param param namekd2.0/param command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ state_interface nametemperature/ /joint joint namearm_j5_joint param namecan_buscan1/param param nametx_id3/param param nameoffset0/param param namemotor_typeDM3510/param param namekp40.0/param param namekd2.0/param command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ state_interface nametemperature/ /joint joint namearm_j4_joint param namecan_buscan1/param param nametx_id5/param param nameoffset0/param param namemotor_typeDM3510/param param namekp40.0/param param namekd2.0/param command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ state_interface nametemperature/ /joint joint namearm_j3_joint param namecan_buscan1/param param nametx_id7/param param nameoffset0/param param namemotor_typeDM/param param namekp30.0/param param namekd1.5/param command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ state_interface nametemperature/ /joint joint namearm_j1_joint param namecan_buscan1/param param nametx_id9/param param nameoffset0/param param namemotor_typeDM/param param namekp30.0/param param namekd1.5/param command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ state_interface nametemperature/ /joint joint namearm_pitch1_joint param namecan_buscan1/param param nametx_id19/param param nameoffset0/param param namemotor_typeDM/param param namekp25.0/param param namekd1.0/param command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ state_interface nametemperature/ /joint joint namegripper_joint param namecan_buscan1/param param nametx_id17/param param nameoffset0/param param namemotor_typeDM3510/param param namekp20.0/param param namekd0.8/param command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/ state_interface nameeffort/ state_interface nametemperature/ /joint /ros2_control /robot里有joint namefront_left_wheel_joint所以 YAML 里也必须写front_left_wheel_command_joint_name: front_left_wheel_joint名字错一个字符controller 就找不到硬件接口。底盘运动学参数kinematics: wheels_radius: 0.076 sum_of_robot_center_projection_on_X_Y_axis: 0.51这两个参数用于麦轮运动学解算。简单理解你给底盘一个 vx、vy、wz 控制器根据轮半径和车体尺寸 算出四个轮子的转速例如你发一个速度命令前进 1 m/smecanum_drive_controller会计算四个轮子应该转多快。frame 参数是什么base_frame_id: base_link odom_frame_id: odom enable_odom_tf: true这和 TF 坐标系有关。简单理解odom 是世界/里程计坐标系 base_link 是机器人底盘坐标系如果enable_odom_tf: true控制器会发布odom - base_link这表示机器人在世界里移动到了哪里。reference_timeout 是什么reference_timeout: 0.5意思是如果 0.5 秒没有收到新的速度指令就认为控制输入超时这是安全参数。真机很重要。比如遥控器断了不能让机器人继续沿着上一条速度命令跑。use_stamped_vel 是什么use_stamped_vel: false如果是false控制器通常接收普通速度消息geometry_msgs/msg/Twist如果是true可能接收带时间戳的速度消息geometry_msgs/msg/TwistStamped我的遥控配置里底盘 topic 是vt13_chassis_remote_node: ros__parameters: joy_topic: /astra/remote/joy chassis_topic: /chassis_controller/reference_unstamped gripper_topic: /gripper_controller/commands motor_enable_topic: /astra/motor_enable # Axes come from STM32 AstraRemoteState.ch[] after normalization to [-1, 1]. # Default: ch1 forward/back, ch0 left/right, ch2 yaw. linear_x_axis: 1 linear_y_axis: 0 angular_z_axis: 2 linear_x_scale: 1.0 linear_y_scale: 1.0 angular_z_scale: 2.0 deadband: 0.05 # Button mapping from /astra/remote/joy: # 0..15 key bits, 16 mouse_l, 17 mouse_r, # 18 mode_sw 1, 19 mode_sw 2, 20 mode_sw 3, 21 stop. enable_button_index: 19 disable_button_index: 21 gripper_open_button_index: 17 gripper_close_button_index: 16 gripper_open_position: 0.6 gripper_close_position: -0.6 publish_rate: 50.0 remote_timeout: 0.3chassis_topic: /chassis_controller/reference_unstamped这也说明现在走的是不带时间戳的速度命令。arm_controller 是什么arm_controller: ros__parameters: joints: - arm_j6_joint - arm_j5_joint - arm_j4_joint - arm_j3_joint - arm_j1_joint - arm_pitch1_joint这是机械臂轨迹控制器。它控制这些关节的位置。command_interfaces: - position意思是这个 controller 往硬件写 position 命令state_interfaces: - position - velocity意思是这个 controller 从硬件读取 position 和 velocity 状态这也必须和 xacro 匹配。我的 xacro 里机械臂关节有command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/所以 YAML 里写position/velocity是合理的。open_loop_control 是什么真机配置open_loop_control: false仿真配置open_loop_control: true简单理解false控制器更依赖真实反馈 true控制器更相信自己发出去的命令真机有真实电机反馈所以用open_loop_control: false仿真里经常简化处理所以用open_loop_control: trueconstraints 是什么constraints: goal_time: 1.0 stopped_velocity_tolerance: 0.05这是轨迹控制器的目标约束。大致意思goal_time: 允许目标完成有 1 秒误差 stopped_velocity_tolerance: 认为停止时的速度容忍值如果机械臂执行轨迹时误差太大controller 可能认为执行失败。gripper_controller 是什么gripper_controller: ros__parameters: joints: - gripper_joint interface_name: position这是一个简单的 forward command controller。它不做复杂轨迹规划只是把收到的命令直接转发给gripper_joint的position接口。遥控节点会往这里发命令gripper_topic: /gripper_controller/commands所以链路是遥控器按钮 - vt13_chassis_remote_node - /gripper_controller/commands - gripper_controller - gripper_joint position怎么从零写一个 controller YAML最小模板controller_manager: ros__parameters: update_rate: 100 my_controller: type: some_controller_package/SomeController my_controller: ros__parameters: some_param: 123对你这个工程新增 controller 一般要三步。第一步在 YAML 里注册类型controller_manager: ros__parameters: my_controller: type: xxx/XXXController第二步写它自己的参数my_controller: ros__parameters: joints: - some_joint interface_name: position第三步在controller.py里启动它_spawner(my_controller)名字必须三处一致YAML controller_manager 里的名字 YAML 参数块的名字 controller.py spawner 的名字举个实际例子如果你要新增一个 lift_controller假设你以后有一个升降关节lift_joint它用 position 控制。YAML 可以这样写controller_manager: ros__parameters: lift_controller: type: forward_command_controller/ForwardCommandController lift_controller: ros__parameters: joints: - lift_joint interface_name: position然后controller.py里加_spawner(lift_controller)但是还不够xacro 里也必须有joint namelift_joint command_interface nameposition/ state_interface nameposition/ state_interface namevelocity/ /joint所以新增一个可控关节时通常要同时检查三处xacro硬件有没有这个 joint 和 interface YAMLcontroller 有没有配置这个 joint controller.pycontroller 有没有被 spawner 启动写这类代码时的顺序以后不要上来就乱写 YAML。按这个顺序先看 xacro 里有哪些 joint。看 joint 支持什么command_interface。选择合适的 controller 类型。在 YAML 的controller_manager里注册 controller。给 controller 写参数。在controller.py里添加 spawner。启动后用命令检查 controller 状态。验证命令ros2 control list_controllers看接口ros2 control list_hardware_interfaces看参数ros2 param list /controller_manager看话题ros2 topic list最容易犯的错YAML 缩进错。错误arm_controller: ros__parameters:正确arm_controller: ros__parameters:controller 名字不一致。错误arm_controller:但 spawner 写_spawner(arm_control)joint 名字和 xacro 不一致。错误front_left_wheel_joint_name: front_left_joint但 xacro 实际是front_left_wheel_jointinterface 不匹配。YAML 写command_interfaces: - velocity但 xacro 只有command_interface nameposition/这样 controller 会启动失败。控制器的type不是你随便起的名字它来自 ROS 2 已安装的 controller 插件可以理解成控制器名字: type: 提供这个控制器的包名/控制器插件类名比如arm_controller: type: joint_trajectory_controller/JointTrajectoryController意思是我创建一个叫 arm_controller 的控制器 它使用 joint_trajectory_controller 包里的 JointTrajectoryController 插件。怎么知道该用哪个类型先不要从type开始想应该从“我要控制什么”开始想。你想做什么常用 controller type发布所有关节状态/joint_statesjoint_state_broadcaster/JointStateBroadcaster控制机械臂按轨迹运动joint_trajectory_controller/JointTrajectoryController直接给某个关节发位置/速度/力矩命令forward_command_controller/ForwardCommandController控制差速底盘diff_drive_controller/DiffDriveController控制麦轮底盘mecanum_drive_controller/MecanumDriveController类型从哪里查第一种最直接启动后查当前系统支持哪些 controller type。ros2 control list_controller_types如果 controller manager 已经运行它会列出类似joint_state_broadcaster/JointStateBroadcaster joint_trajectory_controller/JointTrajectoryController forward_command_controller/ForwardCommandController mecanum_drive_controller/MecanumDriveController第二种查有哪些 controller 包PowerShell 里可以用ros2 pkg list | Select-String controller你看到类似这些包名就说明系统里装了对应 controllerjoint_state_broadcaster joint_trajectory_controller forward_command_controller mecanum_drive_controller controller_manager第三种看你的package.xml。你工程里已经依赖了这些dependcontroller_manager/depend dependros2_control/depend dependros2_controllers/depend exec_dependmecanum_drive_controller/exec_depend这说明当前 bringup 包运行时需要ros2_control、ros2_controllers和mecanum_drive_controller。以上是我最近在写一个项目刚好用到了这些突发奇想写一些可能又不全的地方也可能有错误的地方还希望各位大佬指正