1、nodelet简介
通常情况下,我们使用的ros节点都是独立的可执行文件,每个节点启动后在系统中都是以一个独立的进程存在的,即节点之间的通信就是进程间的通信,并且通信过程需要消耗网络的带宽。为了提高通信的效率和减少网络带宽的占用,ros中有一类特殊的节点-nodelet,这类节点可以在单个进程下以多个线程的形式运行,这样节点间的通信就是线程间的通信了。比如摄像头这类数据量大的传感器,可以使用nodelet方式与其他节点通信,从而大大提高传输效率。
2、nodelet的运行
2.1 环境配置
首先需要安装nodelet_tutorial_math功能包(通过sudo apt-get install ros-$ROS_DISTRO-nodelet-tutorial-math安装),并且在终端中运行roscore
2.2 运行管理器(manager)
所有的nodelet节点都是在manager下运行,所以首先运行manager
rosrun nodelet nodelet manager __name:=nodelet_manager
这里将manager 的名字改为nodelet_manager
2.3 运行nodelet节点
用如下命令运行一个nodelet节点,可执行程序nodelet会联系nodelet_manager,并且让他实例化一个nodelet_tutorial_math/Plus对象
rosrun nodelet nodelet load nodelet_tutorial_math/Plus nodelet_manager __name:=nodelet1 nodelet1/in:=foo _value:=1.1
如果你在终端中运行rostopic list,你可以看到
/foo
/nodelet1/out
如果你在终端中运行rosnode list,你可以看到
/nodelet1
/nodelet_manager
2.3.1 运行测试
在终端中运行
rostopic pub /foo std_msgs/Float64 5.0 -r 10
rostopic echo /nodelet1/out
在终端中将会看到不断输出6.1,(5.0+1.1)
2.4 在roslaunch中运行
<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>
首先启动manager,然后运行三个nodelet节点。
3、创建自己的nodelet节点
如果还没有创建catkin工作空间,则首先创建catkin工作空间,然后在在src目录下创建一个功能包,可以命名为nodelet_test。
然后在/include/nodelet_test目录下新建一个文件MyNodeletClass.h,文件内容如下:
#include <nodelet/nodelet.h>
namespace nodelet_test
{
class MyNodeletClass : public nodelet::Nodelet
{
public:
virtual void onInit();
};
}
将类写在命名空间下是一种好的编程习惯,不是强制的。 定义的类MyNodeletClass继承自nodelet::Nodelet类,函数onInit()是类nodelet::Nodelet的纯虚函数,子类必须重新实现。并且manager动态加载MyNodeletClass类后,onInit()函数会自动执行。
在src目录下新建文件MyNodeletClass.cpp,内容如下
#include <pluginlib/class_list_macros.h>
// Include your header
#include <nodelet_test/MyNodeletClass.h>
// watch the capitalization carefully
PLUGINLIB_EXPORT_CLASS(nodelet_test::MyNodeletClass, nodelet::Nodelet)
namespace example_pkg
{
void MyNodeletClass::onInit()
{
NODELET_DEBUG("Initializing nodelet...");
}
}
因为要把类MyNodeletClass编译成插件(动态库),因此还需要新建文件nodelet_plugins.xml,内容如下
<library path="lib/libMyNodeletClass">
<class name="nodelet_test/MyNodeletClass" type="nodelet_test::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>
...
CMakeLists.txt文件部分关键内容如下:
find_package(catkin REQUIRED COMPONENTS
nodelet
)
...
catkin_package(
INCLUDE_DIRS include
)
include_directories(
include
${catkin_INCLUDE_DIRS}
)
## Declare a C++ library
add_library(${PROJECT_NAME}
src/MyNodeletClass.cpp
)
target_link_libraries(${PROJECT_NAME}
${catkin_LIBRARIES}
)
...
最后利用catkin_make编译。
在/launch文件夹下新建文件nodelet_test.launch,内容如下
<launch>
<node pkg="nodelet" type="nodelet" name="standalone_nodelet" args="manager" output="screen"/>
<node pkg="nodelet" type="nodelet" name="MyNodeletClass" args="load nodelet_test/MyNodeletClass standalone_nodelet" output="screen">
</node>
</launch>