ROS系列之Nodelet---基础知识

nodelet参考API

为什么需要Nodelet

ROS是一种基于分布式网络通讯的操作系统,整个机器人控制系统是由一个Master主节点和若干个功能相对独立的Node子节点组成,这也是ROS系统最主要的特点就是分布式以及模块化的设计。在ROS通讯过程中Master节点存储着各个子节点的topics和services的注册信息,每个功能节点在请求服务之前先向主节点进行注册,然后节点之间就可以直接进行信息传递。ROS的底层通信都是基于XML-RPC协议实现的。
ROS系列之Nodelet---基础知识_第1张图片
XML-RPC协议是XML Remote Prodecure Call的简称,是一种远程过程调用的分布式网络协议。它允许跨平台的软件间通过发送和接收XML格式的消息进行远程调用,即允许不同的操作系统、不同环境中的程序实现基于Internet过程调用的规范和一系列方法的实现。这种远程过程调用使用http作为传输协议,XML作为传送信息的编码格式。
ROS系列之Nodelet---基础知识_第2张图片
XML-RPC的远程调用过程为:首先客户端发起请求后需要按照协议格式对请求信息进行填充;填充完毕以后XML格式的信息会被转化为数据流,通过传输层进行传输。服务端收到客户端发出来的数据流,会将其再转化为XML格式的信息,然后按照XML-RPC协议获取客户端的请求信息,并对请求信息进行处理,处理完毕以后将反馈信息发送给客户端。

以XML-RPC的方式传输数据存在一定的延时和阻塞。在数据量小、频率低的情况下,传输耗费的时间可以忽略不计。但当传输图像流,点云等数据量较大的消息,或者执行有一定的实时性要求的任务时,因传输而耗费的时间就不得不考虑。Nodelet包就是为改善这一状况设计的,它提供一种方法,可以让多个算法程序在一个进程中用 shared_ptr 实现零拷贝通信(zero copy transport),以降低因为传输大数据而损耗的时间。简单讲就是可以将多个node捆绑在一起管理,使得同一个manager里面的topic的数据传输更快。

摘录自:ROS nodelet的使用

Intraprocess Publishing(进程内发布)

如果希望no-copy pub/sub工作,您必须将消息发布为shared_ptr。

当发布服务器和订阅服务器作用在相同节点的同一个话题,roscpp可以跳过序列化/反序列化步骤(可能节省大量的处理和延迟)。但只有当消息被发布为shared_ptr,才会这样处理。
示例:

ros::Publisher pub = nh.advertise("topic_name", 5);
std_msgs::StringPtr str(new std_msgs::String);
str->data = "hello world";
pub.publish(str);

注意,当用这种方式发布,你和roscpp之间的隐性契约:你不可以修改你发送后的消息,因为指针会直接传递到用户的任何进程内。如果你想发送另一条消息,你必须分配一个新的。

介绍

nodelet包被设计为提供在算法之间具有零拷贝传输相同进程运行多个算法的方式。
这个包提供了实现节点所需的节点基类,以及用于实例化节点的NodeletLoader类。

原理

  • Nodelets旨在提供一种在单机器单进程运行多个算法而不会在进程中传递消息时产生复制成本的方法。
  • roscpp具有在同一节点内的发布和订阅调用之间进行零拷贝指针传递的优化
  • 为了做到这一点,nodelet允许将类动态加载到同一个节点,然而它们提供了简单的单独命名空间,使得尽管nodelet在同一个进程中,它仍然像一个独立的节点
  • 这进一步扩展了,它在运行时使用pluginlib动态可加载的。

应用程序

高吞吐量数据流可以由许多节点组成,然后加载到同一进程,以避免复制和网络流量。

设计目标

  • 使用现有的C++ ROS接口。
  • 允许节点之间的数据的零拷贝传递
  • 动态加载为插件以打破构建时间依赖性
  • 位置透明,除了性能改进
  • 节点或节点中编写代码将有最小差异。

技术

  • 定义将用于动态加载的基类nodelet :: Nodelet。
  • 所有nodelet都将继承这个基类,并且可以使用pluginlib进行动态加载
  • 它将提供命名空间,自动重映射参数和参数,就像它们是第一个类节点一样。
  • 将有一个nodelet_manager进程,一个或多个nodelet可以加载到其中。
  • 它们之间的任何通信都可以使用带有boost共享指针的零拷贝roscpp发布调用。

Nodelet基类

公共方法:

//动态加载时使用的默认构造函数
Nodelet() 
//这个方法是一个描述nodelet如何启动。
//参数是管理器启动节点所需的参数。这将初始化nodelet基类,然后调用子类的onInit()方法。
void init (const std::string& name, const ros::M_string& remapping_args, const std::vector& my_argv); 

子类中使用的受保护成员和方法:

std::string             getName() //Get the name of the nodelet
ros::NodeHandle&        getNodeHandle () // Get the node handle (provides this nodelets custom remappings and name)
ros::NodeHandle&        getPrivateNodeHandle () // Get the private node handle (provides this nodelets custom remappings in its private namespace)
ros::NodeHandle&        getMTNodeHandle () // Get the node handle with the Multi Threaded callback queue. (provides this nodelets custom remappings and name)
ros::NodeHandle&        getMTPrivateNodeHandle () // Get the private node handle with the Multi Threaded callback queue. (provides this nodelets custom remappings in its private namespace)
ros::CallbackQueue&     getMTCallbackQueue () // Get the callback queue (threadpool available from the manager)
std::vector getMyArgv() // Get command line arguments to the nodelet stripped of ROS and nodelet specific args.  

用于在子类中启动ROS API的初始化方法:

virtual void onInit () = 0 //Virtual and must be overridden by subclass. All initialization of the ROS infrastructure must be put into this function.

NODELET ROSCONSOLE宏

  • 这些Nodelet封装是rosconsole宏。
  • 它们包括详细程度级别DEBUG,INFO,WARN,ERROR和FATAL。
  • 这些宏只会在nodelet方法中编译。
  • 它们通过在运行的nodelet名称中设置命名的日志记录器来操作,以便您可以区分在运行的同一管理器下的两个相同类型的节点的输出。
  • 他们也有优势,你可以转一个具体nodelet进入调试,而不是所有特定类型的节点。

示例:

#include "nodelet/nodelet.h"

//... inside a nodelet method
NODELET_DEBUG("My debug statement")
NODELET_DEBUG_STREAM("my debug statement " << (double) 1.0)
NODELET_DEBUG_COND( 1 == 1, "my debug_statement")
NODELET_DEBUG_STREAM_COND( 1 == 1, "my debug statement " << (double) 1.0)

线程模型

节点管理器具有线程池,该线程池在管理器内运行的所有节点之间共享。
这由参数“num_worker_threads”设置。
在nodelet中运行的代码中有两种可能的线程API。
默认线程模型有一个线程用于所有回调。还有一个多线程API。

onInit,这个方法在init上调用,不应该阻塞或做重要的工作。

  • Single Threaded API单线程API,使用方法getNodeHandle()和getPrivateNodeHandle()将保证所有回调都并行到达。
  • Multi Threaded API多线程API,使用方法getMTNodeHandle()和getMTPrivateNodeHandle()回调将分布在管理器线程池。
  • Additional Threads其他线程,它是节点创建自己的线程进行操作的有效操作。这些线程应该在析构函数中正确清理。
  • Thread Sharing线程共享,所有nodelet共享管理器的线程池。如果nodelet阻塞线程,它们可能会阻止其他nodelet获得回调。确保管理器配置了足够的线程以防止阻塞。

注意:即使单个线程的节点句柄也可以每个节点使用池的1个线程。

你可能感兴趣的:(ROS,ROS,nodelet)