|
Jetson nanoはアメリカの半導体メーカーであるNVIDIA社が製造・販売をするシングルボードコンピューターです。NVIDIA社のJetbotホームページでは、Jetson Nano を搭載したロボットをDIYで制作する手順が紹介されてます。このJetbotホームページでは、Jetson Nano を搭載したロボット群 JetBot AI Robot の完成品も紹介されており、実際に注文取り寄せもできます。日本国内で、「Jetson Nano」を使ったAI自律走行車「JetBot」を実際に作成して、操作した実例が 「JetBotを動かしてみよう 第1回 部品の調達から作成したモデルによる走行」に掲載されています。JetBot ロボットを自作することに興味のある方には参考になります。
このページでは、完成したロボットを前提として、Jetson Nano Developer Kit B01 を搭載したRobotの操作について説明します。使用するRobotはRT社製の Jetson Nano Mouse です。Jetson Nano Mouse は、NVIDIA社の Jetson Nano を搭載したロボット群 JetBot AI Robot の中で紹介されているロボットの一つです。
RT Corporation社の製品紹介では以下のように説明されています。「Jetson Nano MouseはAI関連技術とロボット制御技術を同時に学ぶことができる小型二輪移動ロボットです。広角カメラ2台を前方に搭載しており、Jetson Nanoの性能を活かして機械学習や画像処理技術の研究開発に利用できます。ロボット制御用のソフトウェアをGitHubで公開しているため、AGVや自動運転で用いる技術開発、研究のプロトタイピングにも活用できます。」
このページでは、ROS2をJetson Nanoにインストールして、Jetson Nano Mouse ロボットを操作する手順について説明します。ROS2の最新版 Humble Hawksbill はサポートするOSが Ubuntu 22.04(Jammy)なので、Jetson Nanoにはインストールできません。Jetson Nano には Ubuntu 20.04をインストールできるので、Ubuntu 20.04 をサポートする ROS 2 Foxy Fitzroy をインストールします。なお、ROS1 をインストールして、Jetson Nano Mouse ロボットを操作することに関する説明は、このページを読んでください。
Last updated: 2022.5.27
Jetson Nano への ROS2 のインストール |
NVIDIAが提供している公式のイメージファイルJetPackはUbuntu18.04の上でPython 3.6がインストールされています。Jetson NanoにROS(Robot Operating System)2をインストールしたいので、Ubuntu 20.04にアップグレードすることが必要です。JetPackにUbuntu 20.04をインストールする手続きは、Install Ubuntu 20.04 on Jetson Nanoなどにあります。JetPack 4.6にインストールされているUbuntu 18.04をUbuntu 20.04にアップグレードするには相当な時間がかります。
ここでは、既にUbuntu 20.04がインストールされたイメージファイルを活用することにします。具体的には、Q-engineeringが提供しているUbuntu 20.04にアップグレードされたイメージファイルをダウンロードします。QengineeringのGithubからダウンロードします。このイメージファイルのサイズは約11GBあります。解凍すると、約30GBになります。64GB以上のサイズのmicroSDカードにコピーして下さい。このイメージファイルには以下のソフトがインストールされています。
Ubuntu 20.04
OpenCV 4.5.3
TensorFLow 2.4.1
Pytorch 1.9.0
TorchVision 0.10.0
TeamViewer aarch64 15.24.5
Jtop 3.1.2
イメージファイルをmicroSDカードへコピーした後は、Jetson Nanoに挿入して使用します。パスワードは「jetson」です。
例えば、64GBのmicroSDを使用しているとき、64GBのストレージのうち約30GBのみが利用できるサイズとして割り当てられていて、残りの容量は使用できない未割り当てのままです。パーティション管理ソフトGPartedを用いて割り当てストレージを拡大します。
$ sudo apt install gparted $ gparted
とします。拡張したいパーティション(使用中の約30GBの欄)を右クリックします。リサイズ画面が表示されます。上端のパーティションバーの右端にカーソルを合わせ、右方向にドラッグすることでサイズを調整できます。パーティションサイズが決まったら、右下にある「resize」ボタンをクリックします。(この段階では仮決定なので、処理は実行されません。)「resize」をクリックしたら、画面上部の「チェックマーク(処理の実行)」をクリックすると仮決定した処理が実行されます。
Ubuntuの日本語化をするときは、web上で解説を探して、例えば、Ubuntu20.04の日本語化が参考になります。
ROS 2 Foxy Fitzroy をインストールします。Ubuntu 20.04 (Focal): arm64 は Foxy の Tier 1 platform になっているので、ソースコードをバイナリー形式でダウンロードできます。ROS 2 Documentationのinstallページの説明に沿ってインストールします。この公式ページにあるコードをコピペする方がエラーが出ないと思います。最初に locale の確認をします。
$ locale # check for UTF-8 $ sudo apt update && sudo apt install locales $ sudo locale-gen en_US en_US.UTF-8 $ sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 $ export LANG=en_US.UTF-8 $ locale # verify settings
ROS 2 apt repositoriesをシステムに追加します。そして、GPG key with aptを認証します。
$ sudo apt update && sudo apt install curl gnupg2 lsb-release $ sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
sources listにリポジトリの追加をします。
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
apt repository caches をアップデートし、アップグレードします。
$ sudo apt update $ sudo apt upgrade
foxy desktop をインストールします。
$ sudo apt install ros-foxy-desktop ---------- パッケージリストを読み込んでいます... 完了 依存関係ツリーを作成しています 状態情報を読み取っています... 完了 ... アップグレード: 0 個、新規インストール: 574 個、削除: 0 個、保留: 0 個。 183 MB のアーカイブを取得する必要があります。 この操作後に追加で 1,276 MB のディスク容量が消費されます。 続行しますか? [Y/n] Y 取得:1 http://packages.ros.org/ros2/ubuntu focal/main arm64 python3-catkin-pkg-modules all 0.5.1-1 [43.2 kB] 取得:2 http://ports.ubuntu.com/ubuntu-ports focal/universe arm64 libqt5quickparticles5 arm64 5.12.8-0ubuntu1 [165 kB] 取得:3 http://packages.ros.org/ros2/ubuntu focal/main arm64 python3-ifcfg all 0.18-2osrf~focal [9,300 B] ...
とダウンロードが完了します。約1.3GBのディスク容量が占有されます。
foxy を起動するために、
$ source /opt/ros/foxy/setup.bash
とシェルスクリプト setup.bash を読み込みます。インストールが正常に完了したことを確認するために、ターミナルを2個立ち上げて、以下の操作をします。1つ目のターミナルで、以下のコードを打って、C++ talker を起動します。
$ source /opt/ros/foxy/setup.bash $ ros2 run demo_nodes_cpp talker ---- [INFO] [1653611014.441792578] [talker]: Publishing: 'Hello World: 1' [INFO] [1653611015.441833355] [talker]: Publishing: 'Hello World: 2' [INFO] [1653611016.441776614] [talker]: Publishing: 'Hello World: 3' [INFO] [1653611017.441793556] [talker]: Publishing: 'Hello World: 4' [INFO] [1653611018.441816002] [talker]: Publishing: 'Hello World: 5' [INFO] [1653611019.441822754] [talker]: Publishing: 'Hello World: 6' [INFO] [1653611020.441838344] [talker]: Publishing: 'Hello World: 7' ...
次に、2番目のターミナルで、以下のコード入力します。
$ source /opt/ros/foxy/setup.bash $ ros2 run demo_nodes_py listener ... ---- [INFO] [1653611014.508596365] [listener]: I heard: [Hello World: 1] [INFO] [1653611015.444842826] [listener]: I heard: [Hello World: 2] [INFO] [1653611016.444773012] [listener]: I heard: [Hello World: 3] [INFO] [1653611017.444757401] [listener]: I heard: [Hello World: 4] [INFO] [1653611018.444808493] [listener]: I heard: [Hello World: 5] [INFO] [1653611019.444777276] [listener]: I heard: [Hello World: 6] [INFO] [1653611020.444764845] [listener]: I heard: [Hello World: 7] [INFO] [1653611021.444819482] [listener]: I heard: [Hello World: 8] ...
のように、「I heard: [Hello World・・」と表示されればOKです。
Turtlesim を用いたROS2の使用法 |
ROS2を起動させるためにはシェルスクリプト setup.bash を読み込む必要があります。毎回この操作をするのは煩雑なので、bashrcに書き込みます
$ echo "source /opt/ros/foxy/setup.bash" >> ~/.bashrc
ROS2 のインストール先がどこかを調べておきます。
$ printenv | grep -i ROS ---- ROS_VERSION=2 ROS_PYTHON_VERSION=3 AMENT_PREFIX_PATH=/opt/ros/foxy PYTHONPATH=/opt/ros/foxy/lib/python3.8/site-packages LD_LIBRARY_PATH=/opt/ros/foxy/opt/yaml_cpp_vendor/lib:/opt/ros/foxy/opt/rviz_ogre_vendor/lib:/opt/ros/foxy/lib/aarch64-linux-gnu:/opt/ros/foxy/lib ROS_LOCALHOST_ONLY=0 PATH=/opt/ros/foxy/bin:/home/jetson/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin ROS_DISTRO=foxy
となっています。各ロボットにROS_DOMAIN_ID を設定することができます。1台のロボットのみを操作するケースでは、ロボット間の干渉はないので、ロボットのIDを設定する必要はないでしょう。
ROS2の操作の準備として、turtlesim パッケージをダウンロードします。
$ sudo apt update $ sudo apt install ros-foxy-turtlesim $ ros2 pkg executables turtlesim ---- turtlesim draw_square turtlesim mimic turtlesim turtle_teleop_key turtlesim turtlesim_node
正常にインストールされました。以下のコマンドを打つと亀さんがいる画像がポップアップします。
ros2 run turtlesim turtlesim_node
続いて、二つ目のターミナルを起動して、以下のコマンドを打ちます。
$ ros2 run turtlesim turtle_teleop_key
ここで使用した「ros2 run」はパッケージ(turtlesim)にある実行ファイル名(turtlesim_node、turtle_teleop_keyというノード)を起動します。
亀さんがいる画像が見えるように、二つのターミナルを並べて表示します。turtle_teleop_keyを打ったターミナルで、矢印キーを使用して、「↓」キーを打つと、亀さんが後方に移動します。「→」を打つと、回転します。
$ ros2 node list ---- /turtlesim /teleop_turtle
は起動しているノードのリストを表示します。
三つ目のターミナルを起動して、
$ sudo apt update $ sudo apt install ~nros-foxy-rqt*
RQT をインストールします。rqtを起動します。
$ rqt
灰色の矩形ウインドウがポップアップします。上のメニューバーの「Plugins」をクリックして、「service」の横の枠で「turtle1/set_pen/」を出します。下の図のように数字を修正します。「call」をクリックします。
turtle_teleop_keyを打ったターミナルで、「↓」キーを打つと、亀さんが後方に移動する軌道が幅広の赤色に変わります。
ros2 run コマンドはパッケージ内にある実行ファイルを起動します。
ros2 run {package_name} {executable_name}
の形式で利用します。turtlesim の turtlesim_node を起動します。
ros2 run turtlesim turtlesim_node
続いて、二つ目のターミナルを起動して、turtle_teleop_key を起動します。以下のコマンドを打ちます。
$ ros2 run turtlesim turtle_teleop_key
3つ目のターミナルを起動して、アクティブなノードの存在を確認するために、以下のコマンドを打ちます。
$ ros2 node list ---- /turtlesim /teleop_turtle
と表示されます。
ノード間でのトピック通信の方法について説明します。turtlesim と teleop_turtle を起動した状態で、3つ目のターミナルで以下のコマンドを打つとトピックの内容が見えます。
$ ros2 topic list -t ---- /parameter_events [rcl_interfaces/msg/ParameterEvent] /rosout [rcl_interfaces/msg/Log] /turtle1/cmd_vel [geometry_msgs/msg/Twist] /turtle1/color_sensor [turtlesim/msg/Color] /turtle1/pose [turtlesim/msg/Pose]
ノード:teleop_turtleはデータを/turtle1/cmd_velを介してノード:turtlesimに送信できます。このデータを用いて亀さんを操作します。このトピックデータの形式を見てみましょう。
$ ros2 topic echo /turtle1/cmd_vel ---- linear: x: 2.0 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 ---
となっています。データはYAML形式で記述されています。
ノード:turtlesimは三次元要素を持つ二つのベクトル(linear, angular)からなるメッセージを受信することを予想しています。トピックにデータを送信してみましょう。
$ ros2 topic pub --once /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y:0.0, z: 1.8}}"
亀さんが左回転を始めて、止まります。オプション 「--once」を「--rate 1」とすると、1 Hzの速度で回転し続けます。
Launchファイルを用いたROS2の操作について取り上げます。インストールされているパッケージturtlesimを使います。「ros2 launch」コマンドでlaunchファイルを起動します。
$ ros2 launch turtlesim multisim.launch.py
multisim.launch.pyファイルを起動しました。この例でのlaunchファイルはPythonで記述されています。その拡張子は、.launch.py になっています。(ROS1のようにXMLファイイル形式で記述することもできます。)その中身は以下の通りです。
# turtlesim/launch/multisim.launch.py from launch import LaunchDescription import launch_ros.actions def generate_launch_description(): return LaunchDescription([ launch_ros.actions.Node( namespace= "turtlesim1", package='turtlesim', executable='turtlesim_node', output='screen'), launch_ros.actions.Node( namespace= "turtlesim2", package='turtlesim', executable='turtlesim_node', output='screen'), ])
このスクリプトからすぐ理解できることは、二つのノードを立ち上げていることです。turtlesim1とturtlesim2です。一匹の亀さんがいるウインドウが2枚ポップアップします。ここでは、pythonで書かれたファイルを使用していますが、xmlファイル及びyamlファイルで書くことができます。ここでは詳細に触れません。
次に、二つ目のターミナルを起動して、以下のコマンドを打って下さい。
$ ros2 topic pub /turtlesim1/turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}"
一匹の亀さんが回転します。3つ目のターミナルを起動して、以下のコマンドを打つと
$ ros2 topic pub /turtlesim2/turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}"
二匹目の亀さんが回転し始めます。
こうして、launchファイルを利用すると、1つのコマンドで複数個のノードを起動することができます。launchファイルの詳細については、この公式サイトのページを読んでください。
パッケージの作成:トピックデータの通信 |
ワークスペースはROS2パッケージを保存しているディレクトリです。ROS2を使用する前に、作業する予定のターミナルでROS2用のワークスペースを作成する必要があります。
また、「オーバーレイ」の起動用シェルスクリプトを用意するオプションもあります。これは、既存のROS2ワークスペース、つまり「アンダーレイ」に干渉することなく、新しいパッケージを追加できるセカンダリワークスペースです。アンダーレイには、オーバーレイ内のすべてのパッケージの依存関係が含まれている必要があります。 オーバーレイ内のパッケージは、アンダーレイ内のパッケージをオーバーライドします。ここでは、これらの話は無視します。
ROS2用のワークスペースを作成します。その前に、rosdep及びcolconをインストールします。
#rosdepのインストール $ sudo apt install python3-rosdep $ rosdep update #colcon のインストール: $ sudo apt install python3-colcon-common-extensions
ワークスペース用のディレクトリ dev_ws を作成します。
$ mkdir -p ~/dev_ws/src $ cd ~/dev_ws/src
最良の方法は、ワークスペース内のパッケージをsrcディレクトリに配置することです。公式のパッケージROS-tutorialsをsrcディレクトリ内にダウンロードします。
$ git clone https://github.com/ros/ros_tutorials.git -b foxy-devel
ワークスペースをビルドする前に、パッケージの依存関係を解決する必要があります。 すでにパッケージの依存関係が構築されている可能性もありますが、新しいパッケージをクローンするたびに依存関係を確認することをした方が望ましいと言えます。ワークスペースのルート(dev_ws)から以下のコマンドを打ちます。
# cd if you're still in the ``src`` directory with the ``ros_tutorials`` clone $ cd .. $ rosdep install -i --from-path src --rosdistro foxy -y
「#All required rosdeps installed successfully」と表示されればOKです。
次に、パッケージをcolconを用いてビルドします。
$ colcon build ---- Starting >>> turtlesim ... Finished <<< turtlesim [1.49s] Summary: 1 package finished [1.58s]
この結果、dev_ws ディレクトリ内に、build, install, log というフォルダが新たに作成されます。
新しくターミナルを起動させるたびに、ROS2用のワークスペースへアクセスするためのbashファイルを読み込む必要があります。
$ cd ~/dev_ws $ . install/local_setup.bash
以上で、turtlesim パッケージをオーバーレイから読み込むことができます。
$ ros2 run turtlesim turtlesim_node
独自のパッケージを作成することにします。ただ、Pythonスクリプトをゼロから作成することはやめて、 Githubのrepoから入手することにします。py_pubsubというパッケージを作成するために、以下のコマンドを打ちます。「--build-type ament_python」はPythonのパッケージを作成するときのオプションの指定です。C++用のパッケージ作成に「--build-type ament_cmake」というオプションもあります。
$ cd dev_ws/src $ ros2 pkg create --build-type ament_python py_pubsub ---- 結果 going to create a new package package name: py_pubsub destination directory: /home/jetson/dev_ws/src package format: 3 version: 0.0.0 description: TODO: Package description maintainer: ['jetson'] licenses: ['TODO: License declaration'] build type: ament_python dependencies: [] creating folder ./py_pubsub creating ./py_pubsub/package.xml creating source folder creating folder ./py_pubsub/py_pubsub creating ./py_pubsub/setup.py creating ./py_pubsub/setup.cfg creating folder ./py_pubsub/resource creating ./py_pubsub/resource/py_pubsub creating ./py_pubsub/py_pubsub/__init__.py creating folder ./py_pubsub/test creating ./py_pubsub/test/test_copyright.py creating ./py_pubsub/test/test_flake8.py creating ./py_pubsub/test/test_pep257.py
py_pubsubというパッケージが作成されました。
$ cd ~/dev_ws/src/py_pubsub/py_pubsub $ wget https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_publisher/examples_rclpy_minimal_publisher/publisher_member_function.py
publisherノードを作成するpythonスクリプトをダウンロードしました。このpublisher_member_function.pyの内容は以下の通りです。
# Copyright 2016 Open Source Robotics Foundation, Inc. import rclpy from rclpy.node import Node from std_msgs.msg import String class MinimalPublisher(Node): def __init__(self): super().__init__('minimal_publisher') self.publisher_ = self.create_publisher(String, 'topic', 10) timer_period = 0.5 # seconds self.timer = self.create_timer(timer_period, self.timer_callback) self.i = 0 def timer_callback(self): msg = String() msg.data = 'Hello World: %d' % self.i self.publisher_.publish(msg) self.get_logger().info('Publishing: "%s"' % msg.data) self.i += 1 def main(args=None): rclpy.init(args=args) minimal_publisher = MinimalPublisher() rclpy.spin(minimal_publisher) # Destroy the node explicitly # (optional - otherwise it will be done automatically # when the garbage collector destroys the node object) minimal_publisher.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
次に、listnerノードを作成するスクリプトをダウンロードします。
$ wget https://raw.githubusercontent.com/ros2/examples/foxy/rclpy/topics/minimal_subscriber/examples_rclpy_minimal_subscriber/subscriber_member_function.py
subscriber_member_function.pyは以下のようなスクリプトです。
# Copyright 2016 Open Source Robotics Foundation, Inc. import rclpy from rclpy.node import Node from std_msgs.msg import String class MinimalSubscriber(Node): def __init__(self): super().__init__('minimal_subscriber') self.subscription = self.create_subscription( String, 'topic', self.listener_callback, 10) self.subscription # prevent unused variable warning def listener_callback(self, msg): self.get_logger().info('I heard: "%s"' % msg.data) def main(args=None): rclpy.init(args=args) minimal_subscriber = MinimalSubscriber() rclpy.spin(minimal_subscriber) # Destroy the node explicitly # (optional - otherwise it will be done automatically # when the garbage collector destroys the node object) minimal_subscriber.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
setup.py, setup.cfg, package.xml の中身をパッケージに合わせて修正します。最初にpackage.xmlを修正します。
$ cd ~/dev_ws/src/py_pubsub $ nano package.xml
とファイルを開いて
<exec_depend>rclpy</exec_depend> <exec_depend>std_msgs</exec_depend>
を追加します。setup.pyスクリプトを修正します。
$ nano setup.py ---- 以下のentry_pointsの[]括弧内部分を追加修正 entry_points={ 'console_scripts': [ 'talker = py_pubsub.publisher_member_function:main', 'listener = py_pubsub.subscriber_member_function:main', ], },
package.xml ファイル
と setup.py ファイル
内にあるdescription、maintainer(email,user)、license の部分をパッケージに合わせて修正するように要請があります。この修正は本質的なものではないと思います。詳しくは、公式サイトのTutorialsを参照ください。
パッケージをビルドします。
$ cd ~/dev_ws $ rosdep install -i --from-path src --rosdistro foxy -y $ colcon build --packages-select py_pubsub
talkerノードを起動します。
$ . install/setup.bash $ ros2 run py_pubsub talker ---- [INFO] [minimal_publisher]: Publishing: "Hello World: 0" [INFO] [minimal_publisher]: Publishing: "Hello World: 1" [INFO] [minimal_publisher]: Publishing: "Hello World: 2" [INFO] [minimal_publisher]: Publishing: "Hello World: 3" [INFO] [minimal_publisher]: Publishing: "Hello World: 4" ...
2つ目のターミナルを開いて、listnerを起動します。
$ cd ~/dev_ws $ . install/setup.bash $ ros2 run py_pubsub listner ---- [INFO] [minimal_subscriber]: I heard: "Hello World: 10" [INFO] [minimal_subscriber]: I heard: "Hello World: 11" [INFO] [minimal_subscriber]: I heard: "Hello World: 12" [INFO] [minimal_subscriber]: I heard: "Hello World: 13" [INFO] [minimal_subscriber]: I heard: "Hello World: 14" ...
listner ノードが talker ノードからのメッセージ「Hello World」を受信します。
以上で、トピック上のデータを発信するノードとそれを受信するノードを作成することができました。このパッケージをビルドし、実行させる前に、依存関係、エントリポイントなどを package configuration ファイル(package.xml、setup.py、setup.cfgなど)内に追加する必要がありました。このような手続きを用いて、ROS2のパッケージが作成できます。
サービスを用いたデータ通信の使用方法については公式サイトのTutorialsを読んでください。
サービスを用いたデータ通信のパッケージの作成の手順を簡単に説明します。py_srvcliというパッケージを作成します。
$ cd ~/dev_ws/src $ ros2 pkg create --build-type ament_python py_srvcli --dependencies rclpy example_interfaces
パッケージpy_srvcliに関係するファイルが自動生成されます。「--dependencies」オプションを指定しているので、package.xmlの中にrcplyに関するラインが自動的に追加されます。
ノードを作成するために、dev_ws/src/py_srvcli/py_srvcliディレクトリの中に必要なpythonスクリプトを書き込んでいきます。パッケージの仕様に合わせて、package.xml及びsetup.pyファイルを修正します。以下、ビルドして、実行するという順序になります。
TurtleBot3を用いたシミュレーション |
TurtleBot3を用いたロボット操作のシミュレーションを取り上げます。TurtleBot3 ロボットは実際にハードウエアとして制作・販売されています。Raspberry Pi 4 とJetson Nano を搭載することを想定しています。ハードウエアの特徴は、このサイトに説明されています。ここでは、物理的なTurtleBot3ではなく、シミュレーション用のTurtleBot3環境を使用します。 emanual.robotis.comのサイトの説明に準拠して、インストールします。TurtleBot3を用いるために必要なGazebo関連モジュールをインストールします。
# Install Gazebo11 $ sudo apt -y install ros-foxy-gazebo-* # Install Cartographer $ sudo apt -y install ros-foxy-cartographer ros-foxy-cartographer-ros # Install Navigation2 $ sudo apt -y install ros-foxy-navigation2 ros-foxy-nav2-bringup # Install TurtleBot3 via Debian Packages. $ source ~/.bashrc $ sudo apt -y install ros-foxy-dynamixel-sdk ros-foxy-turtlebot3-msgs ros-foxy-turtlebot3
環境設定をしまます。
# Environment Configuration:Set the ROS environment for PC $ echo 'export ROS_DOMAIN_ID=30 #TURTLEBOT3' >> ~/.bashrc $ echo 'export TURTLEBOT3_MODEL=burger' >> ~/.bashrc $ source ~/.bashrc
waffleやwaffle_piというモデルもありますが、ここではburgerをモデルとして使用します。ROS_DOMAIN_IDの設定は必須ではありませんが、ここでは設定しておきます。次に、 シミュレーションパッケージをインストールします。コードは https://github.com/ROBOTIS-GIT/turtlebot3 にあります。
# Install Simulation Package $ cd ~/dev_ws/src/ $ git clone -b foxy-devel https://github.com/ROBOTIS-GIT/turtlebot3_simulations.git $ cd ~/dev_ws && colcon build --symlink-install
TurtleBot3のシミュレーション環境は3種類あります。Gazeboを起動するとき、以下の3種類のコマンドが使用可能です。
# Empty World $ ros2 launch turtlebot3_gazebo empty_world.launch.py # TurtleBot3 World $ ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py # TurtleBot3 House $ ros2 launch turtlebot3_gazebo turtlebot3_house.launch.py
gazebo-world:中央下にある灰色の丸がロボットです
通常は、turtlebot3_world.launch.py を実行します。Gazeboが起動するまで結構時間(数分)がかかります。これらの3種類の環境をみて、好きなものを選択して下さい。Gazeboが起動したら、2番目のターミナルで、Gazeboをkeyboardで操作するために、以下のコマンド打ちます。
# Operate TurtleBot3 $ ros2 run turtlebot3_teleop teleop_keyboard ---- 表示 Control Your TurtleBot3! --------------------------- Moving around: w a s d x w/x : increase/decrease linear velocity (Burger : ~ 0.22, Waffle and Waffle Pi : ~ 0.26) a/d : increase/decrease angular velocity (Burger : ~ 2.84, Waffle and Waffle Pi : ~ 1.82) space key, s : force stop CTRL-C to quit
Turtlesimに比較して、Gazeboのレスポンスが非常に遅い。Turtlesim でのシミュレーションで利用されるキーボード操作用のノードが実行されます。キーの操作は以下の通りです。
i : 前進
j : 左旋回
k : 停止
l : 右旋回
< : 後退
「k」を入力すると停止するとなっていますが、動き続けます。「s」キーを打つとすぐに停止します。「w」キーを打ち続けると、進行速度がスピードアップします。このキーボードの操作に対応して、(teleop_keyboard)ノードからトピック・メッセージが配信されます。TurtleBot3のモーター・ノードがこのデータを受信します。
障害物から一定の距離を保ち、衝突を回避するために曲がる単純な衝突回避ノードが用意されています。TurtleBot3の世界でTurtleBot3を自律的に駆動するには、2番目のターミナルに以下のコマンドを入力します。
$ ros2 run turtlebot3_gazebo turtlebot3_drive
RViz2は、シミュレーションの実行中に公開されたトピックを視覚化します。上記のコマンドを打った後で、 3番目のターミナルで以下のコマンドを入力すると、新しいウィンドウにRViz2を起動できます。ロボットの衝突回避運動が視覚的に観察できます。
$ ros2 launch turtlebot3_bringup rviz2.launch.py
SLAM(Simultaneous Localization and Mapping)は、任意の空間の現在位置を推定して地図を描く手法です。SLAMは、旧TurtleBotからのよく知られた機能です。ナビゲーション(Navigation)とは、特定の環境でロボットを1つの場所から指定された目的地に移動させることです。 この目的のために、特定の環境の家具、オブジェクト、および壁のジオメトリ情報を含むマップが必要です。マップはセンサーによって取得された距離情報とロボット自体のポーズ情報を使用して作成されます。
ナビゲーションを使用すると、ロボットは、マップ、ロボットのエンコーダー、IMUセンサー、および距離センサーを使用して、現在のポーズからマップ上の指定された目的地に移動できます。
SLAM Simulation 及び Navigation Simulationも実行できますが、説明は省略します。なお、Manipulation や Autonomous Driving のシミュレーション・コードはROS1に対しては提供されていますが、ROS2にはまだ公開されていません。
Jetson Nano Mouseの操作 |
ROS2を用いたJetson Nano Mouse ロボットの操作を説明します。Jetson Nano Mouse ロボットの説明は、このページにあります。Jetson Nano Mouse ロボットを操作するためには、デバイスドライバなどをインストールする必要があります。
最初に、デバイスドライバをインストールします。
$ git clone https://github.com/rt-net/JetsonNanoMouse.git $ cd JetsonNanoMouse $ make build $ sudo make install
次に、セットアップスクリプトをダウンロードします。
$ git clone https://github.com/rt-net/jnmouse_utils.git
RT社のサイトにあるJetson Nano用OSの書き込みと初期設定に従い、Jetson Nanoのパフォーマンス設定とブートローダ更新を以下のように行います。次のコマンドを実行し、Jetsonのパフォーマンス設定を行います
$ cd ~/jnmouse_utils/scripts $ ./configure-jetson.sh
次のコマンドを実行し、ブートローダを更新します
$ cd ~/jnmouse_utils/scripts $ ./update-qspi.sh
SPI通信の有効化を行います。Jetson NanoのGPIOを設定するためのツールであるJetson-IOを使って、SPI1を有効にし、 Jetson NanoとJetson Nano Mouseの基板が通信できるようにします。 これによりJetson Nano Mouse前方の距離センサが使えるようになります。次のコマンドを実行し、Jetson-IOを起動します。ターミナルウィンドウのサイズが小さいと、Jetson-IOのメニュー表示が途中で切れてしまいます。ターミナルウィンドウは縦に長くしてから実行すること。
$ sudo /opt/nvidia/jetson-io/jetson-io.py
「Configure 40-pin expansion header」を選択し、「Configure header pins manually」を選択すると、GPIO画面になります。spi1をクリックして、「*」と有効にします。この選択後はBackを選び、メニューに戻ります。 「Select one of the following options:」で、「Save and reboot to reconfigure pins」を選択して、任意のキーを叩いて再起動します。この設定でライトセンサからの信号を読めることになります。
ロボットのデバイスとGIOPピン番号との関係は「取扱説明書」に書いてある通りです。GPIOの状態を見てみましょう。/sys/class/gpio/というディレクトリがあることを確認します。ルート権限でlsを使います。
$ ls -l /sys/class/gpio/ $ ls -l /dev/rt*
ロボットを操作するためのパッケージを作成します。RT社が提供するパッケージを利用します。このパッケージはRaspberry Pi 向けに書かれていますが、Jetson Nano Mouse でも、そのままの形で利用できます。ワークスペースのディレクトリ ros2_ws/src を作成して、このパッケージをダウンロードします。raspimouse_ros2_examplesはゲームパッド用なので、ダウンロードするのは任意です。
$ mkdir -p ~/ros2_ws/src $ cd ~/ros2_ws/src # Clone package $ git clone -b $ROS_DISTRO-devel https://github.com/rt-net/raspimouse2 $ git clone -b $ROS_DISTRO-devel https://github.com/rt-net/raspimouse_ros2_examples
依存関係を整備し、ビルドします。
# Install dependencies $ rosdep install -r -y -i --from-paths . # Build & Install $ cd ~/ros2_ws $ colcon build --symlink-install ----- Starting >>> raspimouse_msgs Finished <<< raspimouse_msgs [26.9s] Starting >>> raspimouse [Processing: raspimouse] Finished <<< raspimouse [55.7s] Summary: 2 packages finished [1min 24s]
インストールが終了したので、ロボットを操作します。1つ目のターミナルを起動して、
# Terminal 1 $ source ~/ros2_ws/install/setup.bash $ ros2 run raspimouse raspimouse
2つ目のターミナルを起動して、ブザーを鳴らしてみます。
# Terminal 2 $ source ~/ros2_ws/install/setup.bash $ ros2 lifecycle set raspimouse configure # Set buzzer frequency $ ros2 topic pub -1 /buzzer std_msgs/msg/Int16 '{data: 1000}' $ ros2 topic pub -1 /buzzer std_msgs/msg/Int16 '{data: 0}'
ここで利用されているのは、managed-lifecycle node と言われるものです。「ros2 lifecycle set raspimouse configure」でこのノードの作動準備をしています。ロボットのモーターの電源スイッチを入れて下さい。車輪を浮かせて下さい。以下のコマンドを順次打ちます。
# Terminal 2 # rotate motors $ ros2 lifecycle set raspimouse activate $ ros2 service call /motor_power std_srvs/SetBool '{data: true}' $ ros2 topic pub -1 /cmd_vel geometry_msgs/Twist '{linear: {x: 0.05, y: 0, z: 0}, angular: {x: 0, y: 0, z: 0.05}}' $ ros2 lifecycle set raspimouse deactivate
モーターが回転します。「ros2 lifecycle set raspimouse activate」でmanaged-lifecycle nodeがactiveの状態になり、「ros2 lifecycle set raspimouse deactivate」によりinactiveの状態になって、モーターの回転が停止します。
キーボードから操作してみましょう。ROSパッケージのteleop_twist_keyboardを利用します。ロボットを自由に運動できる環境に置きます。Jetson Nanoの電源とモーターのスイッチを入れます。リモートPCからssh接続でJetson Nanoに接続します。一つ目のターミナルで
# Terminal 1 $ source ~/ros2_ws/install/setup.bash $ ros2 run raspimouse raspimouse
2つ目のターミナルを起動して、以下のコマンドを順次打ちます。
# Terminal 2 $ source ~/ros2_ws/install/setup.bash $ ros2 lifecycle set raspimouse configure $ ros2 lifecycle set raspimouse activate $ ros2 service call /motor_power std_srvs/SetBool '{data: true}' $ ros2 run teleop_twist_keyboard teleop_twist_keyboard
This node takes keypresses from the keyboard and publishes them as Twist messages. It works best with a US keyboard layout. --------------------------- 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
デフォルトのパラメータではロボットの車輪回転速度が速すぎるので、速度を下げます。「x」キーを連打して0.15m/s以下まで下げます。
currently: speed 0.5 turn 1.0 currently: speed 0.45 turn 1.0 currently: speed 0.405 turn 1.0 currently: speed 0.36450000000000005 turn 1.0 currently: speed 0.32805000000000006 turn 1.0 currently: speed 0.2952450000000001 turn 1.0 currently: speed 0.2657205000000001 turn 1.0 currently: speed 0.23914845000000007 turn 1.0 currently: speed 0.21523360500000008 turn 1.0 currently: speed 0.19371024450000007 turn 1.0 currently: speed 0.17433922005000008 turn 1.0 currently: speed 0.15690529804500009 turn 1.0 currently: speed 0.14121476824050008 turn 1.0 currently: speed 0.12709329141645007 turn 1.0
この状態で、キーボードを操作します。「I」で前進、「>」で後進、「J」で左旋回、「L」で右旋回します。途中、「K」で停止の命令をロボットに送ることができます。
以下のコマンドで操作を停止します。
$ ros2 lifecycle set raspimouse deactivate
プログラムを作成してJetson Nano Mouse ロボットを走行させる手順について説明します。ロボットを走行させるプログラムのパッケージを jnmouse_ros2 とします。新しくjnmouse_ros2パッケージを作成するために、ターミナルで以下のコマンドを実行します。
$ cd ~/ros2_ws/src $ ros2 pkg create --build-type ament_python --node-name move jnmouse_ros2
ノードを起動するスクリプトが「move.py」として作成されます。ここでは、Jetson Nano Mouse が 2 秒間前進してから停止するという Python プログラム move.py を作成します。
$ cd ~/ros2_ws/src/jnmouse_ros2/jnmouse_ros2 $ nano move.py
このコマンドで、テキストエディターを起動し、「move.py」の中に以下のコードを書き込みます。
#!/usr/bin/env python3 import rclpy from rclpy.node import Node import sys from geometry_msgs.msg import Twist from rclpy.clock import Clock, ClockType from rclpy.time import Duration class Publisher(Node): def __init__(self): super().__init__('publisher') self.publisher = self.create_publisher(Twist, 'cmd_vel', 10) self.move_cmd = Twist() self.stop_cmd = Twist() self.move_cmd.linear.x = 0.2 self.move_cmd.linear.y = 0.0 self.move_cmd.linear.z = 0.0 self.move_cmd.angular.x = 0.0 self.move_cmd.angular.y = 0.0 self.move_cmd.angular.z = 0.0 self.duration = 2 # sec self.stop_time = Clock(clock_type=ClockType.ROS_TIME).now() + Duration(seconds=self.duration) self.timer_period = 0.1 # seconds self.timer = self.create_timer(self.timer_period, self.timer_callback) def __del__(self): self.publisher.publish(self.stop_cmd) def timer_callback(self): self.publisher.publish(self.move_cmd) if Clock(clock_type=ClockType.ROS_TIME).now() > self.stop_time: self.publisher.publish(self.stop_cmd) self.timer.cancel() sys.exit(0) def main(): rclpy.init(args=sys.argv) pub = Publisher() rclpy.spin(pub) pub.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
setup.py, setup.cfg, package.xml の中身をパッケージに合わせて修正します。package.xmlを修正します。
$ cd ~/ros2_ws/src/jnmouse_ros2 $ nano package.xml
とファイルを開いて
を追加します。setup.pyスクリプトを修正する必要はありません。
作成したパッケージをビルドします。
$ cd ~/ros2_ws $ rosdep install -i --from-path src --rosdistro foxy -y $ colcon build --packages-select jnmouse_ros2
トピックの配信ノードを起動します。1つ目のターミナルで
# Terminal 1 $ source ~/ros2_ws/install/setup.bash $ ros2 run raspimouse raspimouse
2つ目のターミナルで、モーターを操作します。
# Terminal 2 # move motors $ source ~/ros2_ws/install/setup.bash $ ros2 lifecycle set raspimouse configure $ ros2 lifecycle set raspimouse activate $ ros2 service call /motor_power std_srvs/SetBool '{data: true}' $ ros2 run jnmouse_ros2 move
Jetson Nano Mouse ロボットが2秒間直進して、停止します。以下のコマンドで操作を終了します。
$ ros2 lifecycle set raspimouse deactivate
衝突回避走行などのように、ロボットを自律的に走行させるためのプログラムは、このパッケージの中にPython スクリプトとして追加すれば実行できます。ただ、Jetson Nano Mouse には、TurtleBot3などで利用されている Laser Distance Sensor が搭載されていません。LiDARを想定したプログラムが利用できませんので、距離センサーがLightSenserのケースに対応した処理が必要です。
Jetson Nano Mouse に搭載されている光センサーを用いた自律走行のプログラムを作成します。以下のスクリプトを「wall_stop.py」として、「move.py」と同じディレクトリに保存して下さい。
#!/usr/bin/env python3 import rclpy from rclpy.node import Node import sys from geometry_msgs.msg import Twist from raspimouse_msgs.msg import LightSensors from rclpy.qos import qos_profile_sensor_data class PublisherSubscriber(Node): def __init__(self): super().__init__('publisher_subscriber') self.publisher = self.create_publisher(Twist, 'cmd_vel', 10) self.subscription = self.create_subscription( LightSensors, 'light_sensors', self.subscription_callback, qos_profile=qos_profile_sensor_data) self.subscription # prevent unused variable warning self.move_cmd = Twist() self.rotate_cmd = Twist() self.stop_cmd = Twist() self.move_cmd.linear.x = 0.2 self.move_cmd.linear.y = 0.0 self.move_cmd.linear.z = 0.0 def subscription_callback(self, data): d = data self.distance = d.forward_r + d.forward_l if self.distance >= 200: self.publisher.publish(self.stop_cmd) else: self.publisher.publish(self.move_cmd) def main(): rclpy.init(args=sys.argv) pubsub = PublisherSubscriber() rclpy.spin(pubsub) pubsub.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
このプログラムは、直線上に走行して、壁の手前で停止するという走行を行います。
衝突回避走行のプログラムも若干複雑になりますが、容易に作成できます。以下のスクリプトを「auto.py」というファイルで作成して、保存して下さい。このプログラムは、「TurtleBot3 のプログラムによる自律制御」で解説されているスクリプトを修正したものです。
#!/usr/bin/env python3 import rclpy from rclpy.node import Node import sys from geometry_msgs.msg import Twist from raspimouse_msgs.msg import LightSensors from rclpy.qos import qos_profile_sensor_data from rclpy.clock import Clock, ClockType from rclpy.time import Duration import numpy as np class PublisherSubscriber(Node): def __init__(self): super().__init__('publisher_subscriber') self.publisher = self.create_publisher(Twist, 'cmd_vel', 10) self.subscription = self.create_subscription( LightSensors, 'sensor_values', self.subscription_callback, qos_profile=qos_profile_sensor_data) self.subscription # prevent unused variable warning self.move_cmd = Twist() self.rotate_cmd = Twist() self.stop_cmd = Twist() self.move_cmd.linear.x = 0.2 self.move_cmd.linear.y = 0.0 self.move_cmd.linear.z = 0.0 self.move_cmd.angular.x = 0.0 self.move_cmd.angular.y = 0.0 self.move_cmd.angular.z = 0.0 self.rotate_cmd.linear.x = 0.0 self.rotate_cmd.linear.y = 0.0 self.rotate_cmd.linear.z = 0.0 self.rotate_cmd.angular.x = 0.0 self.rotate_cmd.angular.y = 0.0 self.rotate_cmd.angular.z = 3.0 self.rotate_duration = 0.5 # sec self.isRotating = False self.distance = 300 self.rotationStartTime = 0 def subscription_callback(self, data): d = data self.distance = d.forward_r + d.forward_l + d.left + d.right if self.distance >300: self.isRotating = True self.rotationStartTime = Clock(clock_type=ClockType.ROS_TIME).now() if not self.isRotating: self.publisher.publish(self.move_cmd) else: if Clock(clock_type=ClockType.ROS_TIME).now() > self.rotationStartTime + Duration(seconds=self.rotate_duration): self.isRotating = False else: self.publisher.publish(self.rotate_cmd) def main(): rclpy.init(args=sys.argv) pubsub = PublisherSubscriber() rclpy.spin(pubsub) pubsub.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
setup.pyファイルを追加修正します。
$ cd ~/ros2_ws/src/jnmouse_ros2 $ nano setup.py ---- これを、以下のように追加編集します。 (略) entry_points={ 'console_scripts': [ 'move = jnmouse_ros2.move:main', 'wall_stop = jnmouse_ros2.wall_stop:main', 'auto = jnmouse_ros2.auto:main' ], },
上記の例と同じくビルドします。
$ cd ~/ros2_ws $ rosdep install -i --from-path src --rosdistro foxy -y $ colcon build --packages-select jnmouse_ros2
壁に向かってロボットを置いて下さい。上記と同じ手順で、2個のターミナルを立ち上げて、実行します。最初に、壁の手前で停止するプログラムのケースでは、
# Terminal 1 $ source ~/ros2_ws/install/setup.bash $ ros2 run raspimouse raspimouse # Terminal 2 $ source ~/ros2_ws/install/setup.bash $ ros2 lifecycle set raspimouse configure $ ros2 lifecycle set raspimouse activate $ ros2 service call /motor_power std_srvs/SetBool '{data: true}' $ ros2 run jnmouse_ros2 wall_stop
と打ちます。壁の手前で停止することを確認して下さい。走行の停止は「control + c」です。
この後、衝突回避の自律走行のプログラムのケースを実行します。壁の前にロボットを置いて下さい。
$ ros2 run jnmouse_ros2 auto
と打ちます。壁の手前で回転して、直進することを確認して下さい。走行の停止は「control + c」です。最後に、「$ ros2 lifecycle set raspimouse deactivate」を入力して下さい。「Terminal 1」も「control + c」で終了します。
Jetson Nano Mouse のLiDARプログラムによるロボット制御を実現するために、LiDAR機器をJetson Nano Mouse に搭載します。RT社の Jetson Nano Mouse オプションキット No.1 [マルチLiDARマウント]を取り付けます。この製品の説明は、マルチLiDARマウントにあります。
ここでは、LiDAR機器として「RPLIDAR A1M8 2D」レーザー距離センサーキットを使用します。Jetson Nanoとの接続のためにUSBケーブルが必要です。マルチLiDARマウントへのRPLIDARの搭載手順はこのPDFファイルに説明されています。 取扱説明書に沿って、ロボットに搭載します。RPLIDAR通信基板のmicro USB(Type-B)端子にUSBケーブルを接続し、反対側をJetson Nano のUSB3.0端子に接続します。rplidarのシリアルポートを確認して、アクセスを可能にします。
Jetson Nano Mouse に搭載した RPLIDAR
$ ls -l /dev | grep ttyUSB $ sudo chmod 666 /dev/ttyUSB0
rplidar ros packageをこのgithubからダウンロードして、ビルドします。
$ cd ~/ros2_ws/src/ $ git clone https://github.com/allenh1/rplidar_ros $ rosdep install -r -y -i --from-paths . # Build & Install $ cd ~/ros2_ws $ colcon build --symlink-install --packages-select rplidar_ros
このrplidar_rosを起動すると、rplidarの測定値を sensor_msgs/LaserScan 形式のデータとして発信します。
$ source ~/ros2_ws/install/setup.bash $ ros2 launch rplidar_ros rplidar.launch.py ------- [INFO] [launch]: All log files can be found below /home/jetson/.ros/log/2022-06-15-15-14-14-169300-nano-15814 [INFO] [launch]: Default logging verbosity is set to INFO [INFO] [rplidar_composition-1]: process started with pid [15816] [rplidar_composition-1] [INFO] [1655273654.548077964] [rplidar_composition]: RPLIDAR running on ROS 2 package rplidar_ros. SDK Version: '1.12.0' [rplidar_composition-1] [INFO] [1655273657.055082245] [rplidar_composition]: RPLIDAR S/N:EDFBED93C0EA98C9A5E698F278254669 [rplidar_composition-1] [INFO] [1655273657.055190632] [rplidar_composition]: Firmware Ver: 1.29 [rplidar_composition-1] [INFO] [1655273657.055226413] [rplidar_composition]: Hardware Rev: 7 [rplidar_composition-1] [INFO] [1655273657.056720387] [rplidar_composition]: RPLidar health status : '0' [rplidar_composition-1] [INFO] [1655273657.598961255] [rplidar_composition]: current scan mode: Sensitivity, max_distance: 12.0 m, Point number: 7.9K , angle_compensate: 2, flip_x_axis 0 ...
[rplidar_composition-1]というノードからrplidarの測定データが発信されています。
「UserWarning: The parameter 'node_executable' is deprecated, use 'executable' instead」という警告が出ます。
2つ目のターミナルで以下のコマンドを打つとノード名とトピック('scan')の内容が見えます。
$ source ~/ros2_ws/install/setup.bash $ ros2 topic list -t $ ros2 topic echo scan