news 2026/7/11 1:53:49

ROS2_control控制器配置YAML

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ROS2_control控制器配置YAML

前一节学习了launch的一些基本操作,接下来学习一下YAML,它负责这些控制器怎么工作

YAML是一种配置文件格式。ROS2常用它来写参数

最基础结构

节点名: ros__parameters: 参数名: 参数值

例如我的remote_control.yaml

vt13_chassis_remote_node: ros__parameters: joy_topic: "/astra/remote/joy" publish_rate: 50.0

意思是:给vt13_chassis_remote_node这个节点传入两个参数:

joy_topic="/astra/remote/joy"

publish_rate=50.0

注意两个下划线:ros__parameters

YAML基本语法:

字符串:

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.0

YAML 最重要的是缩进。缩进错了,配置含义就变了。

接下来来看我的enginner_real.yaml

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" 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 version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="engineer"> <xacro:include filename="$(find tide_robot_description)/urdf/base/engineer_description_base.urdf.xacro"/> <ros2_control name="TideHardwareInterface" type="system"> <hardware> <plugin>tide_hw_interface/TideHardwareInterface</plugin> <param name="enable_virtual_control">false</param> <param name="need_calibration">false</param> <param name="transport">serial</param> <param name="serial_port">/dev/ttySTM32</param> <param name="serial_baudrate">115200</param> <param name="serial_command_period">0.02</param> </hardware> <joint name="front_left_wheel_joint"> <param name="can_bus">can0</param> <param name="tx_id">1</param> <param name="offset">0</param> <param name="motor_type">M3508</param> <command_interface name="velocity"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> </joint> <joint name="front_right_wheel_joint"> <param name="can_bus">can0</param> <param name="tx_id">2</param> <param name="offset">0</param> <param name="motor_type">M3508</param> <command_interface name="velocity"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> </joint> <joint name="rear_left_wheel_joint"> <param name="can_bus">can0</param> <param name="tx_id">3</param> <param name="offset">0</param> <param name="motor_type">M3508</param> <command_interface name="velocity"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> </joint> <joint name="rear_right_wheel_joint"> <param name="can_bus">can0</param> <param name="tx_id">4</param> <param name="offset">0</param> <param name="motor_type">M3508</param> <command_interface name="velocity"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> </joint> <joint name="arm_j6_joint"> <param name="can_bus">can1</param> <param name="tx_id">1</param> <param name="offset">0</param> <param name="motor_type">DM3510</param> <param name="kp">40.0</param> <param name="kd">2.0</param> <command_interface name="position"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> <state_interface name="temperature"/> </joint> <joint name="arm_j5_joint"> <param name="can_bus">can1</param> <param name="tx_id">3</param> <param name="offset">0</param> <param name="motor_type">DM3510</param> <param name="kp">40.0</param> <param name="kd">2.0</param> <command_interface name="position"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> <state_interface name="temperature"/> </joint> <joint name="arm_j4_joint"> <param name="can_bus">can1</param> <param name="tx_id">5</param> <param name="offset">0</param> <param name="motor_type">DM3510</param> <param name="kp">40.0</param> <param name="kd">2.0</param> <command_interface name="position"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> <state_interface name="temperature"/> </joint> <joint name="arm_j3_joint"> <param name="can_bus">can1</param> <param name="tx_id">7</param> <param name="offset">0</param> <param name="motor_type">DM</param> <param name="kp">30.0</param> <param name="kd">1.5</param> <command_interface name="position"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> <state_interface name="temperature"/> </joint> <joint name="arm_j1_joint"> <param name="can_bus">can1</param> <param name="tx_id">9</param> <param name="offset">0</param> <param name="motor_type">DM</param> <param name="kp">30.0</param> <param name="kd">1.5</param> <command_interface name="position"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> <state_interface name="temperature"/> </joint> <joint name="arm_pitch1_joint"> <param name="can_bus">can1</param> <param name="tx_id">19</param> <param name="offset">0</param> <param name="motor_type">DM</param> <param name="kp">25.0</param> <param name="kd">1.0</param> <command_interface name="position"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> <state_interface name="temperature"/> </joint> <joint name="gripper_joint"> <param name="can_bus">can1</param> <param name="tx_id">17</param> <param name="offset">0</param> <param name="motor_type">DM3510</param> <param name="kp">20.0</param> <param name="kd">0.8</param> <command_interface name="position"/> <state_interface name="position"/> <state_interface name="velocity"/> <state_interface name="effort"/> <state_interface name="temperature"/> </joint> </ros2_control> </robot>

里有:

<joint name="front_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/s

mecanum_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.3
chassis_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 name="position"/> <state_interface name="position"/> <state_interface name="velocity"/>

所以 YAML 里写position/velocity是合理的。

open_loop_control 是什么

真机配置:

open_loop_control: false

仿真配置:

open_loop_control: true

简单理解:

false:控制器更依赖真实反馈 true:控制器更相信自己发出去的命令

真机有真实电机反馈,所以用:

open_loop_control: false

仿真里经常简化处理,所以用:

open_loop_control: true

constraints 是什么

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_jointposition接口。

遥控节点会往这里发命令:

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 name="lift_joint"> <command_interface name="position"/> <state_interface name="position"/> <state_interface name="velocity"/> </joint>

所以新增一个可控关节时,通常要同时检查三处:

xacro:硬件有没有这个 joint 和 interface YAML:controller 有没有配置这个 joint controller.py:controller 有没有被 spawner 启动

写这类代码时的顺序

以后不要上来就乱写 YAML。按这个顺序:

  1. 先看 xacro 里有哪些 joint。
  2. 看 joint 支持什么command_interface
  3. 选择合适的 controller 类型。
  4. 在 YAML 的controller_manager里注册 controller。
  5. 给 controller 写参数。
  6. controller.py里添加 spawner。
  7. 启动后用命令检查 controller 状态。

验证命令:

ros2 control list_controllers

看接口:

ros2 control list_hardware_interfaces

看参数:

ros2 param list /controller_manager

看话题:

ros2 topic list

最容易犯的错

  1. YAML 缩进错。

错误:

arm_controller: ros__parameters:

正确:

arm_controller: ros__parameters:
  1. controller 名字不一致。

错误:

arm_controller:

但 spawner 写:

_spawner("arm_control")
  1. joint 名字和 xacro 不一致。

错误:

front_left_wheel_joint_name: "front_left_joint"

但 xacro 实际是:

front_left_wheel_joint
  1. interface 不匹配。

YAML 写:

command_interfaces: - velocity

但 xacro 只有:

<command_interface name="position"/>

这样 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"

你看到类似这些包名,就说明系统里装了对应 controller:

joint_state_broadcaster joint_trajectory_controller forward_command_controller mecanum_drive_controller controller_manager

第三种,看你的package.xml。你工程里已经依赖了这些:

<depend>controller_manager</depend> <depend>ros2_control</depend> <depend>ros2_controllers</depend> <exec_depend>mecanum_drive_controller</exec_depend>

这说明当前 bringup 包运行时需要ros2_controlros2_controllersmecanum_drive_controller

以上是我最近在写一个项目刚好用到了这些,突发奇想写一些,可能又不全的地方,也可能有错误的地方,还希望各位大佬指正

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/11 1:50:46

AVL树 vs 红黑树:10万次插入/删除操作性能实测与 5 大选型指南

AVL树 vs 红黑树&#xff1a;10万次插入/删除操作性能实测与5大选型指南在数据库索引、语言标准库实现等需要高效查找的场景中&#xff0c;自平衡二叉搜索树是核心数据结构。AVL树和红黑树作为两种经典实现&#xff0c;各自有着独特的性能特点和适用场景。本文将基于10万次随机…

作者头像 李华
网站建设 2026/7/11 1:48:42

记录BUG:开发violet_agents过程中出现Python的循环导入

记录BUG&#xff1a;开发violet_agents过程中出现Python的循环导入 问题复现 在测试violet_agents包时&#xff0c;我们输入以下代码测试 from violet_agents import SimpleAgent from violet_agents import VioletAgentsLLM from dotenv import load_dotenvload_dotenv() agen…

作者头像 李华
网站建设 2026/7/11 1:48:35

防火玻璃门经常验收不通过?重点把控这几项施工细节

在办公楼、商业综合体、住宅小区消防工程验收阶段&#xff0c;防火玻璃门验收不合格是十分普遍的问题。很多工程采购方和施工人员只看重门扇外观好看&#xff0c;忽略国标规范细节&#xff0c;最后出现返工整改、延误整体工程进度的情况。本文依据GB12955‑2024新版防火门国家标…

作者头像 李华
网站建设 2026/7/11 1:48:12

STM32F723ZE与TPA3128D2构建高性能数字音频系统

1. 项目概述&#xff1a;打造高性能数字音频系统 在数字音频处理领域&#xff0c;如何将高质量音频信号高效地转换为震撼人心的声音输出&#xff0c;一直是工程师们追求的目标。这个项目通过结合ST公司的STM32F723ZE高性能微控制器和TI的TPA3128D2 D类音频功放&#xff0c;构建…

作者头像 李华