ros2学习笔记16--详述ros2接口(代码片段)

鸿_H 鸿_H     2022-12-04     711

关键词:

概要:这篇主要进一步介绍ros2接口.

环境:ubuntu20.04,ros2-foxy,vscode

最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.

2.2.8拓展ros2接口(原文:https://docs.ros.org/en/foxy/Tutorials/Single-Package-Define-And-Use-Interface.html

>>教程>>拓展ros2接口

你正阅读的是ros2较老版本(Foxy),但仍然支持的说明文档.想查看最新版本的信息,请看galactic版本链接( https://docs.ros.org/en/galactic/Tutorials.html

详述ros2接口

目标:学习更多的方法来使用自定义ros2接口

课程等级:初级

时长:15min

目录

1.背景
2.预备知识
3.步骤
3.1创建一个包
3.2创建一个msg文件
3.3使用同一个包的同一个接口
3.4测试
3.5(额外的)使用现有的接口定义
4.总结
5.下一步
6.相关内容

1.背景

前面课程中,你学了怎样创建定制化msgsrv接口.

虽然在专用接口包里面声明接口是最佳的,但有时声明,创建和使用接口都在同一包里面(进行)是(更)方便的.

之前说过,接口当前只能定义在CMake包里面.然而,(使用ament_cmake_python的话)pyhon库和节点是有可能在CMake包里面的,所以在一个包里面一起定义python节点和接口是可行的.简单起见,这里使用的是cmake包和c++节点.

课程这里专注于msg类型接口,但是这些步骤适用于所有类型接口的.

2.预备知识

假设在开始本课程前,你温习了前面创建自定义msgsrv文件课程的基础部分.

你应该安装了ros2,有了工作空间,并且懂得包的创建.

老规矩,新开终端别忘了source一下ros2环境变量.

3.步骤

3.1创建一个包

在你的工作空间src目录线,创建一个名为more_interfaces包,也里面创建一个文件夹放msg文件:

ros2 pkg create --build-type ament_cmake more_interfaces
mkdir more_interfaces/msg

3.2创建一个msg文件

more_interfaces/msg基础上,创建新文件AddressBook.msg

复制下面代码去创建一个消息(类型),其表示的是一个人的信息:

bool FEMALE=true
bool MALE=false

string first_name
string last_name
bool gender
uint8 age
string address

信息由5个部分组成:

姓:字符串类型
名:字符串类型
性别:布尔类型,不是男就是女
年龄:unit8类型
地址:字符串类型

注意,有些地方是可以设置默认参数的.看这里(https://docs.ros.org/en/foxy/Concepts/About-ROS-Interfaces.html#interfaceconcept),你可以找到很多方式来自定义接口.

接着,我们需要确认msg文件是可以用到c++,python或者其他语言的源码的.

3.2.1创建一个msg文件

打开文件package.xml,添加一下几行:

<buildtool_depend>rosidl_default_generators</buildtool_depend>

<exec_depend>rosidl_default_runtime</exec_depend>

<member_of_group>rosidl_interface_packages</member_of_group>

注意,编译时候,我们需要rosidl_default_generators;运行时候,我们只需要rosidl_default_runtime

打开CMakeLists.txt,添加下面几行:

找到那个包,可以将msg/srv文件生成消息类型:

find_package(rosidl_default_generators REQUIRED)

声明你想要生成消息类型列表:

set(msg_files
  "msg/AddressBook.msg"
)

手动添加.msg文件,我们确保cmake可以找得到,当你添加了其他.msg文件之后,cmake一定会重置系统配置.

生成消息:

rosidl_generate_interfaces($PROJECT_NAME
  $msg_files
)

也要保证你导出信息运行时的依赖:

ament_export_dependencies(rosidl_default_runtime)

现在,你可以用自定义msg文件来生成一个源文件.当完成上面四步,我们直接跳过编译步骤.

3.2.2(特别)设置多个接口

注意:

你可以使用set来简洁列出接口:

set(msg_files
  "msg/Message1.msg"
  "msg/Message2.msg"
  # etc
  )

set(srv_files
  "srv/Service1.srv"
  "srv/Service2.srv"
   # etc
  )

一次性生成所有列举的,像这样子:

rosidl_generate_interfaces($PROJECT_NAME
  $msg_files
  $srv_files
)

3.3使用同一个包的同一个接口

现在,我们开始使用该消息类型编写代码:

more_interfaces/src目录创建文件publish_address_book.cpp,并且复制下面代码到里面:

#include <chrono>
#include <memory>

#include "rclcpp/rclcpp.hpp"
#include "more_interfaces/msg/address_book.hpp"

using namespace std::chrono_literals;

class AddressBookPublisher : public rclcpp::Node

public:
  AddressBookPublisher()
  : Node("address_book_publisher")
  
    address_book_publisher_ =
      this->create_publisher<more_interfaces::msg::AddressBook>("address_book", 10);

    auto publish_msg = [this]() -> void 
        auto message = more_interfaces::msg::AddressBook();

        message.first_name = "John";
        message.last_name = "Doe";
        message.age = 30;
        message.gender = message.MALE;
        message.address = "unknown";

        std::cout << "Publishing Contact\\nFirst:" << message.first_name <<
          "  Last:" << message.last_name << std::endl;

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

private:
  rclcpp::Publisher<more_interfaces::msg::AddressBook>::SharedPtr address_book_publisher_;
  rclcpp::TimerBase::SharedPtr timer_;
;


int main(int argc, char * argv[])

  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<AddressBookPublisher>());
  rclcpp::shutdown();

  return 0;

3.3.1代码解析

#include "more_interfaces/msg/address_book.hpp"

包含新创建的AddressBook.msg头文件

using namespace std::chrono_literals;

class AddressBookPublisher : public rclcpp::Node

public:
  AddressBookPublisher()
  : Node("address_book_publisher")
  
    address_book_publisher_ =
      this->create_publisher<more_interfaces::msg::AddressBook>("address_book");

创建一个节点和AddressBook发布器:

auto publish_msg = [this]() -> void 

创建一个回调来周期性发布消息:

auto message = more_interfaces::msg::AddressBook();

创建我们后面发布的AddressBook消息:

message.first_name = "John";
message.last_name = "Doe";
message.age = 30;
message.gender = message.MALE;
message.address = "unknown";

输出AddressBook每项内容:

std::cout << "Publishing Contact\\nFirst:" << message.first_name <<
  "  Last:" << message.last_name << std::endl;

this->address_book_publisher_->publish(message);

最后定期发布消息:

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

创建秒的定时器来实现每秒调用一次publish_msg函数.

3.3.2编译发布者

CMakeLists.txt文件中,我们需要为这个节点创建新target:

find_package(rclcpp REQUIRED)

add_executable(publish_address_book
  src/publish_address_book.cpp
)

ament_target_dependencies(publish_address_book
  "rclcpp"
)

install(TARGETS publish_address_book
 DESTINATION lib/$PROJECT_NAME)

3.3.3连接指定的接口

为了在同一个包里面使用生成的消息类型,我们需要使用一下CMake代码:

rosidl_target_interfaces(publish_address_book
  $PROJECT_NAME "rosidl_typesupport_cpp")

这会找到用到AddressBook.msg编写的相关c++代码,让你target针对性连接它.

你可能会发现,当使用的接口是来自别的包里面的,这一步是没有必要的.当我们使用的接口来同一个包(操作包和接口包属于同一个时),这个CMake代码才是必要的.

3.4测试

返回工作空间的根目录,编译这个包:

linux:

cd ~/dev_ws
colcon build --packages-up-to more_interfaces

然后source一下工作空间(环境变量),运行发布器:

linux:

. install/local_setup.bash
ros2 run more_interfaces publish_address_book

我们这次不创建侦听器,但是你可以尝试写一个练习一下(参考 编写简单发布器和侦听器(c++)课程)

3.5(特别)使用现有的接口定义

注意:

你可以将一个现有的定义接口用于新接口定义.例如,我们看这里,一个消息类型为Contact.msg,它就是属于ros2包里面的rosidl_tutorials_msgs,假设它的定义等同于前面自定义的AddressBook.msg接口.

在这种情况下,你已经定义了AddressBook.msg(接口和你节点在同一个包里面)作为类型Contact(接口和操作节点的包不相同),像这样子:

rosidl_tutorials_msgs/Contact[] address_book

为了生成这个消息(类型),你应该在Contact.msg包里面的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)

rosidl_generate_interfaces($PROJECT_NAME
  $msg_files
  DEPENDENCIES rosidl_tutorials_msgs
)

为了能够把contacts加到你的address_book发布器节点里面,你要添加Contact.msg头文件.

#include "rosidl_tutorials_msgs/msg/contact.hpp"

你在回调部分更改一些内容,像这样子:

auto publish_msg = [this]() -> void 
   auto msg = std::make_shared<more_interfaces::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);
 ;

编译和运行更改后(的源码),会表现出所预期的消息类型msg,和上面所定义的消息msg阵列.

4.总结

在本课程,你尝试了不同方式来定义接口,然后(练习了)构建一个接口和其被使用都在同一包进行的方式.

你也应该学习如何使用另外一个接口作为一个类型,package.xml, CMakeLists.txt,#include声明(如何写)来使用(该接口功能)也是很必要的.

5.下一步

下面,你会创建一个简单的ros2包,会学习launch文件里面定制化参数如何设置.当然,你可以选择c++或者python来写它(包源文件).

6.相关内容

这里有几个设计文档(https://design.ros2.org/#interfaces)关于ros2接口和idl(接口定义语言)的.

其他

个人认为重点:

同一包,接口定义和调用的实现,包内文件配置方式;

将其他现有的接口作为新接口时,包内文件配置方式

****************
个人碰到问题:自定义msg是无法生成头文件的,缘由未知;由于前面编译通过不了,后面的使用现有接口定义文件练习这些也是没有实操的.

报错理由是cmake出错了,也就是CMakeLists.txt文件里面下面语句有问题:

rosidl_target_interfaces(publish_address_book
  $PROJECT_NAME "rosidl_typesupport_cpp")

编译报错窗口提示:

CMake Error at /opt/ros/foxy/share/rosidl_cmake/cmake/rosidl_target_interfaces.cmake:40 (message):
  rosidl_target_interfaces() the second argument 'more_interfaces' must be a
  valid target name
Call Stack (most recent call first):
  CMakeLists.txt:31 (rosidl_target_interfaces)

目前还没有解决这个问题,有小伙伴知道的话,麻烦留言告知一下哈,多谢了.
*****************
这课程是在等毕业证那十几天搞的,室友问,现在在线翻译这么强大,为啥还在这里瞎折腾呢?我说,我的目地是好好认真看一下,了解一下,自己折腾,目前是我想到最好的办法来获得最佳效果,即使这翻译有点别扭,哈哈哈.

#####################
不积硅步,无以至千里
好记性不如烂笔头
感觉有点收获的话,麻烦大大们点赞收藏哈

ros2学习笔记18-velodyne16雷达点云在ros2中可视化案例参考(代码片段)

环境:ubuntu20.04,ros-foxy,vscode,velodyne-16背景:练习如何将velodyne-16雷达点云数据在ros2中读取并可视化.折腾了很久,并没有找到类似案例,所以自己总结一下.1.修改电脑ip这里修改自己电脑ip,跟大多数... 查看详情

ros2学习笔记12--创建ros2包(代码片段)

概要:这篇主要介绍如何创建ros2包环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.2.2.2创建自己第一个ros2包(原文:https://docs.ros.org/en/foxy/Tutor... 查看详情

ros2学习笔记25--ros2话题统计编写教程(c++)(代码片段)

概要:这篇内容主要介绍用c++编写能够统计话题的侦听器环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现4.1ros2话题统计教程(C++)(... 查看详情

ros2学习笔记19--探索ros2doctor工具(代码片段)

概要:这篇主要介绍ros2doctor工具环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.2.2.11探索ros2doctor工具(原文:https://docs.ros.org/en/foxy/Tutorials/Get... 查看详情

ros2学习笔记10--使用ros2bag进行录制和回放数据(代码片段)

概要:这篇主要介绍使用ros2bag进行录制和回放数据环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.2.1.10录制和回放数据(原文:https://docs.ros.or... 查看详情

ros2学习笔记28--ros2环境下,多激光雷达启动的launch文件编写样式参考(代码片段)

背景:一次需要读取多个velodyne的雷达点云数据,但是ros2知识缺少,一番折腾终有成果.1.准备velodyne的ros2版本驱动链接:https://github.com/ros-drivers/velodyne/tree/ros22.launch文件编写2.1官方 查看详情

ros2学习笔记29--项目从ros1迁移到ros2的经验参考(代码片段)

环境:ubuntu20.04,ros-foxy(ros2),vscode背景:项目需要,一直折腾把ros1下面的包升级到ros2版本.以下纯属个人查找资料摸索,自我理解所得,有错误的地方,望大佬们不吝赐教.1.消失的句柄ro... 查看详情

ros2学习笔记20--创建一个action(代码片段)

概要:这篇内容主要介绍如何创建一个action环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.3.1创建一个动作(原文:https://docs.ros.org/en/foxy/Tutori... 查看详情

ros2学习笔记11--工作空间的创建(代码片段)

概要:这篇主要介绍工作空间的创建环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.2.2.1创建工作空间(原文:https://docs.ros.org/en/foxy/Tutorials/Wor... 查看详情

ros2学习笔记9--创建一个launch文件(代码片段)

概要:这篇主要介绍launch文件环境:ubuntu20.04,ros2-foxy,vscode最后没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.2.1.9创建一个launch文件(原文:https://docs.ros.org/en/foxy/Tutorials/Launch-Files... 查看详情

ros2学习笔记24--用colcon对包进行编译(代码片段)

概要:这篇内容主要介绍用colcon对包进行编译环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现3.6用colcon对包进行编译(原文:https://docs.ros.org/en/foxy/Tu... 查看详情

ros2学习笔记27--实现自定义内存分配器(代码片段)

概要:这篇内容主要介绍如何实现自定义内存分配器环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现4.3实现自定义内存分配器(原文:https://docs.ros.or... 查看详情

ros2学习笔记17--在类中使用参数(c++)(代码片段)

概要:这篇主要介绍在类中使用参数(C++)环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.2.2.9在类中使用参数(C++)(... 查看详情

ros2学习笔记22--使用launch启动/监听多个node节点(代码片段)

概要:这篇内容主要介绍如何使用launch启动/监听多个node节点环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现3.4使用launch启动/监听多个node节点ÿ... 查看详情

ros2学习笔记23--将多个node节点组合到单一进程中(代码片段)

概要:这篇内容主要介绍将多个node节点组合到单一进程中环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现3.5多个node节点组合到单一进程中(原文:ht... 查看详情

ros2学习笔记26--使用快速dds查找服务作为查找协议(社区贡献)(代码片段)

概要:这篇内容主要介绍使用快速DDS查找服务作为查找协议(社区贡献)环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现4.2使用快速DDS查找服务作为... 查看详情

ros2学习笔记21--编写action服务器和客户端(c++)(代码片段)

概要:这篇内容主要介绍如何使用C++来编写动作服务器和客户端环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.3.2编写动作服务器和客户端(... 查看详情

ros2学习笔记13--编写一个简单的发布器和侦听器(c++)(代码片段)

概要:这篇主要介绍编写发布器和侦听器的简单套路(C++)环境:ubuntu20.04,ros2-foxy,vscode最后如果没有陈述实操过程中碰到问题的话,则表示该章节都可被本人正常复现.2.2.3编写一个简单的发布器和... 查看详情