ROS2(3)入门篇 - 使用客户端库
基于官方教程的实践。
1.使用colcon构建包
colcon是用来代替catkin_make、catkin_make_isolated等。
安装colcon:
sudo apt install python3-colcon-common-extensions
添加colcon的tab命令补全功能:
source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash# 可将上面指令放入~/.bashrc文件
1.1 工作区目录介绍
工作区目录下会有下面几个目录:
- src,ros包源码
- build,存储中间文件,每个包都有对应的子文件夹
- install,包安装位置,默认情况下,每个包都安装到单独的子目录中
- log,存储colcon调用的日志记录
注意和catkin构建方式相比,没有了devel目录。
1.2 下载示例
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
git clone https://github.com/ros2/examples src/examples -b humble
这里可能报超时,无法访问github.com,此时继续用【安装篇】的方法,在网站https://sites.ipaddress.com/中找到github.com和github.global.ssl.fastly.net的地址,修改hosts文件:
sudo vi /etc/hosts
# 添加下面两行
140.82.112.3 github.com
151.101.1.194 github.global.ssl.fastly.net
继续构建:
colcon build --symlink-install# 带--symlink-install 选项的好处
- 构建产物不会被复制,而是在 install/ 目录创建指向构建空间(build/)中对应文件的符号链接。
- 编译后修改源文件时,可以更快地看到效果(适合开发调试)。
- 节省磁盘空间,构建速度更快(尤其是多次构建时)。
报错1:
WARNING:colcon.colcon_core.package_selection:Some selected packages are already built in one or more underlay workspaces:'examples_rclcpp_minimal_service' is in: /opt/ros/humble'examples_rclcpp_minimal_timer' is in: /opt/ros/humble'examples_rclcpp_minimal_action_client' is in: /opt/ros/humble'examples_rclpy_minimal_publisher' is in: /opt/ros/humble
这里是说humble安装路径下已经有这些包了,会在当前工作区再创建一份,那么问题来了,在执行同名包时,到底会用哪一个呢?答案是如果没有执行
source install/setup.bash,那么默认就会执行humble安装目录下的包。
报错2:
/usr/lib/python3/dist-packages/setuptools/command/install.py:34: EasyInstallDeprecationWarning: easy_install command is deprecated.
这表示安装的setuptools包已经不支持easy_install方式了,可以通过降低setuptools版本实现:
# 安装pip
sudo apt install python3-pip
# 重装setuptools
sudo pip install setuptools==58.2.0 --target=/usr/lib/python3/dist-packages
# 重新编译
colcon build --symlink-install
看到目录:
cd ~/ros2_ws/src
tree -L 2
.
└── examples├── CONTRIBUTING.md├── launch_testing├── LICENSE├── rclcpp├── rclpy└── README.md
执行测试用例:
cd ~/ros2_ws/
colcon test
添加环境,重要!!添加完之后才能ros2 run tab时找到包的提示,ros2 pkg才能找到包
source install/setup.bash
# 添加到bashrc文件中
演示:
ros2 run examples_rclcpp_minimal_subscriber subscriber_member_function
# 打开新终端
ros2 run examples_rclcpp_minimal_publisher publisher_member_function
1.3 ros2 pkg 指令
查看包列表
ros2 pkg list
查看某个包的路径
ros2 pkg prefix XXX包名
1.4 创建自己的包
- colcon使用REP149中定义的package.xml规范;
- colcon支持多种构建类型,推荐的构建类型是
ament_cmake和ament_python,当然也支持纯cmake包; - 可以使用
ros2 pkg create创建一个包模板
对
catkin使用者,这和catkin_create_package一样
1.5 colcon_cd快速切包路径
colcon_cd可以允许你从当前目录切换到某个包的目录中去。
echo "source /usr/share/colcon_cd/function/colcon_cd.sh" >> ~/.bashrc
echo "export _colcon_cd_root=/opt/ros/humble/" >> ~/.bashrc
然后就可以colcon_cd xxxpackagename切换过去(测试发现不行-,-)
1.5 其他技巧 - 不想构建某个包
src中的包,某个不想构建,可以在包目录中添加一个空文件夹COLCON_IGNORE
1.6 其他技巧 - 避免构建测试用例
- 如果想要避免再CMake包中配置和构建测试用例,可以传入
--cmake-args -DBUILD_TESTING=0选项。 - 如果只想运行某个包中独立的测试用例,可以用下面的指令:
colcon test --packages-select YOUR_PKG_NAME --ctest-args -R YOUR_TEST_IN_PKG
1.7 colcon build 参数
参考《ROS2(补充)Docker容器 + vscode开发》,colcon 构建时传递的参数更加丰富。
2. 创建工作区
首先按照上个章节的步骤,已经安装了例程。
2.1 检查依赖
构建工作区之前,应该先解析包依赖关系,这样可避免构建长时间等待失败后,却发现缺少依赖项。
这里需要使用rosdep来检查依赖关系,具体安装rosdep的方法参考ROS2(1) 安装。
cd ~/ros2_ws
rosdep install -i --from-path src --rosdistro humble -y
结果:
#All required rosdeps installed successfully
3. 创建包
ROS 2 中的包创建使用 ament 作为其构建系统,使用 colcon 作为其构建工具。ROS2中,一般使用ament_cmake(C++)或者ament_python 创建包,关于ament_xx会在后面的指南中介绍。
CMake类型的包包含的最少内容:
- CMakeLists.txt 描述如何在包中生成代码
- include/<package_name> 包含包的公共头文件
- package.xml 包含有关包的元信息的文件
- src 包源代码
Python类型包含的最少内容:
- package.xml 包含有关包的元信息的文件
- resource/<package_name> 包的标记文件
- setup.cfg 给ros2 run使用
- setup.py 包含有关如何安装软件包的说明
- <package_name>- ,和包名相同的一个目录,包含__init__.py,可以通过ROS2的工具来发现包。
最简单的包目录可能如下:
cmake目录:
my_package/CMakeLists.txtinclude/my_package/package.xmlsrc/python目录:
my_package/package.xmlresource/my_packagesetup.cfgsetup.pymy_package/
注意工作区中可以同时放CMake和Python的包。
3.1 创建测试
注意一定要进入src目录下再执行。
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake --license Apache-2.0 <package_name>
比如创建mypackage包:
going to create a new package
package name: mypackage
destination directory: /home/bob/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['bob <bob@todo.todo>']
licenses: ['Apache-2.0']
build type: ament_cmake
dependencies: []
creating folder ./mypackage
creating ./mypackage/package.xml
creating source and include folder
creating folder ./mypackage/src
creating folder ./mypackage/include/mypackage
creating ./mypackage/CMakeLists.txt
用python创建:
ros2 pkg create --build-type ament_python --license Apache-2.0 mypackage_python
3.2 构建测试包
cd ~/ros2_ws
colcon build #编译src所有的包
colcon build --packages-select mypackage #编译指定的包
3.3 使用测试包
要执行新包中的测试文件,首先执行下面的语句:
cd ~/ros2_ws
source install/local_setup.bash
这个在前面已经讲到了。
3.4 执行包中的节点
ros2 run my_package my_node
3.5 自定义package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3"><name>mypackage</name><version>0.0.0</version><description>TODO: Package description</description><maintainer email="bob@todo.todo">bob</maintainer><license>Apache-2.0</license><buildtool_depend>ament_cmake</buildtool_depend><test_depend>ament_lint_auto</test_depend><test_depend>ament_lint_common</test_depend><export><build_type>ament_cmake</build_type></export>
</package>
这里的TODO需要自己填写。
_depend关键字和依赖有关系,这里先按住不表。
4.编写简单的发布订阅者(c++)
这里的例子是写了一个基于topic的简单的talker和listener,一个节点发布,另一个节点接收。
创建一个包cpp_pubsub,并拉取代码:
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake --license Apache-2.0 cpp_pubsub
cd ~/ros2_ws/src/cpp_pubsub/src
wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/humble/rclcpp/topics/minimal_publisher/member_function.cpp
4.1 发布节点示例
使用vscode打开代码如下:
#include <chrono>
#include <functional>
#include <memory>
#include <string>#include "rclcpp/rclcpp.hpp" //包含这个来使用ros2
#include "std_msgs/msg/string.hpp" //包含这个来发布数据using namespace std::chrono_literals;/* This example creates a subclass of Node and uses std::bind() to register a* member function as a callback from the timer. *///创建MinimalPublisher的节点
class MinimalPublisher : public rclcpp::Node
{
public:MinimalPublisher(): Node("minimal_publisher"), //节点名称minimal_publishercount_(0){//创建发布者对象,名称为"topic",缓冲大小为10,其中存储的数据类型为std_msgs::msg::Stringpublisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);//创建一个定时器,定时时间500ms,定时回调timer_callback//注意std::bind类成员函数时,第一个参数为类中函数,第二个必须指定类对象的地址timer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));}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_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv); //rclcpp::init对节点进行了初始化,rclcpp::spin(std::make_shared<MinimalPublisher>()); //rclcpp::spin开始处理回调,包括定时器,程序会卡在这里,里面有个无限循环rclcpp::shutdown();return 0;
}
4.1.1 增加依赖
打开package.xml文件,首先对下面关于这个包的描述、维护者、许可证进行编辑
<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
在ament_cmake描述的编译依赖过后之后粘贴下面两行
<exec_depend>rclcpp</exec_depend>
<exec_depend>std_msgs</exec_depend>
这两行表示了在执行的时候需要rclpp 和 std_msgs这两个依赖,然后保存文件。
4.1.2 修改CMake.list文件
打开同目录下的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(TARGETStalkerDESTINATION lib/${PROJECT_NAME})
我们可以删除CMakeLists.txt中没有用的部分和一些注释,最后文件内容如下
cmake_minimum_required(VERSION 3.5)
project(cpp_pubsub)#Default to C++14
if(NOT CMAKE_CXX_STANDARD)set(CMAKE_CXX_STANDARD 14)
endif()if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")add_compile_options(-Wall -Wextra -Wpedantic)
endif()find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)install(TARGETStalkerDESTINATION lib/${PROJECT_NAME})ament_package()
现在就可以编译节点然后source一下就可以运行了,等写好订阅者过后一起编译方便查看整体运行效果。
4.2 订阅节点示例
首先下载listener的样例代码
cd ~/ros2_ws/src/cpp_pubsub/src
wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/master/rclcpp/minimal_subscriber/member_function.cpp
我们可以看到现在目录下面有两个文件:
publisher_member_function.cpp subscriber_member_function.cpp
subscriber_member_function.cpp内容如下:
#include <memory>#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;class MinimalSubscriber : public rclcpp::Node
{public:MinimalSubscriber(): Node("minimal_subscriber"){//创建订阅者对象,订阅主题"topic",缓冲区大小10,回调响应topic_callbacksubscription_ = this->create_subscription<std_msgs::msg::String>("topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));}private:void topic_callback(const std_msgs::msg::String::SharedPtr msg) const{RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());}rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalSubscriber>());rclcpp::shutdown();return 0;
}
因为和发布者在同一个包中,所以package.xml无需再修改。
CMakeLists.txt如下:
cmake_minimum_required(VERSION 3.5)
project(cpp_pubsub)# Default to C++14
if(NOT CMAKE_CXX_STANDARD)set(CMAKE_CXX_STANDARD 14)
endif()if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")add_compile_options(-Wall -Wextra -Wpedantic)
endif()find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs)install(TARGETStalkerlistenerDESTINATION lib/${PROJECT_NAME})ament_package()
4.3 编译节点并运行
首先检查依赖,–from-path后面根表示的是要进行依赖检查的目录。:
cd ~/ros2_ws
sudo rosdep install -i --from-path src/cpp_pubsub --rosdistro eloquent -y
开始编译,我们选择只编译我们新建的包:
colcon build --packages-select cpp_pubsub
打开新的命令终端,执行:
cd ~/ros2_ws
source install/setup.bash
ros2 run cpp_pubsub talker
发布者开始运行:
[INFO] [minimal_publisher]: Publishing: "Hello World: 0"
[INFO] [minimal_publisher]: Publishing: "Hello World: 1"
[INFO] [minimal_publisher]: Publishing: "Hello World: 2"
[INFO] [minimal_publisher]: Publishing: "Hello World: 3"
[INFO] [minimal_publisher]: Publishing: "Hello World: 4"
打开另一个终端:
cd ~/ros2_ws
source install/setup.bash
ros2 run cpp_pubsub listener
打印:
[INFO] [minimal_subscriber]: I heard: "Hello World: 10"
[INFO] [minimal_subscriber]: I heard: "Hello World: 11"
[INFO] [minimal_subscriber]: I heard: "Hello World: 12"
[INFO] [minimal_subscriber]: I heard: "Hello World: 13"
[INFO] [minimal_subscriber]: I heard: "Hello World: 14"
可以观测一下缓冲区满的情况【TODO】
5.编写简单的服务端客户端(c++)
直接看例程代码即可。
minimal_service的main.cpp:
#include <cinttypes>
#include <memory>#include "example_interfaces/srv/add_two_ints.hpp"
#include "rclcpp/rclcpp.hpp"//依赖了example_interfaces库,其中定义了AddTwoInts服务结构
using AddTwoInts = example_interfaces::srv::AddTwoInts;
rclcpp::Node::SharedPtr g_node = nullptr;//回调函数,rclcpp 库规定了服务回调函数的参数数量、类型和顺序(必须包含请求头、请求数据、响应数据)。
//AddTwoInts定义了服务的请求和响应数据结构,分别为AddTwoInts::Request和AddTwoInts::Response。
void handle_service(const std::shared_ptr<rmw_request_id_t> request_header,const std::shared_ptr<AddTwoInts::Request> request,const std::shared_ptr<AddTwoInts::Response> response)
{(void)request_header;RCLCPP_INFO(g_node->get_logger(),"request: %" PRId64 " + %" PRId64, request->a, request->b);response->sum = request->a + request->b;
}int main(int argc, char ** argv)
{rclcpp::init(argc, argv);g_node = rclcpp::Node::make_shared("minimal_service"); //创建节点 节点名称//利用Node的create_service方法创建服务,使用AddTwoInts服务类型,服务名称为"add_two_ints",回调函数为handle_serviceauto server = g_node->create_service<AddTwoInts>("add_two_ints", handle_service);rclcpp::spin(g_node); // 保持节点运行,等待服务请求,会卡在这里rclcpp::shutdown();g_node = nullptr;return 0;
}
client端:
#include <chrono>
#include <cinttypes>
#include <memory>#include "example_interfaces/srv/add_two_ints.hpp"
#include "rclcpp/rclcpp.hpp"using AddTwoInts = example_interfaces::srv::AddTwoInts;int main(int argc, char * argv[])
{rclcpp::init(argc, argv);auto node = rclcpp::Node::make_shared("minimal_client");auto client = node->create_client<AddTwoInts>("add_two_ints");// 等待服务出现,最多等待1秒。while (!client->wait_for_service(std::chrono::seconds(1))) {if (!rclcpp::ok()) {RCLCPP_ERROR(node->get_logger(), "client interrupted while waiting for service to appear.");return 1;}RCLCPP_INFO(node->get_logger(), "waiting for service to appear...");}auto request = std::make_shared<AddTwoInts::Request>();request->a = 41;request->b = 1;// 异步发送请求,返回一个future,用于获取服务的响应。auto result_future = client->async_send_request(request);// 等待服务响应,最多等待1秒。if (rclcpp::spin_until_future_complete(node, result_future) !=rclcpp::FutureReturnCode::SUCCESS){RCLCPP_ERROR(node->get_logger(), "service call failed :(");client->remove_pending_request(result_future);return 1;}// 获取服务响应。auto result = result_future.get();RCLCPP_INFO(node->get_logger(), "result of %" PRId64 " + %" PRId64 " = %" PRId64,request->a, request->b, result->sum);rclcpp::shutdown();return 0;
}


6.创建自定义msg和srv文件
这里创建了一个tutorial_interfaces的包,其中定义了一个msg和srv。其他包想用msg或者srv时,可以依赖这个包即可。
6.1 创建新的包
cd ros2_ws/src
ros2 pkg create --build-type ament_cmake --license Apache-2.0 tutorial_interfaces
包目录如下:
.
├── CMakeLists.txt
├── LICENSE
├── include
│ └── tutorial_interfaces
├── package.xml
└── src
.msg 和 .srv 文件需要分别放置在名为 msg 和 srv 的目录中。在 ros2_ws/src/tutorial_interfaces 中创建目录:
mkdir msg srv
6.2 创建msg文件
在msg目录中,创建两个文件Num.msg和Sphere.msg:
touch Num.msg Sphere.msg
Num.msg文件,用于传输名为 num 的单个 64 位整数:
int64 num
Sphere.msg文件,注意使用了来自另一个消息包的消息表示中心点:
geometry_msgs/Point center
float64 radius
6.3 创建srv文件
进入srv目录,创建AddThreeInts.srv文件:
int64 a
int64 b
int64 c
---
int64 sum
它请求名为 a、b 和 c 的三个整数,并使用名为 sum 的整数进行响应。
6.4 CMakeList.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
)
必须将文件名放到rosidl_generate_interfaces里面,同时由于引用了geometry_msgs包中的消息,所以要包含该包。
6.5 package.xml
在 package.xml 的 元素中添加以下行:
<depend>geometry_msgs</depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
这些是固定用法。
6.6 编译
简单编译:
colcon build --packages-select tutorial_interfaces
或者使用前文中vscode的编译指令:
colcon build --symlink-install --event-handlers console_cohesion+ --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
注意,这种编译,其中的–symlink-install会导致tutorial_interfaces编译失败,因为这种纯消息定义的包,添加上–symlink-install进行编译多次是有问题的。只需要手动删除build/tutorial_interfaces即可重新编译。
执行:
source install/setup.bash #别落下
ros2 interface list | grep tutorial_i
# 显示tutorial_interfaces/msg/Numtutorial_interfaces/msg/Spheretutorial_interfaces/srv/AddThreeInts
6.7 使用
在之前的cpp_pubsub中或者ro2的例程中进行msg测试。
特别注意的是,#include "tutorial_interfaces/msg/num.hpp"时,在vscode的代码提示中会找不到路径,需要编译时添加-DCMAKE_EXPORT_COMPILE_COMMANDS=ON选项。
发布者:
#include <chrono>
#include <functional>
#include <memory>
#include <string>#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "tutorial_interfaces/msg/num.hpp" #CHANGEDusing namespace std::chrono_literals;/* This example creates a subclass of Node and uses std::bind() to register a* member function as a callback from the timer. */class MinimalPublisher : public rclcpp::Node
{
public:MinimalPublisher(): Node("minimal_publisher"), count_(0){//创建订阅者对象,订阅主题"topic",缓冲区大小10,回调响应topic_callback//publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);publisher_ = this->create_publisher<tutorial_interfaces::msg::Num>("topic", 10);#CHANGEDtimer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));}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());//替换新的消息类型auto message = tutorial_interfaces::msg::Num(); #CHANGEDmessage.num = count_++; #CHANGED RCLCPP_INFO(this->get_logger(), "Publishing: '%d'", message.num); #CHANGEDpublisher_->publish(message);}rclcpp::TimerBase::SharedPtr timer_;rclcpp::Publisher<tutorial_interfaces::msg::Num>::SharedPtr publisher_; #CHANGEDsize_t count_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalPublisher>());rclcpp::shutdown();return 0;
}
订阅者:
#include <functional>
#include <memory>#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "tutorial_interfaces/msg/num.hpp"using std::placeholders::_1;class MinimalSubscriber : public rclcpp::Node
{
public:MinimalSubscriber(): Node("minimal_subscriber"){//subscription_ = this->create_subscription<std_msgs::msg::String>(subscription_ = this->create_subscription<tutorial_interfaces::msg::Num>("topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));}private:// void topic_callback(const std_msgs::msg::String & msg) const// {// RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str());// }void topic_callback(const tutorial_interfaces::msg::Num & msg) const{RCLCPP_INFO(this->get_logger(), "I heard: '%d'", msg.num);}rclcpp::Subscription<tutorial_interfaces::msg::Num>::SharedPtr subscription_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalSubscriber>());rclcpp::shutdown();return 0;
}
CMakeList的修改:
find_package(tutorial_interfaces REQUIRED) #都添加
ament_target_dependencies(subscriber_member_function rclcpp std_msgs tutorial_interfaces) # 单独修改
ament_target_dependencies(publisher_member_function rclcpp std_msgs tutorial_interfaces) # 单独修改
package.xml修改:
<depend>tutorial_interfaces</depend> #新增
编译测试:
如果用下面指令统一编译,那可能会报错找不到tutorial_interfaces包,此时执行一下source install/setup.bash就好了:
colcon build --symlink-install --event-handlers console_cohesion+ --cmake-args -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
[INFO] [1761287892.427723421] [minimal_publisher]: Publishing: 'Hello, world! 0'
[INFO] [1761287892.891390853] [minimal_publisher]: Publishing: 'Hello, world! 1'
[INFO] [1761287893.358288585] [minimal_publisher]: Publishing: 'Hello, world! 2'
[INFO] [1761287893.824926935] [minimal_publisher]: Publishing: 'Hello, world! 3'
[INFO] [1761287894.291569120] [minimal_publisher]: Publishing: 'Hello, world! 4'
[INFO] [1761287894.758282034] [minimal_publisher]: Publishing: 'Hello, world! 5'
[INFO] [1761287895.225036484] [minimal_publisher]: Publishing: 'Hello, world! 6'
[INFO] [1761287895.691645775] [minimal_publisher]: Publishing: 'Hello, world! 7'
run examples_rclcpp_minimal_subscriber subscriber_member_function
[INFO] [1761287865.364473903] [minimal_subscriber]: I heard: 'Hello, world! 0'
[INFO] [1761287865.828627647] [minimal_subscriber]: I heard: 'Hello, world! 1'
[INFO] [1761287866.295444309] [minimal_subscriber]: I heard: 'Hello, world! 2'
[INFO] [1761287866.761893913] [minimal_subscriber]: I heard: 'Hello, world! 3'
[INFO] [1761287867.228254012] [minimal_subscriber]: I heard: 'Hello, world! 4'
[INFO] [1761287867.694139983] [minimal_subscriber]: I heard: 'Hello, world! 5'
7.实现自定义接口
原文中这篇将msg文件和使用它的包放到了一起,并没有单独建立独立的消息定义包,在这里就不再介绍了,具体的配置就是把单独定义包的package.xml、CMakeList文件中的内容移到了这个统一的包中。
8.在类中使用参数(c++)
主要包括以下几个接口:
// 声明参数
this->declare_parameter("my_parameter", "world"); //读取参数值
std::string my_param = this->get_parameter("my_parameter").as_string(); //设置参数
this->set_parameter(rclcpp::Parameter("my_parameter", "world")); //批量设置参数
std::vector<rclcpp::Parameter> all_new_parameters{rclcpp::Parameter("my_parameter", "world")//其他参数};
this->set_parameters(all_new_parameters);
9.ros2doctor
ros2doctor 检查 ROS 2 的各个方面,包括平台、版本、网络、环境、运行系统等,并警告您可能出现的错误和问题原因。
# 简单使用
ros2 doctor# 获取完整报告
ros2 doctor --report
比如会检查某些节点发布的队列无人订阅等等。
10.创建和使用插件(c++)
略
