ROS 参数服务器用于在运行时存储和读取配置。节点名称、话题名称、控制频率、坐标系名称、传感器参数、算法阈值等都可以通过参数传入,而不是写死在代码中。

合理使用参数服务器可以让同一份节点代码适配不同机器人、不同传感器和不同环境。本文以 ROS1 为例,介绍参数的命名、读取、launch 配置和常见问题。

参数服务器是什么

ROS master 内部维护了一个参数字典,节点可以通过 API 读取和写入参数。查看所有参数:

1
rosparam list

查看某个参数:

1
rosparam get /robot_name

设置参数:

1
rosparam set /robot_name demo_bot

删除参数:

1
rosparam delete /robot_name

参数服务器适合存储配置,不适合高频数据传输。实时变化的数据应该使用 topic、service 或 action。

全局参数和私有参数

ROS 参数有命名空间。全局参数以 / 开头:

1
2
/robot_name
/use_sim_time

私有参数以 ~ 表示,绑定到当前节点命名空间。例如节点名为 /planner,读取 ~rate 实际对应:

1
/planner/rate

私有参数适合节点内部配置,避免不同节点之间参数名冲突。

Python 节点读取参数

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3
import rospy

def main():
rospy.init_node("simple_controller")

rate_hz = rospy.get_param("~rate", 20.0)
frame_id = rospy.get_param("~frame_id", "base_link")
max_speed = rospy.get_param("~max_speed", 0.5)

rospy.loginfo("rate=%s frame_id=%s max_speed=%s", rate_hz, frame_id, max_speed)

rate = rospy.Rate(rate_hz)
while not rospy.is_shutdown():
# controller logic
rate.sleep()

if __name__ == "__main__":
main()

get_param 第二个参数是默认值。如果参数不存在,会使用默认值。

如果某个参数必须提供,可以不传默认值:

1
map_frame = rospy.get_param("~map_frame")

缺少时会抛异常,节点启动失败。对于关键参数,这是合理的。

C++ 节点读取参数

C++ 中可以使用 private_nh.param

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <ros/ros.h>

int main(int argc, char** argv) {
ros::init(argc, argv, "simple_controller");
ros::NodeHandle nh;
ros::NodeHandle pnh("~");

double rate_hz;
std::string frame_id;
double max_speed;

pnh.param("rate", rate_hz, 20.0);
pnh.param<std::string>("frame_id", frame_id, "base_link");
pnh.param("max_speed", max_speed, 0.5);

ROS_INFO("rate=%.2f frame_id=%s max_speed=%.2f",
rate_hz, frame_id.c_str(), max_speed);

ros::Rate rate(rate_hz);
while (ros::ok()) {
ros::spinOnce();
rate.sleep();
}
return 0;
}

NodeHandle("~") 表示私有命名空间。

在 launch 文件中配置参数

单个参数:

1
2
3
4
5
6
7
<launch>
<node pkg="demo_pkg" type="simple_controller.py" name="controller" output="screen">
<param name="rate" value="20.0" />
<param name="frame_id" value="base_link" />
<param name="max_speed" value="0.5" />
</node>
</launch>

这些参数会成为节点私有参数:

1
2
3
/controller/rate
/controller/frame_id
/controller/max_speed

全局参数可以放在 node 外部:

1
<param name="/robot_name" value="demo_bot" />

使用 YAML 管理参数

参数多时,建议放到 YAML 文件:

1
2
3
4
5
6
7
rate: 20.0
frame_id: base_link
max_speed: 0.5
pid:
kp: 1.0
ki: 0.0
kd: 0.1

在 launch 中加载:

1
2
3
<node pkg="demo_pkg" type="simple_controller.py" name="controller" output="screen">
<rosparam file="$(find demo_pkg)/config/controller.yaml" command="load" />
</node>

读取嵌套参数:

1
2
pid = rospy.get_param("~pid")
kp = pid["kp"]

或者直接读取:

1
kp = rospy.get_param("~pid/kp", 1.0)

参数更新的注意事项

节点启动后,如果使用 rosparam set 修改参数,节点不会自动感知,除非代码周期性重新读取参数或使用 dynamic_reconfigure。

对于启动配置,例如话题名、frame id、模型路径,启动时读取一次即可。

对于运行中需要调整的参数,例如 PID、阈值、速度限制,可以考虑:

  • 定时读取参数。
  • 提供 service 更新配置。
  • 使用 dynamic_reconfigure。

不要在高频循环中频繁访问参数服务器。参数服务器不是实时配置数据库,频繁访问会影响性能和稳定性。

常见问题

参数读不到时,先确认命名空间:

1
rosparam list | grep controller

很多问题来自全局参数和私有参数混用。例如代码读取 ~rate,但 launch 中设置的是 /rate

YAML 中字符串、数字、布尔值要注意类型。"false" 是字符串,false 才是布尔值。

多个 launch 文件加载同名全局参数时,后加载的值会覆盖前面的值。复杂系统中建议减少全局参数,优先使用节点私有参数。

小结

ROS 参数服务器适合管理节点启动配置和低频参数。实践中建议使用私有参数减少冲突,用 YAML 管理复杂配置,在 launch 中加载,并在代码中提供合理默认值和必要校验。需要运行时频繁调整的参数,应使用 dynamic_reconfigure 或专门的配置更新机制。