ROS nodelet - 零拷貝的通信方式

Nodelet

  1. ROS 中 Publisher/Subscriber 通信機制方便靈活,但是每次接收的數據都會拷貝一份,對於數據量較大的 msg 如lidar,效率十分低下
  2. Nodelet 提供了一種方式, 能讓多個運行的算法節點之間不用這種低效的拷貝,達到共享數據的方式.

Nodelet 設計目的

  1. use the existing C++ ROS interfaces. 使用當前ros 的接口
  2. allow zero copy passing of data between nodelets 零拷貝
  3. dynamically load as plugins to break build time dependencies
  4. location transparent except for performance improvements。
  5. writing code in a node or a nodelet will be minimally different. 節點內的代碼儘量少的改變

Nodelet 技術實現

  • 定義 nodelet::Nodelet 基類,這個類將會被動態的載入, 所有的nodelets會動態的重載此類
  • 這個類將會提供 命名空間,參數
  • 一個 nodelet_manager 將會處理一個或多個 nodelets,任何nodelet 之間的信息傳輸將會使用 boost::shared_ptr , 從而避免了多次拷貝
  • Define a base class nodelet::Nodelet which will be used for dynamic loading. All nodelets will inherit from this base class, and be dynamically loadable using pluginlib.
  • It will provide the namespace, remapping arguments and parameters automatically, like they were a first class node.
  • There will be a nodelet_manager process into which one or more nodelets can be loaded. Any communications between them can use the zero copy roscpp publish call with a boost shared pointer.

Nodelet 的使用

# nodelet usage:
nodelet load pkg/Type manager #Launch a nodelet of type pkg/Type on manager manager
nodelet standalone pkg/Type   # Launch a nodelet of type pkg/Type in a standalone node
nodelet unload name manager   # Unload a nodelet a nodelet by name from manager
nodelet manager              # Launch a nodelet manager node

Bring up a manager

A nodelet will be run inside a NodeletManager. A nodelet manager is a c++ program which is setup to listen to ROS services and will be the executable in which the nodelet is dynamically loaded. In this case we will run a standalone manager, but in many cases these managers will be embedded within running nodes.

rosrun nodelet nodelet manager __name:=nodelet_manager

We have renamed this node to nodelet_manager for clarity.

Launching the nodelet

Nodelets are launched remotely by using the nodelet executable as well.

What this code does: The nodelet executable, called here, will contact the nodelet_manager and ask it to instantiate an instance of the nodelet_tutorial_math/Plus nodelet. It will pass through the name, nodelet1, and also any remappings if applied to the code inside the nodelet. And parameters appear in the right namespace too.

rosrun nodelet nodelet load nodelet_tutorial_math/Plus nodelet_manager __name:=nodelet1 nodelet1/in:=foo _value:=1.1

If you do a rostopic listyou will see:

/foo
/nodelet1/out

If you look at the output of rosnode list you will see:

/nodelet1
/nodelet_manager

Testing Operation
In separate terminals run:

rostopic pub /foo std_msgs/Float64 5.0 -r 10
rostopic echo /nodelet1/out

Will show: 6.1 which is 5.0 + 1.1 .

Using within roslaunch files

Here is an example launch file (available in nodelet_tutorial_math pkg) with multiple nodelets running on the same standalone manager:

<launch>
  <node pkg="nodelet" type="nodelet" name="standalone_nodelet"  args="manager"/>

  <node pkg="nodelet" type="nodelet" name="Plus"
        args="load nodelet_tutorial_math/Plus standalone_nodelet">
    <remap from="/Plus/out" to="remapped_output"/>
  </node>
  
  <rosparam param="Plus2" file="$(find nodelet_tutorial_math)/plus_default.yaml"/>
  
  <node pkg="nodelet" type="nodelet" name="Plus2" args="load nodelet_tutorial_math/Plus standalone_nodelet">
    <rosparam file="$(find nodelet_tutorial_math)/plus_default.yaml"/>
  </node>
  
  <node pkg="nodelet" type="nodelet" name="Plus3" args="standalone nodelet_tutorial_math/Plus">
    <param name="value" type="double" value="2.5"/>
    <remap from="Plus3/in" to="Plus2/out"/>
  </node>
  
</launch>

Porting nodes to nodelets

MyNodeletClass.h
#include <nodelet/nodelet.h>

namespace example_pkg
{

    class MyNodeletClass : public nodelet::Nodelet
    {
        public:
            virtual void onInit();
    };

}
MyNodeletClass.cpp
// this should really be in the implementation (.cpp file)
#include <pluginlib/class_list_macros.h>

// Include your header
#include <example_pkg/MyNodeletClass.h>

// watch the capitalization carefully
PLUGINLIB_EXPORT_CLASS(example_pkg::MyNodeletClass, nodelet::Nodelet)

namespace example_pkg
{
    void MyNodeletClass::onInit()
    {
        NODELET_DEBUG("Initializing nodelet...");
    }
}
nodelet_plugins.xml

This file should be placed along with the package.xml file

<library path="lib/libMyNodeletClass">
  <class name="example_pkg/MyNodeletClass" type="example_pkg::MyNodeletClass" base_class_type="nodelet::Nodelet">
  <description>
  This is my nodelet.
  </description>
  </class>
</library>
package.xml
<build_depend>nodelet</build_depend>
<run_depend>nodelet</run_depend>
<export>
  <nodelet plugin="${prefix}/nodelet_plugins.xml" />
</export>
mynodelet.launch

Launch file should be placed under the launch/ repertory of your package, you can create it if it does not exist.

<launch>
  <node pkg="nodelet" type="nodelet" name="standalone_nodelet"  args="manager" output="screen"/>

  <node pkg="nodelet" type="nodelet" name="MyNodeletClass" args="load example_pkg/MyNodeletClass standalone_nodelet" output="screen">
  </node>
</launch>
  • launch 文件中單獨啓動一個 nodelet manager
  • 自己編寫的 nodelet 節點需要加入參數 args, 先載入自己的, 再載入manager 名字

參考文檔

  1. nodelet 官方介紹
  2. nodelet Tutorials - Running a nodelet
  3. nodelet Tutorials - Porting nodes to nodelets

2020.02.12

Turku . Finland

發佈了51 篇原創文章 · 獲贊 25 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章