2. 参数声明与初始化:declare_parameter()方法、节点启动时参数初始化、参数默认值设置
参数声明,说白了就是告诉ROS2系统:「我这个节点要用到哪些配置项」。很多新手上来就直接用get_parameter()去拿参数,结果节点一跑就崩——因为参数根本不存在。嗯,这里要注意,ROS2的参数系统是「先声明,后使用」的。
2.1 declare_parameter() 的核心用法
我个人习惯在节点的构造函数里就把所有参数声明好。这样代码结构清晰,别人接手你的代码时一眼就能看到所有可配置项。
// C++ 示例
class MyNode : public rclcpp::Node
{
public:
MyNode() : Node("my_node")
{
// 声明参数,并设置默认值
this->declare_parameter<std::string>("robot_name", "default_robot");
this->declare_parameter<double>("max_speed", 1.0);
this->declare_parameter<int>("log_level", 20);
this->declare_parameter<std::vector<double>>("pid_gains", {1.0, 0.0, 0.0});
}
};
# Python 示例
class MyNode(Node):
def __init__(self):
super().__init__('my_node')
# 声明参数,设置默认值
self.declare_parameter('robot_name', 'default_robot')
self.declare_parameter('max_speed', 1.0)
self.declare_parameter('log_level', 20)
self.declare_parameter('pid_gains', [1.0, 0.0, 0.0])
关键点:第二个参数就是默认值。如果启动时用户没有提供该参数,节点就使用这个默认值运行。
2.2 参数类型与默认值设置
ROS2支持多种参数类型。我在项目中遇到过最坑的就是类型不匹配——你声明的是double,启动文件里传了个int,虽然Python里能跑,但C++节点直接报错。
| 参数类型 | C++ 声明示例 | Python 声明示例 | 默认值写法 |
|---|---|---|---|
| bool | declare_parameter<bool>("flag", true) |
declare_parameter('flag', True) |
true / True |
| int | declare_parameter<int>("count", 10) |
declare_parameter('count', 10) |
整数 |
| double | declare_parameter<double>("rate", 50.0) |
declare_parameter('rate', 50.0) |
浮点数 |
| string | declare_parameter<std::string>("name", "bot") |
declare_parameter('name', 'bot') |
字符串 |
| array | declare_parameter<std::vector<double>>("gains", {1.0, 0.0}) |
declare_parameter('gains', [1.0, 0.0]) |
列表 / 数组 |
我的小技巧:字符串类型的默认值最好用有意义的名称,比如"default_robot"而不是"none"。这样调试时看日志就知道用的是默认值还是用户配置值。
2.3 节点启动时的参数初始化流程
你想想看,一个节点从启动到参数就位,中间经历了什么?我画了张图帮你理清这个流程。
流程其实不复杂:节点启动 → 读取外部传入的参数 → 执行declare_parameter() → 判断参数是否已提供 → 选择使用用户值或默认值。
2.4 启动时传入参数的方式
参数声明好了,怎么在启动时传进去?我常用的有三种方式。
方式一:命令行直接传
ros2 run my_package my_node --ros-args -p robot_name:=turtlebot -p max_speed:=2.0
这种方式适合快速测试。我记得有一次调试电机控制,直接在终端里改max_speed参数,省去了改YAML文件再重启的麻烦。
方式二:YAML配置文件
# config/my_params.yaml
my_node:
ros__parameters:
robot_name: "turtlebot"
max_speed: 2.0
log_level: 30
pid_gains: [1.5, 0.1, 0.05]
ros2 run my_package my_node --ros-args --params-file config/my_params.yaml
我曾经踩过的坑:YAML文件里的节点名必须和C++/Python代码里Node("my_node")的名字完全一致,否则参数加载不进去。检查了半天才发现是节点名拼写错了。
方式三:Launch文件传入
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='my_package',
executable='my_node',
name='my_node',
parameters=['config/my_params.yaml']
)
])
2.5 参数覆盖优先级
如果同一个参数在多个地方都设置了值,到底听谁的?我整理了一个优先级规则表。
| 优先级 | 来源 | 说明 |
|---|---|---|
| 最高 | 命令行 -p 参数 |
运行时直接指定,覆盖一切 |
| 高 | Launch文件中的parameters字段 |
启动时加载的YAML文件 |
| 中 | 节点默认的YAML配置文件 | 代码中硬编码的配置文件路径 |
| 低 | declare_parameter()的默认值 |
代码中声明的后备值 |
我的建议:开发阶段多用命令行传参,方便调试;部署阶段统一用YAML文件管理,便于维护和版本控制。
2.6 参数声明的最佳实践
做了这么多年机器人项目,我总结了几条参数声明的经验。
- 所有参数集中声明:在构造函数里统一声明,不要散落在各个成员函数中。这样别人看你的代码,一眼就知道有哪些可配置项。
- 默认值要有意义:不要用
0或""这种空值,除非它真的代表「关闭」或「未设置」。我见过有人把电机最大速度默认设为0,结果机器人启动后一动不动,排查了半天才发现是参数没配。 - 类型要明确:C++里模板参数别写错,Python里注意
int和float的区别。ROS2对类型检查很严格,类型不匹配会直接抛异常。 - 参数命名要规范:用蛇形命名法(snake_case),比如
max_speed、pid_gains。别用maxSpeed或MaxSpeed,ROS2社区约定俗成用蛇形。
核心原则:参数声明是节点与外界交互的接口。接口设计得好,后续的调试、部署、复用都会轻松很多。