ROS(Robot Operating System)によるRobotの操作


ROS(Robot Operating System)はOpen Source Robotics Foundationによって開発されたロボット・フレームワークです。ROSは様々なロボットを操作するための汎用ミドルウエアですが、サポートしているOSはLinuxだけなので、コンピュータのOSとしてLinux Ubuntuをインストールする必要があります。Raspberry Pi Robot(例えば、RT/RaspberryPI Mouseなど)の操作をROSを用いて実行するためには、Raspberry PiへLinux Ubuntuをインストールする必要があります。ここでは、すでにUbuntuがインストールされていることを前提とします。
  はじめに、ROSのインストール手順を説明します。ROSのインストール手順はUbuntu Mateだけではなく、一般的にPC用のUbuntu 16.04のOSでも実行できる内容になっています。(Windows PCでの作業はインターネット上に沢山の情報があふれていますので、ここでは個人的理由からMacOS PCで作業するケースになります。OSがLinux系のケースはここに記述されている手順と類似します。)
  なお、2017年12月に、ROSの新しいバージョンROS2がリリースされています。ROSはPython 2.xをサポートしていますが、ROS2はPython 3.xをサポートします。ROS2は通信用ソフトとして汎用性の高いThe Data Distribution Service for real-time systems (DDS)を採用しています。(DDS は、最近開発された、 financial trading, air-traffic control, smart grid management, big data 処理などの応用分野で使用されるnetworking middlewareです。しかし、ROS2はまだ未完成の状態(The first non-beta release)で、ROS2の中でROSを併用することはできます。ここでは、ROS2については説明をしません。詳しい情報は、http://www.ros.org/news/2017/12/ros-2-ardent-apalone-released.html を参照下さい。

Last updated: 2018.3.16


関連記事
Raspberry Pi Mouseロボットでの操作の実際
RaspberryPi へのUbuntu 16.04のインストール
Deep Learningと人工知能
OpenCVで画像処理/Mac
RaspberryPi 入門
PythonのTutorials/Jupyter Notebook
GitHub repositories
ROS wiki Tutorials

*********************************************************************
ROSのインストールと起動
*********************************************************************

ROS(Robot Operating System)をLinux Ubuntu上でインストールする手順を説明します。OSとしてUbuntu 16.04LTSまたは15.10がインストールされているPCまたはUbuntu MateがインストールされているラズパイへのROSのインストールについて説明します。PCのデスクトップGUIでは、

http://wiki.ros.org/

に行って、「installation」のページに行き、「ROS Kinetic Kame」バージョンをクリックします。次に、「select your platform」のページでubuntuを選択します。すると、「Ubuntu install of ROS Kinetic」のページに移行するので、そこに説明されている通りの手順でインストールする。注意書きに、ROS Kinetic ONLY supports Wily (Ubuntu 15.10), Xenial (Ubuntu 16.04) and Jessie (Debian 8) for debian packages.という記述があります。ラズパイにインストールしたOSはXenial (Ubuntu 16.04)に対応します。インストールの手順が以下のようになっています。Terminal上にコピペして実行してください。(デスクトップのGUI操作でインストールはできません。)
 まず、PCからssh接続でラズパイのUbuntu Terminalを開いてください。「packages.ros.org.」からのソフトを受け入れるためにRaspberry PiのOSをセットアップします(長いですが全部でコマンド1行文です)。
$ sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
以下のコマンドでキーをセットアップする。(全部でコマンド1行文です)
$ sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116
インストールするにあたって、始めに、Debian package indexをアップデートする。
$ sudo apt-get update
次のコマンドでDesktop-Fullをインストールする。GUIを含めた基本セットをインストールしたい場合は、ros-kinetic-desktop-fullを選択して、インストールしてください。(GUIを用いたシミュレーションをしない場合は、ros-kinetic-desktopでも十分ですが、disktop-fullをインストール方が無難でしょう。)
$ sudo apt-get install ros-kinetic-desktop-full
ROSを使用する前に、rosdepを初期化し、アップデートする必要があります。
$ sudo rosdep init
$ rosdep update
ROS環境変数を設定するために、
$ echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
$ source ~/.bashrc
と入力して、bashファイルをシェルに組み込む。更に、PythonのROSパッケージなどを組み込むために
$ sudo apt-get install python-rosinstall python-rosinstall-generator python-wstool build-essential
とコマンドを入力する。これで、一応のインストールは終了します。インストールが成功していれば、
$ roscore
とコマンド入力すると、ROSの本体が作動します。以下の通りに、ROSが起動されたことが表示されます。
-------------------------------------------------------------------------------------------------------------------------------
ubuntu@Raspi:~$ roscore
... logging to /home/ubuntu/.ros/log/eee38daa-f417-11e7-bd6e-b827eb3b9c1b/roslaunch-Raspi-1782.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://localhost:44753/
ros_comm version 1.12.7


SUMMARY
========

PARAMETERS
 * /rosdistro: kinetic
 * /rosversion: 1.12.7

NODES

auto-starting new master
process[master]: started with pid [1794]
ROS_MASTER_URI=http://localhost:11311/

setting /run_id to eee38daa-f417-11e7-bd6e-b827eb3b9c1b
process[rosout-1]: started with pid [1809]
started core service [/rosout]
-------------------------------------------------------------------------------------------------------------------------------
ROSの終了は「Ctrl」キーを押しながら「c」キーを押します。
詳細は、http://wiki.ros.org/kinetic/Installation/Ubuntu を読んでください。このページにあるコマンドをコピペする方がベターかもしれません。ROSの使い方ガイドは http://wiki.ros.org/ROS/Tutorials を参照してください。

*********************************************************************
ROSの初歩
*********************************************************************


最初に、ROSの構造について初歩的な説明をします。複数のノード(プログラム)の間を通信で結合させる通信ライブラリがROSの中心部分です。ノードはロボットを構成する各器官に対応していると理解します。各ノードの送信と受信はTopicという回線チャネルを介して行われます。Topicにデータを送る(publishする)ノードをpublisher、データを受信するノードをsubscriberと言います。Topicが使用可能なデータ(messages)の型は規格化されていて、それに対応するライブラリがあります。
ROSにはPythonの様々なモジュールが内蔵されているので、各ノードの作成やmessageの送・受信の仕方などは主にPythonのプログラムで書かれます。ロボットをブラウザ上で操作するためのWebページを作成する必要があるので、htmlファイルとそれに付随したjavascriptファイル、CSSファイルが活用されます。ロボットのデバイス・ドライバーをインストールするためには、C++で書かれたプログラムでシェルに組み込む必要もあります。
ROSのプログラムを書く前に、kinetic-desktop-fullをインストールした場合、Turtlesimという亀を用いたシミュレーションをしてみましょう。まず、ラズパイのデスクトップでTerminalを3つ立ち上げます。(PCにインストールしたROSで行うときは、当該PCのデスクトップでTerminalを3つ立ち上げます)一つ目で、
$ roscore
と入力して、ROS Masterを立ち上げます。二つ目のTerminalで
$ rosrun turtlesim turtlesim_node
と入力します。画面に亀アイコンが表示されています。これは亀をロボットとして使ったシミュレータです。次に、残ったTerminalで
$ rosrun turtlesim turtle_teleop_key
と入力します。今回立ち上げた「turtlesim_node」と「turtle_teleop_key」はROSの中に作られた二つのノードで、このノード間で通信をさせます。Topicという名称のチャンネルを介して通信します。turtle_teleop_keyを打ったTerminalにおける上下左右(↑↓)のキーを押すと、下の写真のように亀アイコンがその通りに移動します。
turtle.jpg

亀ロボットの操作


各ノード間の通信を可視化してみましょう。4つ目のTerminalを立ち上げて、
$ rqt_graph
と入力する。各ノード間の通信の様子が伺えます。ROSにおける各ノード間の通信の仕方が理解できたと思います。ROSには、ここで見たTopic 通信以外に、service、actionというデータ通信の方法もあります。複雑なデータの送受信については実際のロボットを使用する必要がありますが、ROSにはgazeboというロボット・シミュレーションも用意されています。以下の写真がそうです。
gazebo.jpg

Gazeboによるロボット・シミュレーション


*********************************************************************
ROSのプログラミング入門
*********************************************************************


ロボットに一連の動作をさせるためのプログラム群を納める作業スペースをcatkinワークスペースと言います。ROSを使用するにあたって最初にすることはこのcatkinワークスペースを作成することです。正確に言い換えると、例えば「catkin_ws」というわかりやすい名称のディレクトリを作成することです。このディレクトリの中にコードや設定ファイルを納めると、ロボットを動かす時に必要なもの一式が「catkin_make」というコマンド一つで生成できます。詳しい説明はhttp://wiki.ros.org/ROS/Tutorialsを参照してください。そこからのコピペがベターかもしれません。
以下のコマンドを入力して、作業用のワークスペースを作成します。
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/src
$ catkin-init-workspace
$ cd ~/catkin_ws/
$ catkin_make
このコマンド「catkin_make」はC言語のビルド・システムのmakeに対応していて、C++でのコードのコンパイルに関係する仕事をしてます。この結果、「source」ディレクトリの中に「CMakeLists.txt」が作成され、「catkin_ws」ディレクトリの下に「devel」と「build」という名称の二つのディレクトリが生成されます。いくつかの「setup.*sh」ファイルも生成されます。
このワークスペース関連の設定をシェルに読み込ませるために、
$ source ~/catkin_ws/devel/setup.bash
と入力する必要があります。ラズパイを立ち上げたときに毎回、この読み込みの入力をすることは煩雑です。この設定を自動的に起動するためには.bashrcファイルを修正する必要があります。そこで、
$ sudo nano ~/.bashrc
と.bashrcファイルを開いて、

source /opt/ros/kinetic/setup.bash

の行を

source ~/catkin_ws/devel/setup.bash

と書き換えます。
$ echo $ROS-PACKAGE-PATH
と打って、Terminalの標示からcatkin_ws/srcへのパスが確認できれば成功です。
ロボットに一連の動作をさせるための自作のプログラム・ファイル作成した時、これらを一つのパッケージにしてROSのワークスペース(catkin_ws)にビルドする必要があります。このパッケージ名がros_testとするとき、以下のようなコマンド入力が必要です。
$ cd ~/catkin_ws/src
$ catkin_create_pkg ros_test std_msgs rospy roscpp
ここでのcatkin_create_pkgはパッケージの雛形を作ることを指示します。Terminalには
Created file ros_test/CMakeLists.txt
Created file ros_test/package.xml
Created folder ros_test/include/ros_test
Created folder ros_test/src
Successfully created files in /home/koichi/catkin_ws/src/ros_test. Please adjust the values in package.xml.
と表示されます。CMakeLists.txtはROSのcmakeというビルド・システムの設定ファイルです。package.xmlはROSのパッケージ管理システムが利用するファイルです。このファイルの中にはこのパッケージが利用するモジュール(rospy、roscpp、std_msgsなど)の依存関係が書かれています。ここまで来たら一度catkin_wsに戻って、catkin_makeする必要があります。
$ cd ~/catkin_ws
$ catkin_make
このコマンドを実行すると、
Base path: /home/koichi/catkin_ws
Source space: /home/koichi/catkin_ws/src
Build space: /home/koichi/catkin_ws/build
Devel space: /home/koichi/catkin_ws/devel
Install space: /home/koichi/catkin_ws/install
####
#### Running command: "make cmake_check_build_system" in "/home/koichi/catkin_ws/build"
####
-- Using CATKIN_DEVEL_PREFIX: /home/koichi/catkin_ws/devel
-- Using CMAKE_PREFIX_PATH: /home/koichi/catkin_ws/devel;/opt/ros/kinetic
-- This workspace overlays: /home/koichi/catkin_ws/devel;/opt/ros/kinetic
-- Using PYTHON_EXECUTABLE: /usr/bin/python
-- Using Debian Python package layout
-- Using empy: /usr/bin/empy
-- Using CATKIN_ENABLE_TESTING: ON
-- Call enable_testing()
-- Using CATKIN_TEST_RESULTS_DIR: /home/koichi/catkin_ws/build/test_results
-- Found gtest sources under '/usr/src/gtest': gtests will be built
-- Using Python nosetests: /usr/bin/nosetests-2.7
-- catkin 0.7.8
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~  traversing 4 packages in topological order:
-- ~~  - beginner_tutorials
-- ~~  - raspimouse_ros
-- ~~  - ros_start
-- ~~  - ros_test
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ processing catkin package: 'beginner_tutorials'
-- ==> add_subdirectory(beginner_tutorials)
-- +++ processing catkin package: 'raspimouse_ros'
-- ==> add_subdirectory(raspimouse_ros)
-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
-- Generating .msg files for action raspimouse_ros/Music /home/koichi/catkin_ws/src/raspimouse_ros/action/Music.action
-- raspimouse_ros: 10 messages, 2 services
-- +++ processing catkin package: 'ros_start'
-- ==> add_subdirectory(ros_start)
-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
-- Generating .msg files for action ros_start/GoUntilBumper /home/koichi/catkin_ws/src/ros_start/action/GoUntilBumper.action
-- ros_start: 7 messages, 1 services
-- +++ processing catkin package: 'ros_test'
-- ==> add_subdirectory(ros_test)
-- Configuring done
-- Generating done
-- Build files have been written to: /home/koichi/catkin_ws/build
####
#### Running command: "make -j2 -l2" in "/home/koichi/catkin_ws/build"
####
--------------- 以下略 -------------
と表示されます。これで、自作のパッケージがROSで利用できるようになります。ROSの中のディレクトリを探すとき、cdの代わりにroscdを使います。以下のコマンドでパッケージが構成されていることを確認しましょう。
$ roscd ros_test
$ ls
ロボットを動かすための一連のプログラムをこのパッケージに書き込んでいくことになります。以下にパッケージ内容のリストの実例を示します。
koichi@Raspi-Ubuntu:~/catkin_ws/src/raspimouse_ros$ tree
.
├── action
│   └── Music.action
├── CMakeLists.txt
├── LICENSE
├── misc
│   ├── keikyu.bash
│   └── keikyu_stop.bash
├── msg
│   ├── LightSensorValues.msg
│   ├── MotorFreqs.msg
│   └── Switches.msg
├── package.xml
├── raspimouse.launch
├── raspimouse_ns.launch
├── README.md
├── scripts
│   ├── check_driver_io.py
│   ├── rtbuzzer.py
│   ├── rtlightsensors.py
│   ├── rtmotor.py
│   └── rtswitches.py
├── srv
│   ├── PutMotorFreqs.srv
│   └── SwitchMotors.srv
└── test
    ├── lightsensors_output
    ├── switches_output
    ├── travis_prepare.bash
    └── travis_test.bash

6 directories, 23 files
この実例は小型移動ロボットRaspberry Pi Mouse(RT社製造)を操作するための入門パッケージです。パッケージの内容は上田隆一氏(『RaspberryPiで学ぶROSロボット入門』日経BP)によって提供されたものです。現段階で必要な部分はscriptsの部分に対応するプログラムです。action、misc、srv、testのディレクトリにあるプログラムは無視しましょう。
上記に見られるscriptsに対応する部分を簡単な例で書いてみましょう。トピックへのメッセージの送信(発行)者と受信者のそれぞれのプログラムをPythonで書きます。送信者のノード名をsender、受信者のノード名をreceiverとしましょう。したがって、scriptsディレクトリにsender.pyとreceiver.pyという名前のファイルを作成します。
$ roscd ros_test
$ mkdir scripts
$ cd scripts
$ vi sender.py
でファイルsender.pyを新しく開いて、Pythonスクリプトを記述します。エディターはviでもgeditでも好きな方を使ってください。以下のコードを入力してください。

#!/usr/bin/env python
import rospy
from std_msgs.msg import String

rospy.init_node('sender')
pub = rospy.Publisher('test', String, queue_size=10)
rate = rospy.Rate(10)
while not rospy.is_shutdown():
    hello_str = String()
    hello_str.data = "is successful. %s" % rospy.get_time()
    pub.publish(hello_str)
    rate.sleep()
第1行目の「#!/usr/bin/env python」はPythonで実行ファイルを作成するときのお決まりでです。第2行目でモジュール「rospy」をインポートしています。「rospy」はPythonからROSを使用するためのPythonモジュールです。第3行目の「from std_msgs.msg import String」はメッセージで送受信するデータの型を、ここでは、文字列なのでstringという型をインポートしています。rospy.init_node(...)はクオーテーションマーク内の「sender」というノードを立ち上げ、初期化をしています。rospy.Publisher('test', String, queue_size=10)は、文字列型のデータをtestという名称のトピックとして発信することを意味します。queue_size=10はバッファのサイズが10の値を持つという意味です。rate = rospy.Rate(10)は1秒間に10回の割合でプログラムを実行するというインスタンスを作成しています。while not rospy.is_shutdown():はプログラムが「ctrl+c」の入力で終了するまで無限ループで続くことを意味します。hello_str = String()は文字列のインスタンスhello_strを作成しています。hello_str.data = ".."は文字列".."をhello_strのdataというメンバー変数に書き込んでいます。%は文字列演算子で、後に続く%以下の変数値を代入することです。pub.publish(hello_str)はメッセージとしてhello_strを送信しています。rate.sleep()はインスタンスrateの機能を使って、1秒間に10回プログラムが実行されるように、作動を休みます。
  同じく、
$ vi receiver.py
と打って、以下のような内容のreceiver.pyを作成してください。

#/!usr/bin/env python
import rospy
from std_msgs.msg import String

def callback(message):
    rospy.loginfo("This test  %s", message.data)

rospy.init_node('receiver')
sub = rospy.Subscriber('test', String, callback)
rospy.spin()
これで、scriptsディレクトリに2つのプログラムが作成できました。この2つのノード間で通信を行うことになります。通信のトピック名を'test'としてあります。rospy.init_node('receiver')はreceverというノードを立ち上げます。rospy.Subscriber('test', String, callback)は、トピックtestを受信して、callback関数で定義された動作をすることを意味します。rospy.spin() は無限ループで受信を待つことを意味します。ここでは、Terminalに「This test is successful.」と表示する動作になっています。
 スクリプトを実行可能な状態にするために
$ chmod +x sender.py receiver.py
と打ちます。各ノード間の通信を実行させるために、3つのターミナルを立ち上げます。一つ目で、roscoreを入力して、二つ目で
$ rosrun ros_test sender.py
3つ目のターミナルで
$ rosrun ros_test receiver.py
と入力します。すると、このターミナルに通信結果のデータが表示されます。
  3つのターミナルを立ち上げるのが面倒となるので、一つのターミナルで処理したいときはlaunchファイルを作成します。まずlaunchディレクトリを作成して、そこにlaunchファイルを作ります。
$ roscd ros_test
$ mkdir launch
$ cd launch
$ vi test.launch
とファイルを新しく開いて、以下のコードを入力します。

このファイルの文法はxmlです。このファイルを実行するためには
$ roslaunch ros_test test.launch
と一つのコマンドを入力すれば済みます。ROSが自動的に起動します。この場合、各ノードの動作は見れませんが、別のターミナルで$rostopic echo /testとコマンドを入力すると動作状況が表示できます。また、$rostopic listと入力するとトピックの情報が見れます。ノードが正しく作動していることを確認するためには、$rosnode listと入力します。topicの送受信の設定に関する詳しい情報は、wiki.ros.org/rospy_tutorials/Tutorials/WritingPublisherSubscriberにあります。
 以上でROSの初歩的なプログラミングの仕方は理解できたでしょう。Pythonコードを実行するときエラーがでることがよくありますが、これはPythonのPATHの設定に原因があります。ROSが使用しているPythonのバージョンは2.7です。Raspberry Pi以外で、例えば、ubuntu PCにROSをインストールして利用して、ROS以外のPython(anacondaなど)を使用しているケースではPythonPATHの衝突が起こりますので要注意です。

*********************************************************************
「service」を用いたノード間通信
*********************************************************************


  トピックを介した通信はメッセージを送受するノードが互いに待つことはなく、非同期で動作します。サービスを利用すると、相手のノードの処理を待ち、その結果を受け取ることができます。サービスを実装するプログラムを作成します。ロボットのモーターの「on/off」など、一度しか送信しない指令で、「on」になったことを知りたいときには、「service」を利用します。使用できるサービスの型については、
$ rossrv list
と入力すると表示されます。サービスのサーバーとクライアントの二つのノードを立ち上げるコードを作成します。 以下の通り、スクリプトを作成してください。
#!/usr/bin/env python

import rospy
from std_srvs.srv import Empty
from std_srvs.srv import EmptyResponse

def handle_service(req):
    rospy.loginfo('called!')
    return EmptyResponse()

def service_server():
    rospy.init_node('service_server')
    s = rospy.Service('call_me', Empty, handle_service)
    print "Ready to serve."
    rospy.spin()

if __name__ == "__main__":
    service_server()
このファイルをservice_server.pyとして/ros_test/scriptsディレクトリに保存してください。if __name__ == "__main__": はこれ以下がメインプログラムであることを意味します。実行ファイルとして使う場合の、お定まりの書き方だと思ってください。from std_srvs.srv import Empty と from std_srvs.srv import EmptyResponse はサービスの型パッケージstd_srvs.srv からEmpty及びEmptyResponse を利用することを宣言しています。関数service_server で'service_server'という名前のノードを立ち上げ、サービスの名前を'call_me'とするとなっています。サービスの型はEmptyで、コールバック関数はhandle_serviceとなっています。
  以下のファイルを作成して、service_client.pyという名前でscriptsディレクトリに保存してくだい。

#!/usr/bin/env python

import rospy
from std_srvs.srv import Empty

def service_client():
    rospy.loginfo('waiting service')
    rospy.wait_for_service('call_me')
    try:
        service = rospy.ServiceProxy('call_me', Empty)
        response = service()
    except rospy.ServiceException, e:
        print "Service call failed: %s" % e

if __name__ == "__main__":
    service_client()
	
最初のdefで関数service_clientを作成します。rospy.wait_for_service('call_me')はメッセージの'call_me'を受信を待ちます。 service = rospy.ServiceProxy('call_me', Empty)はメッセージ'call_me'を、Empty型で受信するserviceProxyを立ち上げて、それをservice(service clientにして)という変数に入れます。service()の内容物をresponseに返します。プログラムが読み込まれると、if __name__ == "__main__":以下にあるservice_client()が実行されます。
  3つのターミナルを開いてください。一つ目のターミナルで、roscoreを起動して、2番目のターミナルで
$ roscd ros_test/scripts
$ chmod +x service_server.py service_client.py
$ rosrun ros_test service_server.py
と打ちます。続いて、残りのターミナルから、
$ rosrun ros_test service_client.py
と入力します。サーバー側のターミナルに、結果が表示されます。

*** 続く ***


続く:ロボット実機での操作の実際

トップページに戻る