節點是通過ROS Graph進行通信的可執行進程。在本文中,節點將通過話題以字符串消息的形式相互傳遞信息。這里使用的例子是一個簡單的"talker"和“listener”系統,一個節點發布數據,另一個節點訂閱話題,以便接收該數據。
這些示例中使用的代碼可以在這里找到。
1.創建一個功能包
在上一節的基礎上,即擁有dev_ws
功能包的前提下,執行以下命令:
cd dev_ws/src
ros2 pkg create --build-type ament_cmake cpp_pubsub
終端將返回一條消息,驗證cpp_pubsub
包及其所有必要的文件和文件夾的創建。
導航到dev_ws/src/cpp_pubsub/src
中,這就是包含可執行文件的源文件所在的目錄。
2.編寫一個發布節點
首先,通過以下命令下載示例talker代碼(在src
目錄下):
wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/foxy/rclcpp/topics/minimal_publisher/member_function.cpp
打開剛剛下載的文件publisher_member_function.cpp
:
#include <chrono>
#include <memory>
#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_publisher<std_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::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}
2.1審閱代碼
代碼的頂部為程序所需要的C++頭文件,在頭文件之后是rclcpp/rclcpp.hpp
,它包含了ROS2系統中最常見的部分,最后一是std_msgs/msg/string.hpp
,它包含了用於發布數據的內置消息類型。
這些代碼行表示了節點的依賴關系:
#include <chrono>
#include <memory>
#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
,並將coun_
初始化為0。在構造函數內部,使用String
消息類型、話題名稱topic
,並且限制消息隊列的大小來初始化發布者。接下來,初始化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);
}
最后是定時器(timer)、發布器(publisher)和計數器(counter)字段的聲明:
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
在MinimalPublisher
類之后是main
,其中節點實際執行的位置。rclcpp::init
初始化ROS 2, rclcpp::spin
開始處理節點的數據,包括定時器的回調。
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}
2.2添加依賴
返回到dev_ws/src/cpp_pubsub
目錄,其中已經包含CMakeLists.txt
和package.xml
文件。
打開package.xml
文件,確保<description>
, <maintainer>
and <license>
這些標簽填寫完畢:
<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
構建工具依賴項后添加一行,並粘貼以下依賴項對應於節點的include語句:
<depend>rclcpp</depend>
<depend>std_msgs</depend>
這表明了程序執行是需要依賴rclcpp
和std_msgs
。
不要忘記保存文件。
2.3CMakeLists.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
:
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(TARGETS
talker
DESTINATION lib/${PROJECT_NAME})
ament_package()
現在可以編譯包,生成本地設置文件並運行它(不要忘記設置環境變量,否則提示包不存在):
ros2 run cpp_pubsub talker

3.編寫訂閱者節點
回到dev_ws/src/cpp_pubsub/src
來創建訂閱者節點,在終端輸入:
wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/foxy/rclcpp/topics/minimal_subscriber/member_function.cpp
在終端輸入ls
,此時返回:
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")
{
subscription_ = 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;
}
3.1審閱代碼
訂閱者節點的代碼與發布者的代碼幾乎相同。現在該節點被命名為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
宏將其寫入控制台。
該類中唯一的字段聲明是subscription_。
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_;
main
函數完全相同,只是現在它啟動MinimalSubscriber
節點。對於發布者節點,spin
意味着啟動計時器,但對於訂閱者,它僅僅意味着准備在消息到來時接收它們。
由於此節點與發布者節點具有相同的依賴關系,因此無需向package.xml
添加任何新內容。
3.2CMakeLists.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})
保存文件。
4.編譯和運行
在工作空間中編譯新包:
colcon build --packages-select cpp_pubsub
打開一個新終端,source設置文件:
. install/setup.bash
現在運行tlaker節點:
ros2 run cpp_pubsub talker
終端應該每0.5秒發布一次信息消息,像這樣:

打開另一個終端,再次從dev_ws
設置setup文件,然后啟動listener節點:
ros2 run cpp_pubsub listener
listener將開始向控制台打印消息,從當時打開的消息計數開始,如下所示:

在每個終端中輸入Ctrl+C
,停止節點運行。
5.總結
本文創建了兩個節點來發布和訂閱話題上的數據。在編譯和運行它們之前,需要將它們的依賴項和可執行文件添加到包配置文件中。
如果給您帶來幫助,希望能給點個關注,以后還會陸續更新有關機器人的內容,點個關注不迷路~歡迎大家一起交流學習。
都看到這了,點個推薦再走吧~
未經允許,禁止轉載。