ROS 2 公式文書(英語) 日本語訳シリーズです。
本ブログの日本語翻訳版のトップページは以下のリンクを参照下さい。
※2019/05/08 現在のものです。
※作成中:これは、カスタム ROS I/F を作成および使用するためのチュートリアルのドラフトです。
※免責事項:提供されているコードは説明上の補助的なものであり、古くなっている可能性があり、そのままコンパイルできない場合もあります。ご了承下さい。
msg : msg ファイルは ROS メッセージのフィールドを記述する単純なテキストファイルです。種々の開発言語のメッセージのソースコードを生成するのに使われます。
srv : srv ファイルはサービスを記述します。リクエストとレスポンスの記述部分によって構成されています。リクエストとレスポンスはメッセージ宣言です。
メッセージは、1 行に 1 つのフィールドタイプと 1 つのフィールド名を持つ単純なテキストファイルです。使用できるフィールドタイプは以下のとおりです。
- int8、int16、int32、int64(および uint*)
- float32、float64
- string
- 他の msg ファイル
- 可変長 配列[]、固定長 配列[C]、有界長 配列[<= C]
これは、文字列プリミティブと他の 2 つのメッセージを使用した msg の例です。
string child_frame_id geometry_msgs/PoseWithCovariance pose geometry_msgs/TwistWithCovariance twist
srv ファイルは msg ファイルと似ていますが、リクエストとレスポンスの 2 つの部分があります。 両者は「-」線で区切られています。次が srv ファイルの例です。
float64 A float64 B --- float64 sum
上記の例では、A と B がリクエストで、Sum がレスポンスです。
msg ファイルはパッケージの msg ディレクトリに格納され、srv ファイルは srv ディレクトリに格納されます。
これらはほんの一例です。 msg
およびsrv
ファイルを作成する方法に関する詳細については About ROS Interfaces を参照してください。
msg パッケージを作成する
注:現在 ament_cmake パッケージのみがメッセージを生成できます(ament_python パッケージは不可)。
このチュートリアルでは rosidl_tutorials リポジトリのパッケージを使います。
cd ~/ros2_overlway_ws/src git clone -b rosidl_tutorials https://github.com/ros2/tutorials.git cd rosidl_tutorials/rosidl_tutorials_msgs
msg ファイルを作成する
ここでは、個人に関する情報を伝えるためのメッセージを作成します。
msg/Contact.msg
を開くと表示されます。
bool FEMALE=true bool MALE=false string first_name string last_name bool gender uint8 age string address
このメッセージは 5 つのフィールドで構成されています。
- first_name : string 型
- last_name : string 型
- gender : bool 型で、MALE でも FEMALE でもかまいません
- age : uint8 型
- address : 文字列型
もう 1 つステップがあります。 msg ファイルが C++、Python、その他の言語のソースコードに変換されていることを確認しましょう。
msg ファイルのビルド
package.xml
を開き、次の2行のコメントを外します。
<buildtool_depend>rosidl_default_generators</buildtool_depend> <exec_depend>rosidl_default_runtime</exec_depend>
注記:ビルド時には "rosidl_default_generators"が必要ですが、実行時には "rosidl_default_runtime"のみが必要です。
CMakeLists.txt
を開き、次の行のコメントが解除されていることを確認してください。
msg/srv ファイルからメッセージコードを生成するパッケージを探します。
find_package(rosidl_default_generators REQUIRED)
生成したいメッセージのリストを宣言します。
set(msg_files "msg/Contact.msg" )
手動で .msg ファイルを追加することによって、他の .msg ファイルを追加した後に CMake がいつプロジェクトを再構成する必要があるかを保証できます。
メッセージの生成は次のように記述します。
rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files} )
また、必ずメッセージのランタイム依存関係をエクスポートしてください。
ament_export_dependencies(rosidl_default_runtime)
これで、msg 定義からソースファイルを生成する準備が整いました。
srv ファイルを作成する
パッケージに srv 宣言を追加します。
srv/AddTwoFloats.srv ファイルを開き、この srv 宣言を貼り付けます。
float64 a
float64 b
---
float64 sum
srv ファイルを構築する
CMakeLists.txt
でサービスを宣言します。
set(srv_files "srv/AddTwoFloats.srv")
上述した rosidl_generate_interfaces を修正し、メッセージに加えてサービスも生成できるようにします。
rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files} ${srv_files} )
カスタムメッセージを使う
他のパッケージから msg/srv を使う
前のセクションで作成した Contact.msg を使用して C++ ノードを書きましょう。
rosidl_tutorials パッケージに移動して、src/publish_contact.cppファイルを開きます。
#include <iostream> #include <memory> #include "rclcpp/rclcpp.hpp" #include "rosidl_tutorials_msgs/msg/contact.hpp" using namespace std::chrono_literals; class ContactPublisher : public rclcpp::Node { public: ContactPublisher() : Node("address_book_publisher") { contact_publisher_ = this->create_publisher<rosidl_tutorials_msgs::msg::Contact>("contact"); auto publish_msg = [this]() -> void { auto msg = std::make_shared<rosidl_tutorials_msgs::msg::Contact>(); msg->first_name = "John"; msg->last_name = "Doe"; msg->age = 30; msg->gender = msg->MALE; msg->address = "unknown"; std::cout << "Publishing Contact\nFirst:" << msg->first_name << " Last:" << msg->last_name << std::endl; contact_publisher_->publish(msg); }; timer_ = this->create_wall_timer(1s, publish_msg); } private: rclcpp::Publisher<rosidl_tutorials_msgs::msg::Contact>::SharedPtr contact_publisher_; rclcpp::timer::TimerBase::SharedPtr timer_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto publisher_node = std::make_shared<ContactPublisher>(); rclcpp::spin(publisher_node); return 0; }
コードの説明
#include "rosidl_tutorials_msgs/msg/contact.hpp"
ここでは、使用したいメッセージのヘッダーをインクルードします。
ContactPublisher()
: Node("address_book_publisher")
{
ここでノードを定義します
auto publish_msg = [this]() -> void {
定期的にメッセージを送信する publish_msg 関数
auto msg = std::make_shared<rosidl_tutorials_msgs::msg::Contact>(); msg->first_name = "John"; msg->last_name = "Doe"; msg->age = 30; msg->gender = msg->MALE; msg->address = "unknown";
連絡先メッセージを作成し、そのフィールドに入力します。
std::cout << "Publishing Contact\nFirst:" << msg->first_name << " Last:" << msg->last_name << std::endl; contact_publisher_->publish(msg);
最後にパブリッシュします。
timer_ = this->create_wall_timer(1s, publish_msg);
毎秒 publish_msg
関数を呼び出すために、1 秒のタイマを作成します。
さぁ、ビルドしましょう!
このメッセージを使用するには、package.xml
でrosidl_tutorials_msgsへの依存関係を宣言する必要があります。
<build_depend>rosidl_tutorials_msgs</build_depend> <exec_depend>rosidl_tutorials_msgs</exec_depend>
そしてまたCMakeLists.txt
の中で:
find_package(rosidl_tutorials_msgs REQUIRED)
そして最後に、メッセージパッケージを実行可能ファイルのターゲット依存関係として宣言する必要があります。
ament_target_dependencies(publish_contact "rclcpp" "rosidl_tutorials_msgs" )
同一パッケージから msg/srv を使う
ほとんどの場合、メッセージはインタフェースパッケージ内で宣言されますが、メッセージを 1 つのパッケージ内で宣言、作成、使用できるのも便利です。
rosidl_tutorials パッケージにメッセージを作成します。 rosidl_tutorials パッケージに msg ディレクトリを作成し、そのディレクトリ内に AddressBook.msg を作成します。その msg に以下の行を記載します:
rosidl_tutorials_msgs/Contact[] address_book
ご覧のとおり、先ほど作成した連絡先メッセージに基づいてメッセージを定義します。
このメッセージを生成するには、package.xml
でこのパッケージへの依存関係を宣言する必要があります。
<build_depend>rosidl_tutorials_msgs</build_depend> <exec_depend>rosidl_tutorials_msgs</exec_depend>
そしてCMakeLists.txt
では以下の通り:
find_package(rosidl_tutorials_msgs REQUIRED) set(msg_files "msg/AddressBook.msg" ) rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files} DEPENDENCIES rosidl_tutorials_msgs )
これで、このメッセージを使うコードを書き始めることができます。
src / publish_address_book.cpp を開きます。
#include <iostream> #include <memory> #include "rclcpp/rclcpp.hpp" #include "rosidl_tutorials/msg/address_book.hpp" #include "rosidl_tutorials_msgs/msg/contact.hpp" using namespace std::chrono_literals; class AddressBookPublisher : public rclcpp::Node { public: AddressBookPublisher() : Node("address_book_publisher") { address_book_publisher_ = this->create_publisher<rosidl_tutorials::msg::AddressBook>("address_book"); auto publish_msg = [this]() -> void { auto msg = std::make_shared<rosidl_tutorials::msg::AddressBook>(); { rosidl_tutorials_msgs::msg::Contact contact; contact.first_name = "John"; contact.last_name = "Doe"; contact.age = 30; contact.gender = contact.MALE; contact.address = "unknown"; msg->address_book.push_back(contact); } { rosidl_tutorials_msgs::msg::Contact contact; contact.first_name = "Jane"; contact.last_name = "Doe"; contact.age = 20; contact.gender = contact.FEMALE; contact.address = "unknown"; msg->address_book.push_back(contact); } std::cout << "Publishing address book:" << std::endl; for (auto contact : msg->address_book) { std::cout << "First:" << contact.first_name << " Last:" << contact.last_name << std::endl; } address_book_publisher_->publish(msg); }; timer_ = this->create_wall_timer(1s, publish_msg); } private: rclcpp::Publisher<rosidl_tutorials::msg::AddressBook>::SharedPtr address_book_publisher_; rclcpp::timer::TimerBase::SharedPtr timer_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto publisher_node = std::make_shared<AddressBookPublisher>(); rclcpp::spin(publisher_node); return 0; }
コードの説明
#include "rosidl_tutorials/msg/address_book.hpp"
新しく作成した AddressBook msg のヘッダを含めます。
#include "rosidl_tutorials_msgs/msg/contact.hpp"
カスタムメッセージ kaddress_book に連絡先を追加できるようにするために Contact msg のヘッダーを含めます。
using namespace std::chrono_literals; class AddressBookPublisher : public rclcpp::Node { public: AddressBookPublisher() : Node("address_book_publisher") { address_book_publisher_ = this->create_publisher<rosidl_tutorials::msg::AddressBook>("address_book");
ノードと AddressBook のパブリッシャを作成します。
auto publish_msg = [this]() -> void {
メッセージを定期的にパブリッシュするためのコールバックを作成します。
auto msg = std::make_shared<rosidl_tutorials::msg::AddressBook>();
AddressBook メッセージインスタンスを作成します。後ほどパブリッシュします。
{ rosidl_tutorials_msgs::msg::Contact contact; contact.first_name = "John"; contact.last_name = "Doe"; contact.age = 30; contact.gender = contact.MALE; contact.address = "unknown"; msg->address_book.push_back(person); } { rosidl_tutorials_msgs::msg::Contact person; contact.first_name = "Jane"; contact.last_name = "Doe"; contact.age = 20; contact.gender = contact.FEMALE; contact.address = "unknown"; msg->address_book.push_back(contact); }
Contact メッセージを作成と値の入力をし、それらを address_book メッセージに追加します。
std::cout << "Publishing address book:" << std::endl; for (auto contact : msg->address_book) { std::cout << "First:" << contact.first_name << " Last:" << contact.last_name << std::endl; } address_book_publisher_->publish(msg);
そして、定期的にメッセージを送ります。
timer_ = this->create_wall_timer(1s, publish_msg);
毎秒publish_msg
関数を呼び出すために 1 秒タイマーを作成します。
それではビルドしましょう!CMakeLists.txt
でこのノードの新しいターゲットを作成します。
add_executable(publish_address_book
src/publish_address_book.cpp
)
ament_target_dependencies(publish_address_book
"rclcpp"
)
同じパッケージで生成されたメッセージを使用するには、以下のcmake コードを使用する必要があります。
get_default_rmw_implementation(rmw_implementation) find_package("${rmw_implementation}" REQUIRED) get_rmw_typesupport(typesupport_impls "${rmw_implementation}" LANGUAGE "cpp") foreach(typesupport_impl ${typesupport_impls}) rosidl_target_interfaces(publish_address_book ${PROJECT_NAME} ${typesupport_impl} ) endforeach()
これは msg/srv から関連する生成コード(C++)を検出し、ターゲットがそれらに対してリンクできるようにします。
使用されているインターフェースが事前にビルドされたパッケージからのものである場合、このステップは不要となります。このCMake コードは、msg/srv がビルドされたパッケージと同一のパッケージ内でインターフェースを使用しようとしているときにのみ必要となります。