ROS2编程基础
实验介绍
本实验旨在通过实践操作,使学生掌握ROS2(Robot Operating System 2)的基础编程技能,包括ROS2工作空间的配置、节点的创建与通信、话题(Topics)和服务(Services)的使用等核心概念。通过本实验,学生将能够搭建简单的ROS2系统,实现基本的机器人控制与信息交互功能。
ROS2是一个开源的机器人操作系统框架,它为机器人软件开发者提供了丰富的工具和库,以支持复杂且鲁棒的机器人应用的开发。相比于ROS1,ROS2提供了更好的实时性能、安全通信以及对多种编程语言的支持,是当前和未来机器人技术研究与开发的重要平台。
实验要求
- 熟悉Linux操作系统基本操作。
- 安装并配置好ROS2环境。
- 理解ROS2的基本概念,如节点、话题、服务等。
- 完成实验内容,实现指定功能,并撰写实验报告。
实验过程
节点和软件包
节点(Node)和软件包(Package)是ROS2程序的组织形式。其中节点相当于一个ROS2的程序文件。当运行一个ROS2程序时,实际上运行的就是一个或者多个节点文件软件包是节点文件的容器,通常是一个包含了一系列配置文件的目录。而节点源码文件,就放在软件包目录的某个子文件夹(一般是src文件夹)中。下面通过编写一个节点程序来理解这两个概念。
创建软件包
首先在工作空间中创建一个软件包。打开一个新的终端窗口输人如下指令进人 ROS2 工作空间。
cd ~/ros2 ws/src
然后用如下指令创建一个名为“my_pkg”的软件包
ros2 pkg create topic_pkg
然后打开 VSCode,可以在[资源管理器]窗口中看到工作空间的[src]下新增加了一个[my_pkg]软件包。
编写节点
创建好软件包后,接下来在这个软件包中创建一个节点,具体操作步骤如下
1.编写节点代码
先创建这个节点的源码文件。用鼠标右键单击下图中软件包下的[src]子目录,弹出的快捷菜单中选择[新建文件]。
此时会提示输人文件名,如下图所示,输入“my_node.cpp”,然后按[Enter]键就会创建一个名为“my_node.cpp”的源码文件。
此时在右侧的编辑区可以编写这个源码文件的内容,下面编写这个源码文件,其内容如下
#include "rclcpp/rclcpp.hpp"
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("my_node");
RCLCPP_INFO(node->get_logger(), "Hello world!");
while (rclcpp::ok())
{
;
}
rclcpp::shutdown();
return 0;
}
代码编写完毕后,需要按组合键[Ctrl+S]保存刚才编写的代码。保存成功后、编辑界面文件名后面的圆点符号会变成一个叉符号。
2.设置编译规则
节点源码的编译规则写在my_pkg的CMakeLists.txt文件中,在 VSCode中打开这个文件。
在这个文件里添加 my_node 节点的编译规则,首先使用如下代码寻找节点源码中用到的rclepp依赖项。
find_package(rclcpp REQUIRED)
然后使用如下代码添加节点源码的编译规则。
add_executable(my_node src/my_node.cpp)
ament_target_dependencies(my_node "rclcpp")
最后使用如下代码添加节点编译完成后的安装规则。
install(TARGETS
my_node
DESTINATION lib/${PROJECT_NAME})
这些内容可以从 wpr_simulation2的例程文件中找到。如果编译报错,可以与 wpr_simulation2\demo_cmakelists\3 my_node.txt文件中的代码进行比对。
上述规则添加完毕后,一定要按组合键[Ctrl+S]保存文件,否则规则无法生效。
3.修改软件包信息
在VSCode 中打开[my_pkg]下的[package.xml]文件,修改软件包信息,使用如下代码添加依赖项信息。
<depend>rclcpp</depend>
这些内容可以从 wpr_simulation2的例程文件中找到。如果编译报错,可以与 wpr_simulation2\demo_package\3_my_node.xml 文件中的代码进行比对。
文件修改后,一定要按组合键[Ctrl+S]保存文件,否则新的包信息无法生效。
4.编译软件包
修改完上述文件后,打开终端窗口。执行如下指令,进人工作空间,然后执行如下指令,对工作空间中的所有软件包进行编译。
cd ~/ros2_ws
colcon build
运行节点
下面来运行刚才编写的节点。在运行前,需要加载一下当前工作空间中的环境设置参数,这样ros2指令才能找到刚才编译后的软件包和节点文件。保持终端的当前路径依然在工作空间目录ros2_ws中,然后执行如下指令。然后,使用如下指令运行刚才编写的my_node 节点。节点运行起来之后,如下图所示,可以在终端看到之前在my_node节点输出的’Hello world!”信息。
如果想终止这个 my_node节点的运行,可以在终端窗口按组合键[Ctrl+C]。
source install/setup.bash
ros2 run my_pkg my_node
话题和消息
在ROS2中,节点之间的通信最常用的方式是话题(Topie)和消息(Message)。其中话题可以理解为一个网络聊天室,不同的节点可通过话题名称(可以理解为聊天室的名称)来加人这个话题。在这个话题聊天室里发言的节点,可以称为发布者(Publisher);而聊天室中所有看到发言内容的节点,可以称为订阅者(Subscriber)。
在话题聊天室中传送的发言内容,就是消息。所以上述通信机制可以概括为:
1)发布者将要发送的数据打包成消息,然后发送到话题中。
2)订阅者订阅一个话题,从话题中获取消息,然后把消息中的数据解析出来,进行后续的计算和使用。
其中发布者和订阅者所使用的消息格式必须一致,否则无法完成通信。在ROS2中定义了丰富的消息格式,下面将使用其中的字符串消息格式std_msgs::Sting来实现一个发布者和订阅者。可以通过这个编程实验来学习使用话题和消息这种通信形式。
编写话题发布者
通过以上内容的学习,该如何进行话题发布者的编写呢?具体操作步骤如下。
1.创建软件包
首先在工作空间中创建一个软件包。按组合键[Ctrl+Alt+T]打开一个新的终端窗口,输人如下指令,进人ROS2 工作空间。然后用如下指令创建一个名为“topic_pkg”的软件包,打开 VSCode,可以在[资源管理器]窗口中看到工作空间的[src]下新增加了一个[topic_pkg]软件包,将其展开后,其中包含的项目如下图所示。
cd ~/ros2_ws/src
ros2 pkg create topic_pkg
2.编写节点代码
先创建这个节点的源码文件。用鼠标右键单击软件包下的[src]子目录在弹出的快捷菜单中选择「新建文件]。此时会提示输人文件名。,输入“publisher_node.cpp”,然后按[Enter键,就会创建一个名为“publisher_node.cpp”的源码文件。在编辑区可以编写这个源码文件,其内容如下。
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("publisher_node");
auto publisher = node->create_publisher<std_msgs::msg::String>("/my_topic", 10);
std_msgs::msg::String message;
message.data = "Hello World!";
rclcpp::Rate loop_rate(1);
while (rclcpp::ok())
{
publisher->publish(message);
loop_rate.sleep();
}
rclcpp::shutdown();
return 0;
}
代码编写完毕后,需要按组合键[Ctrl+S]保存刚才编写的代码。保存成功后、编辑界面文件名后面的圆点符号会变成一个叉符号。
3.设置编译规则
节点源码的编译规则写在 topic_pkg的 CMakeLists.txt文件中。在 VSCode 中打开这个文件,在这个文件中添加 publisher_node 节点的编译规则。首先使用如下代码寻找节点源码中用到的依赖项。
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
然后使用如下代码添加节点源码的编译规则。
add_executable(publisher_node src/publisher_node.cpp)
ament_target_dependencies(publisher_node "rclcpp" "std_msgs")
最后使用如下代码添加节点编译完成后的安装规则。
install(TARGETS
publisher_node
DESTINATION lib/${PROJECT_NAME})
这些内容可以从 wpr_simulation2的例程文件中找到。如果编译报错,可以与wpr_simulation2_demo_cmakelists\3_publisher_node.txt 文件中的代码进行比对上述规则添加完毕后,一定要保存文件,否则规则无法生效。
4.修改软件包信息
在 VSCode 中打开[topie_pkg]下的[paekage.xml]文件,使用如下代码添加依赖项信息。
<depend>rclcpp</depend>
<depend>std_msgs</depend>
这些内容可以从 wpr_simulation2 的例程文件中找到。如果编译报错,可以与 wpr_simulation2\demo_package\3_my_node.xml 文件中的代码进行比对。
文件修改后,一定要保存文件,否则所添加的信息无法生效。
5.编译软件包
修改完上述文件后,打开终端窗口,执行如下指令,进人工作空间
cd ~/ros2_ws
然后执行如下指令,对工作空间中的所有软件包进行编译
colcon build
6.运行节点
下面运行刚才编写的节点。在运行前,需要加载一下当前工作空间中的环境设置,这样ros2指令才能找到刚才编译后的软件包和节点文件。保持终端的当前路依然在工作空间日录ros2_ws中,然后执行如下指令。
source install/setup.bash
然后,使用如下指令运行刚才编写的 publisher_node 节点
ros2 run topic_pkg publisher_node
节点运行起来之后,终端没有提示。这时可以使用一些ROS2自带的工具指令来查看话题的当前状况。第一个工具是用来查看当前话题列表的指令。在Terminato终端中,按组合键[Ctrl+Shift+O],将终端分为上、下两个子窗口。上面的窗口仍然保持publisher_node 运行,下面的窗口可以输入新的指令并运行。
在新的终端子窗口中输入如下指令
ros2 topic list
按[Enter]键执行后,会看到当前的 ROS2 网络中正在活跃的话题列表。
在列表中,可以看到一个名称为“/my_topic”的话题。与前面节点源码中的话题名称一致,说明这个是 publishernode节点发布的话题。然后可以使用指令查看这个话题里发布的消息内容。在终端子窗口中输人如下指令。
ros2 topic echo /my_topic
按[Enter]键执行后,可以看到终端出现了“data:Hello World!”的内容信息如下图所示。
对照前面的节点源码,可以确认这就是 publisher_node 节点在话题中发送的消息内容,说明 poicher_node 这个发布者节点已经成功地在指定话题里发布了指定内容的消息包。
编写话题订阅者
在上一小节中,创建了一个名为“topie_pkg”的软件包,并在这个软件包中实现了-个发布者节点。在这一小节中,在这个topie_pkg软件包中添加一个订阅者节点。
1.添加节点源码文件
打开 VSCode,在〔资源管理器]窗口中找到[topic_pkg]软件包目录,用鼠标右键单击软件包下的[sr]子目录,在弹出的快捷菜单中选择[新建文件],此时会提示输入文件名,在所示位置输入“subscriber_node.cpp”,然后按[Enter]键,创建一个名为“subscriber_node.cpp”的源码文件。
2.编写节点代码
在 VSCode 的编辑区编写 subseriber_node.cpp,其内容如下。
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
std::shared_ptr<rclcpp::Node> node;
void Callback(const std_msgs::msg::String::SharedPtr msg)
{
RCLCPP_INFO(node->get_logger(),"Receive : %s", msg->data.c_str());
}
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
node = std::make_shared<rclcpp::Node>("subscriber_node");
auto subscriber= node->create_subscription<std_msgs::msg::String>("/my_topic", 10, &Callback);
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
上述代码可以从 wpr_simulation2的例程文件中找到。如果编译报错,可以与 wpr_simulation2\demo_cpp\3_subscriber_node.cpp 文件中的代码进行比对。
代码编写完毕后,需要保存代码。保存成功后,编界面文件名后面的圆点符号会变一个叉符号
3.设置编译规则
节点源码的编译规则写在topie_pkg的 CMakeLists.txt文件里。在 VSCode 中打开并编辑这个文件。在上一小节中,已经添加了编译需要的依赖项。这里只需要使用如下代码添加新的节点源码编译规则。
add_executable(subscriber_node src/subscriber_node.cpp)
ament_target_dependencies(subscriber_node "rclcpp" "std_msgs")
最后修改安装规则,修改后的安装规则如下,即在上一小节的publisher_node 后面添加一个 subscriber_node。
install(TARGETS
publisher_node
subscriber_node
DESTINATION lib/${PROJECT_NAME})
这些内容可以从 wpr_simulaion2的例程文件中找到。如果编译报错,可以与wpr _siomdtion2\demo_cmakelists\3_subscriber _node.txt文件中的代码进行比对。
上述规则修改完毕后,一定要保存文件,否则规则无法生效。另外在上一小节里,已在package.xml中添加了依赖项,所以这一小节无须再进行这一步操作。
4.编译软件包
修改完上述文件后,打开终端窗口,执行如下指令,进入工作空间
cd ~/ros2_ws
然后执行如下指令,对工作空间中的所有软件包进行编译
colcon build
5.运行节点
在运行前,需要加载当前工作空间中的环境设置,这样ros2指令才能找到编译后的软件包和节点文件。保持终端的当前路径依然在工作空间目录ros2_ws中,执行如下指令。
source install/setup.bash
然后,先使用如下指令运行上一小节编写的 pubisher_node 节点
ros2 run topic_pkg publisher_node
publisher_node 节点运行起来之后,再运行这一小节编写的 subscriber_node 节点。在 Terminator 终端中,按组合键[Ctrl+Shift+O],终端分为上、下两个子窗口。其中上面的窗口仍然保持 publisher_node 节点运行,下面的窗口将用来运行 subscriber_node 节点。在下面的窗口中执行如下指令
source install/setup.bash
ros2 run topic_pkg subscriber_node
按[Enter]键执行后,可以看到如下图运行subscriber_node 节点的窗口中出现了“Re-ceive:Hello World!”的内容。
这就是 subscriber_node 节点接收到publisher_node 节点发送来的消息内容。说明subseriber_node 和 publisher_node 这两个节点通过话题“/my_topic”建立起了数据通信,使用 std_msgs::msg::String类型的消息包在两个节点间传递数据。
面向对象的节点实现
话题发布者的类封装
在前面的实验里,使用的都是过程式编程,可以很清晰地展示节点的运行过程。但是在大量的开源项目中,最流行的是面向对象的编程方式,也就是将节点封装成类(Class)。为了让读者能够更好地融人ROS2的开源社区,下面将学习如何将前面使用过程式编程的节点代码封装成类。先以发布者节点为例,具体操作步骤如下:
1)构建一个名称为“PublisherNode”的类。其父类设置为rclcpp::Node,以继承rclepp::Node的所有功能。
2)消息发布需要用到的发布对象和消息包,将以成员对象的形式,声明在类的结构定义里。这样在类的所有成员函数里,都可以直接使用它们。
3)在PublisherNode 类的构造函数里进行话题发布这类初始化操作。
4)消息的发布操作,由原来的while()循环,替换成定时器激发。在PublisherNode 类的构造函数里进行定时器的启动。
5)在定时器的响应函数里,进行消息包的发送操作。
6)在 main()函数数里构建这个类的实例对象,并运行这个对象。
调整后的代码可以在 wpr_simulation2\demo_cpp\3_publisher_class.cpp 进行查阅
代码内容如下。
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
class PublisherNode : public rclcpp::Node
{
public:
PublisherNode()
: Node("publisher_node")
{
publisher_ = create_publisher<std_msgs::msg::String>("/my_topic", 10);
timer_ = create_wall_timer(
std::chrono::milliseconds(1000),
std::bind(&PublisherNode::publishMessage, this)
);
}
private:
void publishMessage()
{
message_.data = "Hello World!";
publisher_->publish(message_);
}
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
std_msgs::msg::String message_;
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<PublisherNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
如上就是对发布者节点的代码进行类封装。可以将封装后的代码替换到之前的publisher_node.cpp 文件中,然后按照之前的实验流程再编译运行一遍,对比一下效果。
话题订阅者的类封装
完成了对发布者节点的类封装,下面开始对订阅者节点进行类封装,具体操作步骤如下:
1)构建一个名称为“SubscriberNode”的类。其父类设置为rclepp::Node,以继承父类的所有功能。
2)将订阅对象声明为类的成员变量,这样在类的所有成员函数里,都可以访问它
3)在 SubscriberNode 类的构造函数里进行话题订阅操作。
4)在main()函数里构建这个类的实例对象,并运行这个对象。
调整后的代码可以在 wpr_simulation2\demo_cpp\3_subscriber_class.cpp 进行查阅。
代码内容如下。
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
class SubscriberNode : public rclcpp::Node
{
public:
SubscriberNode()
: Node("subscriber_node")
{
subscriber_ = create_subscription<std_msgs::msg::String>(
"/my_topic",
10,
std::bind(&SubscriberNode::Callback, this, std::placeholders::_1));
}
private:
void Callback(const std_msgs::msg::String::SharedPtr msg)
{
RCLCPP_INFO(get_logger(), "Receive : %s", msg->data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared<SubscriberNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
如上就是对订阅者节点进行的类封装。可以将封装后的代码替换到之前的subscriber_node.cpp 文件中,然后按照之前的实验流程再运行一遍,对比一下运行效果
Launch 文件
在ROS2中,可以通过Launch 文件一次启动多个节点,省去了逐个节点输入指令启动的烦琐操作。在ROS2中,支持用3种语言来编写Launch 文件,分别是XML、YAML和 Python。其中XML的语法和ROS1的Launch文件格式类似;YAML格式的 Launch 文件使用比较少;而在ROS2的开源社区中,则以Python语言的Launch文件最为流行。下面将分别使用这3种语言构建 Launch 文件,将前面编写的发布者节点和订阅者节点同时运行起来。
XML 格式的 Launch 文件
在 VSCode 中找到前面实验构建的 topic_pkg软件包,用鼠标右键单击[topic_pkg],在弹出的快捷菜单中选择[新建文件夹],将这个新建的文件夹命名为“launch”,用鼠标右键单击[launch]文件夹,在弹出的快捷菜单中选择[新建文件]。将这个新建的文件命名为“pub_sub.launch.xml”,然后编写 pub_sub.launch.xml,其内容如下。
<launch>
<node pkg = "topic_pkg" exec = "publisher_node" name = "publisher_node" />
<node pkg = "topic_pkg" exec = "subscriber_node" name = "subscriber_node" />
</launch>
在XML格式的 Launch 文件里,将每个节点转换为相应的标签。在标签里还有pkg、exec和 name3个属性值。其中,pkg属性值为要启动的节点所在软件包的名字:exec 属性值为要启动的节点名字;name 属性值为这个节点启动后,在当前的ROS2里的别名。一般为了直观表示节点的功能,会将name设置成和exec 属性一样的名字。需要注意的是,这个name 在 ROS2里具备唯一性。也就是说,当一个ROS2 里同时运行两个 name 相同的节点时,先启动的节点会被后启动的同名节点顶替掉。
在这个 Launch 文件中,总共启动了两个节点。第一个是topic_pkg的publisher_node 节点;第二个是同在 topic_pkg 的 subscriber_node 节点。最后,将这两个节点的标签放置在标签内,形成完整的 Launch 文件。这些内容可以从 wpr_simulation2 的例程文件中找到。如果编译报错,可以与wpr_simulation2\demo_launch\3_pub_sub.launch.xml 文件中的代码进行比对。
文件内容编写完成后,需要进行保存。
Launch 文件编写完成后,还需要安装才能运行。在 VSCode 中打开 topic_pkg 软件包的CMakeLists.txt文件。在文件中添加如下安装规则
install(
DIRECTORY
launch
DESTINATION
share/${PROJECT_NAME}
)
这些内容可以从 wpr_simulation2的例程文件中找到。如果编译报错,可以与 wpr_simulation2\demo_cmakelists\3_launch.txt文件中的代码进行比对。
同样,文件内容编写完成后,需要进行保存。然后进行安装,打开一个端窗口,执行如下指令,进入工作空间
cd ~/ros2_ws/
然后执行如下指令,完成 Launch 文件的安装
colcon build
下面运行这个 Launch 文件。在终端窗口输入如下指令,加载新的设置
source install/setup.bash
输人如下指令,运行 Launch 文件
ros2 launch topic_pkg pub_sub.launch.xml
按[Enter]键执行后,可以看到如下图所示信息。
可以看到,topic_pkg 软件包 subscriber_node 节点输出了从话题中接收到的字符串消息包这个消息包是 topic_pkg 软件包的 publisher_node 节点发出来的,说明 publisher_node 节点也运行起来了。发布者节点和订阅者节点通过这个XML,格式的 Launch 文件都运行起来了。
YAML 格式的 Launch 文件
下面继续在topic_pkg软件包中实现YAML格式的Launch文件。在VSCode 中找到前面实验构建的[topic_pkg]软件包,用鼠标右键单击其中的[launch]文件夹,在弹出的快捷菜单中选择[新建文件]。将这个新建的文件命名为“pub_sub.launch.yaml”,然后编写 pub_sub. launch.yaml,其内容如下。
launch:
- node:
pkg: "topic_pkg"
exec: "publisher_node"
name: "publisher_node"
- node:
pkg: "topic_pkg"
exec: "subscriber_node"
name: "subscriber_node"
需要特别注意的是,YAML格式的文件是按照缩进来判断键值的层级和从属关系的,所以一定要特别仔细地对每一项键值的缩进量进行核对,以免运行时出现错误。
在 YAML 格式的 Launch 文件里,第一行先定义一个名为“launch”的顶级键。在这个顶级键的下面定义了两个node列表项,每个node 列表项都包含了pkg、exec 和name 这3个属性键。
在这个 Launch 文件中,总共启动了两个节点。第一个是topic_pkg的 publisher_node 节点;第二个是同在 topic_pkg的subscriber_node 节点。
这些内容可以从 wpr_simulation2的例程文件中找到。如果编译报错,可以与 wpr_simula-tion2\demo_launch\3_pub_sub.launch.yaml 文件中的代码进行比对。
文件内容编写完成后,需要进行保存。Launch 文件编写完成后,还需要安装才能运行。
在 VSCode 中打开 topic_pkg 软件包的 CMakeLists.txt 文件,确认如下安装规则已经添加。
install(
DIRECTORY
launch
DESTINATION
share/${PROJECT_NAME}
)
如果没有这部分内容,则需要将其添加进去,然后按组合键[Ctrl+S]保存。然后打开一个终端窗口,执行如下指令,进入工作空间。
cd ~/ros2_ws/
然后执行如下指令,完成Launch 文件的安装
colcon build
下面运行这个 Launch 文件。在终端窗口中输人如下指令,加载新的设置
source install/setup.bash
输人如下指令运行 Launch 文件
ros2 launch topic_pkg pub_sub.launch.yaml
按[Enter]键执行后,可以看到如下图 所示信息。
可以看到,topic_pkg软件包 subscriber_node 节点输出了从话题中接收到的字符串消息包。这个消息包是 topic_pkg 软件包的 publisher_node 节点发出来的,说明 publisher_node 节点运行起来了。发布者节点和订阅者节点通过这个YAML格式的 Launch 文件都运行起来了。
Python 格式的 Launch 文件
下面继续在 topic_pkg 软件包中实现Python 格式的 Launch 文件,具体操作步骤如下。
在 VSCode 中找到前面实验构建的[topic_pkg]软件包,用鼠标右键单击其中的[launch]文件夹,在弹出的快捷菜单中选择[新建文件]。将这个新建的文件命名为“pub_ sub.launch. py”,然后编写 pub_sub.launch.py,其内容如下。
from launch_ros.actions import Node
from launch import LaunchDescription
def generate_launch_description():
# 定义发布者节点
publisher_cmd = Node(
package='topic_pkg',
executable='publisher_node',
name='publisher_node'
)
# 定义订阅者节点
subscriber_cmd = Node(
package='topic_pkg',
executable='subscriber_node',
name='subscriber_node'
)
# 创建 LaunchDescription 对象
ld = LaunchDescription()
# 将节点添加到 LaunchDescription 中
ld.add_action(publisher_cmd)
ld.add_action(subscriber_cmd) # 使用正确的变量名 subscriber_cmd
# 返回 LaunchDescription 对象
return ld
这个 Launch 文件内容编写完成后,需要进行[Ctrl+S]保存。
Launch 文件编写完成后,还需要安装才能运行。在 VSCode 中打开 topic_pkg 软件包的CMakeLists.txt 文件,确认如下安装规则已经添加。
install(
DIRECTORY
launch
DESTINATION
share/${PROJECT_NAME}
)
如果没有这部分内容,则需要将其添加进去,然后按组合键[C+S]保存。然后打开一个终端窗口,执行如下指令,进入工作空间。
cd ~/ros2_ws/
然后执行如下指令,完成Launch 文件的安装
colcon build
下面运行这个 Launch文件。在终端窗口中输人如下指令,加载新的设置
source install/setup.bash
输入如下指令,运行 Launch 文件
ros2 launch topic_pkg pub_sub.launch.py
按[Enter]键执行后,可以看到如下图所示信息:
可以看到,topic_pkg软件包 subscriber_node 节点输出了从话题中接收到的字符串消息包。这个消息包是 topic_pkg 软件包的 publisher_node 节点发出来的,说明 publisher_node节点也运行起来了。这个 Python 格式的 Launch 文件运行成功。
实验小结
本实验主要是对ROS2的基本程序概念进行介绍和编程。首先详细介绍了ROS2程序的组织形式–节点和软件包,以及节点之间最常用的通信方式–话题和消息的实现;接着使用面向对象的方式,编程实现节点,包括话题发布者和话题订阅者的类封装;最后,利用3 种语言构建 Launch 文件,实现一次同时启动多个节点。