Nodelet
- ROS 中 Publisher/Subscriber 通信机制方便灵活,但是每次接收的数据都会拷贝一份,对于数据量较大的 msg 如lidar,效率十分低下。
- Nodelet 提供了一种方式, 能让多个运行的算法节点之间不用这种低效的拷贝,达到共享数据的方式.
Nodelet 设计目的
- use the existing C++ ROS interfaces. 使用当前ros 的接口
- allow zero copy passing of data between nodelets 零拷贝
- dynamically load as plugins to break build time dependencies
- location transparent except for performance improvements。
- 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 list
you 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 名字