Windows で ROS2 のシミュレーションを行う (3)
Turtlesim で ROS2 を体験する
本ページでは、Turtlesim という ROS2 のサンプルプログラムを用いて ROS2 を体験する。
前ページで紹介した TurtleBot3 ではなくまず Turtlesim を用いるのは、
設定や操作がシンプルであり、ROS2 の本質を理解しやすいと考えられるためである。
本ページは以下の内容で構成される。
演習の前にターミナルに関する注意を述べておく。
新しいターミナルを開き、キーボードで Ctrl + Shift + T を実行しよう (CtrlキーとShiftキーを押しながら T)。
すると、ターミナルに2つめの「タブ」が現れる。左上の「+」が書かれたアイコンをクリックしても同じ効果がある。
タブを3つ以上に増やすのも同じ操作でできる。試してみると良い。
本ページ以降、複数のコマンドを同時に実行する機会が増える。そのたびに新しいターミナルを起動すると、
ターミナルの枚数が増えすぎて収拾がつかなくなる。
そのため、複数のコマンドを実行する場合、一つのターミナル上に複数のタブを作成してその上でそれぞれのコマンドを実行するようにしよう。
それでやっていることの見通しがかなり立つはずである。
それでは、Turtlesim を手動で操作するという演習を行ってみよう。
まず、ターミナルの一つのタブで以下のコマンドで Turtlesim のノードを起動する。
ros2 run turtlesim turtlesim_node
すると、以下のような画面が現れる。中央に見えるのは Turtle (亀) である。
この見ためは起動するたびにランダムに変化するので、見ためが違っても気にする必要はない。
この亀を、仮想空間上に存在するロボットだと思うことにしよう。ロボットは仮想空間上で静止している。
これをキーボードで操作するための簡易的なプログラムがあるので、そのノードを起動しよう。
Turtlesim を起動したターミナルはそのままにしておき、そのターミナルで新しいタブを一つ開こう。
Ctrl + Shift + T か、左上の + アイコンで行うのだった。
その新しいタブで以下のコマンドを実行しよう。
ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args --remap cmd_vel:=/turtle1/cmd_vel
すると、画面に以下のようなメッセージが表示される。
Moving around:
u i o
j k l
m , .
For Holonomic mode (strafing), hold down the shift key:
---------------------------
U I O
J K L
M < >
t : up (+z)
b : down (-z)
anything else : stop
q/z : increase/decrease max speeds by 10%
w/x : increase/decrease only linear speed by 10%
e/c : increase/decrease only angular speed by 10%
CTRL-C to quit
currently: speed 0.5 turn 1.0
これは、ターミナルのこのタブ上でキーボードを入力すると、ロボットを操作できることを意味している。
ロボットの操作に最低限用いるキーは以下の4 つである。
- i : 前進。一度押すと一定時間だけ前進する
- j : 左旋回。一度押すと一定時間だけ左旋回する
- l : 右旋回。一度押すと一定時間だけ右旋回する
- k : 停止。一度押すとすぐに停止する
これらのキーは、右手(あるいは左手)の、3本の指で操作するのが良いだろう。
動作を表すキーは、どれも「一定時間動作する」という挙動なのだが、
動作中でも別コマンドは受け付けられる。色々試して操作に慣れてみよう。
なお、操作時に注意して欲しいのは、「teleop_twist_keyboard」にフォーカスが合っているときのみ、亀を操作できる、ということである。
Turtlesim のウインドウにフォーカスが合っているときは亀は操作できないことに注意すること。
操作してみるとすぐわかるが、どの操作も亀の動作は非常に遅い。下記の速度変更ボタンも利用すると良いだろう。
押している間は速度が変化し、離したところで速度確定、というイメージである。
- q : 直進速度と回転速度が 10% アップ
- z : 直進速度と回転速度が 10% ダウン
- w : 直進速度のみ 10% アップ
- x : 直進速度のみ 10% ダウン
- e : 回転速度のみ 10% アップ
- c : 回転速度のみ 10% ダウン
既に述べたように、この亀をロボットであるとイメージして欲しい。
実際、前ページで紹介した TurtleBot3 を、このプログラムで操作することができる。
さて、ここまでの状況を整理するために、ターミナルの新しいタブで以下のコマンドを実行しよう。
ros2 run rqt_graph rqt_graph
開いたウインドウで、下図の赤丸部のように「Nodes only」にセットしてから左側の青い更新ボタンを押してみよう。
上図は、キー入力プログラム (teleop_twist_keyboard) とロボット (turtlesim) が
/turtle1/cmd_vel というもの (トピックと呼ばれる) を通じて結び付いていることを示している。
cmd_vel は command for velocity (速度コマンド) の略であろう。
すなわち、ロボットが cmd_vel という速度コマンドを受け付けるよう設計されており、
キー入力プログラムでその cmd_vel を発行してロボットを制御していることを上図は意味している。
なお、赤丸の部分を下図のように「Nodes/Topic (all)」にすると、「トピック」というものも表示されるようになる。
ROS 上で動作するプログラムを書くためには、このトピックについても理解しなければならないが、本資料では省略するので、
いずれ教科書などで勉強して欲しい。
以上が終ったら、ターミナル上の全てのタブでプログラムを終了しよう。
この時点で3つのプログラムが動いているので、それを実行している3つのタブで
それぞれ「Ctrl-C」 (Ctrlキーを押しながら C) を入力すればよい。
(turtlesim と teleop_twist_keyboard と rqt_graph の3つである)
さて、ROS2 の演習を行うと、どうしても「既存のサンプルを動作させる」ことが多くなるので、
「ROS2 で動作するプログラムを書く」ということがピンと来なくなることが多い。
そこで、さきほどの亀を自律制御するための簡単なプログラムを書く演習を行ってみよう。
用いる言語は Python バージョン3である。
ROS2 では、プログラムを配置するために、パッケージというものを作成しなければならない。
そして、そのパッケージの中に Python プログラムを作成するのである。
そのため、新しいターミナルを開き、以下の二つのコマンドを一つずつ順番に実行しよう。
cd ~/colcon_ws/src
ros2 pkg create --build-type ament_python --node-name autonomous_controller ros_practice
一つ目は colcon_ws ディレクトリの中の src ディレクトリに移動する、と言うコマンドである。「ディレクトリ」とは Windows の「フォルダ」に対応する。
二つ目のコマンドで、ros_practice パッケージを新規に作成し、その中に autonomous_controller.py というファイルが作成される。
コマンドの実行が終わったら、作成されたはずの Python ファイル autonomous_controller.py をテキストエディタで開き、編集してみよう。
まず、デスクトップ左下のボタンをクリックし、下図のように検索窓に「text」と入力してみよう。
すると、候補として「テキストエディタ」が現れるので、クリックして起動しよう。
正式には gedit と呼ばれるテキストエディタが起動する。
現れたテキストエディターで、左上の「開く」ボタンをクリックし、さらに「その他のドキュメント」をクリック。
現れたウインドウで「colcon_ws」→「src」→「ros_practice」→「ros_practice」→「autonomous_controller.py」の順でダブルクリックで選択していこう。
以下の内容が現れるはずである。
def main():
print('Hi from ros_practice.')
if __name__ == '__main__':
main()
autonomous_controller.py は自動生成されたファイルであるので、これから書くべきプログラムの雛型が表示されているのである。
この雛型をすべて削除してから以下のプログラムを記述しよう。
コピーして貼り付けで構わない (結局はコピーして貼り付けるだけになってしまうが)。
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
import sys
from geometry_msgs.msg import Twist
from turtlesim.msg import Pose
class PublisherSubscriber(Node):
def __init__(self):
super().__init__('autonomous_controller')
self.publisher = self.create_publisher(Twist, 'cmd_vel', 10)
self.subscription = self.create_subscription(
Pose,
'pose',
self.subscription_callback,
10)
self.subscription # prevent unused variable warning
self.cmd_vel = Twist()
self.nowRotating = False
self.boundary = 1.0
def subscription_callback(self, data):
if (data.x < self.boundary or data.x > 11.08-self.boundary or data.y < self.boundary or data.y > 11.08-self.boundary) and not self.nowRotating:
self.cmd_vel.linear.x = 0.0
self.cmd_vel.angular.z = 2.0
self.nowRotating = True
else:
self.cmd_vel.linear.x = 2.0
self.cmd_vel.angular.z = 0.0
self.nowRotating = False
self.publisher.publish(self.cmd_vel)
def main():
rclpy.init(args=sys.argv)
pubsub = PublisherSubscriber()
rclpy.spin(pubsub)
pubsub.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
記述したらファイルを保存する。右側にある「保存」ボタンで上書き保存しよう。
保存したファイルを実行可能なプログラムにするため、ターミナルで以下の3つのコマンドを順に実行する。
cd ~/colcon_ws/
colcon build
source ~/.bashrc
一つ目のコマンドは、ホームディレクトリの colcon_ws ディレクトリに移動している。
二つ目のコマンドは、パッケージのビルドを行っている。
三つ目のコマンドは、新たに作った ros_practice 内のプログラムを利用可能にするためのものである。
なお、autonomous_controller.py を何らかの理由で書き換えた場合、一つ目と二つ目のコマンド
「cd ~/colcon_ws/」と「colcon build」のみ、再実行が必要である。
三つ目のコマンド「source ~/.bashrc」は一度だけの実行でよい。
さて、以上でロボットを自律制御するためのプログラムが完成したので早速実行してみよう。
ターミナルに二つのタブを開き、以下の2つのコマンドを別々のタブで実行しよう。
一つ目のタブ:turtlesim の起動
ros2 run turtlesim turtlesim_node
二つ目のタブ:自律制御用プログラム
ros2 run ros_practice autonomous_controller --ros-args --remap cmd_vel:=/turtle1/cmd_vel --remap pose:=/turtle1/pose
うまく実行できると、下図のように、壁に近付くと向きを変えて壁にぶつからないようにする挙動が実現される
(簡易的なプログラムなので、実際にはぶつかっているが)。
このプログラムの解説は本資料の範囲を超えるが、エッセンスだけを簡単に解説する。
まず、プログラム中にある以下の部分は、ロボットに左旋回命令を与えることを意味する。
(キーボードの j と同じ)
self.cmd_vel.linear.x = 0.0
self.cmd_vel.angular.z = 2.0
一方、プログラム中にある以下の部分は、ロボットに直進命令を与えることを意味する。
(キーボードの i と同じ)
self.cmd_vel.linear.x = 2.0
self.cmd_vel.angular.z = 0.0
そして、プログラムの以下の部分は、
「ロボットが boundary の領域に入っていたらロボットを回転させ、
そうでなかったら直進させる」
という制御に対応する。
if (data.x < self.boundary or data.x > 11.08-self.boundary or data.y < self.boundary or data.y > 11.08-self.boundary) and not self.nowRotating:
self.cmd_vel.linear.x = 0.0
self.cmd_vel.angular.z = 2.0
self.nowRotating = True
else:
self.cmd_vel.linear.x = 2.0
self.cmd_vel.angular.z = 0.0
self.nowRotating = False
ちなみに、以下の部分を「-2.0」に変更して保存し、「cd ~/colcon_ws/」と「colcon build」を実行してから三つ目のタブの自律制御用プログラムを実行し直すと、
左旋回でなく右旋回で壁をさける制御になるので試してみるとよい。
self.cmd_vel.angular.z = 2.0
boundary については下図を参照。
さて、以上の自律制御が動作している状態で、ターミナルの新しいタブで以下のコマンドを実行し、ノードの関係を観察してみよう。
ros2 run rqt_graph rqt_graph
「Node only」を選択して再読み込みボタンをクリックすると以下のような関係が現れるはずである。
この図は、自律制御プログラム ( /autonomous_controller ) は、
ロボット ( /turtlesim ) の状態 (/turtle1/pose ) を観察し、
ロボットに速度コマンド (/turtle1/cmd_vel) を与えている、
という関係を示している。
このような関係は、ロボットの制御において頻繁に見られることは想像できるだろう
(例:カメラで人間を認識してロボットを移動させる、など)。
なお、この例ではロボットの状態として観察されたのは、ロボットの二次元座標 (x, y) である。
天井に設置したカメラからロボットの移動範囲を撮影すれば (x, y) は取得可能であるとはいえ、
現実のロボットでは、このように (x, y) が容易に得られることはあまりないことに注意して欲しい。
その点は、これが簡易的な演習であるということで御容赦いただきたい。
さて、以上が終ったらプログラムが動作している全てのタブで Ctrl-C を入力してプログラムを終了しよう。
さて、ここまでの演習で、ROS2 の演習では複数のプログラムを同時に動かさなければならないことに気づいただろう。
Turtlesim の手動操作では「turtlesim」+「キー入力プログラム」の2つ、
Turtlesim の自律制御では「turtlesim」+「自律制御プログラム」の2つである
(ノード間の関係を表示する rqt_graph は省いて数えた)。
毎回これらを一つ一つ実行するのは大変なので、一つのコマンドで複数のプログラムをまとめて実行する仕組みが用意されている。
その際に用いるのが Launch ファイルである。
ここでは Launch ファイルの利用を体験してみよう。なお上の「キー入力プログラム」は Launch ファイルで実行することができなかったので、
ここからは「turtlesim」+「自律制御プログラム」の2つを Launch ファイルで実行することにする。
まず、 ros_practice パッケージに Launch ファイルを格納するための launch ディレクトリを作成しよう。
以下のコマンドで実現できる。
mkdir ~/colcon_ws/src/ros_practice/launch
次に、この launch ディレクトリ内の Launch ファイルを認識させるための設定を行おう。
テキストエディタを開き、「colcon_ws」→「src」→「ros_practice」→「setup.py」というファイルを開こう。
このファイルの冒頭は以下のようになっている。
from setuptools import find_packages, setup
package_name = 'ros_practice'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
(略)
このファイルに、以下のように追加しよう。
import os
from glob import glob
from setuptools import find_packages, setup
package_name = 'ros_practice'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name), glob('launch/*_launch.py'))
],
(略)
追加されたのは、上で太字になっている1行目、2行目および「(os.path.join('share', package_name), glob('launch/*_launch.py'))」の行である。
追加したら上書き保存した上でテキストエディタを閉じて構わない。
次に、Turtlesim の自律制御プログラムを実行するための launch ファイルを作成しよう。
テキストエディタを新規で開き、以下の内容を記そう。
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='turtlesim',
executable='turtlesim_node',
name='turtlesim'
),
Node(
package='ros_practice',
executable='autonomous_controller',
name='autonomous_controller',
remappings=[
('/pose', '/turtle1/pose'),
('/cmd_vel', '/turtle1/cmd_vel'),
]
)
])
記述したら、以下の手順で保存を行う。
- 「保存」ボタンをクリック
- 「colcon_ws」→「src」→「ros_practice」→「launch」とダブルクリックしてディレクトリをたどる
- 最上部の「無題のドキュメント」の部分を「autonomous_launch.py」と書き換える。これが保存するファイル名となり、つづりを間違えると後で困るのでコピー&貼り付けが確実
- 右上の「保存」ボタンで保存完了。
保存が完了したらテキストエディタは閉じて構わない。
そして、以下のようにビルドを行おう。
cd ~/colcon_ws/
colcon build
以上で、Turtlesim の自律制御プログラムを実行するための launch ファイルが完成したはずである。
他の ROS2 プログラムが何も実行されていない状態で、以下のコマンドを実行してみよう。
ros2 launch ros_practice autonomous_launch.py
ここまでの作業に全て成功していれば、Turtlesim が起動し、さらに亀が自律移動を始めるはずである。
前のページ「Windows で ROS2 のシミュレーションを行う (2) ROS のセットアップ」/
次のページ「Windows で ROS2 のシミュレーションを行う (4) TurtleBot3 をプログラムで制御する」
トップページに戻る