前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ROS2编程基础课程--msg/srv

ROS2编程基础课程--msg/srv

作者头像
zhangrelay
发布2019-09-19 14:12:36
1.4K0
发布2019-09-19 14:12:36
举报

版权声明:本文为博主原创文章,遵循 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 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;

}

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 Contact\nFirst:" << 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等命令的使用。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年09月12日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Introduction to msg and srv interfaces
  • msg和srv接口简介
    • Creating a msg package 创建一个msg包
      • Creating a msg file 创建一个msg文件
      • Building msg files 编译msg文件
      • Creating an srv file 创建srv文件
      • Building srv files 编译srv文件
    • Using custom messages 使用自定义消息
      • Using msg/srv from other packages 从其他包使用msg/srv
      • Using msg/srv from the same package 从使用同一个包msg/srv
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档