ROS包全攻略:消息vs服务;可执行vs插件

ROS包通过catkin_create_pkg创建,是实现系统的基本单元。根据对信息的传递和处理方式,ROS包可以分为消息模式和服务模式;根据编译后的调用方法,又可分为可执行包和插件包。ROS包的实现大多在上述的分类范围内。

1、ROS包消息/服务模式与要点

从功能上看,ROS包是信息交互和处理的基本单元。根据信息的交互和处理方式,ROS包有以下两大类:

  1. 消息发布者订阅者
  2. 服务器客户端

对于消息模式的包,信息的提供者主动,信息的使用者被动:信息使用者接收到消息后执行回调函数处理信息,即处理信息的回调函数由信息发布者触发;

对于服务模式的包,信息的提供者被动,信息的使用者主动:信息使用者需要时向信息提供者查询,提供者收到查询参数后,调用回调函数计算结果然后给予应答,即处理信息的回调函数由服务的查询者(信息使用者)触发;

要点如下:

1.1 消息发布与订阅

消息的定义方法:
  1. 消息定义于包目录的msg/xxx.msg中
  2. 定义消息的包可以与发布者/订阅者的包不一样
  3. 在消息定义包的package.xml中加上build_depend和run_depend约束项
  4. 在消息定义包的CMakeLists.txt中设置好find_package(), catkin_package(), add_message_files(), generate_messages()
消息的发布
  1. 完成节点初始化 ros::init()
  2. 创建节点句柄 ros::NodeHandle
  3. 调用NodeHandle的advertise方法创建消息发布者
  4. 调用消息发布者的publish方法发布消息
消息的订阅
  1. 创建订阅到消息后需要执行的回调函数
  2. 完成节点初始化 ros::init()
  3. 创建节点句柄 ros::NodeHandle
  4. 调用NodeHandle的subscribe方法创建消息订阅者并绑定回调函数
  5. ros::spin()
消息发布和订阅的命令行工具

通过命令行发布和订阅消息,不用通过ROS节点,测试的时候非常方便:
1. rosmsg show [message type] 显示消息的数据格式
2. rostopic echo /topic_name 显示特定topic接收到的消息
3. rostopic pub <topic-name> <topic-type> [data...] 向特定的topic发送消息

1.2 服务器与客户端

服务的定义方法:
  1. 服务定义于包目录的srv/xxx.srv中
  2. 定义服务的包可以与服务器/客户端的包不一样
  3. 在消息定义包的CMakeLists.txt中设置好find_package(), add_service_files(), generate_messages()
服务器
  1. 创建处理服务数据的回调函数
  2. 完成节点初始化 ros::init()
  3. 创建节点句柄 ros::NodeHandle
  4. 调用节点句柄的advertiseService函数启动服务
  5. ros::spin()
客户端
  1. 完成节点初始化 ros::init()
  2. 创建节点句柄 ros::NodeHandle
  3. 调用节点句柄的serviceClient函数创建客户端对象client,通过服务名称绑定服务
  4. 申明服务类型的对象(srv),并将srv.request部分赋初值
  5. 调用client的call(srv)函数调用服务
  6. 服务执行成功后,通过srv.response取得服务器的应答结果
命令行工具
  1. rosservice call <service-name> [service-args]调用特定服务

1.3 需要注意的细节

  1. 消息/服务模式的节点实现时要include消息/服务对应的头文件
  2. 上述头文件需要先编译消息定义包来生成,因此需要在消息/服务使用者对应的CMakeLists.txt中加上add_dependencies()约束,否则会出现找不到头文件的现象
  3. 信息交互中的被动方,即消息模式的消息订阅者服务模式的服务器在都要调用ros::spin(),该函数确保回调能够发生。

2、可执行包/插件包及要点

刚刚从信息交互和处理角度介绍了ROS包的两种模式,不管是哪种模式的ROS包,根据编译后调用的方式,都可以分为以下两种情况:

  1. 可执行包
  2. 插件包

可执行包可以通过rosrun直接调用;

插件包类似于动态链接库,提供特定的功能。配合软件总线的方式动态加载与卸载,可以实现功能的可重构;

这两种包在实现上的区别主要体现在编译指导语句方面。

2.1 可执行包

在对应包的CMakeLists.txt中添加

add_executable(<exec_name> src/xxx.cpp)
target_link_libraries(<exec_name> ${catkin_LIBRARIES})
add_dependencies(<exec_name> <msg/srv_pkg_name>_gencpp)

其中,<exec_name>为可执行文件的名字,xxx.cpp是源代码名字,msg/srv_pkg_name为所用到的消息/服务的对应包的名字。

编译后,可执行包可以通过rosrun直接执行。

2.2 插件包

插件的定义方法
  1. 定义一个基类,所有的插件将继承这一基类。在该基类中以公有成员函数(虚函数)的形式定义插件所具备的共同的接口。基类放到一个.h文件中,该文件可以与具体插件的实现不在一个ROS包中
  2. 定义具体的插件类,继承上述基类,放到.h文件中
  3. 在.cpp文件中实现插件类的具体功能,并在该.cpp文件中额外加上两行:

    
    #include <pluginlib/class_list_macros.h>
    
    PLUGINLIB_EXPORT_CLASS(<plugin_namespace::plugin_class>, <baseClass_namespace::base_class>))
  4. 在插件包对应的CMakeLists.txt中加入:

    • find_package()中加入pluginlib
    • add_library(<plugins_lib_name> src/xxx.cpp)
  5. 在插件包目录中增加一个名为_plugin.xml文件,文件内容如下:

    <library path="lib/lib<plugins_lib_name>">
         <class name="<pkg_name>/<plugin_name>" type="<plugin_namespace::plugin_class>" base_class_type="<baseClass_namespace::base_class>">
           <description>
            This is a xxx plugin.
           </description>
         </class>
    </library>
  6. 在插件包的package.xml文件中增加

    <build_depend>pluginlib</build_depend>
    <run_depend>pluginlib</run_depend>
    
    <export>
        <<base_class_pkg_name> plugin="${prefix}/<plugins_lib_name>_plugin.xml"/>
    </export>
插件的加载与调用
  1. 在使用插件的代码前include两个头文件
    • pluginlib/class_loader.h
    • 定义了插件的base_class的头文件
  2. 定义一个插件的加载器:

    pluginlib::ClassLoader<<base_class_namespace::base_class>> <loader_name>("<base_class_pkg_name>", "<base_class_namespace::base_class>") 
  3. 使用ClassLoader的createInstance("<plugin_namespace::plugin_class>")函数加载具体插件,该函数返回一个插件指针,通常将其赋值给一个智能指针boost::shared_ptr,以便后续调用插件功能。

  4. 上述第3步往往放在try中执行,并通过catch (const pluginlib::PluginlibException& ex)捕获异常,并打印ex.what()以显示发生的异常(如果有异常的话)
  5. 假设第3步中的createInstance返回的指针为ptr,通过ptr-><function>可以调用插件中的函数
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值