ROS 学习记录(二)——ROS 的通信机制

文章目录

话题通信

模型

ROS 话题通信中涉及三个角色:

  • ROS Master(管理者)
  • Talker(发布者)
  • Listener(订阅者)

话题通信由以下步骤实现:

  1. Talker 注册
  2. Listener 注册
  3. ROS Master 匹配已注册的 Talker 和 Listener
  4. Listener 向 Talker 发送请求
  5. Talker 确认请求
  6. Listener 与 Talker 建立连接
  7. Talker 向 Lkstener 发送消息

注意

  1. 通信步骤的前五步使用 RPC 协议,后两步使用 TCP 协议。
  2. Talker 和 Listener 启动顺序无要求。
  3. 一个 Talker 可以与多个 Listener 建立连接,反之亦然。
  4. Talker 与 Listener 建立连接后,不再需要 ROS Master。此时即使关闭 ROS Master 也不会影响通信。

C++ 实现话题通信

创建一个新的包 communication_package,通信部分的脚本都在这个包中测试。

1catkin_create_pkg communication_package roscpp rospy std_msgs

communication_package/src/ 下创建 topic_talker_cpp.cpp 作为 C++ 实现的 Talker:

 1#include "ros/ros.h"
 2#include "std_msgs/String.h"
 3#include <sstream>
 4
 5int main(int args, char **argv)
 6{
 7    setlocale(LC_ALL, "");
 8    ros::init(args, argv, "topic_talker_cpp");                                           // 初始化节点,节点名称为 topic_talker_cpp,这也是节点的唯一标识符
 9    ros::NodeHandle n;                                                                   // 创建节点句柄
10    ros::Publisher chatter_pub = n.advertise<std_msgs::String>("topic_chatter_cpp", 10); // 创建发布者,话题名称为 topic_chatter_cpp,最多保存 10 条消息
11
12    std_msgs::String msg; // 创建消息对象
13    std::string msg_front = "Hello from C++ talker!";
14
15    int count = 0;
16    ros::Rate loop_rate(1); // 设置循环频率为 1Hz
17
18    while (ros::ok())
19    {
20        std::stringstream ss;
21        ss << msg_front << " " << count;
22        msg.data = ss.str();
23
24        chatter_pub.publish(msg);                     // 发布消息
25        ROS_INFO("C++ Talker: %s", msg.data.c_str()); // 打印发布的消息内容
26        loop_rate.sleep();                            // 控制循环频率
27        count++;
28        ros::spinOnce(); // 刷新 ROS 事件队列
29    }
30
31    return 0;
32}

创建 topic_listener_cpp.cpp 作为 C++ 实现的 Listener:

 1#include "ros/ros.h"
 2#include "std_msgs/String.h"
 3
 4void callback(const std_msgs::String::ConstPtr &msg_ptr) // 用 ConstPtr 指针接收消息,msg_ptr 是指向消息的指针
 5{
 6    ROS_INFO("C++ Listener: %s", msg_ptr->data.c_str()); // 打印消息内容;msg_ptr->data.c_str() 等价于 (*msg_ptr).data.c_str()
 7}
 8
 9int main(int args, char **argv)
10{
11    setlocale(LC_ALL, "");
12    ros::init(args, argv, "topic_listener_cpp");
13    ros::NodeHandle n;
14    ros::Subscriber chatter_sub = n.subscribe("topic_chatter_cpp", 10, callback); // 订阅话题,话题名为 "topic_chatter_py",消息队列长度为 10
15
16    ros::spin(); // 阻塞,等待回调函数 callback 执行完毕
17    return 0;
18}

CMakeLists.txt 中添加编译目标:

1add_executable(topic_talker_cpp_node src/topic_talker_cpp.cpp)
2add_executable(topic_listener_cpp_node src/topic_listener_cpp.cpp)
3
4target_link_libraries(topic_talker_cpp_node
5  ${catkin_LIBRARIES}
6)
7target_link_libraries(topic_listener_cpp_node
8  ${catkin_LIBRARIES}
9)

配置 topic_communication.launch 文件:

1<launch>
2    <!-- C++ nodes -->
3    <node pkg="communication_package" type="topic_talker_cpp_node" name="topic_talker_cpp"
4        output="screen" />
5    <node pkg="communication_package" type="topic_listener_cpp_node" name="topic_listener_cpp"
6        output="screen" />
7</launch>

roslauunch communication_package topic_communication.launch 启动节点。

Python 实现话题通信

创建 scripts/topic_talker_py.py

 1import rospy
 2from std_msgs.msg import String
 3
 4
 5def main():
 6    rospy.init_node("topic_talker_py")  # 初始化节点
 7    chatter_pub = rospy.Publisher(
 8        "topic_chatter_py", String, queue_size=10
 9    )  # 创建发布者对象
10
11    msg = String()  # 创建消息对象
12    msg_front = "Hello from Python talker!"
13
14    count = 0
15    loop_rate = rospy.Rate(1)  # 设置循环频率为 1Hz
16
17    while not rospy.is_shutdown():
18        msg.data = msg_front + " " + str(count)
19
20        chatter_pub.publish(msg)  # 发布消息
21        rospy.loginfo("Python Talker: " + msg.data)
22        loop_rate.sleep()  # 控制循环频率
23        count += 1
24
25
26if __name__ == "__main__":
27    main()

创建 scripts/topic_listener_py.py

 1import rospy
 2from std_msgs.msg import String
 3
 4
 5def callback(msg):
 6    rospy.loginfo("Python Listener: " + msg.data)
 7
 8
 9def main():
10    rospy.init_node("topic_listener_py")  # 初始化节点
11    sub = rospy.Subscriber(
12        "topic_chatter_py", String, callback=callback, queue_size=10
13    )  # 创建订阅者对象并注册回调函数
14    rospy.spin()  # 保持节点运行,直到节点关闭
15
16
17if __name__ == "__main__":
18    main()

编辑 CMakeLists.txt

1catkin_install_python(PROGRAMS
2  scripts/topic_listener_py.py
3  scripts/topic_talker_py.py
4  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
5)

配置 topic_communication.launch

 1<launch>
 2    <!-- C++ nodes -->
 3    <node pkg="communication_package" type="topic_talker_cpp_node" name="topic_talker_cpp"
 4        output="screen" />
 5    <node pkg="communication_package" type="topic_listener_cpp_node" name="topic_listener_cpp"
 6        output="screen" />
 7
 8    <!-- Python nodes -->
 9    <node pkg="communication_package" type="topic_talker_py.py" name="topic_talker_py"
10        output="screen" />
11    <node pkg="communication_package" type="topic_listener_py.py" name="topic_listener_py"
12        output="screen" />
13</launch>

自定义消息类型

ROS 给出的消息类型一般都比较简单,所以有时我们需要自定义消息类型。假设我们想创建一个 Person 类型,这个类型包含字符串 name、整型 age 和布尔型 gender

在包下创建 msg/Person.msg 文件:

1cd communication_package
2mkdir msg
3touch msg/Person.msg

编辑 msg/Person.msg 文件:

1string name
2int32 age
3bool gender

package.xml 中添加编译依赖和执行依赖:

1<build_depend>message_generation</build_depend>
2<exec_depend>message_runtime</exec_depend>

CMakeLists.txt 中添加编译目标:

 1find_package(catkin REQUIRED COMPONENTS
 2  roscpp
 3  rospy
 4  std_msgs
 5  message_generation  # 添加 message_generation 依赖
 6)
 7
 8add_message_files(
 9  FILES
10  Person.msg  # 添加 Person.msg 自定义消息
11)
12
13generate_messages(  # 生成自定义消息类型
14  DEPENDENCIES
15  std_msgs
16)
17
18catkin_package(
19#  INCLUDE_DIRS include
20#  LIBRARIES communication_package
21 CATKIN_DEPENDS roscpp rospy std_msgs message_runtime  # 添加执行时依赖
22#  DEPENDS system_lib
23)

此时可以先按 Ctrl+Shift+B 编译消息文件,编译完成后,在 ros-project/devel/lib/communication_package/ 目录下会出现 Person.h 文件,在 devel/lib/python3/dist-packages/communication_package/msg/ 目录下会出现 _Person.py 文件,这两个就是编译后的自定义消息所在文件,后续写代码时可以直接使用。

C++ 调用自定义消息类型

发布方代码:

 1#include "ros/ros.h"
 2#include "communication_package/Person.h"
 3
 4int main(int argc, char **argv)
 5{
 6    setlocale(LC_ALL, "");
 7    ros::init(argc, argv, "person_talker_cpp");
 8    ros::NodeHandle n;
 9    ros::Publisher person_pub = n.advertise<communication_package::Person>("person_chatter_cpp", 10);
10
11    communication_package::Person person;
12    person.name = "John Doe";
13    person.age = 13;
14    person.gender = true;
15
16    ros::Rate loop_rate(1);
17    ros::Duration(3.0).sleep();  // 等待订阅者连接,否则订阅者有可能无法收到发出的第一条消息
18    while (ros::ok())
19    {
20        person_pub.publish(person);
21        person.age++;
22        loop_rate.sleep();
23        ros::spinOnce();
24    }
25
26    return 0;
27}

订阅方代码:

 1#include "ros/ros.h"
 2#include "communication_package/Person.h"
 3
 4void callback(const communication_package::Person::ConstPtr &msg_ptr)
 5{
 6    ROS_INFO("Name: %s, Age: %d, Gender: %s from C++", msg_ptr->name.c_str(), msg_ptr->age, msg_ptr->gender ? "Male" : "Female");
 7}
 8
 9int main(int argc, char **argv)
10{
11    setlocale(LC_ALL, "");
12    ros::init(argc, argv, "person_listener_cpp");
13    ros::NodeHandle n;
14    ros::Subscriber person_sub = n.subscribe("person_chatter_cpp", 10, callback);
15    ros::spin();
16    return 0;
17}

配置 CMakeLists.txt

 1add_executable(person_talker_node src/person_talker_cpp.cpp)
 2add_executable(person_listener_node src/person_listener_cpp.cpp)
 3
 4add_dependencies(person_talker_node ${PROJECT_NAME}_generate_messages_cpp)
 5add_dependencies(person_listener_node ${PROJECT_NAME}_generate_messages_cpp)
 6
 7target_link_libraries(person_talker_node
 8  ${catkin_LIBRARIES}
 9)
10target_link_libraries(person_listener_node
11  ${catkin_LIBRARIES}
12)

顺序不能变!

配置 person_communication.launch

1<launch>
2    <!-- C++ nodes -->
3    <node pkg="communication_package" type="person_talker_node" name="person_talker_cpp"
4        output="screen" />
5    <node pkg="communication_package" type="person_listener_node" name="person_listener_cpp"
6        output="screen" />
7</launch>

使用 roslaunch communication_package topic_communication.launch 运行节点。

Python 调用自定义消息类型

发布方代码:

 1import rospy
 2from communication_package.msg import Person
 3
 4
 5def main():
 6    rospy.init_node("person_talker_py")
 7    person_pub = rospy.Publisher("person_chatter_py", Person, queue_size=10)
 8
 9    person = Person()
10    person.name = "Jane Doe"
11    person.age = 13
12    person.gender = False
13
14    loop_rate = rospy.Rate(1)
15    rospy.sleep(rospy.Duration(3, 0))  # 等待订阅者连接
16    while not rospy.is_shutdown():
17        person_pub.publish(person)
18        person.age += 1
19        loop_rate.sleep()
20
21
22if __name__ == "__main__":
23    main()

订阅方代码:

 1import rospy
 2from communication_package.msg import Person
 3
 4
 5def callback(msg):
 6    rospy.loginfo(
 7        "Name: %s, Age: %d, Gender: %s from Python",
 8        msg.name,
 9        msg.age,
10        "Male" if msg.gender else "Female",
11    )
12
13
14def main():
15    rospy.init_node("person_listener_py")
16    person_sub = rospy.Subscriber("person_chatter_py", Person, callback, queue_size=10)
17    rospy.spin()
18
19
20if __name__ == "__main__":
21    main()

配置 CMakeLists.txt

1catkin_install_python(PROGRAMS
2  scripts/person_listener_py.py
3  scripts/person_talker_py.py
4  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
5)

配置 person_communication.launch

 1<launch>
 2    <!-- C++ nodes -->
 3    <node pkg="communication_package" type="person_talker_node" name="person_talker_cpp"
 4        output="screen" />
 5    <node pkg="communication_package" type="person_listener_node" name="person_listener_cpp"
 6        output="screen" />
 7
 8    <!-- Python nodes -->
 9    <node pkg="communication_package" type="person_talker_py.py" name="person_talker_py"
10        output="screen" />
11    <node pkg="communication_package" type="person_listener_py.py" name="person_listener_py"
12        output="screen" />
13</launch>

使用 roslaunch communication_package person_communication.launch 运行节点。

服务通信

模型

ROS 服务通信中同样涉及三个角色:

  • ROS Master(管理者)
  • Server(服务端)
  • Client(客户端)

服务通信由以下步骤实现:

  1. Server 注册
  2. Client 注册
  3. ROS Master 匹配已注册的 Server 和 Client
  4. Client 向 Server 发送请求
  5. Server 向 Client 发送响应

注意

  1. 客户端请求被处理时,需要确保服务端已经注册。
  2. 一个服务端可以匹配多个客户端,反之亦然。

自定义服务类型

在包目录下创建 srv 目录并且创建 Division.srv

1cd communication_package
2mkdir srv
3touch srv/Division.srv

编辑 srv/Division.srv 文件:

1float64 numerator
2float64 denominator
3---
4float64 result

自定义服务中,--- 前是客户端请求的消息类型,--- 后是服务端响应的消息类型。

package.xml 中添加编译依赖和执行依赖:

1<build_depend>message_generation</build_depend>
2<exec_depend>message_runtime</exec_depend>

CMakeLists.txt 中添加编译目标:

 1find_package(catkin REQUIRED COMPONENTS
 2  roscpp
 3  rospy
 4  std_msgs
 5  message_generation
 6)
 7
 8add_service_files(
 9  FILES
10  Division.srv  # 添加 Division.srv 服务类型
11)
12
13generate_messages(  # 生成自定义消息类型
14  DEPENDENCIES
15  std_msgs
16)

现在编译服务类型。编译完成后,在 devel/include/communication_package/ 目录下会出现 Division.h DivisionRequest.h DivisionResponse.h 三个文件,在 devel/lib/python3/dist-packages/communication_package/srv/ 目录下会出现 _Division.py 文件。

C++ 实现服务通信

服务端代码:

 1#include "ros/ros.h"
 2#include "communication_package/Division.h"
 3
 4bool do_request(communication_package::Division::Request &req, communication_package::Division::Response &res)
 5{
 6    double numerator = req.numerator, denominator = req.denominator;
 7    ROS_INFO("Received numerator: %f, denominator: %f", numerator, denominator);
 8
 9    if (denominator == 0.0)  // 业务逻辑
10    {
11        ROS_ERROR("Denominator cannot be zero");
12        return false;
13    }
14
15    double result = numerator / denominator;
16    res.result = result;
17    ROS_INFO("Sending result: %f", result);
18    return true;
19}
20
21int main(int argc, char **argv)
22{
23    setlocale(LC_ALL, "");
24    ros::init(argc, argv, "division_server_cpp");
25    ros::NodeHandle n;
26    ros::ServiceServer server = n.advertiseService("division_service_cpp", do_request);  // 创建服务器对象
27    ROS_INFO("Starting division service from C++");
28    ros::spin();
29    return 0;
30}

客户端代码:

 1#include "ros/ros.h"
 2#include "communication_package/Division.h"
 3
 4int main(int argc, char **argv)
 5{
 6    setlocale(LC_ALL, "");
 7    ros::init(argc, argv, "division_client_cpp");
 8    ros::NodeHandle n;
 9    ros::ServiceClient client = n.serviceClient<communication_package::Division>("division_service_cpp");
10    client.waitForExistence();  // 等待服务端启动
11
12    communication_package::Division div_srv;  // 创建服务对象,包含请求和响应
13    ros::Rate loop_rate(1);  // 设置循环频率
14
15    for (int i = 3; i >= 0; i--)
16    {
17        div_srv.request.numerator = 10.0;  // 设置请求参数
18        div_srv.request.denominator = double(i);
19
20        bool flag = client.call(div_srv);  // 调用服务
21        if (flag)
22        {
23            ROS_INFO("Result: %f", div_srv.response.result);
24        }
25        else
26        {
27            ROS_ERROR("Failed to call service");
28        }
29        loop_rate.sleep();  // 控制循环频率
30    }
31}

CmakeLists.txtservice_communication.launch 文件与之前相同。

Python 实现服务通信

服务端代码:

 1import rospy
 2from communication_package.srv import Division, DivisionRequest, DivisionResponse
 3
 4
 5def do_request(req):  # 与 C++ 中不同,这里的回调函数的参数是请求对象,返回值是响应对象
 6    rospy.loginfo(
 7        "Received numerator: %f, denominator: %f", req.numerator, req.denominator
 8    )
 9
10    if req.denominator == 0:
11        rospy.logerr("Denominator cannot be zero")
12        return DivisionResponse(result=float("nan"))
13
14    result = req.numerator / req.denominator
15    res = DivisionResponse(result=result)
16    rospy.loginfo("Sending result: %f", result)
17    return res
18
19
20def main():
21    rospy.init_node("division_server_py")
22    server = rospy.Service("division_service_py", Division, handler=do_request)
23    rospy.spin()
24
25
26if __name__ == "__main__":
27    main()

客户端代码:

 1import rospy
 2from communication_package.srv import Division, DivisionRequest, DivisionResponse
 3
 4
 5def main():
 6    rospy.init_node("division_client_py")
 7    client = rospy.ServiceProxy("division_service_py", Division)  # 创建客户端
 8    client.wait_for_service()  # 等待服务端启动
 9
10    req = DivisionRequest()  # 创建请求对象
11    loop_rate = rospy.Rate(1)
12
13    for i in range(4)[::-1]:
14        req.numerator = 10.0
15        req.denominator = i
16
17        res = client.call(req)  # 调用服务端
18        if res.result != res.result:  # 判断返回值是否为 nan
19            rospy.logerr("Failed to call service")
20        else:
21            rospy.loginfo("Result: %f", res.result)
22        loop_rate.sleep()
23
24
25if __name__ == "__main__":
26    main()

跨语言支持

不难发现,C++ 服务端的回调函数返回布尔值,而 Python 服务端的回调函数返回响应对象。C++ 服务端的返回值只是为了确认服务是否执行成功,而并非真正的响应。在 Python 的服务端中,如果服务调用成功,则会自动返回一个 True。为了实现跨语言支持,我们在服务文件的响应部分添加 success 字段用于表示请求是否成功。略微修改服务端代码,确保不同语言的客户端都能正常调用服务。

C++ 客户端:

 1#include "ros/ros.h"
 2#include "communication_package/Division.h"
 3
 4bool do_request(communication_package::Division::Request &req, communication_package::Division::Response &res)
 5{
 6    double numerator = req.numerator, denominator = req.denominator;
 7    ROS_INFO("Received numerator: %f, denominator: %f", numerator, denominator);
 8
 9    if (denominator == 0.0)
10    {
11        ROS_ERROR("Denominator cannot be zero");
12        res.success = false;  // 响应中添加 success 字段
13    }
14    else
15    {
16        double result = numerator / denominator;
17        res.success = true;
18        res.result = result;
19        ROS_INFO("Sending result: %f", result);
20    }
21
22    return true; // 返回值为 true 代表服务调用成功
23}
24
25int main(int argc, char **argv)
26{
27    setlocale(LC_ALL, "");
28    ros::init(argc, argv, "division_server_cpp");
29    ros::NodeHandle n;
30    ros::ServiceServer server = n.advertiseService("division_service_cpp", do_request);
31    ROS_INFO("Starting division service from C++");
32    ros::spin();
33    return 0;
34}

Python 客户端:

 1import rospy
 2from communication_package.srv import Division, DivisionRequest, DivisionResponse
 3
 4
 5def main():
 6    rospy.init_node("division_client_py")
 7    client = rospy.ServiceProxy("division_service_cpp", Division)  # 连接 C++ 服务端
 8    client.wait_for_service()
 9
10    req = DivisionRequest()
11    loop_rate = rospy.Rate(1)
12
13    for i in range(4)[::-1]:
14        req.numerator = 10.0
15        req.denominator = i
16
17        res = client.call(req)
18        if res.success:  # 判断 success 字段
19            rospy.loginfo("Result: %f", res.result)
20        else:
21            rospy.logerr("Failed to call service")
22        loop_rate.sleep()
23
24
25if __name__ == "__main__":
26    main()

Python 服务端:

 1import rospy
 2from communication_package.srv import Division, DivisionRequest, DivisionResponse
 3
 4
 5def do_request(req):
 6    rospy.loginfo(
 7        "Received numerator: %f, denominator: %f", req.numerator, req.denominator
 8    )
 9
10    if req.denominator == 0:
11        rospy.logerr("Denominator cannot be zero")
12        return DivisionResponse(success=False)
13
14    result = req.numerator / req.denominator
15    res = DivisionResponse(success=True, result=result)
16    rospy.loginfo("Sending result: %f", result)
17    return res
18
19
20def main():
21    rospy.init_node("division_server_py")
22    server = rospy.Service("division_service_py", Division, handler=do_request)
23    rospy.spin()
24
25
26if __name__ == "__main__":
27    main()

C++ 客户端:

 1#include "ros/ros.h"
 2#include "communication_package/Division.h"
 3
 4int main(int argc, char **argv)
 5{
 6    setlocale(LC_ALL, "");
 7    ros::init(argc, argv, "division_client_cpp");
 8    ros::NodeHandle n;
 9    ros::ServiceClient client = n.serviceClient<communication_package::Division>("division_service_py");
10    client.waitForExistence();
11
12    communication_package::Division div_srv;
13    ros::Rate loop_rate(1);
14
15    for (int i = 3; i >= 0; i--)
16    {
17        div_srv.request.numerator = 10.0;
18        div_srv.request.denominator = double(i);
19
20        if (client.call(div_srv) && div_srv.response.success)  // 同时满足服务调用成功且服务端返回 success 字段为 true
21        {
22            ROS_INFO("Result: %f", div_srv.response.result);
23        }
24        else
25        {
26            ROS_ERROR("Failed to call service");
27        }
28        loop_rate.sleep();
29    }
30}

参数服务器

模型

ROS 参数服务器中涉及三个角色:

  • ROS Master(管理者)
  • Talker(参数设置者)
  • Listener(参数调用者)

话题通信由以下步骤实现:

  1. Talker 设置参数
  2. Listener 获取参数
  3. ROS Master 向 Listener 发送参数值

C++ 实现参数服务器

写入参数:

 1#include "ros/ros.h"
 2
 3int main(int argc, char **argv)
 4{
 5    setlocale(LC_ALL, "");
 6    ros::init(argc, argv, "paramset_cpp");
 7    ros::NodeHandle n;
 8
 9    // 写入参数
10    n.setParam("name", "John Doe");
11    n.setParam("age", 13);
12    n.setParam("gender", "Male");
13
14    ros::Duration(3.0).sleep();
15
16    // 删除参数
17    n.deleteParam("name");
18    n.deleteParam("age");
19    n.deleteParam("gender");
20
21    return 0;
22}

读取参数:

 1#include "ros/ros.h"
 2#include <string>
 3
 4using namespace std;
 5
 6int main(int argc, char **argv)
 7{
 8    setlocale(LC_ALL, "");
 9    ros::init(argc, argv, "paramget_cpp");
10    ros::NodeHandle n;
11
12    ros::Duration(1.0).sleep();
13    string name, gender;
14    int age;
15    // 读取参数
16    n.getParam("name", name);
17    n.getParam("age", age);
18    n.getParam("gender", gender);
19    ROS_INFO("Name: %s, Age: %d, Gender: %s", name.c_str(), age, gender.c_str());
20
21    ros::Duration(3.0).sleep();
22    if (n.getParam("name", name) && n.getParam("age", age) && n.getParam("gender", gender))
23    {
24        ROS_INFO("Name: %s, Age: %d, Gender: %s", name.c_str(), age, gender.c_str());
25    }
26    else
27    {
28        ROS_ERROR("Failed to get parameters");
29    }
30    return 0;
31}

Python 实现参数服务器

写入参数:

 1import rospy
 2
 3
 4def main():
 5    rospy.init_node("paramset_py")
 6    rospy.set_param("name", "Jane Doe")
 7    rospy.set_param("age", 13)
 8    rospy.set_param("gender", "Female")
 9
10    rospy.sleep(rospy.Duration(3))
11
12    rospy.delete_param("name")
13    rospy.delete_param("age")
14    rospy.delete_param("gender")
15
16
17if __name__ == "__main__":
18    main()

读取参数:

 1import rospy
 2
 3
 4def main():
 5    rospy.init_node("paramget_py")
 6
 7    rospy.sleep(rospy.Duration(1))
 8    name = rospy.get_param("name")
 9    age = rospy.get_param("age")
10    gender = rospy.get_param("gender")
11
12    rospy.loginfo("Name: %s, Age: %d, Gender: %s", name, age, gender)
13
14    rospy.sleep(rospy.Duration(3))
15    # 如果参数不存在,则返回默认值,此处设置为 None
16    name = rospy.get_param("name", None)
17    age = rospy.get_param("age", None)
18    gender = rospy.get_param("gender", None)
19    if name and age and gender:
20        rospy.loginfo("Name: %s, Age: %d, Gender: %s", name, age, gender)
21    else:
22        rospy.logerr("Failed to get parameters")
23
24
25if __name__ == "__main__":
26    main()

launch 文件配置参数

除了在节点中动态设置参数,在 launch 文件中也可以静态配置参数,例如使用下面的 launch 文件配置参数:

 1<launch>
 2    <!-- C++ nodes -->
 3    <!-- <node pkg="communication_package" type="paramset_node" name="paramset_cpp"
 4        output="screen" /> -->
 5    <node pkg="communication_package" type="paramget_node" name="paramget_cpp"
 6        output="screen" />
 7
 8    <!-- Python nodes -->
 9    <!-- <node pkg="communication_package" type="paramset_py.py" name="paramset_py"
10        output="screen" /> -->
11    <node pkg="communication_package" type="paramget_py.py" name="paramget_py"
12        output="screen" />
13
14    <param name="name" type="string" value="jackgdn" />
15    <param name="age" type="int" value="20" />
16    <param name="gender" type="string" value="Male" />
17
18</launch>

如果参数很多,可以将参数放在一个参数文件中,例如 ros-project/src/communication_package/config/Person.yaml

1name: "jackgdn"
2age: 20
3gender: "Male"

然后修改 param_server.launch 文件:

 1<launch>
 2    <!-- C++ nodes -->
 3    <!-- <node pkg="communication_package" type="paramset_node" name="paramset_cpp"
 4        output="screen" /> -->
 5    <node pkg="communication_package" type="paramget_node" name="paramget_cpp"
 6        output="screen" />
 7
 8    <!-- Python nodes -->
 9    <!-- <node pkg="communication_package" type="paramset_py.py" name="paramset_py"
10        output="screen" /> -->
11    <node pkg="communication_package" type="paramget_py.py" name="paramget_py"
12        output="screen" />
13
14    <!-- <param name="name" type="string" value="jackgdn" />
15    <param name="age" type="int" value="20" />
16    <param name="gender" type="string" value="Male" /> -->
17
18    <rosparam file="$(find communication_package)/config/Person.yaml" command="load" />
19
20</launch>

这样,参数就能被加载到参数服务器中。

系列文章