ROS2編程基礎課程–msg/srv

  • 2019 年 10 月 5 日
  • 筆記

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/ZhangRelay/article/details/100773773

Introduction to msg and srv interfaces

msg和srv介面簡介

INCOMPLETE: this is a draft of an upcoming tutorial for creating and using custom ROS interfaces.

未完成:這是即將發布的用於創建和使用自定義ROS介面的教程的草稿。

Disclaimer: The code provided is to support the explanation, it is likely outdated and should not be expected to compile as is

免責聲明:提供的程式碼是為了支援解釋,它可能已經過時,不應該按原樣編譯

  • msg: msg files are simple text files that describe the fields of a ROS message. They are used to generate source code for messages in different languages.

msg:msg文件是描述ROS消息欄位的簡單文本文件。它們用於生成不同語言的消息的源程式碼。

  • srv: an srv file describes a service. It is composed of two parts: a request and a response. The request and response are message declarations.

srv:srv文件描述服務。它由兩部分組成:請求和響應。請求和響應是消息聲明。

msgs are just simple text files with a field type and field name per line. The field types you can use are:

msgs只是簡單的文本文件,每行有一個欄位類型和欄位名稱。可以使用的欄位類型是:

  • int8, int16, int32, int64 (plus uint*)
  • float32, float64
  • string
  • other msg files
  • variable-length array[], fixed-length array[C], bounded-length array[<=C]

Here is an example of a msg that uses a string primitive, and two other msgs:

這是一個使用字元串和另外兩個消息的msg示例:

string child_frame_id

geometry_msgs/PoseWithCovariance pose

geometry_msgs/TwistWithCovariance twist

srv files are just like msg files, except they contain two parts: a request and a response. The two parts are separated by a 『—』 line. Here is an example of a srv file:

srv文件類似msg文件,但是它們包含兩部分:請求和響應。這兩部分用' – '線分開。

以下是srv文件的示例:

float64 A

float64 B

float64 Sum

In the above example, A and B are the request, and Sum is the response.

在上面的例子中,A和B是請求,Sum是響應。

msg files are stored in the msg directory of a package, and srv files are stored in the srv directory.

msg文件存儲在包的msg目錄中,srv文件存儲在srv目錄中。

These are just simple examples. For more information about how to create msg and srv files please refer to About ROS Interfaces.

這些只是簡單的例子。有關如何創建msg和srv文件的更多資訊,請參考關於ROS介面

Creating a msg package 創建一個msg包

NOTE: only ament_cmake packages can generate messages currently (not ament_python packages).

注意:只有ament_cmake包可以生成當前的消息(不是ament_python包)。

For this tutorial we will use the packages stored in the rosidl_tutorials repository.

在本教程中,將使用存儲在rosidl_tutorials庫中的包。

cd ~/ros2_overlway_ws/src

git clone -b rosidl_tutorials https://github.com/ros2/tutorials.git

cd rosidl_tutorials/rosidl_tutorials_msgs

Creating a msg file 創建一個msg文件

Here we will create a message meant to carry information about an individual.

在這裡,將創建一條消息,用於傳遞有關個人的資訊。

Open msg/Contact.msg and you will see:

打開msg/Contact.msg,會看到:

bool FEMALE=true

bool MALE=false

string first_name

string last_name

bool gender

uint8 age

string address

This message is composed of 5 fields: 此消息由5個欄位組成:

  • first_name: of type string

first_name:類型為字元串

  • last_name: of type string

last_name:類型為字元串

  • gender: of type bool, that can be either MALE or FEMALE

性別:bool類型,可以是男性或女性

  • age: of type uint8

年齡:uint8型

  • address: of type string

地址:字元串類型

There』s one more step, though. We need to make sure that the msg files are turned into source code for C++, Python, and other languages.

不過還有一個步驟。需要確保將msg文件轉換為C ++、Python和其他語言的源程式碼。

Building msg files 編譯msg文件

Open the package.xml, and uncomment these two lines:

打開package.xml,並取消注釋這兩行:

<buildtool_depend>rosidl_default_generators</buildtool_depend>

<exec_depend>rosidl_default_runtime</exec_depend>

Note that at build time, we need 「rosidl_default_generators」, while at runtime, we only need 「rosidl_default_runtime」.

請注意,在編譯時,我們需要「rosidl_default_generators」,而在運行時,我們只需要「rosidl_default_runtime」。

Open the CMakeLists.txt and make sure that the following lines are uncommented.

打開CMakeLists.txt並確保取消以下行的注釋。

Find the package that generates message code from msg/srv files:

找到從msg / srv文件生成消息程式碼的包:

find_package(rosidl_default_generators REQUIRED)

Declare the list of messages you want to generate: 聲明要生成的消息列表:

set(msg_files

"msg/Contact.msg"

)

By adding the .msg files manually, we make sure that CMake knows when it has to reconfigure the project after you add other .msg files.

通過手動添加.msg文件,我們確保CMake知道在添加其他.msg文件後何時必須重新配置項目。

Generate the messages: 生成消息:

rosidl_generate_interfaces(${PROJECT_NAME}

${msg_files}

)

Also make sure you export the message runtime dependency:

還要確保導出消息運行時依賴項:

ament_export_dependencies(rosidl_default_runtime)

Now you』re ready to generate source files from your msg definition.

現在已準備好從msg定義生成源文件。

Creating an srv file 創建srv文件

We will now add a srv declaration to our package. 現在將向包中添加srv聲明。

Open the srv/AddTwoFloats.srv file and paste this srv declaration:

打開srv / AddTwoFloats.srv文件並粘貼此srv聲明:

float64 a

float64 b

float64 sum

Building srv files 編譯srv文件

Declare the service in the CMakeLists.txt:

在CMakeLists.txt以下內容中聲明服務:

set(srv_files

"srv/AddTwoFloats.srv")

Modify the existing call to rosidl_generate_interfaces to generate the service in addition to the messages:

修改現有的rosidl_generate_interfaces調用以生成除消息之外的服務:

rosidl_generate_interfaces(${PROJECT_NAME}

${msg_files}

${srv_files}

)

Using custom messages 使用自定義消息

Using msg/srv from other packages 從其他包使用msg/srv

Let』s write a C++ node using the Contact.msg we created in the previous section.

使用在上一節中創建的Contact.msg編寫一個C ++節點。

Go to the rosidl_tutorials package and open the src/publish_contact.cpp file.

轉到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 ContactnFirst:" << 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;

}

The code explained 程式碼解釋

#include "rosidl_tutorials_msgs/msg/contact.hpp"

Here we include the header of the message that we want to use. 這裡包含想要使用的消息的頭文件。

ContactPublisher(): Node("address_book_publisher")

{

Here we define a node 這裡定義一個節點

auto publish_msg = [this]() -> void {

A publish_msg function to send our message periodically 一個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";

We create a Contact message and populate its fields. 創建一個Contact消息並填充其欄位。

std::cout << "Publishing ContactnFirst:" << msg->first_name <<

" Last:" << msg->last_name << std::endl;

contact_publisher_->publish(msg);

Finally we publish it 最後發布它

timer_ = this->create_wall_timer(1s, publish_msg);

Create a 1second timer to call our publish_msg function every second

創建一個1秒的計時器,每秒調用publish_msg函數

Now let』s build it! 現在來編譯吧!

To use this message we need to declare a dependency on rosidl_tutorials_msgs in the package.xml:

要使用此消息,需要在以下內容中聲明對rosidl_tutorials_msgs的依賴package.xml:

<build_depend>rosidl_tutorials_msgs</build_depend>

<exec_depend>rosidl_tutorials_msgs</exec_depend>

And also in the CMakeLists.txt: 而且在CMakeLists.txt:

find_package(rosidl_tutorials_msgs REQUIRED)

And finally we must declare the message package as a target dependency for the executable.

最後,必須將消息包聲明為可執行文件的目標依賴項。

ament_target_dependencies(publish_contact

"rclcpp"

"rosidl_tutorials_msgs"

)

Using msg/srv from the same package 從使用同一個包msg/srv

While most of the time messages are declared in interface packages, it can be convenient to declare, create and use messages all in the one package.

雖然大多數時候消息是在介麵包中聲明的,但是在一個包中聲明,創建和使用消息都很方便。

We will create a message in our rosidl_tutorials package. Create a msg directory in the rosidl_tutorials package and AddressBook.msg inside that directory. In that msg paste:

在rosidl_tutorials包中創建一條消息。在rosidl_tutorials包中創建一個msg目錄,在該目錄中創建AddressBook.msg。在那個msg粘貼中:

rosidl_tutorials_msgs/Contact[] address_book

As you can see we define a message based on the Contact message we created earlier.

根據之前創建的Contact消息定義消息。

To generate this message we need to declare a dependency on this package in the package.xml:

要生成此消息,需要在以下內容中聲明對此包的依賴package.xml:

<build_depend>rosidl_tutorials_msgs</build_depend>

<exec_depend>rosidl_tutorials_msgs</exec_depend>

And in the CMakeLists.txt: 並在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

)

Now we can start writing code that uses this message. 現在可以開始編寫使用此消息的程式碼。

Open src/publish_address_book.cpp: 打開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;

}

The code explained 程式碼解釋

#include "rosidl_tutorials/msg/address_book.hpp"

We include the header of our newly created AddressBook msg.

包括新創建的AddressBook消息的頭文件。

#include "rosidl_tutorials_msgs/msg/contact.hpp"

Here we include the header of the Contact msg in order to be able to add contacts to our address_book.

這裡包含Contact msg的標題,以便能夠將聯繫人添加到address_book。

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");

We create a node and an AddressBook publisher. 創建一個節點和一個AddressBook發布器。

auto publish_msg = [this]() -> void {

We create a callback to publish the messages periodically

創建一個回調來定期發布消息

auto msg = std::make_shared<rosidl_tutorials::msg::AddressBook>();

We create an AddressBook message instance that we will later publish.

創建一個稍後將發布的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);

}

We create and populate Contact messages and add them to our address_book message.

創建並填充聯繫人消息並將其添加到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);

Finally send the message periodically. 最後定期發送消息。

timer_ = this->create_wall_timer(1s, publish_msg);

Create a 1second timer to call our publish_msg function every second

創建一個1秒的計時器,每秒調用publish_msg函數

Now let』s build it! We need to create a new target for this node in the CMakeLists.txt:

現在來編譯吧!需要在CMakeLists.txt以下位置為此節點創建新目標:

add_executable(publish_address_book

src/publish_address_book.cpp)

ament_target_dependencies(publish_address_book

"rclcpp")

In order to use the messages generated in the same package we need to use the following cmake code:

為了使用在同一個包中生成的消息,需要使用以下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()

This finds the relevant generated C++ code from msg/srv and allows your target to link against them.

這將從msg / srv中找到相關的生成的C ++程式碼,並允許目標鏈接它們。

You may have noticed that this step was not necessary when the interfaces being used were from a package that was built beforehand. This CMake code is only required when you are trying to use interfaces in the same package as that in which they are built.

可能已經注意到,當使用的介面來自事先編譯的包時,此步驟不是必需的。僅當嘗試在與編譯它們的包相同的包中使用介面時,才需要此CMake程式碼。

查看一下turtlesim的msg和srv。

Pose.msg

float32 x

float32 y

float32 theta

float32 linear_velocity

float32 angular_velocity

Spawn.srv

float32 x

float32 y

float32 theta

string name # Optional. A unique name will be created and returned if this is empty

string name

熟練掌握msg、service、srv等命令的使用。