ROS2(Galactic)初学者教程(下)
使用colcon构建包
背景
colcon是ROS构建工具catkin_make、catkin_make_isolated、catkin_tools和ament_tools的迭代。
有关colcon设计的更多信息,请参阅本文档。
源代码可以在colcon GitHub组织中找到。
先决条件
安装colcon
sudo apt install python3-colcon-common-extensions
基础
ROS工作区是具有特定结构的目录。
通常有一个src子目录。
子目录中是ROS包的源代码所在的位置。
通常,目录以其他方式开始为空。
colcon执行源代码外的构建。
默认情况下,它将创建以下目录作为src目录的对等方:
- build目录将是存储中间文件的位置。
对于每个包,将创建一个子文件夹,例如正在其中调用CMake。 - install目录是每个包的安装位置。
默认情况下,每个包都将安装到单独的子目录中。 - log目录包含关于每个colcon调用的各种日志记录信息。
创建工作区
首先,创建一个目录(ros2_ws)以包含我们的工作区:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
此时,工作区包含一个空目录src
添加一些源
让我们将示例存储库克隆到工作区的src目录中:
git clone https://github.com/ros2/examples src/examples -b galactic
源参考底图
重要的是,我们已经为现有的ROS 2安装采购了环境,该安装将为我们的工作区提供示例包所需的构建依赖项。
这是通过寻找二进制安装或源安装(即另一个colcon工作区)提供的安装脚本来实现的(请参阅安装)。
我们称这种环境为底层。
我们的工作区ros2_ws将覆盖现有的ros2安装。
通常,当您计划迭代少量的包时,建议使用覆盖,而不是将所有包放入同一个工作区。
在工作区的根目录中,运行colcon build
。
由于诸如ament_cmake之类的构建类型不支持devel空间的概念,并且需要安装包,因此colcon支持选项--symlink-install
。
这允许通过更改源空间中的文件(例如Python文件或其他未编译的资源)来更改已安装的文件,以加快迭代速度。
colcon build --symlink-install
运行测试
要对我们刚刚构建的包运行测试,请运行以下命令:
colcon test
寻找环境来源
当colcon成功完成构建后,输出将位于安装目录中。
在使用任何已安装的可执行文件或库之前,需要将它们添加到路径和库路径中。
colcon将在安装目录中生成bash/bat文件,以帮助设置环境。
这些文件将把所有必需的元素添加到路径和库路径中,并提供由包导出的任何bash或shell命令。
. install/setup.bash
尝试演示
有了源环境,我们可以运行colcon构建的可执行文件。
让我们从示例中运行一个订户节点:
ros2 run examples_rclcpp_minimal_subscriber subscriber_member_function
在另一个终端中,让我们运行发布者节点(不要忘记获取安装脚本的源代码):
ros2 run examples_rclcpp_minimal_publisher publisher_member_function
您应该会看到来自发布者和订阅者的消息,其中数字递增。
创建您自己的程序包
colcon使用该软件包。
REP 149中定义的xml规范(也支持格式2)。
colcon支持多种构建类型。
建议的构建类型为ament_cake和ament_python。
还支持纯cmake包。
ament_python构建的一个示例是ament_index_pythen包,其中的设置。
setup.py是建筑的主要入口点。
demo_nodes_cpp等包使用ament_cmake构建类型,并使用cmake作为构建工具。
为了方便起见,您可以使用工具ros2 pkg create
基于模板创建新的包。
设置colcon_cd
命令colcon_cd
允许您快速将shell的当前工作目录更改为包的目录。
例如,colcon_cd some_ros_package
会很快将您带到目录~/ros2_install/src/some_ros_package
。
echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
echo "export _colcon_cd_root=/opt/ros/galactic/" >> ~/.bashrc
根据您安装colcon_cd的方式和工作区的位置,上面的说明可能会有所不同,请参阅文档了解更多详细信息。
要在Linux和macOS中撤消此操作,请找到系统的shell启动脚本并删除附加的source和export命令。
设置colcon选项卡完成
如果安装了colcon-argcomplete 包,则命令colcon支持bash和类似bash的shell的命令完成。
echo "source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash" >> ~/.bashrc
根据您安装colcon的方式和工作区的位置,上面的说明可能会有所不同,请参阅文档了解更多详细信息。
要在Linux和macOS中撤消此操作,请找到系统的shell启动脚本并删除附加的source命令。
提示
- 如果您不想构建特定的包,请在目录中放置一个名为
COLCON_IGNORE
的空文件,它将不会被索引。 - 如果要避免在CMake包中配置和构建测试,可以传递:
--CMake args-DBUILD_TESTING=0
。 - 如果要从包中运行单个特定测试:
colcon test --packages-select YOUR_PKG_NAME --ctest-args -R YOUR_TEST_IN_PKG
创建工作区
背景
工作区是包含ROS 2包的目录。
在使用ROS 2之前,有必要在您计划使用的终端中寻找ROS 2安装工作空间。
这使得您可以在该终端中使用ROS 2的软件包。
您还可以选择寻找一个“覆盖层”,这是一个辅助工作区,您可以在其中添加新的软件包,而不会干扰您正在扩展的现有ROS 2工作区或“底层”。
参考底图必须包含覆盖图中所有文件包的依赖项。
覆盖图中的文件包将覆盖参考底图中的软件包。
也可以有多个参考底图和覆盖层,每个连续覆盖层都使用其父参考底图的包。
先决条件
sudo apt update
sudo apt install git-all
sudo apt install -y python3-rosdep
sudo rosdep init
rosdep update
任务
-
Source ROS 2环境
您的主要ROS 2安装将是本教程的基础。(请记住,衬底不一定是ROS 2的主要安装。)
根据您安装ROS 2的方式(源代码或二进制代码),以及您所在的平台,您的确切源代码命令将有所不同:
source /opt/ros/galactic/setup.bash
如果这些命令对您不起作用,请参阅您遵循的安装指南。 -
创建新目录
最佳实践是为每个新工作区创建一个新目录。
名称无关紧要,但它可以指示工作区的用途。
让我们为“开发工作区”选择目录名ros2_ws:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
另一个最佳实践是将工作区中的任何包放入src目录。
上面的代码在ros2_ws中创建一个src目录,然后导航到该目录。 -
Clone a sample repo
在克隆之前,请确保您仍在ros2_ws/src目录中。
在初学者开发人员教程的其余部分中,您将创建自己的软件包,但目前您将练习使用现有软件包将工作区组合在一起。
您将使用的现有包来自ros_tutorials存储库(repo)。
如果您阅读了“初学者:CLI工具”教程,您将熟悉本报告中的一个软件包turtlesim。
您可以在GitHub上看到repo。
请注意目录列表上方左侧的“Branch”下拉列表。
克隆此repo时,添加-b参数,后跟与ROS2发行版对应的分支。
在ros2_ws/src目录中,为您使用的发行版运行以下命令:
git clone https://github.com/ros/ros_tutorials.git -b galactic-devel
现在ros_tutorials已克隆到您的工作区中。
ros_tutorials存储库包含turtlesim包,我们将在本教程的其余部分使用它。
此存储库中的其他包未生成,因为它们包含COLCON_IGNORE
文件。
现在您已经用一个示例包填充了您的工作区,但它还不是一个功能齐全的工作区。
您需要首先解决依赖关系并构建工作区。 -
解决依赖项
在构建工作区之前,需要解决包依赖项。
您可能已经拥有所有依赖项,但最佳实践是每次克隆时都要检查依赖项。
您不希望构建在长时间等待后因为缺少依赖项而失败。
从工作区的根目录(ros2_ws)运行以下命令:
# cd if you're still in the ``src`` directory with the ``ros_tutorials`` clone
cd ..
rosdep install -i --from-path src --rosdistro galactic -y
如果您从源代码或“fat”归档文件在Linux上安装了ROS2,则需要使用安装说明中的rosdep
命令。
如果您已经拥有所有依赖项,控制台将返回:
#All required rosdeps installed successfully
包在package.xml文件中声明其依赖项。
(您将在下一个教程中了解更多关于包的信息)。
此命令将遍历这些声明并安装缺少的声明。
您可以在另一个教程(即将推出)中了解更多关于rosdep
的信息。 -
使用
colcon
构建工作区
现在,您可以从工作区的根目录(ros2_ws)使用以下命令构建包:
colcon build
控制台将返回以下消息:
colcon build
其他有用参数:
--packages-up-to
构建所需的包及其所有依赖项,但不是整个工作区(节省时间)--symlink-install
避免了每次调整python脚本时都需要重新构建--event-handlers console_direct+
显示构建时的控制台输出(可以在日志目录中找到)
构建完成后,在工作区根目录(~/ros2_ws)中输入ls,您将看到colcon创建了新目录:
build install log src
安装目录是工作区的设置文件所在的位置,您可以使用它来获取覆盖图。
- Source the overlay
在寻找覆盖层之前,打开一个新的终端(与构建工作区的终端分开)非常重要。在您构建的同一终端中获取覆盖层,或者同样在获取覆盖层的地方构建覆盖层,可能会产生复杂的问题。
在新终端中,将您的主要ROS 2环境作为“底层”,这样您就可以在其上构建覆盖层:
source /opt/ros/galactic/setup.bash
进入工作区的根目录:
cd ~/ros2_ws
在根目录中,source your overlay
:
. install/local_setup.bash
获取覆盖层的local_setup只会将覆盖层中可用的包添加到您的环境中。
安装程序会获取覆盖图以及创建覆盖图的参考底图,从而允许您使用这两个工作空间。
因此,像您刚才所做的那样,先获取您的主ROS 2安装的设置,然后再获取ros2_ws覆盖的local_setup,这与仅获取ros2_ws的设置相同,因为这包括创建它的参考底图的环境。
现在,您可以从覆盖图中运行turtlesim软件包:
ros2 run turtlesim turtlesim_node
但是你怎么知道这是覆盖的turtlesim在运行,而不是你的主安装的turtlesim?
让我们修改覆盖图中的turtlesim,以便您可以看到效果:
可以单独在参考底图中修改和重建覆盖中的包。
覆盖优先于参考底图。
- 修改overlay
您可以通过编辑乌龟窗口上的标题栏来修改overlay中的乌龟。
为此,请找到turtle_frame。
~/ros2_ws/src/rostutorials/turtlesim/src中的cpp文件。
与您首选的文本编辑器打开turtle_frame.cpp。
在第52行,您将看到函数setWindowTitle(“TurtleSim”)
;。
将值“TurtleSim”更改为“MyTurtleSim”,然后保存文件。
返回之前运行colcon build的第一个终端,然后再次运行它。
返回第二个终端(overlay的来源地),再次运行turtlesim:
ros2 run turtlesim turtlesim_node
现在,您将看到TurtleSim窗口上的标题栏显示“MyTurtleSim”。
尽管您的主要ROS 2环境是在此终端中较早获得的,但ros2_ws环境的覆盖优先于参考底图的内容。
要查看您的衬底是否完好无损,请打开一个全新的终端,并仅查找ROS 2安装。
再次运行turtlesim:
ros2 run turtlesim turtlesim_node
可以看到,覆盖中的修改实际上并未影响参考底图中的任何内容。
总结
在本教程中,您将主要的ROS2发行版安装作为底层,并通过在新的工作区中克隆和构建软件包来创建覆盖层。
覆盖将添加到路径的前面,并优先于参考底图,正如您在修改后的turtlesim中看到的那样。
建议在处理少量包时使用覆盖,这样您就不必将所有内容都放在同一个工作区中,也不必在每次迭代时重建一个巨大的工作区。
创建程序包
背景信息
- 什么是ROS 2程序包?
程序包可以被视为您的ROS 2代码的容器。
如果您希望能够安装代码或与他人共享代码,那么您需要将其组织在一个包中。
有了程序包,您可以发布您的ROS2作品,并允许其他人轻松构建和使用它。
ROS2中的包创建使用ament作为其构建系统,colcon作为其构建工具。
您可以使用官方支持的CMake或Python创建包,尽管存在其他构建类型。 - ROS 2程序包由什么组成?
ROS 2 Python和CMake包都有自己的最低要求内容:- package.xml包含有关包的元信息的文件
- CMakeLists.txt描述如何在包中构建代码的文件
- 工作区中的包
一个工作区可以包含任意多个文件包,每个文件包位于各自的文件夹中。
您还可以在一个工作区中拥有不同构建类型的包(CMake、Python等)。
不能有嵌套包。
最佳实践是在工作区中有一个src文件夹,并在其中创建包。
这样可以保持工作区的顶层“干净”。
任务
-
创建包
首先,获取ROS 2安装的源代码。
让我们将在上一教程中创建的工作区ros2_ws用于您的新软件包。
在运行包创建命令之前,请确保您位于src文件夹中。
cd ~/ros2_ws/src
在ROS 2中创建新包的命令语法为:
ros2 pkg create --build-type ament_cmake <package_name>
对于本教程,您将使用可选参数--node-name
,它在包中创建一个简单的Hello World
类型的可执行文件。
在终端中输入以下命令:
ros2 pkg create --build-type ament_cmake --node-name my_node my_package
现在,您将在工作区的src目录中有一个名为my_package的新文件夹。
运行命令后,终端将返回消息:
您可以看到为新包自动生成的文件。 -
构建包
将包放在工作区中特别有价值,因为您可以通过在工作区根目录中运行colcon build
一次构建多个包。
否则,您必须单独构建每个包。
返回工作区的根目录:
cd ~/ros2_ws
现在,您可以构建包:
colcon build
回想一下上一篇教程,您在ros2_ws中也有ros_tutorials包。
您可能已经注意到,运行colcon build
也构建了turtlesim包。
当您的工作区中只有几个包时,这很好,但当有许多包时,colcon build
可能需要很长时间。
要下次只构建my_package包,可以运行:
colcon build --packages-select my_package
-
获取安装文件
要使用您的新软件包和可执行文件,请首先打开一个新终端并获取您的主要ROS 2安装源代码。
然后,从ros2_ws目录中运行以下命令以获取工作区的源代码:
. install/local_setup.bash
现在您的工作区已经添加到路径中,您将能够使用新包的可执行文件。 -
使用包装
要在包创建期间运行使用--node-name
参数创建的可执行文件,请输入以下命令:
ros2 run my_package my_node
这将向您的终端返回一条消息:
hello world my_package package
-
检查包装内容
在ros2_ws/src/my_package中,您将看到ros2 pkg create
自动创建的文件和文件夹:
CMakeLists.txt include package.xml src
my_node.cpp位于src目录中。
这是所有定制C++节点将来都要用到的地方。 -
自定义package.xml
您可能在创建包后的返回消息中注意到,字段描述和许可证包含TODO注释。
这是因为软件包描述和许可证声明不是自动设置的,但如果您想要发布软件包,则必须设置。
可能还需要填写maintainer字段。
从ros2_ws/src/my_package打开package.xml。
使用首选文本编辑器:
如果尚未自动为您填写姓名和电子邮件,请在maintainer行中输入您的姓名和电子邮件。
然后,编辑描述行以汇总文件包:
<description>Beginner client libraries tutorials practice package</description>
然后更新许可证行。
您可以在此处阅读有关开放源码许可证的更多信息。
由于此软件包仅用于实践,因此可以安全地使用任何许可证。
我们使用Apache License 2.0:
<license>Apache License 2.0</license>
编辑完成后,不要忘记保存。
在许可证标签下面,您将看到一些以_depend
结尾的标签名称。
这就是你的package.xml将列出它对其他包的依赖关系,供colcon搜索。
my_package很简单,没有任何依赖项,但您将在即将到来的教程中看到这个空间正在被利用。
总结
您已经创建了一个包来组织您的代码并使其易于他人使用。
您的包自动填充了必要的文件,然后您使用colcon
构建它,以便可以在本地环境中使用它的可执行文件。
编写简单的发布者和订阅者(C++)
背景
节点是通过ROS图通信的可执行进程。
在本教程中,节点将通过主题以字符串消息的形式相互传递信息。
这里使用的示例是一个简单的“说话者”和“听者”系统;一个节点发布数据,另一个节点订阅主题,以便接收该数据。
这些示例中使用的代码可以在此处找到。
任务
-
创建包
打开一个新终端并获取ROS 2安装的源代码,以便ros2命令可以工作。
导航到上一教程中创建的ros2_ws目录。
回想一下,包应该在src目录中创建,而不是在工作区的根目录中创建。
因此,导航到ros2_ws/src,然后运行包创建命令:
ros2 pkg create --build-type ament_cmake cpp_pubsub
您的终端将返回一条消息,验证您的包cpp_pubsub及其所有必要的文件和文件夹的创建。
导航到ros2_ws/src/cpp_pubsub/src。
回想一下,这是任何CMake包中包含可执行文件的源文件所属的目录。 -
编写发布者节点
输入以下命令下载示例通话器代码:
wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/galactic/rclcpp/topics/minimal_publisher/member_function.cpp
现在将有一个名为publisher_member_function.cpp的新文件。
使用首选文本编辑器打开文件。
-
检验代码
代码顶部包括您将要使用的标准C++头文件。
标准C++标头之后是rclcpp/rclcpp.hpp
包括允许您使用ROS 2系统中最常见的部件。
最后一个是std_msgs/msg/string.hpp
,其中包括用于发布数据的内置消息类型。
这些行表示节点的依赖关系。
回想一下,依赖项必须添加到包中。
package.xml和CMakeLists.txt,这将在下一节中完成。
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
下一行通过继承rclcpp::node
创建节点类MinimalPublisher
。
代码中的每个this
都是指节点。
class MinimalPublisher : public rclcpp::Node
公共构造函数将节点命名为minimal_publisher
,并将count_
初始化为0。
在构造函数中,发布者使用String
消息类型、主题名称主题和所需的队列大小进行初始化,以在备份时限制消息。
接下来,初始化timer_
,这将导致timer_callback
函数每秒执行两次。
public:
MinimalPublisher()
: Node("minimal_publisher"), count_(0)
{
publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
timer_ = this->create_wall_timer(
500ms, std::bind(&MinimalPublisher::timer_callback, this));
}
timer_callback
函数是设置消息数据和实际发布消息的地方。
RCLCPP_INFO
宏确保将每个发布的消息打印到控制台。
private:
void timer_callback()
{
auto message = std_msgs::msg::String();
message.data = "Hello, world! " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}
最后是计时器、发布者和计数器字段的声明。
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
MinimalPublisher
类后面是main
,节点实际上在这里执行。
rclcpp::init
初始化ROS2,rclcpp::spin
开始处理来自节点的数据,包括来自计时器的回调。
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}
-
添加依赖项
导航一级回到ros2_ws/src/cpp_pubsub目录,CMakeLists.txt
和package.xml
文件已为您创建。
文本编辑器打开package.xml。
如前一教程中所述,请确保填写<description>
、<maintainer>
和<license>
标记:
<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
在<buildtool_depend>ament_cmake</buildtool_depend>
后添加新行,并粘贴与节点的include
语句相对应的以下依赖项:
<depend>rclcpp</depend>
<depend>std_msgs</depend>
这声明包在执行代码时需要rclcpp
和std_msgs
。
确保保存文件。 -
CMakeLists.txt
现在打开CMakeLists.txt文件。
在现有依赖项find_package(ament_cmake REQUIRED)
下,添加以下行:
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
然后,添加可执行文件并将其命名为talker,以便可以使用ros2 run
运行节点:
add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
最后,添加install(TARGETS…)
部分,以便ros2 run
可以找到您的可执行文件:
install(TARGETS
talker
DESTINATION lib/${PROJECT_NAME})
您可以清理CMakeLists.txt
中删除一些不必要的部分和注释,因此它看起来像这样:
您现在可以构建包,获取本地设置文件并运行它,但是让我们先创建订户节点,这样您就可以看到整个系统正在运行。
-
-
编写订阅者节点
返回到ros2_ws/src/cpp_pubsub/src以创建下一个节点。
在终端中输入以下代码:
wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/galactic/rclcpp/topics/minimal_subscriber/member_function.cpp
使用文本编辑器打开subscriber_member_function.cpp。
-
检验代码
订阅者节点的代码与发布者的代码几乎相同。
现在,节点名为minimal_subscriber,构造函数使用节点的create_subscription
类来执行回调。
没有计时器,因为只要数据发布到topic
主题,订阅者就会简单地响应。
public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
}
回想一下主题教程,发布者和订阅者使用的主题名称和消息类型必须匹配才能进行通信。
topic_callback
函数接收通过主题发布的字符串消息数据,并使用RCLCPP_INFO
宏将其写入控制台。
此类中唯一的字段声明是订阅。
private:
void topic_callback(const std_msgs::msg::String & msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
main
函数完全相同,只是现在它旋转MinimalSubscriber
节点。
对于发布者节点来说,旋转意味着启动计时器,但对于订阅者来说,这仅仅意味着随时准备接收消息。
由于此节点与发布者节点具有相同的依赖关系,因此没有新内容可添加到package.xml。 -
CMakeLists.txt
重新打开CMakeLists.txt,并在发布者的条目下面添加订阅者节点的可执行文件和目标。
add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs)
``
install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})
确保保存文件,然后您的发布/订阅系统就可以使用了。
-
-
构建并运行
您可能已经在ROS 2系统中安装了rclcpp和std_msgs包。
在构建之前,最好在工作区的根目录(ros2_ws)中运行rosdep
以检查缺少的依赖项:
rosdep install -i --from-path src --rosdistro galactic -y
仍然在工作区的根目录ros2_ws中,构建新包:
colcon build --packages-select cpp_pubsub
打开一个新终端,导航到ros2_ws,然后查找安装文件:
. install/setup.bash
现在运行通话器节点:
ros2 run cpp_pubsub talker
终端应该每隔0.5秒发布一次信息消息,如下所示:
打开另一个终端,再次从ros2_ws中获取设置文件,然后启动侦听器节点:
ros2 run cpp_pubsub listener
侦听器将开始将消息打印到控制台,从发布者当时的消息计数开始,如下所示:
在每个终端中输入Ctrl+C
以停止节点旋转。
总结
您创建了两个节点来发布和订阅主题上的数据。
在编译和运行它们之前,您将它们的依赖项和可执行文件添加到包配置文件中。
编写简单的服务和客户端(C++)
背景
当节点使用服务进行通信时,发送数据请求的节点称为客户端节点,响应请求的节点为服务节点。
请求和响应的结构由.srv文件决定。
这里使用的示例是一个简单的整数加法系统;一个节点请求两个整数的和,另一个节点响应结果。
任务
-
创建包
打开一个新的终端并获取ros2安装的源代码,以便ros2命令可以工作。
导航到上一教程中创建的ros2_ws目录。
回想一下,包应该在src目录中创建,而不是在工作区的根目录中创建。导航到ros2_ws/src并创建新包:
ros2 pkg create --build-type ament_cmake cpp_srvcli --dependencies rclcpp example_interfaces
您的终端将返回一条消息,验证您的包cpp_srvcli及其所有必要的文件和文件夹的创建。
--dependencies
参数将自动向package.xml和CMakeLists.txt添加必要的依赖行。
example_interfaces
是包含.srv文件的包,您需要该文件来构造请求和响应:
int64 a
int64 b
---
int64 sum
前两行是请求的参数,破折号下面是响应。- 更新package.xml
因为您在包创建期间使用了--dependencies
选项,所以不必手动向package.xml或CMakeLists.txt添加依赖项。
不过,与往常一样,确保将描述、维护者电子邮件和姓名以及许可证信息添加到package.xml中。
- 更新package.xml
-
编写服务节点
在ros2_ws/src/cpp_srvcli/src目录中,创建一个名为add_two_ints_server.cpp的新文件并粘贴以下代码:
-
检验代码
前两个#include
语句是包依赖项。
add
函数将请求中的两个整数相加,并向响应提供总和,同时使用日志通知控制台其状态。
主功能逐行完成以下任务:- 初始化ROS 2 C++客户端库
- 创建名为
add_two_ints_server
的节点 - 为该节点创建名为add_two_ints的服务,并使用
&add
方法在网络上自动公布该服务 - 准备好后打印日志消息
- 旋转节点,使服务可用
-
添加可执行文件
add_executable
宏生成一个可执行文件,可以使用ros2 run
运行。
将以下代码块添加到CMakeLists.txt创建一个名为server的可执行文件:
add_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(server
rclcpp example_interfaces)
所以ros2 run
可以找到可执行文件,在文件末尾添加以下行,就在ament_package()
之前:
install(TARGETS
server
DESTINATION lib/${PROJECT_NAME})
您现在可以构建您的包,获取本地设置文件,然后运行它,但是让我们先创建客户端节点,这样您就可以看到整个系统在工作。
-
-
编写客户端节点
在ros2_ws/src/cpp_srvcli/src目录中,创建一个名为add_two_ints_client.cpp的新文件并粘贴以下代码:
- 检查代码
与服务节点类似,以下代码行创建节点,然后为该节点创建客户端:
接下来,创建请求。它的结构由前面提到的.srv文件定义。
while
循环给客户端1秒的时间来搜索网络中的服务节点。
如果找不到,它将继续等待。
如果客户端被取消(例如,在终端中输入Ctrl+C),它将返回一条错误日志消息,说明它被中断。
然后客户端发送请求,节点旋转直到收到响应,或者失败。 - 添加可执行文件
返回CMakeLists.txt为新节点添加可执行文件和目标。
从自动生成的文件中删除一些不必要的样板文件后,您的CMakeLists.txt应该如下所示:
- 检查代码
-
构建并运行
在构建之前,最好在工作区的根目录(ros2_ws)中运行rosdep
以检查缺少的依赖项:
rosdep install -i --from-path src --rosdistro galactic -y
导航回工作区的根目录ros2_ws,并构建新包:
colcon build --packages-select cpp_srvcli
打开一个新终端,导航到ros2_ws,并获取设置文件:
. install/setup.bash
现在运行服务节点:
ros2 run cpp_srvcli server
终端应返回以下消息,然后等待:
[INFO] [rclcpp]: Ready to add two ints.
打开另一个终端,再次从ros2_ws中获取设置文件。
启动客户端节点,后跟空格分隔的任意两个整数:
ros2 run cpp_srvcli client 2 3
例如,如果选择2和3,客户端将收到如下响应:
[INFO] [rclcpp]: Sum: 5
返回运行服务节点的终端。
您将看到它在收到请求和收到的数据时发布了日志消息,并返回了响应:
[INFO] [rclcpp]: Incoming request
a: 2 b: 3
[INFO] [rclcpp]: sending back response: [5]
在服务器终端中输入Ctrl+C
以停止节点旋转。
总结
您创建了两个节点,用于通过服务请求和响应数据。
您将它们的依赖项和可执行文件添加到包配置文件中,以便可以构建和运行它们,并查看正在运行的服务/客户端系统。
创建自定义msg和srv文件
背景
在前面的教程中,您使用了消息和服务接口来了解主题、服务以及简单的发布者/订阅者(C++/Python)和服务/客户端(C++/Python)节点。
在这些情况下,您使用的接口是预定义的。
虽然使用预定义的接口定义是一种良好的实践,但有时您可能还需要定义自己的消息和服务。
本教程将向您介绍创建自定义接口定义的最简单方法。
任务
-
创建新包
对于本教程,您将在自己的包中创建自定义.msg和.srv文件,然后在单独的包中使用它们。
这两个包应该在同一个工作区中。
由于我们将使用先前教程中创建的pub/sub和service/client包,请确保您与这些包(ros2_ws/src)位于同一工作区中,然后运行以下命令创建新包:
ros2 pkg create --build-type ament_cmake tutorial_interfaces
tutorial_interfaces是新包的名称。
请注意,它是一个CMake包;目前无法在纯Python包中生成.msg或.srv文件。
您可以在CMake包中创建自定义接口,然后在Python节点中使用它,这将在最后一节中介绍。
最好将.msg和.srv文件保存在包中各自的目录中。
在ros2_ws/src/tutorial_interfaces中创建目录:
mkdir msg
mkdir srv
-
创建自定义定义
-
msg定义
在刚刚创建的tutorial_interfaces/msg目录中,创建一个名为Num.msg的新文件,其中有一行代码声明其数据结构:
int64 num
这是一条自定义消息,它传输一个名为num
的64位整数。
同样在您刚刚创建的tutorial_interfaces/msg
目录中,创建一个名为Sphere.msg的新文件。邮件内容如下:
geometry_msgs/Point center
float64 radius
此自定义消息使用另一个消息包(在本例中为geometry_msgs/Point)中的消息。 -
srv定义
回到刚才创建的tutorial_interfaces/srv目录,创建一个名为AddThreeInts.srv的新文件。
具有以下请求和响应结构:
int64 a
int64 b
int64 c
---
int64 sum
这是您的定制服务,它请求三个名为a、b和c的整数,并用一个名为sum的整数进行响应。
-
-
CMakeLists.txt
要将您定义的接口转换为特定于语言的代码(如C++和Python),以便在这些语言中使用,请在CMakeLists.txt中添加以下行:
find_package(geometry_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Num.msg"
"msg/Sphere.msg"
"srv/AddThreeInts.srv"
DEPENDENCIES geometry_msgs # Add packages that above messages depend on, in this case geometry_msgs for Sphere.msg
)
-
package.xml
因为接口依赖于rosidl_default_generator
来生成特定于语言的代码,所以需要声明对它的依赖关系。
<exec_depend>
标记用于指定运行时或执行阶段依赖项,rosidl_interface_packages
是包所属的依赖项组的名称,使用<member_of_group>
标记声明。
将以下行添加到package.xml -
构建tutorial_interfaces包
既然自定义接口包的所有部分都已就位,就可以构建包了。
在工作区的根目录(~/ros2_ws)中,运行以下命令:
colcon build --packages-select tutorial_interfaces
现在,其他ROS 2包可以发现这些接口。 -
确认msg和srv创建
在新终端中,从工作区(ros2_ws)中运行以下命令以获取源代码:
. install/setup.bash
现在,您可以使用ros2 interface show
命令确认您的界面创建工作正常:
ros2 interface show tutorial_interfaces/msg/Num
应返回:
int64 num
然后
ros2 interface show tutorial_interfaces/msg/Sphere
应返回:
geometry_msgs/Point center
float64 x然后
ros2 interface show tutorial_interfaces/srv/AddThreeInts应返回:
int64 a -
测试新接口
对于此步骤,您可以使用在先前教程中创建的包。
对节点、CMakeLists和package文件进行一些简单的修改将允许您使用新的接口。-
使用pub/sub测试Num.msg
对上一个教程(C++或Python)中创建的发布者/订阅者包稍作修改后,您可以看到Num.msg正在运行。
由于您要将标准字符串msg更改为数字字符串,因此输出将略有不同。
发布者:
订阅者:
CMakeLists.txt:
添加以下行(仅限C++):
package.xml:
添加以下行:
<depend>tutorial_interfaces</depend>
进行上述编辑并保存所有更改后,构建包:
colcon build --packages-select cpp_pubsub
然后打开两个新终端,在每个终端中源ros2_ws,并运行:
ros2 run cpp_pubsub talker
ros2 run cpp_pubsub listener
由于Num.msg只转发一个整数,所以发布者应该只发布整数值,而不是之前发布的字符串: -
用服务/客户端测试AddThreeInts.srv
对上一个教程(C++或Python)中创建的服务/客户机包稍作修改,就可以看到AddThreeInt.srv正在运行。
由于要将原来的两整数请求srv更改为三整数请求srv,因此输出将略有不同。
服务:
客户端:
CMakeLists.txt:
添加以下行(仅限C++):
package.xml:
添加以下行:
<depend>tutorial_interfaces</depend>
进行上述编辑并保存所有更改后,构建包:
colcon build --packages-select cpp_srvcli
然后打开两个新终端,在每个终端中源ros2_ws,并运行:
ros2 run cpp_srvcli server
ros2 run cpp_srvcli client 2 3 1
-
总结
在本教程中,您学习了如何在自己的包中创建自定义接口,以及如何在其他包中使用这些接口。
这是一种简单的界面创建和利用方法。
您可以在此处了解更多关于接口的信息。
实现自定义接口
背景
在上一个教程中,您学习了如何创建自定义msg和srv接口。
虽然最佳实践是在专用接口包中声明接口,但有时在一个包中声明、创建和使用接口会很方便。
回想一下,接口目前只能在CMake包中定义。
但是,在CMake包中可以有Python库和节点(使用ament_CMake_Python),因此可以在一个包中一起定义接口和Python节点。
为了简单起见,我们将在这里使用CMake包和C++节点。
本教程将重点介绍msg接口类型,但这里的步骤适用于所有接口类型。
任务
-
创建包
在工作区src目录中,创建一个包more_interfaces,并在其中创建一个msg文件文件夹:
ros2 pkg create --build-type ament_cmake more_interfaces
mkdir more_interfaces/msg
-
创建消息文件
在more_interfaces/msg中,创建一个新文件AddressBook.msg
粘贴以下代码以创建用于携带个人信息的消息:
此消息由5个字段组成:
first_name:字符串类型
last_name:字符串类型
gender:bool类型,可以是MALE或FEMALE
age:类型uint8
address:字符串类型
请注意,可以为消息定义中的字段设置默认值。
有关自定义接口的更多方式,请参阅关于ROS 2接口。
接下来,我们需要确保msg文件被转换为C++、Python和其他语言的源代码。- 构建消息文件
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_generator
,而在运行时,我们只需要rosild_default_runtime
。
打开CMakeLists.txt并添加以下行:
查找从msg/srv文件生成消息代码的包:
find_package(rosidl_default_generators REQUIRED)
声明要生成的消息列表:
set(msg_files "msg/AddressBook.msg")
通过手动添加.msg文件,我们确保CMake在添加其他.msg后知道何时需要重新配置项目。
生成消息:
rosidl_generate_interfaces(${PROJECT_NAME} ${msg_files})
还要确保导出消息运行时依赖项:
ament_export_dependencies(rosidl_default_runtime)
现在,您可以根据您的消息定义生成源文件了。
现在我们将跳过编译步骤,因为我们将在下面的步骤4中一起完成。
2.(额外)设置多个接口您可以使用set整齐地列出所有接口
并立即生成所有列表,如下所示
- 构建消息文件
-
使用来自同一包的接口
现在我们可以开始编写使用此消息的代码了。
在more_interfaces/src中,创建一个名为publish_address_book.cpp的文件并粘贴以下代码:
- 代码说明
包括我们新创建的AddressBook.msg的标题。
创建节点和AddressBook发布者。
创建回调以定期发布消息。
创建一个稍后将发布的AddressBook消息实例。
填充AddressBook字段。
最后定期发送消息。
创建一个1秒计时器,每秒调用publish_msg函数。 - 构建发布者
我们需要在CMakeLists.txt中为此节点创建一个新目标: - 针对接口的链接
为了使用同一包中生成的消息,我们需要使用以下CMake代码:
这将从AddressBook.msg中找到相关生成的C++代码。
并允许你的目标链接到它。
您可能已经注意到,当使用的接口来自单独构建的包时,此步骤是不必要的。
此CMake代码仅当您希望在与使用它们的包相同的包中使用接口时才需要。
- 代码说明
-
尝试一下
返回工作区的根目录以生成包:
cd ~/ros2_ws
colcon build --packages-up-to more_interfaces
然后获取工作区并运行发布服务器:
. install/local_setup.bash
ros2 run more_interfaces publish_address_book
您应该看到发布者转发您定义的消息,包括您在publish_address_book.cpp中设置的值。
要确认消息正在address_book主题上发布,请打开另一个终端,找到工作区,然后调用主题echo
:
. install/setup.bash
ros2 topic echo /address_book
在本教程中,我们不会创建订阅者,但您可以尝试自己编写一个订阅者以供练习(使用编写简单的发布者和订阅者(C++)来帮助)。
5.(额外)使用现有接口定义
总结
在本教程中,您尝试了不同的字段类型来定义接口,然后在使用它的同一个包中构建了一个接口。
您还学习了如何使用另一个接口作为字段类型以及package.xml、CMakeLists.txt和#include语句。
在类(C++)中使用参数
背景
在创建自己的节点时,有时需要添加可以从启动文件中设置的参数。
本教程将向您展示如何在C++类中创建这些参数,以及如何在启动文件中设置这些参数。
任务
- 创建包
打开一个新的终端并获取ros2安装的源代码,以便ros2命令可以工作。
按照以下说明创建一个名为ros2_ws的新工作区。
回想一下,包应该在src目录中创建,而不是在工作区的根目录中创建。
导航到ros2_ws/src并创建新包:
ros2 pkg create --build-type ament_cmake cpp_parameters --dependencies rclcpp
您的终端将返回一条消息,验证包cpp_parameters及其所有必要的文件和文件夹的创建。
--dependencies
参数将自动向package.xml和CMakeLists.txt添加必要的依赖行。。- 更新package.xml
因为您在包创建期间使用了--dependencies
选项,所以不必手动向package.xml和CMakeLists.txt添加依赖项。
不过,与往常一样,确保将描述、维护者电子邮件和姓名以及许可证信息添加到package.xml中。
- 更新package.xml
- 编写C++节点
在ros2_ws/src/cpp_parameters/src目录中,创建一个名为cpp_paramenters_node.cpp的新文件并粘贴以下代码:
- 检验代码
顶部的#include
语句是包依赖项。
下一段代码将创建类和构造函数。
此构造函数的第一行创建了一个名为my_parameter
的参数,默认值为world
。
参数类型是根据默认值推断的,因此在本例中,它将被设置为字符串类型。
接下来,以1000ms的周期初始化timer_
,这将导致timer_callback
函数每秒执行一次。
timer_callback
函数的第一行从节点获取参数my_parameter
,并将其存储在my_param
中。
接下来,RCLCPP_INFO
函数确保记录消息。
然后,set_parameters
函数将参数my_parameter
设置回默认字符串值world
。
如果用户在外部更改了参数,这将确保始终将其重置为原始值。
最后是timer_
的声明
遵循我们的MinimalParam
是我们的主要目标。
在这里,ROS2被初始化,MinimalParam
类的实例被构造,rclcpp::spin
开始处理来自节点的数据。 - 添加可执行文件
现在打开CMakeLists.txt文件。
在依赖项find_package(rclcpp REQUIRED)
下面添加以下代码行。
add_executable(minimal_param_node src/cpp_parameters_node.cpp)
ament_target_dependencies(minimal_param_node rclcpp)
install(TARGETS minimal_param_node DESTINATION lib/${PROJECT_NAME})
- 检验代码
- 构建并运行
在构建之前,最好在工作区的根目录(ros2_ws)中运行rosdep
以检查缺少的依赖项:
rosdep install -i --from-path src --rosdistro galactic -y
导航回工作区的根目录ros2_ws,并构建新包:
colcon build --packages-select cpp_parameters
打开一个新终端,导航到ros2_ws,并获取设置文件:
. install/setup.bash
现在运行节点:
ros2 run cpp_parameters minimal_param_node
终端应每秒返回以下消息:
[INFO] [minimal_param_node]: Hello world!
现在您可以看到参数的默认值,但您希望能够自己设置它。有两种方法可以实现这一点。- 通过控制台进行更改
本部分将使用您从教程中获得的有关参数的知识,并将其应用于刚刚创建的节点。
确保节点正在运行:
ros2 run cpp_parameters minimal_param_node
打开另一个终端,再次从ros2_ws中获取设置文件,然后输入以下行:
ros2 param list
在这里,您将看到自定义参数my_parameter
。
要更改它,只需在控制台中运行以下命令行:
ros2 param set /minimal_param_node my_parameter earth
您知道,如果成功获得输出Set parameter successful
,它会运行良好。
如果您查看另一个终端,您应该会看到输出更改为[INFO] [minimal_param_node]: Hello earth!
- 通过启动文件进行更改
您也可以在启动文件中设置参数,但首先需要添加启动目录。
在ros2_ws/src/cpp_parameters/目录中,创建一个名为launch的新目录。
在那里,创建一个名为cpp_parameters_launch.py的新文件
在这里,您可以看到我们在启动节点minimal_param_node时将my_parameter
设置为earth
。
通过添加下面的两行,我们确保在控制台中打印输出。
现在打开CMakeLists.txt文件。
在前面添加的行下面,添加以下代码行。
install( DIRECTORY launch DESTINATION share/${PROJECT_NAME})
打开控制台并导航到工作区的根目录ros2_ws,然后构建新包:
colcon build --packages-select cpp_parameters
然后在新终端中查找设置文件:
. install/setup.bash
现在使用我们刚刚创建的启动文件运行节点:
ros2 launch cpp_parameters cpp_parameters_launch.py
终端应每秒返回以下消息:
[INFO] [custom_minimal_param_node]: Hello earth!
- 通过控制台进行更改
总结
您使用自定义参数创建了一个节点,该参数可以从启动文件或命令行设置。
您将依赖项、可执行文件和启动文件添加到包配置文件中,以便可以构建和运行它们,并查看实际的参数。
使用ros2doctor识别问题
背景
当您的ROS 2设置未按预期运行时,您可以使用ros2doctor工具检查其设置。
ros2doctor检查ROS2的所有方面,包括平台、版本、网络、环境、运行系统等,并警告您可能的错误和问题原因。
任务
-
检查您的设置
让我们与ros2doctor一起检查您的一般ROS2设置。
首先,从新终端中获得ROS 2,然后输入命令:
ros2 doctor
这将对所有安装模块进行检查,并返回警告和错误。
如果您的ROS 2设置处于完美状态,您将看到类似以下消息:
All <n> checks passed
然而,返回一些警告并不罕见。
UserWarning
并不意味着您的设置不可用;
这更可能只是一种指示,表明某些东西的配置方式并不理想。
如果您确实收到了一个警告,它看起来像这样:
<path>: <line>: UserWarning: <message>
例如,如果您使用的是不稳定的ROS 2分布,ros2doctor会发现此警告:
UserWarning: Distribution <distro> is not fully supported or tested. To get more consistent features, download a stable version at https://index.ros.org/doc/ros2/Installation/
如果ros2doctor仅在您的系统中发现警告,您仍然会收到All<n>checks passed(所有<n>检查已通过)
消息。
大多数检查被归类为警告,而不是错误。
这主要取决于你,用户,来确定ros2doctor返回的反馈的重要性。
如果它确实在您的设置中发现一个罕见的错误,如UserWarning:error
:所示,则认为检查失败。
您将在问题反馈列表中看到类似于此的消息:
1/3 checks failed
Failed modules: network
错误表示系统缺少对ROS 2至关重要的重要设置或功能。
应解决错误,以确保系统正常运行。 -
检查系统
您还可以检查正在运行的ROS 2系统,以确定问题的可能原因。
要查看ros2doctor在运行中的系统上的工作,让我们运行Turtlesim,它让节点彼此积极通信。
通过打开一个新终端、寻找ROS 2并输入以下命令来启动系统:
ros2 run turtlesim turtlesim_node
打开另一个终端and获得ROS 2以运行远程操作控制:
ros2 run turtlesim turtle_teleop_key
现在在自己的终端中再次运行ros2doctor。
您将看到上次在设置中运行ros2doctor时出现的警告和错误(如果有的话)。
以下是与系统本身相关的几个新警告:
UserWarning: Publisher without subscriber detected on /turtle1/color_sensor.
UserWarning: Publisher without subscriber detected on /turtle1/pose.
似乎/turlsim节点将数据发布到两个未订阅的主题,ros2doctor认为这可能会导致问题。
如果运行命令以响应/color_sensor和/pose主题,则这些警告将消失,因为发布者将拥有订阅者。
您可以在turtlesim仍在运行时打开两个新终端,在每个终端中获得ROS 2,并在自己的终端中运行以下命令:
ros2 topic echo /turtle1/color_sensor
ros2 topic echo /turtle1/pose
然后在终端中再次运行ros2doctor。
没有订户警告的发布者将消失。(确保在运行echo的终端中输入Ctrl+C)。
现在,尝试退出turtlesim窗口或退出远程操作并再次运行ros2doctor。
由于系统中的一个节点不可用,您将看到更多警告,指示不同主题的发布者没有订阅者或订阅者没有发布者。
在一个有许多节点的复杂系统中,ros2doctor对于识别通信问题的可能原因是非常有用的。 -
获取完整报告
虽然ros2doctor会让您知道有关您的网络、系统等的警告,但使用--report
参数运行它将为您提供更多详细信息,帮助您分析问题。
如果您收到有关网络设置的警告,并想确切地找出导致该警告的配置部分,您可能需要使用--report
。
当您需要开工单以获得ROS 2的帮助时,这也是非常有用的。
您可以将报告的相关部分复制并粘贴到工单中,这样帮助您的人员可以更好地了解您的环境并提供更好的援助。
要获取完整报告,请在终端中输入以下命令:
ros2 doctor --report
这将返回分为五组的信息列表:
您可以将此处的信息与运行ros2 doctor得到的警告进行交叉检查。
例如,如果ros2doctor返回警告(前面提到),您的发行版“未得到完全支持或测试”,您可以查看报告的ROS 2信息部分:
在这里,您可以看到发行版的状态是预发行版,这解释了为什么它不受完全支持。
总结
ros2doctor将报告您ROS 2设置和运行系统中的问题。
通过使用--report
参数,您可以更深入地了解这些警告背后的信息。
请记住,ros2doctor不是调试工具;它对代码中的错误或系统的实现方面的错误没有帮助。
创建和使用插件(C++)
背景
本教程来源于pluginlib以及编写和使用简单插件教程。
pluginlib是一个C++库,用于从ROS包中加载和卸载插件。
插件是从运行库(即共享对象、动态链接库)加载的动态可加载类。
有了pluginlib,用户不必显式地将其应用程序与包含类的库链接起来——相反,pluginib可以在任何时候打开包含导出类的库,而应用程序事先不知道库或包含类定义的头文件。
插件可用于扩展/修改应用程序行为,而无需应用程序源代码。
任务
在本教程中,您将创建两个新包,一个定义基类,另一个提供插件。
基类将定义一个通用多边形类,然后我们的插件将定义特定形状。
-
创建基类包
使用以下终端命令在ros2_ws/src文件夹中创建一个新的空包。
ros2 pkg create --build-type ament_cmake polygon_base --dependencies pluginlib --node-name area_node
打开您最喜欢的编辑器,编辑ros2_ws/src/polygon_base/include/polygen_base/regular_polygon.hpp,并在其中粘贴以下内容:
上面的代码应该很容易解释……我们正在创建一个名为RegularPolygon的抽象类。
需要注意的一点是initialize
方法的存在。
对于pluginlib,类需要一个没有参数的构造函数,因此,如果需要任何参数,我们使用initialize方法来初始化对象。
我们需要让这个头对其他类可用,所以打开ros2_ws/src/polygon_base/CMakeLists.txt进行编辑。
在ament_target_dependencies
命令之后添加以下行。
install(DIRECTORY include/ DESTINATION include)
并将此命令添加到ament_package
命令之前
ament_export_include_directories(include)
稍后我们将返回这个包来编写测试节点。 -
创建插件包
现在我们将编写抽象类的两个非虚拟实现。
使用以下终端命令在ros2_ws/src文件夹中创建第二个空包。- 插件的源代码
打开ros2_ws/src/polygon_plugins/src/poolygo_plugins.cpp进行编辑,并在其中粘贴以下内容:
Square和Triangle类的实现应该相当简单:保存边长,并使用它来计算面积。
唯一特定于pluginlib的部分是最后三行,它调用了一些神奇的宏,将类注册为实际插件。
让我们来看看PLUGINLIB_EXPORT_CLASS
宏的参数:- 插件类的完全限定类型,在本例中为polygon_plugins::Square。
- 基类的完全限定类型,在本例中为polygon_base::RegularPolygon。
- 插件声明XML
上面的步骤使我们可以在加载插件所在的库后创建插件实例,但插件加载程序仍然需要一种方法来找到该库并知道在该库中引用什么。
为此,我们还将创建一个XML文件,该文件与包清单中的一个特殊导出行一起,为ROS工具链提供关于插件的所有必要信息。
创建ros2_ws/src/polygon_plugins/plugins.xml,代码如下:
需要注意的几点:
library标签给出了一个包含我们想要导出的插件的库的相对路径。
在ROS 2中,这只是库的名称。
在ROS 1中,它包含前缀lib,有时也包含lib/lib(即lib/libpolyn_plugins),但这里更简单。
class标签声明了一个插件,我们想从库中导出它。
让我们来看看它的参数:- type:插件的完全限定类型。对我们来说,这就是polygon_plugins::Square。
- base_class:插件的完全限定基类类型。对于我们来说,这就是polygon_base::RegularPolygon。
- description:插件及其功能的描述。
- name:过去有一个name属性,但不再需要。
- CMake插件声明
最后一步是通过CMakeLists.txt导出插件。
这是ROS 1的一个变化,其中导出是通过package.xml完成的。
将以下块添加到ros2_ws/src/polygon_plugins/CMakeLists.txt读取find_package(pluginlib REQUIRED)
行后在ament_package
命令之前,添加
此CMake命令的参数为- 基类的包,即polygon_base
- 插件声明xml的相对路径,即plugins.xml
- 插件的源代码
-
使用插件
现在是时候使用插件了。
这可以在任何包中完成,但这里我们将在基本包中完成。
编辑ros2_ws/src/polygon_base/src/area_node.cpp包含以下内容:
ClassLoader是要理解的关键类,在class_loader.hpp标头中定义。
它是用基类模板化的,即polygon_base::RegularPolygon
- 第一个参数是基类包名的字符串,即
polygon_base
。 - 第二个参数是具有插件的完全限定基类类型的字符串,即
polygon_base::RegularPolygon
。
- 第一个参数是基类包名的字符串,即
有许多方法可以实例化类的实例。
在这个例子中,我们使用共享指针。
我们只需要使用插件类的完全限定类型(在本例中为polygon_plugins::Square)调用createSharedInstance
。
重要提示:定义此节点的polygon_base
包不依赖于polygo_plugins
类。
插件将动态加载,无需声明任何依赖项。
此外,我们正在使用硬编码插件名称实例化类,但您也可以使用参数等动态地这样做。
- 构建并运行
导航回工作区的根目录ros2_ws,并构建新包:
colcon build --packages-select polygon_base polygon_plugins
确保从ros2_ws获取设置文件:
. install/setup.bash
现在运行节点:
ros2 run polygon_base area_node
它应该打印
Triangle area: 43.30
Square area: 100.00
总结
祝贺您刚刚编写并使用了第一个插件。