ロボットシステム学

第8回: Robot Operating System (ROS 2)

千葉工業大学 上田 隆一

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. Creative Commons License

ロボットシステム学第8回

今日やること

  1. ROSの概要の理解
  2. ROS 2のインストール
  3. ROSのノードと通信の基礎
  4. ROS 2プログラミング
  5. まとめ
ロボットシステム学第8回

1. ROSの概要

ロボットシステム学第8回

ROS: robot operating system

  • ロボットのソフトウェアコンポーネントを作って動作させるためのフレームワーク/ミドルウェア
    • OSでは無い
  • 発祥: 2000年代後半、Willow Garage社
  • BSDライセンス
  • サイト
ロボットシステム学第8回

どんなものか

  • 本体: プロセス間通信をつかさどる
    • プロセス同士をpublish-subscribeモデルやclient-serverモデルでつなぐ
    • 通信するデータに型
  • 周辺
    • ビルドシステム、パッケージ管理、テストツール、・・・
と書いてもよくわからんので使うメリットから
ロボットシステム学第8回

ROS化されている重要なソフトウェア

  • 移動ロボットの制御ソフトウェア
    • gmapping, Cartographer, ナビゲーションメタパッケージ
      • 地図生成(次のページにデモ)、位置推定、経路生成
  • マニピュレータのソフトウェア
    • MoveIt!
      • 腕の動作計画 腕先の位置を入力→関節角を計算(逆運動学)
  • 各種センサのインタフェース
    • すぐ使える
    • 以前は(特にLinuxでは)自分でシリアル通信のプログラムを書くなどの苦労があった
ロボットシステム学第8回

ROSを使った移動ロボットの制御

  • コントローラでロボットに移動経路を教え、そのあとロボットが自律で経路を移動
    • 地図と経路を計算するCartographerを利用
  • ポイント: Cartographerの部分は一切手を入れていない
ロボットシステム学第8回

ROSを使ったマニピュレーションの様子

ロボットシステム学第8回

ROSのバージョン

  • ROS 1(もともとROSと呼ばれていたもの)
    • オリジナルのROS
    • 主に研究のフレームワークとして発達
    • まだまだ使われる
  • ROS 2(今回扱うもの)
    • ROSが普及して、当初想定していなかった利用場面が増加
      • セキュリティー、製品化、シビアな通信環境、・・・
    • アーキテクチャから作り直し
      • ROS1とは基本的に互換性なし
ロボットシステム学第8回

2. ROS 2のインストール

ロボットシステム学第8回

~/.bashrcの設定

$ vi ~/.bashrc
(略)
### ROS 1がインストールされている場合は次のようにコメントアウト ###
#source /opt/ros/noetic/setup.bash     ←ここから関係する設定をコメントアウト
#source ~/catkin_ws/devel/setup.bash
#export ROS_MASTER_URI=http://localhost:11311
#export ROS_HOSTNAME=localhost

### ROS 2の通信範囲の設定 ###
export ROS_AUTOMATIC_DISCOVERY_RANGE=LOCALHOST
# export ROS_LOCALHOST_ONLY=1  #Ubuntu 22.04以前の場合はこっち
ロボットシステム学第8回

ROS 2のインストール

  • 手順は次の通り
    • 注意: 時刻がずれていると止まります(date -sでセットを)
  • 余計なものを入れたくない場合はsetup.bashros-${ROS_VER}-desktopros-${ROS_VER}-ros-baseに変更して実行
$ git clone https://github.com/ryuichiueda/ros2_setup_scripts
$ cd ros2_setup_scripts
$ ./setup.bash
$ source ~/.bashrc
### 動作確認 ###
$ ros2
usage: ros2 [-h] Call `ros2 <command> -h` for more detailed usage. ...

ros2 is an extensible command-line tool for ROS 2.
(以下略)
ロボットシステム学第8回

動作確認

  • ひとつプログラムを立ち上げ
    端末1$ ros2 run demo_nodes_py talker
    ・・・
    [INFO] [talker]: Publishing: "Hello World: 61"
    [INFO] [talker]: Publishing: "Hello World: 62"
    ・・・
    
    • 「"Hello World: 数字"を発行」と言っている
  • もうひとつプログラムを立ち上げ
    端末2$ ros2 run demo_nodes_py listener
    ・・・
    [INFO] [listener]: I heard: [Hello World: 55]
    [INFO] [listener]: I heard: [Hello World: 56]
    ・・・
    
    • 「"Hello World: 数字"を聞いた」と言っている
で、これは何をしているのか?
ロボットシステム学第8回

3. ROSのノードと通信の基礎

ロボットシステム学第8回

ROSのノードの基本

  • 基本1: プログラムのことを「ノードと呼ぶ」
  • 基本2: ノードは互いに通信でデータをやりとり
    • データはC言語の構造体のような型を持つ
  • これで何ができるか?
    • ロボットの各機能を別のプログラムとして実装できる
      • 例1: 画像処理のプログラムとナビゲーションのプログラムの連携
        • 画像処理のプログラムは独立したソフトウェアとして公開可能
      • 例2: レーザースキャナをA社からB社に交換
        • A社とB社が同じ出力をするノードを作っていると、交換が容易
    • 型があるのでデバッグしやすい
ロボットシステム学第8回

ROSの通信の基本

  • GUIツールで構造を確認
    端末1$ ros2 run demo_nodes_py talker
    端末2$ ros2 run demo_nodes_py listener
    端末3$ ros2 run rqt_graph rqt_graph
    
  • ノード(楕円)がデータの流路(矢印)で連結
    • /chatter: 流路の名前で「トピック」と呼ばれる
      • 流れるデータは「メッセージ
    • ノードから出る矢印: 「パブリッシャ
    • ノードに入る矢印: 「サブスクライバ
  • ノードは複数のパブリッシャとサブスクライバを持てる
ロボットシステム学第8回

4. ROS 2プログラミング

  • 先ほど動かしたtalker、listenerに相当するノードを作成
  • いろいろ作法が細かいので、コードを書く以外のことの意図も確認
    • 誰が作ったパッケージも同じような構造にするため
ロボットシステム学第8回

ワークスペースの作成

  • ワークスペース: 作業場
  • とりあえずディレクトリを作るだけ
$ mkdir -p ros2_ws/src
$ tree ros2_ws/
ros2_ws/
└── src
1 directory, 0 files
ロボットシステム学第8回

初期状態のパッケージを作る

  • パッケージ: ROSのプログラムの配布単位
    • GitHubのリポジトリにほぼ相当
    • なかにいくつかのノードのプログラムを入れる
    ### パッケージ作成の例 ###
    $ cd ~/ros2_ws/src/
    $ ros2 pkg create mypkg --build-type ament_python                                   
    $ tree mypkg/
    mypkg/
    ├── mypkg
    │   └── __init__.py
    ├── package.xml
    ├── resource
    │   └── mypkg
    ├── setup.cfg
    ├── setup.py
    └── test
        ├── test_copyright.py
        ・・・
    (略)
    
ロボットシステム学第8回

パッケージ情報の記述1

  • package.xmlを編集する
    • パッケージマニフェストというもの
    • パッケージの情報を記述したファイル
      • 説明(description)、メンテナ(maintainer、管理する人)、ライセンスをちゃんと書く
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/..."?>
<package format="3">
  <name>mypkg</name>
  <version>0.0.0</version>
  <description>a package for practice</description>
  <maintainer email="ryuichiueda@example.com">Ryuichi Ueda</maintainer>                       
  <license>BSD-3-Clause</license>

  <test_depend>ament_copyright</test_depend>
・・・
ロボットシステム学第8回

パッケージ情報の記述2

  • setup.pyの編集
    • Pythonのモジュールで使われるインストールスクリプト
      • 変更箇所はpackage.xmlと同じ
from setuptools import setup

package_name = 'mypkg'

setup(
    name=package_name,
    version='0.0.0',
    ・・・
    maintainer='Ryuichi Ueda',
    maintainer_email='ryuichiueda@gmail.com',
    description='a package for practice',
    license='BSD-3-Clause',
・・・
ロボットシステム学第8回

パッケージをビルド

  • ビルド作業
    • colconというコマンドを使用
    $ cd ~/ros2_ws/
    $ colcon build
    (略。警告が出る場合があるけどとりあえず気にしない。エラーは気にする。)                              
    Summary: 1 package finished [0.69s]
      1 package had stderr output: mypkg 
    
  • ros2_ws下のパッケージを利用可能に
    • ~/.bashrcの末尾に2行追加
      source ~/ros2_ws/install/setup.bash
      source ~/ros2_ws/install/local_setup.bash                                                
      
  • sourceして確認
    $ source ~/.bashrc
    $ ros2 pkg list | grep mypkg                                                              
    mypkg
    
ロボットシステム学第8回

パブリッシャを持つノードの作成

ロボットシステム学第8回

コードの前半

  1 import rclpy                #ROS 2のクライアントのためのライブラリ
  2 from rclpy.node import Node      #ノードを実装するためのNodeクラス(クラスは第10回で)
  3 from std_msgs.msg import Int16   #通信の型(16ビットの符号付き整数)
  4
  5 rclpy.init()  
  6 node = Node("talker")            #ノード作成(nodeという「オブジェクト」を作成)
  7 pub = node.create_publisher(Int16, "countup", 10)   #パブリッシャのオブジェクト作成
  8 n = 0 #カウント用変数
  9
 10
  • オブジェクト: 今のところは様々な機能を持つ変数と考えて良い
    • うしろに関数みたいなもの(メソッド)をつけて機能を呼び出せる
      (次ページに続く)
ロボットシステム学第8回

コードの後半

11 def cb():          #20行目で定期実行されるコールバック関数
12     global n       #関数を抜けてもnがリセットされないようにしている
13     msg = Int16()  #メッセージの「オブジェクト」
14     msg.data = n   #msgオブジェクトの持つdataにnを結び付け
15     pub.publish(msg)        #pubの持つpublishでメッセージ送信
16     n += 1
17
18
19 def main():
20     node.create_timer(0.5, cb)  #タイマー設定
21     rclpy.spin(node)            #実行(無限ループ)
ロボットシステム学第8回

パッケージの設定

  • パッケージにtalker.pyや依存するモジュールを登録
    • package.xmlに利用するモジュールを登録
      ・・・
        <license>BSD-3-Clause</license>
        <exec_depend>rclpy</exec_depend>            <-追加
        <exec_depend>std_msgs</exec_depend>         <-追加                           
      ・・・
      
    • setup.pytalker.pyのどの関数を呼び出すかを登録
      • エントリポイントと呼ばれます
      ・・・
              entry_points={
              'console_scripts': [
                  'talker = mypkg.talker:main', #talker.pyのmain関数という意味
                  #'listener = mypkg.listener:main', ←書いておいて後でコメントアウト
              ],
      ・・・
      
ロボットシステム学第8回

依存するパッケージのインストールとビルド

  • 他に利用するパッケージを確認してインストール
    • 今の例の場合はやらなくていいです(一般論として説明してます)
    • jazzyはUbuntu 24.04用のROS 2のバージョン
    • 22.04の場合はhumble
      $ cd ~/ros2_ws
      $ rosdep install -i --from-path src --rosdistro jazzy -y 
      
  • ビルド
    • colcon buildして、その後sourceで設定を読み直し
      $ colcon build    #注意!!必ず~/ros2_wsでやること
      Starting >>> mypkg
      Finished <<< mypkg [2.55s]
      
      Summary: 1 package finished [3.06s]                                           
      $ source ~/.bashrc
      
ロボットシステム学第8回

実行とメッセージの確認

  • ros2 runで実行
    $ ros2 run mypkg talker
    (なにも表示されない)
    
  • 別の端末でros2を使ってサブスクライブする
    $ ros2 topic echo /countup
    data: 13
    ---
    data: 14
    ---
    ・・・
    
ロボットシステム学第8回

サブスクライバを持つノードの記述

  • 機能: /countupからメッセージをもらって表示
    • listener.pyという名前で
        1 import rclpy
        2 from rclpy.node import Node
        3 from std_msgs.msg import Int16
        4
        5
        6 rclpy.init()
        7 node = Node("listener")
        8  
        9
       10 def cb(msg):
       11     global node
       12     node.get_logger().info("Listen: %d" % msg.data)    
       13
       14
       15 def main():
       16     pub = node.create_subscription(Int16, "countup", cb, 10)                      
       17     rclpy.spin(node)
      
ロボットシステム学第8回

talkerとlistenerの実行

  • listener.pyについてsetup.pyへの記述
    • さきほどコメントアウトした行の#を消す
  • colcon buildを済ませておく
端末1$ ros2 run mypkg talker
端末2$ ros2 run mypkg listener
[INFO] [Listener]: Listen: 142
[INFO] [Listener]: Listen: 143
・・・
ロボットシステム学第8回

5. まとめ

  • ROS(ROS 2)
    • 独立したプログラム(プロセス、ノード)を連携
  • パッケージを作り、ふたつのノードを記述
    • 単にプログラムを走らせるだけでなく、様々な手続き
      • 最初は煩わしいが、人に使ってもらうモジュールを作りやすい。
ロボットシステム学第8回

宿題

  • mypkgをGitHubにアップする
    • やりかたは次のページに書きます
    • そのままこれが課題2のリポジトリになります
    • 注意: ~/ros2_ws/src/mypkg下のファイルとかディレクトリがリポジトリの一番上の階層に来るようにアップすること
ロボットシステム学第8回

手元のディレクトリをGitHubのリポジトリにする方法(その1)

  • GitHubで同じ名前(今日の内容だとmypkgという名前)のリポジトリを作る
  • 手元のmypkgで次のように作業
    ### デフォルトでできるブランチ名の指定 ###
    $ git config --global init.defaultBranch main
    ### mypkgをGitのリポジトリに ###
    $ git init
    ### git statusなどで状況を調べましょう ###
    ### add ###
    $ git add -A
    ### コミット ###
    $ git commit -m "Initial commit"
    [master (root-commit) 22f68e2] Initial commit
     8 files changed, 124 insertions(+)
     ・・・
     create mode 100644 test/test_pep257.py
     ### 次のページに続く
    
ロボットシステム学第8回
### 手元のリポジトリとGitHubのリポジトリを紐づけ ###
$ git remote add origin git@github.com:ryuichiueda/mypkg.git
### ブランチ名をmainに ###
$ git branch -M main
### push###
git push -u origin main
### ブラウザでアップロードできていることを確認を ###
ロボットシステム学第8回