ROS2 参数系统
参数概念讲解
1. 什么是参数(Parameter)
在 ROS 2 中,参数(Parameter) 是节点级别的配置值,用于在运行时调整节点的行为。参数可以被看作是节点的"设置项"或"属性",它们允许节点在不需要修改代码的情况下进行灵活配置。
参数的特点:
- 每个节点都可以拥有自己的参数集
- 参数具有名称、类型和值
- 参数可以在节点启动时设置,也可以在运行时动态修改
- 参数值可以被外部工具读取和修改
2. 为什么需要参数机制
参数机制在 ROS 2 开发中扮演着重要角色,它解决了以下问题:
没有参数时的问题
- 修改配置需要修改源代码
- 同一节点无法适应不同场景
- 无法动态调整运行时行为
- 代码耦合度高,灵活性差
有参数后的优势
- 无需重新编译即可调整配置
- 一个节点可适应多种使用场景
- 支持运行时动态调整
- 配置与代码分离,易于维护
说明典型应用场景
- 机器人速度限制、加速度限制
- 传感器采样频率、滤波参数
- PID 控制器的 Kp、Ki、Kd 值
- 算法阈值、超时时间等
3. 参数的基本操作
ROS 2 参数支持以下基本类型:
| 类型 | Python 类型 | 说明 |
|---|---|---|
bool | bool | 布尔值,开关类配置 |
int | int | 整数,如数量、索引等 |
double | float | 浮点数,如速度、比例系数等 |
string | str | 字符串,如路径、名称等 |
byte[] | bytes | 字节数组 |
数组类型 | list | 以上类型的数组形式 |
参数的基本操作包括:
- 声明参数:在节点中定义参数及其默认值
- 获取参数:读取参数的当前值
- 设置参数:修改参数的值
- 删除参数:移除不再需要的参数
学习内容3:参数实践
(一)编写带参数的节点
下面以 countdown_action_server.py 和 countdown_action_client.py 为例,演示如何编写带参数的节点。
1. 带参数的服务端节点
python
import rclpy
from rclpy.node import Node
from rclpy.action import ActionServer
from action_msgs.msg import GoalStatus
from my_action.action import Countdown # 自定义动作接口
class CountdownActionServer(Node):
def __init__(self):
super().__init__('countdown_action_server')
# 声明参数(参数名, 默认值)
self.declare_parameter('start_count', 10) # 起始计数值
self.declare_parameter('step_interval', 1.0) # 计数间隔(秒)
self.declare_parameter('log_enabled', True) # 是否启用日志
# 获取参数值
self.start_count = self.get_parameter('start_count').value
self.step_interval = self.get_parameter('step_interval').value
self.log_enabled = self.get_parameter('log_enabled').value
# 创建动作服务端
self._action_server = ActionServer(
self,
Countdown,
'countdown',
self.execute_callback
)
if self.log_enabled:
self.get_logger().info(
f'动作服务端已启动,参数: start_count={self.start_count}, '
f'step_interval={self.step_interval}, log_enabled={self.log_enabled}'
)
def execute_callback(self, goal_handle):
# 使用参数值作为默认值,可被请求覆盖
count = goal_handle.request.target_count if goal_handle.request.target_count > 0 else self.start_count
feedback_msg = Countdown.Feedback()
result_msg = Countdown.Result()
for i in range(count, 0, -1):
if goal_handle.is_cancel_requested:
goal_handle.canceled()
result_msg.success = False
result_msg.message = '计数被取消'
return result_msg
feedback_msg.current_count = i
goal_handle.publish_feedback(feedback_msg)
if self.log_enabled:
self.get_logger().info(f'反馈: 剩余 {i}')
time.sleep(self.step_interval)
goal_handle.succeed()
result_msg.success = True
result_msg.message = f'计数完成,从 {count} 到 1'
return result_msg
def main(args=None):
rclpy.init(args=args)
node = CountdownActionServer()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()2. 带参数的客户端节点
python
import rclpy
from rclpy.node import Node
from rclpy.action import ActionClient
from my_action.action import Countdown
class CountdownActionClient(Node):
def __init__(self):
super().__init__('countdown_action_client')
# 声明参数
self.declare_parameter('target_count', 5) # 目标计数值
self.declare_parameter('timeout', 30.0) # 超时时间(秒)
self.declare_parameter('auto_start', True) # 是否自动发送请求
# 获取参数值
self.target_count = self.get_parameter('target_count').value
self.timeout = self.get_parameter('timeout').value
self.auto_start = self.get_parameter('auto_start').value
# 创建动作客户端
self._action_client = ActionClient(
self,
Countdown,
'countdown'
)
self.get_logger().info(
f'动作客户端已启动,参数: target_count={self.target_count}, '
f'timeout={self.timeout}, auto_start={self.auto_start}'
)
if self.auto_start:
self.send_goal()
def send_goal(self):
# 等待服务端可用
if not self._action_client.wait_for_server(timeout_sec=self.timeout):
self.get_logger().error('动作服务端不可用')
return
goal_msg = Countdown.Goal()
goal_msg.target_count = self.target_count
self.get_logger().info(f'发送目标: target_count={self.target_count}')
self._send_goal_future = self._action_client.send_goal_async(
goal_msg,
feedback_callback=self.feedback_callback
)
self._send_goal_future.add_done_callback(self.goal_response_callback)
def feedback_callback(self, feedback_msg):
feedback = feedback_msg.feedback
self.get_logger().info(f'收到反馈: 当前计数 {feedback.current_count}')
def goal_response_callback(self, future):
goal_handle = future.result()
if not goal_handle.accepted:
self.get_logger().info('目标被拒绝')
return
self.get_logger().info('目标已被接受')
self._get_result_future = goal_handle.get_result_async()
self._get_result_future.add_done_callback(self.get_result_callback)
def get_result_callback(self, future):
result = future.result().result
self.get_logger().info(f'结果: {result.message}')
rclpy.shutdown()
def main(args=None):
rclpy.init(args=args)
node = CountdownActionClient()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()(二)运行节点并查看参数
编译并运行节点后,可以使用命令行工具查看节点的参数。
1. 查看节点参数列表
bash
# 查看某个节点的所有参数
ros2 param list /countdown_action_server2. 查看参数值
bash
# 查看单个参数的值
ros2 param get /countdown_action_server start_count
ros2 param get /countdown_action_server step_interval
ros2 param get /countdown_action_server log_enabled(三)命令行启动 + 直接赋值参数
在启动节点时,可以通过命令行直接为参数赋值:
bash
# 格式:ros2 run 包名 节点名 --ros-args -p 参数名:=参数值
# 设置单个参数
ros2 run my_action countdown_action_server --ros-args -p start_count:=20
# 设置多个参数
ros2 run my_action countdown_action_server --ros-args \
-p start_count:=15 \
-p step_interval:=0.5 \
-p log_enabled:=false
# 客户端设置参数
ros2 run my_action countdown_action_client --ros-args \
-p target_count:=10 \
-p timeout:=60.0 \
-p auto_start:=true说明参数赋值语法说明
--ros-args:标识后续为 ROS 参数参数-p 参数名:=参数值:设置指定参数的值- 多个参数使用空格分隔
- 字符串参数需要加引号:
-p name:="hello"
(四)YAML 配置文件批量赋值
当参数较多时,使用 YAML 配置文件进行批量管理更加方便。
1. 创建 YAML 配置文件
创建 config/params.yaml 文件:
yaml
# config/params.yaml
countdown_action_server:
ros__parameters:
start_count: 25
step_interval: 0.5
log_enabled: true
countdown_action_client:
ros__parameters:
target_count: 8
timeout: 45.0
auto_start: true2. 使用 YAML 文件启动节点
bash
# 格式:ros2 run 包名 节点名 --ros-args --params-file 配置文件路径
ros2 run my_action countdown_action_server --ros-args --params-file config/params.yaml3. YAML 配置文件结构说明
yaml
# YAML 配置文件结构
节点名称:
ros__parameters:
参数名1: 参数值1
参数名2: 参数值2
...
# 示例:数组参数
my_node:
ros__parameters:
int_list: [1, 2, 3, 4, 5]
string_list: ["hello", "world"]
double_list: [1.0, 2.5, 3.14](五)参数相关命令
ROS 2 提供了完整的参数管理命令行工具:
1. ros2 param list - 列出参数
bash
# 列出指定节点的所有参数
ros2 param list /countdown_action_server
# 列出所有节点的参数
ros2 param list2. ros2 param get - 获取参数值
bash
# 获取指定参数的值
ros2 param get /countdown_action_server start_count
# 输出示例:
# Parameter type: integer
# Parameter value: 103. ros2 param set - 设置参数值
bash
# 动态设置参数值(运行时修改)
ros2 param set /countdown_action_server start_count 30
# 设置不同类型的参数
ros2 param set /countdown_action_server step_interval 2.0
ros2 param set /countdown_action_server log_enabled false
ros2 param set /countdown_action_server name_string "hello"⚠️ 警告注意
使用
ros2 param set设置参数值时,节点必须正在运行。该命令会立即修改运行中节点的参数值,但不会持久化保存。节点重启后参数将恢复为代码中的默认值或启动时指定的值。
4. ros2 param delete - 删除参数
bash
# 删除指定参数(仅对动态声明的参数有效)
ros2 param delete /countdown_action_server temp_parameter说明参数删除限制
在代码中使用
declare_parameter()声明的参数是静态参数,通常无法通过ros2 param delete删除。只有通过代码动态添加的参数才可以被删除。
5. ros2 param dump - 导出参数
bash
# 将节点的当前参数导出为 YAML 文件
ros2 param dump /countdown_action_server
# 指定输出路径
ros2 param dump /countdown_action_server --output-dir ./config/
# 输出示例(默认保存为节点名.yaml):
# countdown_action_server.yaml6. 参数命令汇总表
| 命令 | 功能 | 示例 |
|---|---|---|
ros2 param list | 列出节点的所有参数 | ros2 param list /node_name |
ros2 param get | 获取参数值 | ros2 param get /node_name param_name |
ros2 param set | 设置参数值 | ros2 param set /node_name param_name value |
ros2 param delete | 删除参数 | ros2 param delete /node_name param_name |
ros2 param dump | 导出参数到 YAML | ros2 param dump /node_name |
参数使用最佳实践
1. 参数命名规范
- 使用小写字母和下划线:
max_speed、sample_rate - 命名应具有描述性,便于理解用途
- 避免使用过于简短的名称:
ms→max_speed
2. 参数默认值设置
python
# 推荐:在声明参数时提供合理的默认值
self.declare_parameter('max_speed', 1.0)
self.declare_parameter('enable_logging', True)
self.declare_parameter('device_port', '/dev/ttyUSB0')3. 参数变更回调
当需要在参数值变化时执行特定操作,可以设置参数变更回调:
python
from rclpy.callback_groups import MutuallyExclusiveCallbackGroup
class MyNode(Node):
def __init__(self):
super().__init__('my_node')
self.declare_parameter('interval', 1.0)
# 添加参数变更回调
self.add_on_set_parameters_callback(self.parameter_callback)
def parameter_callback(self, params):
for param in params:
if param.name == 'interval':
if param.value <= 0:
self.get_logger().warn('interval 必须大于 0')
return SetParametersResult(successful=False)
self.get_logger().info(f'interval 已更新为: {param.value}')
return SetParametersResult(successful=True)