ROS上でプログラムを書いてみる
はじめに
前回の記事にて、ROSをインストールできたので、プログラムを動かしてみたいと思います。 ROSの基本動作であるPublishとSubscribeのプログラムを動かします。
環境
・ボード : Raspberry Pi 3
・OS : Raspbian/Buster
・ROSディストリビューション : Kinetic
※ROSのセットアップはこちらで行ったものを使っています。
keimlab.hatenablog.com
catkinワークスペースを作成する
ROSではcatkinワークスペースというディレクトリにて作業を行うのが推奨されているようで、その下に様々なパッケージをぶら下げてプログラムを管理するようになっています。まずは、catkinワークスペースを作成します。
mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src catkin_init_workspace catkin_make
パッケージを作成する
次に、catkinワークスペースにテスト用のパッケージを追加します。catkin_create_pkgの1つ目の引数がパッケージ名
catkin_create_pkg hello_world roscpp rospy std_msgs
パッケージを作成すると、以下のようなファイル・ディレクトリが生成されます。
プログラムを作成する
publish、subscribeでプロセス間での情報の授受をするプログラムで、プログラミングの世界でいう『Hello World!』の代替にします。公式サイトのTutorialから引用させてもらいますが、以下のプログラムを作成し、下図黄色部のように配置します。
// File name : talker.cpp #include "ros/ros.h" #include "std_msgs/String.h" #include <sstream> int main(int argc, char **argv){ ros::init(argc, argv, "talker"); ros::NodeHandle n; ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); ros::Rate loop_rate(10); int count = 0; while (ros::ok()){ std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str(); ROS_INFO("%s", msg.data.c_str()); chatter_pub.publish(msg); ros::spinOnce(); loop_rate.sleep(); ++count; } return 0; }
// File name : listener.cpp #include "ros/ros.h" #include "std_msgs/String.h" void chatterCallback(const std_msgs::String::ConstPtr& msg){ ROS_INFO("I heard: [%s]", msg->data.c_str()); } int main(int argc, char **argv){ ros::init(argc, argv, "listener"); ros::NodeHandle n; ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); ros::spin(); return 0; }
メイクファイルを編集する
書いたソースをコンパイル・リンクするように、~/catkin_ws/src/hello_world/CMakeLists.txt
(上図のオレンジ部)に以下の記述を追加します。
add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_dependencies(talker hello_world_generate_messages_cpp) add_executable(listener src/listener.cpp) target_link_libraries(listener ${catkin_LIBRARIES}) add_dependencies(listener hello_world_generate_messages_cpp)
catkin_makeする
catkinワークスペースのルートディレクトリに戻って、makeをします。これで、書いたソースコードがコンパイル・リンクされて実行形式のファイルができます。普通のCプログラムのコンパイル時より、エラーメッセージが出ても少々見づらいですが、これはやむなし...
cd ~/catkin_ws catkin_make
実行する
makeが通ったら、実行してみましょう。今回作成したプログラムは2つのノード間でやり取りするプログラムなので、ターミナルを2つ起動して、実行します。
まずは、最初のターミナルで、roscoreを実行しておき、listener側のプログラムを実行します。
このとき、roscoreを普通に実行してしまうとターミナルのコントロールを失ってしまいますので、以下のように実行してバックグラウンドで実行させるようにします (これが推奨されるのかどうかは...?)。こうすることで無駄にターミナル画面を表示させておく必要がなくなります。roscoreの出力は、~/catkin_ws/roscore.txt
に出力されますので、何か気になることがあれば、こちらを参照しましょう。
nohup roscore >> ~/catkin_ws/roscore.txt &
ちなみに、roscoreを終了させたいときには、以下のようにします。
ps aux | grep roscore
これを入力すると以下のような出力を得られます。
username 3808 6.4 3.0 62168 28448 pts/0 Sl 16:07 0:02 /usr/bin/python /opt/ros/melodic/bin/roscore username 3850 0.0 0.0 5228 596 pts/0 S+ 16:08 0:00 grep --color=auto roscore
この出力の中の1行目がroscoreのプロセスを指しています。左から2番目の項目がプロセスIDで、これ使って、プロセスを殺します。上記の例でいうと、3808がroscoreのプロセスIDで、以下のようにコマンドを入力します。
kill -INT 3808
roscoreが起動できたら、先ほどcatkin_makeしてできたhello_worldパッケージのlistenerを実行します。
rosrun hello_world listener
これを実行した直後には何も起きません。このままの状態にして、次に、2つ目のターミナルで、talker側のプログラムを実行します。すると以下のような出力を得ることができ、最初に起動したターミナルの方を見てみると、listener側のプログラムにも表示が随時更新されます。ここまでできればROSプログラムの実行が成功です。(私の環境では、listener側のプログラムで、なぜか最初の3つの内容が取りこぼされてしまっていますが...)。終了させるには、Ctrl + c
にて終了させます。
$ rosrun hello_world talker [ INFO] [1588317288.437682266]: hello world 0 [ INFO] [1588317288.537732649]: hello world 1 [ INFO] [1588317288.637634388]: hello world 2 [ INFO] [1588317288.737640345]: hello world 3 [ INFO] [1588317288.837654010]: hello world 4 [ INFO] [1588317288.937638873]: hello world 5 [ INFO] [1588317289.037641393]: hello world 6 [ INFO] [1588317289.137640267]: hello world 7 [ INFO] [1588317289.237632422]: hello world 8 [ INFO] [1588317289.337640775]: hello world 9 [ INFO] [1588317289.437630378]: hello world 10
$ rosrun hello_world listener [ INFO] [1588317501.555192296]: I heard: [hello world 3] [ INFO] [1588317501.654821160]: I heard: [hello world 4] [ INFO] [1588317501.754862987]: I heard: [hello world 5] [ INFO] [1588317501.854811639]: I heard: [hello world 6] [ INFO] [1588317501.954760863]: I heard: [hello world 7] [ INFO] [1588317502.054814930]: I heard: [hello world 8] [ INFO] [1588317502.154751812]: I heard: [hello world 9] [ INFO] [1588317502.254813431]: I heard: [hello world 10] [ INFO] [1588317502.354735208]: I heard: [hello world 11] [ INFO] [1588317502.454727088]: I heard: [hello world 12] [ INFO] [1588317502.554747875]: I heard: [hello world 13] [ INFO] [1588317502.654783088]: I heard: [hello world 14] [ INFO] [1588317502.754856738]: I heard: [hello world 15] [ INFO] [1588317502.854771953]: I heard: [hello world 16] [ INFO] [1588317502.954808781]: I heard: [hello world 17]
おわりに
ここまで見ていただき、ありがとうございました。プロセス間通信をしようと思ったら、もっとたくさんコードを書かなきゃいけないようなイメージを持っていましたが、ROSを使うとお手軽にプロセス間の情報共有が楽にできそうですね。これが複数のノード間でもできるというようで、これから挑戦していきたいと思います。ここまで、ネット検索して多くの先人たちの知恵を参考にさせていただきましたが、無償で公開してくれている皆さんに感謝です。私も皆さんに役立つような知恵を発信していきたいですね。
参考サイト
公開日 : 2020年5月1日