• 11 月 11 日 ROS 学习笔记——ROS 架构及概念



    前言

    本文为 11 月 11 日 ROS 学习笔记——ROS 架构及概念,分为 ROS 文件系统级和计算图级两节。


    一、 ROS 文件系统级

    • 功能包 package:用于创建ROS程序的最小结构和最少内容,包含ROS运行进程(节点)、配置文件等,
    • 功能包清单 package.xml:提供关于功能包、 许可证、依赖关系、编译标志等的信息,
    • 元功能包 Metapackage: 几个具有某些功能的包组织在一起,
    • 元功能包清单:
    • 消息类型 msg type: 进程发送到其他进程的信息,
    • 服务类型 srv type: 为 ROS 中由每个进程提供的服务定义请求和响应数据结构。

    ROS 文件级

    1). 工作空间 Ws

    工作空间就是一个文件夹,包含功能包,功能包又包含源文件和环境或工作空间,提供编译这些功能包的一种方式.

    请添加图片描述

    • 源文件空间 src:放置功能包、项目、复制的包等。最重要的一个文件是 CMakeLists.txt,
    • 编译空间 build:为功能包和项目保存缓存信息、配置和其他中间文件。
    • 开发空间 devel:用来保存编译后的程序,无须安装就能用来测试的程序。

    2). 功能包

    功能包是一种特定结构的文件和文件夹组合,结构如下:

    • include/package_name/:包含需要的库的头文件,
    • msg/: 存放非标准信息,
    • scripts/: 存放Bash、Python或任何其他脚本语言的可执行脚本,
    • src/: 存储程序源文件,
    • srv/: 服务类型,
    • package.xml: 功能包清单文件。

    package.xml 必须在每个功能包中,用来说明此包相关的各类信息,包括包的名称、依赖关系等信息。两个典型标记 :

    • : 显示当前功能包安装之前必须先安装哪些功能包
    • : 显示运行功能包中代码所需要的包.

    3). 消息 msg

    消息类型必须具有:字段 field 和常量 constant,如:

    • int32 id
    • float32 vel
    • string name

    4). 服务 srv

    用以实现节点之间的请求/响应通信。


    二、计算图级

    ROS 创建一个连接到所有进程的网络。在系统中的任何节点都可以访问此网络,并通过该网络与其他节点交互,获取其他节点发送的信息,并将自身数据发布到网络上。

    请添加图片描述

    • 节点 node:计算执行进程,与其他节点进行交互。最好让众多节点都具有单一的功能,而不是在系统中创建一个包罗万象的大节点。
    • 节点管理器 master:用于节点的名称注册和查找等,也设置节点间的通信。如果在整个ROS中没有节点管理器,就无法与节点、服务、消息等通信。
    • 参数服务器: parameter server: 通过参数,就能够在运行时配置节点或改变节点的工作任务。
    • 消息 message: 节点通过消息完成彼此的沟通。消息包含一个节点发送到其他节点的信息数据。
    • 主题 topic:每个消息都必须有一个名称以便被 ROS 网络分发。节点可以通过订阅某个主题,接收来自其他节点的消息。一个节点可以订阅一个主题,而不需要任何其他节点同时发布该主题。
    • 服务 service:服务能够允许我们直接与某个节点进行交互。
    • 消息记录包 bag:用于保存和回放ROS消息 数据的文件格式。

    1). 动态加载节点 nodelet

    内部可通信的多个节点,可以在单个进程中运行多个节点,每个nodelet为一个线程。可以在不使用 ROS 网络的情况下与其他节点通信,节点通信效率更高。 nodelet 对于摄像头和3D传感器这类数据传输量非常大的设备特别有用。

    2). 主题 topic

    节点间用来传输数据的总线。通过主题进行消息传输不需要节点之间直接连接,发布者和订阅者之间不需要知道彼此是否存在。一个主题可以有多个订阅者,也可以有多个发布者。每个主题都是强类型的,发布到主题上的消息必须与主题的 ROS 消息类型相匹配,并且节点只能接收类型匹配的消息:

    • TCP/IP:基于 TCP 传输称为 TCPROS,使用 TCP/IP 长连接,是ROS默认的传输方式;
    • UDP:UDPROS,是一种低延迟高效率的传输方式, 但可能产生数据丢失,最适合远程操控之类的任务。

    3). 服务 srv

    当需要直接与节点通信并以 RPC 方式获得应答时,将无法通过主题实现,而需要使用服务。服务需要由用户开发,节点并不提供标准服务。包含消息源代码的 文件存储在 srv 文件夹中。

    服务类型是包名和 .srv 文件名的组合。例如 chapter2_tutorials/srv/chapter2_srv1.srv 文件的服务类型是 chapter2_tutorials/chapter2_srv1

    4). 消息 msg

    一个节点通过向特定主题发布消息,将信息发送到另一个节点。消息的类型在遵循以下标准命名方式:包名/文件名.msg, 例 如,std_msgs/msg/String.msg 的消息类型是 std_msgs/String

    5). 试用练习

    • 查找 turtlesim 包的路径
    rospack find turtlesim
    >>> /opt/ros/noetic/share/turtlesim
    
    • 1
    • 2
    • 查找在系统中安装过的某个元功能包
    rosstack find ros_comm
    >>> /opt/ros/noetic/share/ros_comm
    
    • 1
    • 2
    • 获得功能包或功能包集下面的文件列表
    rosls turtlesim/
    >>> cmake  images  msg  package.xml  srv
    
    • 1
    • 2
    • 更改当前工作目录
    roscd turtlesim/
    pwd
    >>> /opt/ros/noetic/share/turtlesim
    
    • 1
    • 2
    • 3

    5). 创建工作空间

    • 查看 ROS 正在使用的工作空间
    echo $ROS_PACKAGE_PATH 
    >>> /home/li/Documents/Demo01_Ws/src:/opt/ros/noetic/share
    
    • 1
    • 2
    • 新建此文件夹
    mkdir -p ~/dev/catkin_ws/src
    cd ~/dev/catkin_ws/src/
    catkin_init_workspace
    
    • 1
    • 2
    • 3
    • 编译工作空间
    cd ~/dev/catkin_ws/
    catkin_make
    
    • 1
    • 2
    • 完成配置
    source devel/setup.bash
    
    • 1

    6). 创建 ROS 功能包和元功能包

    cd ~/dev/catkin_ws/src/
    catkin_create_pkg chapter2_tut std_msgs roscpp
    
    • 1
    • 2

    7). 编译ROS功能包

    cd ~/dev/catkin_ws/
    catkin_make
    
    • 1
    • 2

    8). 使用 ROS 节点

    • 启动一个新的节点
    rosrun turtlesim turtlesim_node
    
    • 1
    • 查看用于程序调试的信息
    rosnode info /turtlesim
    
    • 1

    9). 使用主题与节点交互 rostopic

    • 使用箭头键移动海龟
    rosrun turtlesim turtle_teleop_key
    
    • 1
    • 使用以下命令行查看主题清单
    rostopic list
    >>> /rosout
    	/rosout_agg
    	/turtle1/cmd_vel
    	/turtle1/color_sensor
    	/turtle1/pose
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 运行以下命令行并使用箭头键查看消息产生时发送了哪些数据
    rostopic echo /turtle1/cmd_vel
    >>> linear: 
    	  x: 2.0
    	  y: 0.0
    	  z: 0.0
    	angular: 
    	  x: 0.0
    	  y: 0.0
    	  z: 0.0
    ---
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 使用以下命令行查看由主题发送的消息类型
    rostopic type /turtle1/cmd_vel
    >>> geometry_msgs/Twist
    
    • 1
    • 2
    • 使用以下命令查看消息字段
    rosmsg show geometry_msgs/Twist
    >>> geometry_msgs/Vector3 linear
    	  float64 x
    	  float64 y
    	  float64 z
    	geometry_msgs/Vector3 angular
    	  float64 x
    	  float64 y
    	  float64 z
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 直接发布主题使海龟做圆周运动
    rostopic pub /turtle1/cmd_vel geometry_msgs/Twist "linear:
      x: 1.0
      y: 0.0
      z: 0.0
    angular:
      x: 0.0
      y: 0.0
      z: 1.0" 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    10). 使用服务 rosservice

    服务是能够使节点之间相互通信的另一种方法。服务允许节点发送请求和接收响应

    • 列出活动服务
    rosservice list
    >>> /clear
    	/kill
    	/reset
    	/rosout/get_loggers
    	/rosout/set_logger_level
    	/spawn
    	/turtle1/set_pen
    	/turtle1/teleport_absolute
    	/turtle1/teleport_relative
    	/turtlesim/get_loggers
    	/turtlesim/set_logger_level
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 查看某个服务的类型
    rosservice type /clear
    >>> std_srvs/Empty
    
    • 1
    • 2
    • 调用服务
    rosservice call /clear 
    
    • 1
    • 以不同的方向在另一个位置创建另一只海龟
    rosservice type /spawn | rossrv show // 查看该服务的类型
    >>> float32 x
    	float32 y
    	float32 theta
    	string name
    	---
    	string name
    
    rosservice call /spawn 3 3 0.2 "new_turtle" // 调用服务
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    11). 使用参数服务器 rosparam

    参数服务器用于存储所有节点均可访问的数据。ROS中用来管理参数服务器的工具称为 rosparam.

    • 查看所有节点使用的服务器参数
    rosparam list
    >>> /rosdistro
    	/roslaunch/uris/host_li_alienware__41685
    	/rosversion
    	/run_id
    	/turtlesim/background_b
    	/turtlesim/background_g
    	/turtlesim/background_r
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 读取参数的某个值
    rosparam get /turtlesim/background_b
    >>> 255
    
    • 1
    • 2
    • 设定一个新的值
    rosparam set /turtlesim/background_b 100
    
    • 1
    • 使用 dump 参数保存或加载参数服务器的内容
    rosparam dump save.yaml
    
    • 1
    • 使用 load 向参数服务器加载新的数据文件
    rosparam load load.yaml namespace
    
    • 1

    12). 创建节点

    创建两个节点:一个发布数据,另一个接收数据.

    #include "ros/ros.h"
    #include "std_msgs/String.h"
    #include 
    
    /*
        发布方
    */
    
    int main(int argc, char **argv)
    {
        // 初始化节点
        ros::init(argc, argv, "example_a");
        // 进程的处理程序,它允许我们与环境交互
        ros::NodeHandle n;
        // 将节点实例化成发布者,将发布的主题和类型的名称告知节点管理器
        ros::Publisher chatter_pub = 
            n.advertise<std_msgs::String>("message", 1000);
        // 设置发送数据的频率
        ros::Rate loop_rate(10);
    
        while (ros::ok()) {
            // 创建消息变量
            std_msgs::String msg;
            std::stringstream ss;
            ss << "I am the example_a_node";
            msg.data = ss.str();
    
            // 继续发布消息
            chatter_pub.publish(msg);
            // spinOnce 在主循环中执行一次迭代允许用户执行操作
            ros::spinOnce();
            // 将程序挂起
            loop_rate.sleep();
    
        }
      
        return 0;
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    #include "ros/ros.h"
    #include "std_msgs/String.h"
    
    /*
        订阅方
    */
    
    // 回调函数
    // 每次节点收到一条消息时,调用该函数处理数据
    void chatterCallback(const std_msgs::String::ConstPtr& msg) {
        ROS_INFO("I heard: [%s]", msg->data.c_str());
    
    }
    
    int main(int argc, char  **argv)
    {
        ros::init(argc, argv, "example_b");
        ros::NodeHandle n;
    
        // 创建一个订阅者,并从主题获取以message为名称的消息数据
        ros::Subscriber sub = n.subscribe("message", 1000,
                                chatterCallback);
    
        // 运行到这里时调用 chatterCallback 回调函数
        ros::spin();
    
        return 0;
    }
    
    • 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
    • 26
    • 27
    • 28

    13). 编译节点

    • 修改 CMakeLists.txt
    add_executable(example1_a src/example1_a.cpp)
    add_executable(example1_b src/example1_b.cpp)
    
    add_dependencies(example1_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    add_dependencies(example1_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    
    target_link_libraries(example1_a
      ${catkin_LIBRARIES}
    )
    target_link_libraries(example1_b
      ${catkin_LIBRARIES}
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 启动 roscore,并在不同的命令行窗口下分别运行两个节点
    roscore
    rosrun demo01_pub_cli example1_a
    
    • 1
    • 2
    rosrun demo01_pub_cli example1_b
    
    >>> [ INFO] [1700051508.077304984]: I heard: [I am the example_a_node]
    	[ INFO] [1700051508.177173843]: I heard: [I am the example_a_node]
    	[ INFO] [1700051508.277239966]: I heard: [I am the example_a_node]
    	...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 使用 rosnoderostopic 命令来调试和查看当前节点的运行状况
    rosnode info /example_b
    >> Node [/example_b]
    	Publications: 
    	 * /rosout [rosgraph_msgs/Log]
    	
    	Subscriptions: 
    	 * /message [std_msgs/String]
    	
    	Services: 
    	 * /example_b/get_loggers
    	 * /example_b/set_logger_level
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    rostopic info /message 
    >>> Type: std_msgs/String
    
    	Publishers: 
    	 * /example_a
    	
    	Subscribers: 
    	 * /example_b
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    rostopic type /message
    >>> std_msgs/String
    
    • 1
    • 2
    rostopic bw /message
    >>> subscribed to [/message]
    	average: 296.19B/s
    	        mean: 27.00B min: 27.00B max: 27.00B window: 10
    
    • 1
    • 2
    • 3
    • 4

    14). 创建 msg 和 srv 文件

    • 首先创建一个新的 .msg 文件,添加:
    int32 A
    int32 B
    int32 C
    
    • 1
    • 2
    • 3
    • 编辑 package.xml ,取消注释:
    <build_depend>message_generationbuild_depend>
    <exec_depend>message_runtimeexec_depend>
    
    • 1
    • 2
    • 编辑 CMakeList.txt,并编译
    find_package(catkin REQUIRED COMPONENTS
      roscpp
      rospy
      std_msgs
      message_generation
    )
    
    generate_messages(
      DEPENDENCIES
      std_msgs
    )
    
    # Generate messages in the 'msg' folder
    add_message_files(
      FILES
      chapter2_msg1.msg
    )
    
    # Generate added messages and services with any dependencies listed here
    generate_messages(
      DEPENDENCIES
      std_msgs
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 检查编译是否成功,如果看到与 .msg 文件中看到一样的内容,说明编译正确
    rosmsg show demo01_pub_cli/chapter2_msg1
    >>> int32 A
    	int32 B
    	int32 C
    
    • 1
    • 2
    • 3
    • 4
    • 创建一个新的 .srv 文件:
    int32 A
    int32 B
    int32 C
    ---
    int32 sum
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 编辑 package.xml ,取消注释:
    <build_depend>message_generationbuild_depend>
    <exec_depend>message_runtimeexec_depend>
    
    • 1
    • 2
    • 编辑 CMakeList.txt,并编译
    catkin_package(
    #  INCLUDE_DIRS include
    #  LIBRARIES demo01_pub_cli
     CATKIN_DEPENDS message_runtime
    #  DEPENDS system_lib
    )
    
    # Generate messages in the 'msg' folder
    add_message_files(
      FILES
      chapter2_msg1.msg
    )
    
    # Generate services in the 'srv' folder
    add_service_files(
      FILES
      chapter2_srv1.srv
    )
    
    # Generate added messages and services with any dependencies listed here
    generate_messages(
      DEPENDENCIES
      std_msgs
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 检查编译是否成功,如果看到与 .msg 文件中看到一样的内容,说明编译正确
    rossrv show demo01_pub_cli/chapter2_srv1
    >>> int32 A
    	int32 B
    	int32 C
    	---
    	int32 sum
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    15). 使用 srv 和 msg 文件

    该服务将对三个整数求和,需要两个节点:一个 服务器 和一个 客户端

    #include "ros/ros.h"
    #include "demo01_pub_cli/chapter2_srv1.h"
    
    /*
        服务端
    */
    
    // 回调函数
    bool add(demo01_pub_cli::chapter2_srv1::Request &req,
             demo01_pub_cli::chapter2_srv1::Response &res) {
    
        res.sum = req.A + req.B + req.C;
        ROS_INFO("request: A=%d, B=%d, C=%d", (int)req.A, (int)req.B,
                                              (int)req.C);
        ROS_INFO("sending back response: [%d]", (int)res.sum);
    
        return true;
    
    }
    
    int main(int argc, char **argv)
    {
        ros::init(argc, argv, "add_3_ints_server");
        ros::NodeHandle n;
    
        // 创建服务并在 ROS 中发布广播
        ros::ServiceServer service = n.advertiseService("add_3_ints", 
                                                        add);
    
        ROS_INFO("Ready to add 3 ints.");
        ros::spin();
    
        return 0;
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    #include "ros/ros.h"
    #include "demo01_pub_cli/chapter2_srv1.h"
    #include 
    
    /*
        客户端
    */
    
    
    int main(int argc, char **argv)
    {
        ros::init(argc, argv, "add_3_ints_client");
        if (argc != 4) {
            ROS_INFO("usage: add_3_ints_client A B C");
            return 1;
        }
    
        ros::NodeHandle n;
    
        // 创建客户端,名为 add_3_ints
        ros::ServiceClient client = 
            n.serviceClient<demo01_pub_cli::chapter2_srv1>("add_3_ints");
        
        // 创建 srv 请求类型的实例
        // 加入需要发送的数据值
        demo01_pub_cli::chapter2_srv1 srv;
        srv.request.A = atoll(argv[1]);
        srv.request.B = atoll(argv[2]);
        srv.request.C = atoll(argv[3]);
        
        // 调用服务并发送数据
        if (client.call(srv)) {
            ROS_INFO("Sum: %ld", (long int)srv.response.sum);
        } else {
            ROS_ERROR("Failed to call service add_3_ints");
            return 1;
        }
    
        return 0;
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 编译 CMakeList.txt
    add_executable(example2_a src/example2_a.cpp)
    add_executable(example2_b src/example2_b.cpp)
    
    add_dependencies(example2_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    add_dependencies(example2_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    
    target_link_libraries(example2_a
      ${catkin_LIBRARIES}
    )
    target_link_libraries(example2_b
      ${catkin_LIBRARIES}
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 启动节点
    rosrun demo01_pub_cli example2_a
    >>> [ INFO] [1700059229.228592862]: Ready to add 3 ints.
    	[ INFO] [1700059253.580802115]: request: A=1, B=2, C=3
    	[ INFO] [1700059253.580825139]: sending back response: [6]
    
    • 1
    • 2
    • 3
    • 4
    rosrun demo01_pub_cli example2_b 1 2 3
    >>> [ INFO] [1700059253.580914856]: Sum: 6
    
    • 1
    • 2
    • 创建发布和订阅节点
    #include "ros/ros.h"
    #include "demo01_pub_cli/chapter2_msg1.h"
    #include 
    
    int main(int argc, char **argv)
    {
        ros::init(argc, argv, "example3_a");
        ros::NodeHandle n;
    
        ros::Publisher pub = 
            n.advertise<demo01_pub_cli::chapter2_msg1>("message", 1000);
        ros::Rate loop_rate(10);
    
        while (ros::ok()) {
            demo01_pub_cli::chapter2_msg1 msg;
    
            msg.A = 1;
            msg.B = 2;
            msg.C = 3;
            pub.publish(msg);
            ros::spinOnce();
    
            loop_rate.sleep();
    
        }
    
        return 0;
    }
    
    • 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
    • 26
    • 27
    • 28
    #include "ros/ros.h"
    #include "demo01_pub_cli/chapter2_msg1.h"
    
    
    void messageCallback(const demo01_pub_cli::chapter2_msg1::ConstPtr& msg) {
    
        ROS_INFO("I heard: [%d] [%d] [%d]", msg->A, msg->B, msg->C);
    
    }
    
    
    int main(int argc, char **argv)
    {
        ros::init(argc, argv, "example3_b");
        ros::NodeHandle n;
    
        ros::Subscriber sub = n.subscribe("message", 1000, 
                                        messageCallback);
    
        ros::spin();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 编译 CMakeList.txt
    add_executable(example3_a src/example3_a.cpp)
    add_executable(example3_b src/example3_b.cpp)
    
    add_dependencies(example3_a ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    add_dependencies(example3_b ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
    
    target_link_libraries(example3_a
      ${catkin_LIBRARIES}
    )
    target_link_libraries(example3_b
      ${catkin_LIBRARIES}
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 启动节点
    rosrun demo01_pub_cli example3_a
    
    • 1
    rosrun demo01_pub_cli example3_b
    >>> [ INFO] [1700061493.409080424]: I heard: [1] [2] [3]
    	[ INFO] [1700061493.509095136]: I heard: [1] [2] [3]
    	[ INFO] [1700061493.609056521]: I heard: [1] [2] [3]
    	...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    16). launch

    用于启动多个节点。 当执行启动文件时,并不需要在 roscore 命令前启动,roslaunch 会启动它。当在 shell中只运行一个节点时,可以看到 ROS_INFO 输出的消息。但是当运行启动文件时,则看不到。

    • 创建 .launch 文件
    <launch>
        <node name="example1_a" pkg="demo01_pub_cli" type="example1_a" />
        <node name="example1_b" pkg="demo01_pub_cli" type="example1_b" />
    launch>
    
    • 1
    • 2
    • 3
    • 4
    • 启动 .launch 文件
    roslaunch demo01_pub_cli chapter2.launch
    
    • 1
    • 查看运行的节点:
    rosnode list
    >>> /example1_a
    	/example1_b
    	/rosout
    
    • 1
    • 2
    • 3
    • 4
    • 运行 rqt_console 程序看到信息
    rqt_console
    
    • 1

    rqt

    17). 动态参数

    配置一个包含动态重配置实用程序功能的基本节点。

    • 创建配置文件 .cfg
    #! /usr/bin/env python
    PACKAGE = "demo01_pub_cli"
    
    # 初始化参数生成器
    from dynamic_reconfigure.parameter_generator_catkin import *
    
    gen = ParameterGenerator()
    
    # 加入不同的参数类型并设置默认值、描述、范围等
    """
            gen.add()
                    - name: 参数的名称
                    - type: 参数值的类型
                    - level: 一个传递给回调的位掩码
                    - description: 描述
                    - default: 节点启动时的默认值
                    - min: 参数最小值
                    - max: 参数最大值
    """
    gen.add("double_param", double_t, 0, "A double parameter",
            .1, 0, 1)
    gen.add("str_param", str_t, 0, "A string parameter", "Chapter2_dynamic_reconfigure")
    gen.add("int_param", int_t, 0, "An Integer parameter", 1, 0, 100)
    gen.add("bool_param", bool_t, 0, "A Boolean parameter", True)
    
    size_enum = gen.enum([gen.const("Low", int_t, 0, "Low is 0"),
                          gen.const("Medium", int_t, 1, "Medium is 1"),
                          gen.const("High", int_t, 2, "High is 2")],
                          
                                    "Select from the list")
    
    gen.add("size", int_t, 0, "Select from the list", 1, 0, 3, edit_method=size_enum)
    
    # 生成必要的文件并退出程序
    exit(gen.generate(PACKAGE, "demo01_pub_cli", "chapter2_"))
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 修改执行文件的权限
    chmod +x *.cfg
    
    • 1
    • 修改 CMakeList.txt 并编译
    find_package(catkin REQUIRED COMPONENTS
      roscpp
      rospy
      std_msgs
      message_generation
      dynamic_reconfigure
    )
    
    generate_dynamic_reconfigure_options(
      cfg/chapter2.cfg
    )
    
    add_dependencies(example4 demo01_pub_cli_gencfg)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 写具有动态重配置支持的新节点
    #include 
    #include 
    #include 
    
    // 参数访问的方式, 将输出参数的新值
    void callback(demo01_pub_cli::chapter2_Config &config, uint32_t level) {
        ROS_INFO("Reconfigure Request: %d, %f, %s %s %d",
                config.int_param,
                config.double_param,
                config.str_param.c_str(),
                config.bool_param?"True":"False",
                config.size);
    
    }
    
    int main(int argc, char **argv)
    {
        ros::init(argc, argv, "example4_dynamic_reconfigure");
    
        // 初始化服务器
        dynamic_reconfigure::Server<demo01_pub_cli::chapter2_Config> server;
        dynamic_reconfigure::Server<demo01_pub_cli::chapter2_Config>::CallbackType f;
    
        f = boost::bind(&callback, _1, _2);
    
        // 向服务器发送callback函数。当服务器得到重新配置请求时调用 callback 函数
        server.setCallback(f);
    
        ros::spin();
        return 0;
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 修改 CMakeLists.txt , 编译并运行节点和动态重配置 GUI
    add_executable(example4 src/example4.cpp)
    add_dependencies(example4 demo01_pub_cli_gencfg)
    
    • 1
    • 2
    rosrun demo01_pub_cli example4
    >>> [ INFO] [1700312052.497444818]: Reconfigure Request: 1, 0.100000, Chapter2_dynamic_reconfigure True 1
    
    • 1
    • 2
    rosrun rqt_reconfigure rqt_reconfigure
    >>> [ INFO] [1700312097.307494990]: Reconfigure Request: 42, 0.100000, Chapter2_dynamic_reconfigure True 1
    	[ INFO] [1700312099.355379827]: Reconfigure Request: 42, 0.470000, Chapter2_dynamic_reconfigure True 1
    	[ INFO] [1700312101.253127436]: Reconfigure Request: 73, 0.470000, Chapter2_dynamic_reconfigure True 1
    	[ INFO] [1700312103.210104907]: Reconfigure Request: 73, 0.710000, Chapter2_dynamic_reconfigure True 1
    
    • 1
    • 2
    • 3
    • 4
    • 5

    rqt_reconfigure


  • 相关阅读:
    python模块之 aiomysql 异步mysql
    移远_M25_通话音量_DTMF测试
    如何看待区块链技术是数据存储的未来
    SparkSQL DataFrame的介绍及创建
    蚂蚁二面,面试官问我零拷贝的实现原理,当场懵了…
    自动化测试框架pytest命令参数
    C语言--atoi函数详解及模拟实现
    【C++历险记】国庆专辑---探索多态迷宫的代码之旅!
    如何提高UDP传输的可靠性(三大方式RUDP、RTP、UDT)
    JOSEF 同步检查继电器 JT-1 额定电压100V 柜内固定安装,板前接线
  • 原文地址:https://blog.csdn.net/Ashen_0nee/article/details/133718378