節點是通過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.編譯和執行
可以使用如下命令解決依賴問題:
rosdep install -i --from-path src --rosdistro foxy -y
接下來在工作空間中編譯新包:
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.總結
本文建立了兩個節點來發布和訂閱話題上的資料。在編譯和執行它們之前,需要將它們的依賴項和可執行檔案新增到包配置檔案中。
如果給您帶來幫助,希望能給點個關注,以後還會陸續更新有關機器人的內容,點個關注不迷路~歡迎大家一起交流學習。
都看到這了,點個推薦再走吧~
未經允許,禁止轉載。