ROSの勉強 第15弾:パラメータ化


#プログラミング ROS< パラメータ化 >

はじめに

1つの参考書に沿って,ROS(Robot Operating System)を難なく扱えるようになることが目的である.その第15弾として,パラメータ化を扱う.

環境

仮想環境
ソフト VMware Workstation 15
実装RAM 2 GB
OS Ubuntu 64 ビット
isoファイル ubuntu-mate-20.04.1-desktop-amd64.iso
コンピュータ
デバイス MSI
プロセッサ Intel(R) Core(TM) i5-7300HQ CPU @ 2.50GHz 2.50GHz
実装RAM 8.00 GB (7.89 GB 使用可能)
OS Windows (Windows 10 Home, バージョン:1909)
ROS
Distribution noetic
プログラミング言語 Python 3.8.5

パラメータ化

プログラムにパラメータを与える.その方法は無数に存在する.ロボットシステムの開発時には,いろいろな方法でパラメータが設定できると便利.以下のその例をいくつか列挙しておく.
・「デバッグ時にコマンドラインで」
・「roslaunchファイルの中で」
・「グラフィカルインタフェースから」
・「他のROSノードから」
・「複数のプラットフォームや環境向けに明確に動作を定義したパラメータファイルから」
など.

ここでは,コマンドラインでの指定を行う.目標は,ROSのパラメータを使って,前回作った速度コマンドプログラムの並進速度と回転角速度のスケールを指定できるようにすることである.

コマンドラインでのパラメータ設定

パラメータ名のつけ方にもたくさんの方法があるらしい.ここでは,プライベートなパラメータ名を扱う.このプライベートとは,パラメータの完全な名前がノード名の後にパラメータ名を加えるという形式を成すパラメータのことを指す.
といっても,コマンドラインで指定する際には,アンダースコア(_)をパラメータ名の先頭につけて,:=で値を指定する.具体的には,次のようになる.

./keys_to_twist_parameterized.py _linear_scale:=0.5 _angular_scale:=0.4

これにより,ノード起動直前に,keys_to_twist/_linear_scaleパラメータが0.5に設定され,keys_to_twist/_angular_scaleパラメータが0.4に設定される.
また,これらのパラメータはrospyライブラリのhas_param()や,get_param()で取得できる.

実装

先ほどの説明に従い,実装を行う.以下にソースコードとそのときの実行様子を示す.

ソースコード
keys_to_twist_parameterized.py
#! /usr/bin/env python3

"""コマンドラインでのパラメータ設定を利用して,速度のスケーリングを実装"""

import rospy
from std_msgs.msg import String
from geometry_msgs.msg import Twist

#キーの割当:[angular.z, linear.x]
key_mapping = {'w':[ 0, 1], 'x':[0, -1],
               'a':[-1, 0], 'd':[1,  0],
               's':[ 0, 0]}

last_twist = None   #何も押されなかった場合,直前のtwistを配信するための記録用変数
vel_scales = [0.1, 0.1] #デフォルトの速度(非常に遅い)

def keys_callback(msg, twist_pub):
    global last_twist, vel_scale

    if len(msg.data) == 0 or msg.data[0] not in key_mapping.keys():
        return  #データがないもしくはキーマッピングにないデータの場合,何もせずに終了

    velocity = key_mapping[msg.data[0]] #キーマッピングからキーに合わせて抽出
    last_twist = Twist()    #Twistインスタンス生成(0に初期化)
    last_twist.angular.z = velocity[0] * vel_scales[0]  #配列の要素に速度スケール値をかけて代入
    last_twist.linear.x = velocity[1] * vel_scales[1]   #配列の要素に速度スケール値をかけて代入
    twist_pub.publish(last_twist)   #トピック配信

if __name__ == '__main__':
    rospy.init_node('keys_to_twist')    #ノードの初期化
    twist_pub = rospy.Publisher('cmd_vel', Twist, queue_size = 1)   #cmd_vel配信準備
    rospy.Subscriber('keys', String, keys_callback, twist_pub)  #keysを購読し,コールバック関数を呼び出す.引数はさらに後ろで指定する

    rate = rospy.Rate(10)   #10Hz(100ミリ秒ごと)で出力するため
    last_twist = Twist()    #0に初期化

    if rospy.has_param('~linear_scale'):    #コマンドラインで'linear_scale'というパラメータが指定されているかチェック
        vel_scales[1] = rospy.get_param('~linear_scale')    #指定された値を取得
    else:
        #パラメータが指定されておらず,デフォルト値を使うことを警告する
        rospy.logwarn(f"linear scale not provided; using {vel_scales[1]:.1f}")

    if rospy.has_param('~angular_scale'):   #コマンドラインで'angular_scale'というパラメータが指定されているかチェック
        vel_scales[0] = rospy.get_param('~angular_scale')   #指定された値を取得
    else:
        #パラメータが指定されておらず,デフォルト値を使うことを警告する
        rospy.logwarn(f"angular scale not provided; using {vel_scales[0]:.1f}")

    while not rospy.is_shutdown():
        twist_pub.publish(last_twist)
        rate.sleep()
実行様子

考察

パラメータを指定しないときは「指定されていない」と警告を出すようにしている.しかしながら,実行様子からも分かる通り,一度指定した後,指定せずにコマンドを送ると,その警告は出ず,継続して直前に指定したパラメータが適用されている.ここでは示していないが,一度roscoreだけ閉じて再びパラメータ指定なしでコマンドを送ったところ,警告が出てきてくれた.このことから,パラメータ指定した際,その都度初期化されるのではなく,roscoreのサーバで管理されているということがいえる.

感想

今回は,コマンドラインでのパラメータ指定とその利用方法について学んだ.また,print()の代わりにrospy.logwarn()を使うことで,受け手にとって視覚的に理解しやすい表記方法も学んだ.これには,ほかにもrospy.err()rospy.loginfo()のようなものが存在する.これらは,色を変えて表示することで,多くの表示内容から簡単にエラーメッセージなどを発見させやすくし,さらにタイムスタンプも表示されるということで,print()よりも優れる点がいくつかある.何かユーザに促すメッセージであれば,ぜひ活用していきたいと思う.
今回学んだパラメータ化は,できたらlaunchファイルで指定した方がよさそうである.なぜなら,毎回パラメータ指定をせずとも自動で行ってくれるからである.

今回で,ロボットの最大速度を指定することができるようにはなったが,ここで有限の加速度という物理的な問題を扱う必要が出てくる.これに関しては,次回扱うこととする.

参考文献

プログラミングROS Pythonによるロボットアプリケーション開発
        Morgan Quigley, Brian Gerkey, William D.Smart 著
                       河田 卓志 監訳
            松田 晃一,福地 正樹,由谷 哲夫 訳
                  オイラリー・ジャパン 発行