Nodelet
Nodelet旨在提供一種在單機器單進程運行多個算法而不會在進程中傳遞消息時產生複製成本的方法。
爲什麼需要Nodelet
1、ROS是一種基於分佈式網絡通訊的操作系統,整個機器人控制系統是由一個Master主節點和若干個功能相對獨立的Node子節點組成,這也是ROS系統最主要的特點就是分佈式以及模塊化的設計。在ROS通訊過程中Master節點存儲着各個子節點的topics和services的註冊信息,每個功能節點在請求服務之前先向主節點進行註冊,然後節點之間就可以直接進行信息傳遞。ROS的底層通信都是基於XML-RPC協議實現的。
XML-RPC協議是XML Remote Prodecure Call的簡稱,是一種遠程過程調用的分佈式網絡協議。它允許跨平臺的軟件間通過發送和接收XML格式的消息進行遠程調用,即允許不同的操作系統、不同環境中的程序實現基於Internet過程調用的規範和一系列方法的實現。這種遠程過程調用使用http作爲傳輸協議,XML作爲傳送信息的編碼格式。
XML-RPC的遠程調用過程爲:首先客戶端發起請求後需要按照協議格式對請求信息進行填充;填充完畢以後XML格式的信息會被轉化爲數據流,通過傳輸層進行傳輸。服務端收到客戶端發出來的數據流,會將其再轉化爲XML格式的信息,然後按照XML-RPC協議獲取客戶端的請求信息,並對請求信息進行處理,處理完畢以後將反饋信息發送給客戶端。
以XML-RPC的方式傳輸數據存在一定的延時和阻塞。在數據量小、頻率低的情況下,傳輸耗費的時間可以忽略不計。但當傳輸圖像流,點雲等數據量較大的消息,或者執行有一定的實時性要求的任務時,因傳輸而耗費的時間就不得不考慮。Nodelet包就是爲改善這一狀況設計的,它提供一種方法,可以讓多個算法程序在一個進程中用 shared_ptr 實現零拷貝通信(zero copy transport),以降低因爲傳輸大數據而損耗的時間。簡單講就是可以將多個node捆綁在一起管理,使得同一個manager裏面的topic的數據傳輸更快。
以上內容摘自:ROS nodelet的使用
一、Nodelet基本用法
1、nodelet語法
2、啓動nodelet manager管理器
#__name:=nodelet_manager 表示將管理器重命名爲nodelet_manager
rosrun nodelet nodelet manager __name:=nodelet_manager
運行結果:
3、啓動nodelet節點
#load nodelet_tutorial_math/Plus 表示啓動這個節點
#後面接着nodelet_manager,表示本節點加入到名爲nodelet_manager的nodelet管理器
#__name:=nodelet1 重命名本節點
#nodelet1/in:=foo _value:=1.1 傳參
rosrun nodelet nodelet load nodelet_tutorial_math/Plus nodelet_manager __name:=nodelet1 nodelet1/in:=foo _value:=1.1
4、查看當前運行的節點
rosnode list
5、 測試nodelet1
剛剛啓動的nodelet1節點的功能是:接受“foo”這個topic的消息,收到消息後,將該值與給定初始值1.1相加,然後輸出
啓動一個終端,用來監nodelet1的輸出
rostopic echo /nodelet1/out
下面使用rostopic發佈話題,進行測試
rostopic pub /foo std_msgs/Float64 5.0
執行之後,可以看到,輸出值爲6.1
二、實現一個Nodelet(C++)
1、概述
Nodelet節點與傳統的ros節點有點不一樣,不一樣在於,節點的源文件cpp裏面並沒有main函數。
其特點是:是一個類(Class)的形式
所以,編譯的時候,並不是編譯成可執行文件,而是編譯成庫文件
2、項目文件樹
最終的Package文件樹大概長這樣
3、創建Package
在catkin_ws/src目錄下,創建package
catkin_create_pkg base_nodelet
分別創建src、plugins、launch文件夾
4、在src目錄下,開始編寫源代碼
cd src
nodeletclass1.h文件
#include <nodelet/nodelet.h>
class nodeletclass1 :public nodelet::Nodelet //繼承父類nodelet::Nodelet
{
public:
nodeletclass1(); //構造函數,可有可無?
public:
virtual void onInit(); //這個虛函數,在啓動本Nodelet節點時,自動調用
};
nodeletclass1.cpp文件
#include "nodeletclass1.h"
#include <pluginlib/class_list_macros.h>
#include <ros/ros.h>
nodeletclass1::nodeletclass1()
{
}
//重載虛函數,啓動時自動調用
void nodeletclass1::onInit()
{
//輸出信息
NODELET_DEBUG("Init nodelet...");
ROS_INFO("Nodelet is OK for test");
}
//nodelet的本質是把節點作爲插件來調用,因此需要PLUGINLIB的宏定義、
//第一個參數是類名,第二個參數是父類
PLUGINLIB_EXPORT_CLASS(nodeletclass1, nodelet::Nodelet);
5、進入plugins文件夾,創建插件的引用xml文件
cd plugins
nodelet_plugins.xml
<!--這裏的path="",修改成path="lib/lib{項目名}",
項目名就是CMakeLists.txt裏面定義的project(base_nodelet)
我這裏就是path="lib/libbase_nodelet"
-->
<library path="lib/libbase_nodelet" >
<!-- name: launch文件裏面 load 後面接着的插件名
type: c++文件定義的類名
如 name="aaa/nodeletclass1",那麼,launch文件對應啓動如下:
<node pkg="nodelet" type="nodelet" name="nodeletclass1"
args="load aaa/nodeletclass1 nodelet_manager" output="screen">
-->
<class name="aaa/nodeletclass1" type="nodeletclass1" base_class_type="nodelet::Nodelet">
<description>
This is my nodelet.
</description>
</class>
</library>
6、回到項目根目錄
cd base_nodelet
修改package.xml
加入內容:
<build_depend>nodelet</build_depend>
<build_depend>roscpp</build_depend>
<exec_depend>nodelet</exec_depend>
<exec_depend>roscpp</exec_depend>
在<export></export>標籤中間加入如下內容:
<nodelet plugin="${prefix}/plugins/nodelet_plugins.xml" />
修改完成後的package.xml內容如下:
<?xml version="1.0"?>
<package format="2">
<name>base_nodelet</name>
<version>0.1.0</version>
<description>The base_nodelet package</description>
<maintainer email="[email protected]">msi</maintainer>
<license>Apache 2.0</license>
<buildtool_depend>catkin</buildtool_depend>
<!-- 插入內容 -->
<build_depend>nodelet</build_depend>
<build_depend>roscpp</build_depend>
<exec_depend>nodelet</exec_depend>
<exec_depend>roscpp</exec_depend>
<!-- 指定nodelet插件xml路徑 -->
<export>
<nodelet plugin="${prefix}/plugins/nodelet_plugins.xml" />
</export>
</package>
7、編寫CMakeLists.txt
與普通的ros節點對比,nodelet節點的CMakeLists.txt:
- 不生成二進制文件,即去掉
add_executable(${PROJECT_NAME} src/nodeletclass1.cpp)
- 編譯成庫形式
add_library(${PROJECT_NAME} src/nodeletclass1.cpp)
- 增加
add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS} )
完整的CMakeLists.txt如下:
cmake_minimum_required(VERSION 2.8.3)
project(base_nodelet)
#啓用c++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# List C++ dependencies on ros packages
set(ROS_CXX_DEPENDENCIES
roscpp
nodelet
)
# Find catkin and all required ROS components
find_package(catkin REQUIRED COMPONENTS
${ROS_CXX_DEPENDENCIES}
)
# Set include directories
include_directories(include)
INCLUDE_DIRECTORIES(/opt/ros/kinetic/include/)
catkin_package(
#INCLUDE_DIRS include
#CATKIN_DEPENDS ${ROS_CXX_DEPENDENCIES}
)
#Create node
#add_executable(${PROJECT_NAME} src/nodeletclass1.cpp)
add_library(${PROJECT_NAME} src/nodeletclass1.cpp)
add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS} )
target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES})
8、編寫lanuch文件
進入launch目錄
cd launch
創建nodeletclass1.launch
<launch>
<node pkg="nodelet" type="nodelet" name="nodelet_manager" args="manager" output="screen"/>
<node pkg="nodelet" type="nodelet" name="nodeletclass1" args="load aaa/nodeletclass1 nodelet_manager" output="screen">
</node>
</launch>
- launch文件啓動了兩個節點
- 一個是nodelet manager,並重命名爲 nodelet_manager
- 另外一個則是編寫完成的Nodelet節點插件
9、編譯、測試
回到工作空間根目錄
catkin_make
編譯完成後,
source devel/setup.bash
啓動launch文件
roslaunch base_nodelet nodeletclass1.launch
最終看到調用了虛函數onInit()打印輸出