ROSの勉強 第2弾:メッセージの定義


#プログラミング ROS< メッセージの定義 >

はじめに

1つの参考書に沿って,ROS(Robot Operating System)を難なく扱えるようになることが目的である.改めて初めから学び,復習も兼ねながら学習を進めていくこととする.その第2弾として,メッセージの定義を扱う.

環境

仮想環境
ソフト 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

いつ,メッセージの定義を新たに作るべきなのか?

ROSにはもとより,メッセージ(.msg)は存在しているため,基本的には,既存のものを利用すべき.メッセージ間の変換をうまくしてくれるように設計されているからである.独自でそれを組むには労力がかかりすぎる.「絶対にそうしなければならない」という場合のみメッセージは定義すべきのようだ.

メッセージの定義の注意

C++はPythonに比べてより多くのデータ型をもつ.
したがって,C++で書かれたノードとPythonで書かれたノードがデータをやり取りする際に,微妙な問題に発展する可能性がある

新たなメッセージを定義する流れ

メッセージの定ファイル(.msg)は非常に構造が単純である.フィールド(変数)の型とそのフィールド(変数)名を1行ずつ定義するだけである.実際に作成したものを次に示す.

'#'はコメントである.いま,虚数の実部と虚部をfloat32型(浮動小数点)で扱うことを想定している.

このファイル作成後,ビルドシステムに新しいメッセージ定義を伝え,catkin_makeを実行することで,メッセージを使えるようにする.私は参考書に沿って行ったが,その方法は以下の記事でもきれいにまとめられている.

数時間も悩んだエラー

ここで,メッセージを定義する際によく起こりそうで,すぐに解決できなかったエラーを記しておく.
まず,catkin_makeしたときのエラーメッセージを以下に示す.

-- Using these message generators: gencpp;geneus;genlisp;gennodejs;genpy
CMake Error at /home/yuya/catkin_ws/build/topic_lesson/cmake/topic_lesson-genmsg.cmake:3 (message):
  Could not find messages which
  '/home/yuya/catkin_ws/src/topic_lesson/msg/Complex.msg' depends on.  Did
  you forget to specify generate_messages(DEPENDENCIES ...)?

  Cannot locate message [flaot32] in package [topic_lesson] with paths
  [['/home/yuya/catkin_ws/src/topic_lesson/msg']]
Call Stack (most recent call first):
  /opt/ros/noetic/share/genmsg/cmake/genmsg-extras.cmake:307 (include)
  topic_lesson/CMakeLists.txt:71 (generate_messages)


-- Configuring incomplete, errors occurred!
See also "/home/yuya/catkin_ws/build/CMakeFiles/CMakeOutput.log".
See also "/home/yuya/catkin_ws/build/CMakeFiles/CMakeError.log".
make: *** [Makefile:390: cmake_check_build_system] Error 1
Invoking "make cmake_check_build_system" failed

このようなエラーメッセージが表示されたら,「msgファイルにスペルミスなどの誤りがあるかもしれない」これを疑うとよい.実際に先ほど示したmsgファイルにおいて,floatをflaotとしてしまっていただけであった.意外と気づきにくい.https://programmersought.com/article/12273745289/ このサイトを参考に気づくことができた.

もう1つ起きたミスを記しておく.新たなメッセージを定義した後,新たなターミナル(端末)を開いてmessage_publisher.pyを実行し,先ほどから開いていたターミナル(端末)で,message_subscriberを実行すると,後者のみComplexというメッセージが見当たらないと返された.どうやら,新たなメッセージを作成した後,source devel/setup.bashとしないと,反映してくれないようだ.新たなターミナル(端末)ははじめに自動でそれを実行してくれるように設定していたため,このようなことが起きた.ここも気づきにくいところではある.

実装

ようやく,メッセージを定義できたので,前回の復習も兼ねて,実際にその定義したメッセージを利用してみる.配信と購読を2つのプログラムで行った.以下にそのソースコードを示す.

ソースコード(配信者/ publisher)
message_publisher.py
#! /usr/bin/env python3

import rospy

from topic_lesson.msg import Complex    #topic_lessonパッケージのmsgファイルにあるComlex.msgを読み込む

from random import random

rospy.init_node('message_publisher')

pub = rospy.Publisher('complex', Complex, queue_size = 10)

#配信準備完了

rate = rospy.Rate(2)    #2Hzで配信

while not rospy.is_shutdown():
    msg = Complex()     #クラスと同様に,インスタンスを作成することで,個々のフィールドに値を設定できる
    msg.real = random()         #実部に乱数を与える
    msg.imaginary = random()    #虚部に乱数を与える

    pub.publish(msg)
    rate.sleep()
ソースコード(購読者/ subscriber)
message_subscriber.py
#! /usr/bin/env python3

import rospy

from topic_lesson.msg import Complex

def callback(msg):
    R = msg.real
    I = msg.imaginary

    print(f"Real: {R} \t Imaginary: {I}")

rospy.init_node('message_subscriber')

sub = rospy.Subscriber('complex', Complex, callback)

rospy.spin()

message_subscriber.pyを実行したターミナル(端末)の様子を以下に示す.

うまく出力されていることが分かる.

感想

メッセージの定義は理解できた.単純であるがゆえにミスをしたときには気づきにくいと痛感した.できる限り,ROS既存のものを使うほうがよい理由や,C++とPython間での注意も知ることができた.また,定義をして利用することで,裏側ではこのようにして定義されているから,利用時にはこのような形になるのかといった少し細かいところまで,学ぶことができた.

参考文献

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