Posted at

ROS講座84 Nodeletを使う


環境

この記事は以下の環境で動いています。

項目

CPU
Core i5-8250U

Ubuntu
16.04

ROS
Kinetic

Gazebo
7.14.0

インストールについてはROS講座02 インストールを参照してください。

またこの記事のプログラムはgithubにアップロードされています。ROS講座11 gitリポジトリを参照してください。


概要

ROSのノード間通信はソケット通信を使っているためメモリコピーが必ず発生するために通信のオーバーヘッドが存在します。特に画像のROSトピックではこれが無視できない量になります。Nodeletを使うとノードの記述の仕方が特殊になりますが、メモリコピー無しで実行することができます。

ノードレットとは通常のROSノードに当たるある機能を持ったプログラムをプラグインの形で記述して、実行時には複数のプラグインを1つのプロセスで実行することでメモリコピーを回避して高速化を狙うというものです。


ソースコード


takler


plugin_lecture/src/nodelets/plugin_nodelet_talker.cpp

#include <ros/ros.h>

#include <nodelet/nodelet.h>
#include <pluginlib/class_list_macros.h>
#include <std_msgs/String.h>

namespace plugin_lecture{
class plugin_nodelet_talker : public nodelet::Nodelet{
public:
virtual void onInit();
void timer_callback(const ros::TimerEvent&);
private:
ros::NodeHandle nh_;
ros::NodeHandle pnh_;
ros::Publisher pub_;
ros::Timer timer_;
std::string content_;
};

void plugin_nodelet_talker::onInit(){
NODELET_INFO("Talker Init");
nh_ = getNodeHandle();
pnh_ = getPrivateNodeHandle();

content_ = "hello";
pnh_.getParam("content", content_);
pub_ = nh_.advertise<std_msgs::String>("chatter", 10);
timer_ = nh_.createTimer(ros::Duration(1.0), &plugin_nodelet_talker::timer_callback, this);
}

void plugin_nodelet_talker::timer_callback(const ros::TimerEvent&){
NODELET_INFO("send: %s", content_.c_str());
std_msgs::String string_msg;
string_msg.data = content_;
pub_.publish(string_msg);
}
} // namespace plugin_lecture
PLUGINLIB_EXPORT_CLASS(plugin_lecture::plugin_nodelet_talker, nodelet::Nodelet);



  • pluginの書き方の作法としてパッケージ名のnamespaceで全体をくくります。

  • nodelet::Nodeletを継承したクラスとして機能を実装します。

  • ROS_INFOの代わりにNODELET_INFO()を使用することが推奨されます。

  • Nodeletの立ち上がり時にonInit()関数が呼ばれる。ここに機能を実装する。

  • node handleはgetNodeHandle()、private node handleはgetPrivateNodeHandle()で取得する。これらを使ってpub、subを立ち上げたり、パラメーターを取得したりする。

  • onInitの中で無限ルームを書いてはいけない(ほかのnodeletが呼ばれなくなる)。繰り返し処理はタイマーを使う。

  • pluginlibを使っているので最後にPLUGINLIB_EXPORT_CLASS()を書く、第1引数は今回作ったクラス、第2引数はベースクラス。
    `


listener


plugin_lecture/src/nodelets/plugin_nodelet_listener.cpp

#include <ros/ros.h>

#include <nodelet/nodelet.h>
#include <pluginlib/class_list_macros.h>
#include <std_msgs/String.h>

namespace plugin_lecture{
class plugin_nodelet_listener : public nodelet::Nodelet{
public:
virtual void onInit();
void chatter_callback(const std_msgs::String& string_msg);
private:
ros::NodeHandle nh_;
ros::Subscriber sub_;
};

void plugin_nodelet_listener::onInit(){
NODELET_INFO("Listener Init");
nh_ = getNodeHandle();
sub_ = nh_.subscribe("chatter", 10, &plugin_nodelet_listener::chatter_callback, this);
}

void plugin_nodelet_listener::chatter_callback(const std_msgs::String& string_msg){
NODELET_INFO("recieve: %s", string_msg.data.c_str());
}
} // namespace plugin_lecture
PLUGINLIB_EXPORT_CLASS(plugin_lecture::plugin_nodelet_listener, nodelet::Nodelet)


注意点はほぼtalkerを一緒です。


CmakeList.txt:plugin_lecture/CMakeLists.txt

pluginlibの使い方とほぼ一緒です。それに加えてnodeletへの依存が入ります。生成されるのは実行ファイルではなくシェアードオブジェクトなのでadd_libraryのところに書きます。


plugin_lecture/CMakeLists.txtに追加

find_package(catkin REQUIRED COMPONENTS

pluginlib # この行を追加
nodelet # この行を追加
)
add_library(${PROJECT_NAME}
src/nodelets/plugin_nodelet_talker.cpp # この行を追加
src/nodelets/plugin_nodelet_listener.cpp # この行を追加
)
target_link_libraries(${PROJECT_NAME}
${catkin_LIBRARIES}
)


package.xmlに追加

依存の追加をします。またnodeletのpluginのファイルについての情報をnodeletに通知するためのファイルを作成してそれにパスを通す必要があります。


plugin_lecture/package.xml

  <build_depend>pluginlib</build_depend>

<build_depend>nodelet</build_depend>
<exec_depend>pluginlib</exec_depend>
<exec_depend>nodelet</exec_depend>

<export>
<nodelet plugin="${prefix}/nodelet_description.xml"/>
</export>



nodelet_description

nodeletへpluginの情報を通知するためのものです。libraryのpath要素はシェアードオブジェクトの場所を示すものです。今回は~/catkin_ws/devel/lib/libplugin_lecture.soという場所にsoファイルができるので以下のようにlib/libplugin_lectureと書きます。catkinのルールとしてlib/lib{パッケージ名}になるのが注意です。classタグではtalkerよlistenerで作ったc++のクラスについて記述します。typeはclass名、base_class_typeはその名の通りベースクラスの型名です。nameは何でもよいのですが{パッケージ名}/{class名}にするのが慣習です。


plugin_lecture/nodelet_description.xml

<library path="lib/libplugin_lecture">

<class name="plugin_lecture/plugin_nodelet_talker"
type="plugin_lecture::plugin_nodelet_talker"
base_class_type="nodelet::Nodelet">
<description>
</description>
</class>

<class name="plugin_lecture/plugin_nodelet_listener"
type="plugin_lecture::plugin_nodelet_listener"
base_class_type="nodelet::Nodelet">
<description>
</description>
</class>
</library>



launch(nodelet)

nodeletではmanagerというのを立ち会上げて、それにpluginを起動させるnodeを立ち上げます。実際の処理は全てmanagerのプロセスで実行されます。paramやremapも普通のnodeと同じように使用できます。


plugin_lecture/launch/nodelet_pubsub.launch

<?xml version="1.0"?>

<launch>
<arg name="manager_name" default="nodelet_manager" />
<arg name="manager_threads" default="4" />

<node pkg="nodelet" type="nodelet" name="$(arg manager_name)" args="manager" output="screen">
<param name="num_worker_threads" value="$(arg manager_threads)" />
</node>

<node pkg="nodelet" type="nodelet" name="node_loader0"
args="load plugin_lecture/plugin_nodelet_talker $(arg manager_name)" >
<remap from="chatter" to="chatter_alt" />
<param name="content" value="hello2" />
</node>

<node pkg="nodelet" type="nodelet" name="node_loader1"
args="load plugin_lecture/plugin_nodelet_listener $(arg manager_name)" >
<remap from="chatter" to="chatter_alt" />
</node>
</launch>



ビルド

cd ~/catkin_ws

catkin_make


実行

roslaunch plugin_lecture nodelet_pubsub.launch

以下のような出力になります。

[ INFO] [1556704402.696000510]: Initializing nodelet with 4 worker threads.

[ INFO] [1556704402.878049806]: Talker Init /node_loader0
[ INFO] [1556704402.886763259]: Listener Constructor
[ INFO] [1556704402.886827535]: Listener Init
[ INFO] [1556704403.881021963]: send: hello2
[ INFO] [1556704403.881770658]: recieve: hello2
[ INFO] [1556704404.881586730]: send: hello2
[ INFO] [1556704404.881755524]: recieve: hello2
[ INFO] [1556704405.881102764]: send: hello2
[ INFO] [1556704405.881328273]: recieve: hello2


nodelet pluginの一覧

システムとして認識しているnodeletのpluginの一覧をみれます。上のコマンドではnodelet plugin用のdescriptionファイルの一覧が見えます。下のコマンドではもっと詳しくclassの単位の一覧をみれます。


pluginを定義しているdescriptionファイルの一覧を見る

rospack plugins --attrib=plugin nodelet



pluginを定義しているクラスの一覧を見る

rosrun nodelet declared_nodelets



参考

Nodeletでの通信速度


目次ページへのリンク

ROS講座の目次へのリンク