ros2 从零开始8 HelloWorld前言上节课介绍了colcon的编译以及官方example本章要写自己的HelloWorld。背景关于包package包是你 ros2代码的一个组织单位。 如果你想安装代码或与他人分享那么你需要把代码整理成一个包。 通过包你可以发布ros2作品让其他人轻松构建和使用。ros2的包创建使用 ament 作为构建系统colcon 作为构建工具。 你可以用CMake或Python创建包这两种语言官方支持当然也有其他构建类型。包package的构成包由如下组成CMakeLists.txt include目录、src目录、package.xml描述my_package/ CMakeLists.txt include/my_package/ package.xml src/而一个大型的工程由多个包构成。如下工作区包含多个包。不同构建类型的包比如CMake、Python等。但不能是嵌套包。workspace_folder/ src/ cpp_package_1/ CMakeLists.txt include/cpp_package_1/ package.xml src/ py_package_1/ package.xml resource/py_package_1 setup.cfg setup.py py_package_1/ ... cpp_package_n/ CMakeLists.txt include/cpp_package_n/ package.xml src/实践创建一个包命令语法是ros2 pkg create --build-type ament_cmake --license Apache-2.0 package_name如下我们创建自己的包 mypackagerootbc2bf85b2e4a:~# cd ~/ros2_ws/src/ rootbc2bf85b2e4a:~/ros2_ws/src# ls example_interfaces examples rootbc2bf85b2e4a:~/ros2_ws/src# ros2 pkg create --build-type ament_cmake --license Apache-2.0 mypackage going to create a new package package name: mypackage destination directory: /root/ros2_ws/src package format: 3 version: 0.0.0 description: TODO: Package description maintainer: [root xianlutinnove.com.cn] 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 rootbc2bf85b2e4a:~/ros2_ws/src# ls example_interfaces examples mypackage rootbc2bf85b2e4a:~/ros2_ws/src#以上命令ros2帮我们自动生成了mypackage。如下图显示可以看出来这个命令创建了基本的include目录、src目录、CMakeLists.txt、package.xml创建HelloWorld我们将设计2个节点执行程序talker负责向某个topic定期发送helloworldlistener则监听某个topic来读取这些信息。开始吧2.1 在src里面创建一个cpp文件talker.cpp其内容如下#include chrono #include functional #include memory #include string #include rclcpp/rclcpp.hpp #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. */ class MinimalPublisher : public rclcpp::Node { public: MinimalPublisher() : Node(minimal_publisher), count_(0) { publisher_ this-create_publisherstd_msgs::msg::String(topic, 10); 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::Publisherstd_msgs::msg::String::SharedPtr publisher_; size_t count_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_sharedMinimalPublisher()); rclcpp::shutdown(); return 0; }解析 MinimalPublisher 继承自rclcpp::Node 在初始化时创建publisher_发布者发布的主题名是topic。类型是字符串std_msgs::msg::String。然后又创建了500ms的定时器定期调用timer_callback。 timer_callback生成消息message,并且填充Hello, world! count,发布到主题名是topic的主题上2.2 在src里面一个cpp文件listener.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) { subscription_ this-create_subscriptionstd_msgs::msg::String( 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()); } rclcpp::Subscriptionstd_msgs::msg::String::SharedPtr subscription_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_sharedMinimalSubscriber()); rclcpp::shutdown(); return 0; }解析 MinimalSubscriber 继承自rclcpp::Node 在初始化时创建subscription_订阅者同样订阅的主题名是topic。用topic_callback来接收。topic_callback在收到消息时将其内容打印出来。2.3 修改CMakeLists.txt在ament_package()的前一行新增如下信息add_executable(talker src/talker.cpp) ament_target_dependencies(talker rclcpp std_msgs) add_executable(listener src/listener.cpp) ament_target_dependencies(listener rclcpp std_msgs) install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME})完整的CMakeLists.txt如下cmake_minimum_required(VERSION 3.8) project(mypackage) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES Clang) add_compile_options(-Wall -Wextra -Wpedantic) endif() # find dependencies find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(std_msgs REQUIRED) # uncomment the following section in order to fill in # further dependencies manually. # find_package(dependency REQUIRED) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # comment the line when a copyright and license is added to all source files set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # comment the line when this package is in a git repo and when # a copyright and license is added to all source files set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() add_executable(talker src/talker.cpp) ament_target_dependencies(talker rclcpp std_msgs) add_executable(listener src/listener.cpp) ament_target_dependencies(listener rclcpp std_msgs) install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME}) ament_package()解析add_executable代表编译的程序ament_target_dependencies则指定其依赖项。install则是安装这2个程序到目标目录上开始编译进入工作区用colcon build等待编译完成。rootbc2bf85b2e4a:/# cd ~/ros2_ws rootbc2bf85b2e4a:~/ros2_ws# colcon build --packages-select mypackage Starting mypackage Finished mypackage [8.49s] Summary: 1 package finished [8.80s]运行编译完成后我们需要安装后才能运行使用source install/setup.sh打开一个终端输入如下命令启动listenerrootbc2bf85b2e4a:~/ros2_ws# cd ~/ros2_ws/ rootbc2bf85b2e4a:~/ros2_ws# source install/setup.sh rootbc2bf85b2e4a:~/ros2_ws# ros2 run mypackage listener另开一个终端输入如下命令启动talkerrootbc2bf85b2e4a:~/ros2_ws# cd ~/ros2_ws/ rootbc2bf85b2e4a:~/ros2_ws# source install/setup.sh rootbc2bf85b2e4a:~/ros2_ws# ros2 run mypackage talker [INFO] [1774603979.683269252] [minimal_publisher]: Publishing: Hello, world! 0 [INFO] [1774603980.192648790] [minimal_publisher]: Publishing: Hello, world! 1 [INFO] [1774603980.614148525] [minimal_publisher]: Publishing: Hello, world! 2 [INFO] [1774603981.118238079] [minimal_publisher]: Publishing: Hello, world! 3 [INFO] [1774603981.618117724] [minimal_publisher]: Publishing: Hello, world! 4 [INFO] [1774603982.104371597] [minimal_publisher]: Publishing: Hello, world! 5 [INFO] [1774603982.591424316] [minimal_publisher]: Publishing: Hello, world! 6 [INFO] [1774603983.080861939] [minimal_publisher]: Publishing: Hello, world! 7可以看到listener也会接受到talker发出的信息。总结此时我们已经完成了最简单的功能发布主题以及订阅主题。 相比于传统的订阅模式我们没有不用自己写订阅模式代码也不用关注进程间通信等问题从而有更多精力则关注在业务逻辑处理上。这也是我们用ros2的理由之一。 ROS2 DDS 机器人框架 工具链加油鸭