Welcome to Mashykom WebSite



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

ROS(Robot Operating System)はOpen Source Robotics Foundationによって開発されたロボット・フレームワークです。ROSは様々なロボットを操作するための汎用ミドルウエアですが、サポートしているOSは主にLinuxなので、コンピュータのOSとしてLinux Ubuntuをインストールすることが便利です。Ubuntu 20.04LTS がインストールされていることが前提です。

 最初に、ROSのインストール手順を説明します。2021年12月現在、ROSのLTS最新バージョンはROS Noetic Ninjemys ですが、サポートするプラットフォームは Ubuntu 20.04 です。

 なお、2017年12月に、ROSの新しいバージョンROS2がリリースされています。ROS2はPython 3.xをサポートします。ROS1もROS Melodic Morenia 以降、Python3をサポートしています。ROS2は通信用ソフトとして汎用性の高いThe Data Distribution Service for real-time systems (DDS)を採用しています。(DDS は、最近開発された、 financial trading, air-traffic control, smart grid management, big data 処理などの応用分野で使用されるnetworking middlewareです。ROS2の中でROSを併用することはできます。ここでは、ROS2については説明をしません。ROS2 Foxy のインストールの説明は、ROS2 入門:ROS2-Foxy Fitzroyのページを参照下さい。

Last updated: 2021.12.1


ROSのインストールと起動

ROS(Robot Operating System)をLinux Ubuntu上でインストールする手順を説明します。OSとしてUbuntu 20.04LTSがインストールされているPCへのROSのインストールについて説明します。PCのデスクトップでブラウザを開いて、 http://wiki.ros.orgに行って、「getting started」をクリックして、Get ROS Noetic Ninjemys on Ubuntu Linux のinstall ページに行きます。このページ(http://wiki.ros.org/noetic/Installation/Ubuntu)に説明されている通りの手順でインストールする。

 以下、ROS Noetic Ninjemys のインストールの手順を説明します。まず、PCのUbuntu Terminalを開いてください。「packages.ros.org.」からのソフトを受け入れるために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'

以下のコマンドでキーをセットアップする。


$ sudo apt install curl # if you haven't already installed curl
$ curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -

インストールするにあたって、始めに、Ubuntu package indexをアップデートする。


$ sudo apt update

次のコマンドでDesktop-Fullをインストールする。GUIを含めた基本セットをインストールしたい場合は、ros-kinetic-desktop-fullを選択して、インストールしてください。(GUIを用いたシミュレーションをしない場合は、ros-kinetic-desktopでも十分ですが、disktop-fullをインストール方が無難でしょう。)


$ sudo apt install ros-noetic-desktop-full

ROS環境変数を設定するために、


$ echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
$ source ~/.bashrc

と入力して、bashファイルをシェルに組み込む。更に、PythonのROSパッケージなどを組み込むために


$ sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential

とコマンドを入力する。

ROSを使用する前に、rosdepを初期化し、アップデートする必要があります。


$ sudo rosdep init
$ rosdep update

これで、一応のインストールは終了します。インストールが成功していれば、


$ roscore

とコマンド入力すると、ROSの本体が作動します。以下の通りに、ROSが起動されたことが表示されます。

-------------------------------------------------------------------------------------------------------------------------------
$ roscore
... logging to /home/koichi/.ros/log/0016b --- /roslaunch-MacBookAir-34395.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://MacBooAir:41805/
ros_comm version 1.12.7


SUMMARY
========

PARAMETERS
 * /rosdistro: noetic
 * /rosversion: 1.15.13

NODES

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

setting /run_id to 0016b ---
process[rosout-1]: started with pid [34413]
started core service [/rosout]
-------------------------------------------------------------------------------------------------------------------------------

 ROSの終了は「Ctrl」キーを押しながら「c」キーを押します。詳細は、この説明を読んでください。このページにあるコマンドをコピペする方がベターかもしれません。ROSの使い方ガイドは、このサイトの説明を参照してください。


ROSの構造の初歩的説明


 最初に、ROSの構造について初歩的な説明をします。複数のノード(プログラム)の間を通信で結合させる通信ライブラリがROSの中心部分です。ノードはロボットを構成する各器官に対応していると理解します。各ノードの送信と受信はTopicという回線チャネルを介して行われます。Topicにデータを送る(publishする)ノードをpublisher、データを受信するノードをsubscriberと言います。Topicが使用可能なデータ(messages)の型は規格化されていて、それに対応するライブラリがあります。

 ROSにはPythonの様々なモジュールが内蔵されているので、各ノードの作成やmessageの送・受信の仕方などは主にPythonのプログラムで書かれます。ロボットをブラウザ上で操作するためのWebページを作成する必要があるので、htmlファイルとそれに付随したjavascriptファイル、CSSファイルが活用されます。ロボットのデバイス・ドライバーをインストールするためには、C++で書かれたプログラムでシェルに組み込む必要もあります。

 ROSのプログラムを書く前に、noetic-desktop-fullをインストールした場合、Turtlesimという亀を用いたシミュレーションをしてみましょう。まず、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」というコマンド一つで生成できます。詳しい説明は公式チュートリアルを参照してください。そこからのコピペがベターかもしれません。

 以下のコマンドを入力して、作業用のワークスペースを作成します。


$ source /opt/ros/noetic/setup.bash
$ sudo apt install python3-empy # if pytho3-empy has not been installed
$ mkdir -p ~/catkin_ws/src
$ cd ~/catkin_ws/
$ catkin_make

このコマンド「catkin_make」はC言語のビルド・システムのmakeに対応していて、C++でのコードのコンパイルに関係する仕事をしてます。この結果、「source」ディレクトリの中に「CMakeLists.txt」が作成され、「catkin_ws」ディレクトリの下に「devel」と「build」という名称の二つのディレクトリが生成されます。いくつかの「setup.*sh」ファイルも生成されます。なお、この catkin_make は システム Pythoh3 を使用しますので、このPython へのパスが通っている必要があります。

 このワークスペース関連の設定をシェルに読み込ませるために、


$ 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はパッケージの雛形を作ることを指示します。ros_test のフォルダーが作成されます。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/noetic
-- This workspace overlays: /home/koichi/catkin_ws/devel;/opt/ros/noetic
-- Using PYTHON_EXECUTABLE: /usr/bin/python3
-- 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/googletest': gtests will be built
-- Found gmock sources under '/usr/src/googletest': gmock will be built
-- Using Python nosetests: /usr/bin/nosetests-3
-- catkin 0.8.10
-- BUILD_SHARED_LIBS is on
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- ~~  traversing 1 packages in topological order:
-- ~~  - ros_test
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- +++ 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_test)がROSで利用できるようになります。ROS環境にこのワークスペースを追加するために、新しく作成された setup file をソースします。


$ . ~/catkin_ws/devel/setup.bash

 ROSの中のディレクトリを探すとき、cdの代わりにroscdを使います。以下のコマンドでパッケージが構成されていることを確認しましょう。


$ roscd ros_test
$ ls

 ロボットを動かすための一連のプログラムをこのパッケージに書き込んでいくことになります。以下に 実際のロボット用のパッケージ(名称 raspimouse_ros)内容のリストの実例を示します。raspimouse_ros というパッケージを作成するときは、コマンドで catkin_create_pkg raspimouse_ros std_msgs rospy roscpp と入力します。

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のディレクトリにあるプログラムは無視しましょう。

 以下では、ros_test というパッケージを作成するとして、説明します。上記での手順を踏んで、当該パッケージのscript フォルダーに簡単な例の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 python3
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 python3」はPythonで実行ファイルを作成するときのお決まりです。パスが通っている Python3を用いて実行されます。第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 python3
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

と打ちます。Python へのパスを確実にするために、CMakeLists.txtに以下のコードを追加します。


catkin_install_python(PROGRAMS scripts/sender.py scripts/receiver.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

 この変更を反映させるために、


$ cd ~/catkin_ws
$ catkin_make

 とコマンドを入力します。

各ノード間の通信を実行させるために、3つのターミナルを立ち上げます。一つ目で、roscoreを入力して、二つ目で


$ . ~/catkin_ws/devel/setup.bash
$ rosrun ros_test sender.py

3つ目のターミナルで


$ . ~/catkin_ws/devel/setup.bash
$ rosrun ros_test receiver.py

と入力します。すると、このターミナルに通信結果のデータが表示されます。

 3つのターミナルを立ち上げるのが面倒となるので、一つのターミナルで処理したいときはlaunchファイルを作成します。まずlaunchディレクトリを作成して、そこにlaunchファイルを作ります。

$ roscd ros_test
$ mkdir launch
$ cd launch
$ vi test.launch

とファイルを新しく開いて、以下のコードを入力します。


<launch>
	<node package="ros_test" name="sender" type="sender.py"/>
	<node package="ros_test" name="receiver" type="receiver.py"/>
</launch>

 このファイルの文法はxmlです。このファイルを実行するためには


$ roslaunch ros_test test.launch

と一つのコマンドを入力すれば済みます。ROSが自動的に起動します。この場合、各ノードの動作は見れませんが、別のターミナルで$rostopic echo /testとコマンドを入力すると動作状況が表示できます。また、$rostopic listと入力するとトピックの情報が見れます。ノードが正しく作動していることを確認するためには、$rosnode listと入力します。topicの送受信の設定に関する詳しい情報は、このTutorialsにあります。

 以上でROSの初歩的なプログラミングの仕方は理解できたでしょう。Pythonコードを実行するときエラーがでることがよくありますが、これはPythonのPATHの設定に原因があります。ROS-Noetic Ninjemys が使用しているPythonのバージョンは3.xです。Raspberry Pi以外で、例えば、ubuntu PCにROSをインストールして利用して、ROS以外のPython(anacondaなど)を使用しているケースではPython PATHの衝突が起こりますので要注意です。また、ros を実行する前には必ず、catkin_ws の setup ファイルの読み込みが必要です。$ . ~/catkin_ws/devel/setup.bash を入力します。


サービスを用いたノード間通信


 トピックを介した通信はメッセージを送受するノードが互いに待つことはなく、非同期で動作します。サービスを利用すると、相手のノードの処理を待ち、その結果を受け取ることができます。サービスを実装するプログラムを作成します。ロボットのモーターの「on/off」など、一度しか送信しない指令で、「on」になったことを知りたいときには、「service」を利用します。使用できるサービスの型については、


$ rossrv list

と入力すると表示されます。サービスのサーバーとクライアントの二つのノードを立ち上げるコードを作成します。 以下の通り、スクリプトを作成してください。


#!/usr/bin/env python3

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 python3

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()が実行されます。

 スクリプトを実行可能な状態にするために


$ roscd ros_test/scripts
$ chmod +x service_server.py service_client.py

と打ちます。Python へのパスを確実にするために、CMakeLists.txtに以下のコードを追加します。


catkin_install_python(PROGRAMS scripts/service_server.py scripts/service_client.py
  DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

 この変更を反映させるために、


$ cd ~/catkin_ws
$ catkin_make

 とコマンドを入力します。

 3つのターミナルを開いてください。一つ目のターミナルで、roscoreを起動して、2番目のターミナルで


$ . ~/catkin_ws/devel/setup.bash
$ rosrun ros_test service_server.py

と打ちます。続いて、残りのターミナルから、


$ . ~/catkin_ws/devel/setup.bash
$ rosrun ros_test service_client.py

と入力します。サーバー側のターミナルに、結果が表示されます。詳しい説明は、公式サイトを参照してください。

*** 続く ***


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

このページの先頭に戻る

トップページに戻る