3. 参数获取与读取:get_parameter()、get_parameters() 与类型检查
参数设好了,节点也跑起来了。但说实话,真正干活的时候,我们大部分时间都在「读参数」。你想想看,机器人启动后,PID 系数、速度限制、传感器阈值——这些东西都得从参数服务器拉下来,节点才能按你的意图跑。
ROS2 里读参数的方式很灵活。可以一个一个读,也可以一把全拉下来。我个人习惯是:初始化阶段批量获取,运行阶段按需读取。这样既省事,又不会在关键循环里卡 I/O。
3.1 get_parameter():单个参数读取
这是最基础的方法。你给它一个参数名,它返回一个 rclcpp::Parameter 对象。然后你得调用 .get_value<T>() 把值取出来。
// C++ 示例
auto param = this->get_parameter("max_linear_speed");
double max_speed = param.get_value<double>();
RCLCPP_INFO(this->get_logger(), "最大线速度: %.2f", max_speed);
Python 里更直接,get_parameter() 返回一个命名元组,你可以直接用 .value 拿到值:
# Python 示例
max_speed = self.get_parameter("max_linear_speed").value
self.get_logger().info(f"最大线速度: {max_speed}")
3.2 get_parameters():批量获取
一个节点可能有几十个参数。你一个个 get_parameter() 去读,代码会变得又臭又长。这时候就该 get_parameters() 上场了。
它接受一个 vector<string> 参数名列表,返回一个 vector<Parameter>。我一般在节点的构造函数里用这个方法,一次性把所有参数拉下来:
// C++ 批量获取
std::vector<std::string> param_names = {
"max_linear_speed",
"max_angular_speed",
"acceleration",
"deceleration"
};
auto params = this->get_parameters(param_names);
double max_linear = params[0].get_value<double>();
double max_angular = params[1].get_value<double>();
double accel = params[2].get_value<double>();
double decel = params[3].get_value<double>();
Python 版本更简洁:
# Python 批量获取
param_names = [
"max_linear_speed",
"max_angular_speed",
"acceleration",
"deceleration"
]
params = self.get_parameters(param_names)
max_linear = params[0].value
max_angular = params[1].value
accel = params[2].value
decel = params[3].value
3.3 参数类型检查
嗯,这里要注意。ROS2 的参数是有类型的。你声明了一个 double 类型的参数,结果 YAML 文件里写成了字符串 "1.5",或者启动时传了个整数 1——节点不会自动帮你转换。
所以,拿到参数后,我建议一定要做类型检查。尤其是当你的代码要给别人用时,你永远不知道对方会在启动文件里塞什么奇怪的东西。
每个 Parameter 对象都有一个 .get_type() 方法,返回一个枚举值:
| 类型枚举 | 对应值 | 说明 |
|---|---|---|
rclcpp::ParameterType::PARAMETER_NOT_SET |
0 | 参数未设置 |
PARAMETER_BOOL |
1 | 布尔值 |
PARAMETER_INTEGER |
2 | 64 位整数 |
PARAMETER_DOUBLE |
3 | 双精度浮点数 |
PARAMETER_STRING |
4 | 字符串 |
PARAMETER_BYTE_ARRAY |
5 | 字节数组 |
PARAMETER_BOOL_ARRAY |
6 | 布尔数组 |
PARAMETER_INTEGER_ARRAY |
7 | 整数数组 |
PARAMETER_DOUBLE_ARRAY |
8 | 浮点数数组 |
PARAMETER_STRING_ARRAY |
9 | 字符串数组 |
实战中,我一般这样写类型检查:
// C++ 类型检查示例
auto param = this->get_parameter("max_linear_speed");
if (param.get_type() != rclcpp::ParameterType::PARAMETER_DOUBLE) {
RCLCPP_ERROR(this->get_logger(),
"参数 max_linear_speed 类型错误,期望 double,实际为 %s",
param.get_type_name().c_str());
return;
}
double speed = param.get_value<double>();
Python 里用 .type_ 属性判断:
# Python 类型检查示例
from rclpy.parameter import Parameter
param = self.get_parameter("max_linear_speed")
if param.type_ != Parameter.Type.DOUBLE:
self.get_logger().error(
f"参数 max_linear_speed 类型错误,期望 double,实际为 {param.type_}"
)
return
speed = param.value
max_angular_speed 在 YAML 里写成了 "2.0"(带引号),被解析成了字符串。ROS2 不会报错,但 get_value<double>() 会抛异常。从那以后,我所有参数读取后面都跟了类型检查。
3.4 参数读取的完整流程
把上面这些串起来,一个健壮的参数读取流程应该是这样的:
- 声明参数:用
declare_parameter()给每个参数一个默认值。 - 批量获取:在构造函数里用
get_parameters()一次性拉下来。 - 类型检查:对每个参数检查类型是否匹配。
- 赋值使用:把值赋给成员变量,后续直接用。
下面这张图把这个流程串起来了:
说白了,参数读取这件事,核心就一句话:先声明,再读取,后检查。声明保证参数存在,读取拿到值,检查确保类型正确。三步走完,你的节点才能稳稳当当地跑起来。
getDoubleParam()、getIntParam(),内部自动做类型检查,类型不对就打印警告并返回默认值。这样你的业务代码会干净很多。