一、主要參考來源
Writing a simple publisher and subscriber (C++)
二、具體過程
2.1 創(chuàng)建工作空間和包
首先,創(chuàng)建工作空間及
mkdir -p dev_ws/src
然后,進入
dev_ws/src目錄下,創(chuàng)建我們的功能包
cd dev_ws/src
ros2 pkg create --build-type ament_cmake cpp_pubsub
您的終端將返回一條消息,驗證
cpp_pubsub包及其所有必要的文件和文件夾的創(chuàng)建。
其中--build-type ament_cmake好像是對應與C++語言,使用CMake生成。
然后可以用tree命令查看目錄,命令如下:
tree -L 2 #顯示兩級目錄
返回如下,我的沒有下劃線是因為我創(chuàng)建的時候沒有打下劃線。
.
└── cpppubhub
├── CMakeLists.txt
├── include
├── package.xml
└── src
3 directories, 2 files
2.2 書寫發(fā)布節(jié)點(node)源碼及依賴
接下來,進入源碼環(huán)節(jié),第一份是
C++的發(fā)布者的代碼,應該放在cpp_pubsub/src下;
cd cpp_pubsub/src #進入源目錄
subl publisher_member_function.cpp #在源目錄下創(chuàng)建所需的源文件,不一定要用subl,也可一是gedit等,右鍵創(chuàng)建或者vs-code里面在對應目錄下創(chuàng)建也一樣。
在彈窗出來的空文件里面填寫代碼:
#include <chrono> //及時相關頭文件
#include <functional>
#include <memory> //內存相關頭文件
#include <string>
//ros2的頭文件,一個是cpp的,一個是消息的
#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. */
// 這個例子創(chuàng)建了Node的一個子類,并使用std::bind()注冊一個成員函數作為計時器的回調函數。
class MinimalPublisher : public rclcpp::Node
{
public:
/*公共構造函數將節(jié)點命名為minimal_publisher,
并將coun_初始化為0。在構造函數內部,
使用String消息類型、主題名稱topic和
備份時限制消息所需的隊列大小初始化發(fā)布者。
接下來,初始化timer_,這將導致timer_callback函數每秒執(zhí)行兩次。*/
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:
/*timer_callback函數是設置消息數據和實際發(fā)布消息的地方。
RCLCPP_INFO宏確保將每個發(fā)布的消息打印到控制臺。*/
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_;
};
/*rclcpp::init初始化ROS 2, rclcpp::spin開始處理節(jié)點的數據,包括定時器的回調。*/
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}
返回上一級目錄去修改
CMakeLists.txt和package.xml文件,
cd ..
subl 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下面添加如下兩行依賴
<depend>rclcpp</depend>
<depend>std_msgs</depend>
最后這個文件看起來應該是這樣子的
<?xml version="1.0"?>
<?xml-model schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>cpppubhub</name>
<version>0.0.0</version>
<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
記得保存
然后修改
CMakeLists.txt
subl CMakeLists.txt
在注釋行
# find_package(<dependency> REQUIRED)后面添加尋找依賴包代碼
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
然后,添加可執(zhí)行文件并將其命名為
talker,這樣你就可以使用ros2 run運行你的節(jié)點了:
add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
然后,添加安裝項,方便
ros2 run在系統目錄下找到執(zhí)行文件,這一步不是必須,你也可以讓ros2 run去制定目錄下面執(zhí)行指定的可執(zhí)行文件,二不是放到目錄下,這點用過點CMake的同學應該都很熟悉了。
install(TARGETS
talker
DESTINATION lib/${PROJECT_NAME})
你的
CMakeLists.txt可能是這樣的
cmake_minimum_required(VERSION 3.5)
project(cpppubhub)
# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
# 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 dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
install(TARGETS
talker
DESTINATION lib/${PROJECT_NAME})
ament_package()
可以去掉一些不必要的部分,可能就是這樣的了
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()
2.3 書寫接受節(jié)點(node)源碼及依賴
有了上一小節(jié)的經驗,這一小節(jié)應該會快一些,首先,進入
dev_ws/src/cpp_pubsub/src文件夾下創(chuàng)建subscriber_member_function.cpp文件,書寫代碼如下
#include <memory> //內存相關
//ros2相關
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1;
class MinimalSubscriber : public rclcpp::Node
{
public:
/*
訂閱者節(jié)點的代碼與發(fā)布者的代碼幾乎相同。
在該節(jié)點被命名為minimal_subscriber,
構造函數使用該節(jié)點的create_subscription類來執(zhí)行回調。
發(fā)布者和訂閱者使用的主題名稱和消息類型必須匹配,才能進行通信。
*/
MinimalSubscriber()
: Node("minimal_subscriber")
{
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
}
private:
/*topic_callback函數接收通過主題發(fā)布的字符串消息數據,并使用RCLCPP_INFO宏將其寫入控制臺。*/
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_;
};
/*rclcpp::init初始化ROS 2,
rclcpp::spin開始處理節(jié)點的數據*/
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalSubscriber>());
rclcpp::shutdown();
return 0;
}
繼續(xù)修改
CMakeLists.txt文件,由于ros2依賴包都一樣,所以package.xml文件不用再修改了。在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})
最后的
CMakeLists.txt可能是這樣:
cmake_minimum_required(VERSION 3.5)
project(cpppubhub)
# Default to C99
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
# 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 dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# uncomment the line when a copyright and license is not present in all source files
#set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# uncomment the line when this package is not in a git repo
#set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
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)
# 只安裝talker的
#install(TARGETS
# talker
# DESTINATION lib/${PROJECT_NAME})
# 一起安裝的
install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})
ament_package()
簡化版本可能是這樣的
cmake_minimum_required(VERSION 3.5)
roject(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)
#添加talker可執(zhí)行文件
add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)
#添加listener可執(zhí)行文件
add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs)
# 安裝項目的talker和listener可執(zhí)行文件到系統目錄下
install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})
ament_package()
2.4 構建和運行(Build and run)
檢查
rclcpp和std_msgs是不是都正常作為ros2的一部分而正確安裝了。
rosdep install -i --from-path src --rosdistro <distro> -y #<distro>可以換成你的對應版本,如我的是foxy
如一切順利,你應該可以看到如下返回:
#All required rosdeps installed successfully
如出現錯誤,可以返回去看Ubuntu20 ros2安裝或者Installing ROS 2 on Ubuntu Linux。
接下來,正式進行構建,先進入工作空間根目錄
dev_ws,此時目錄大體應該如下:
.
└── src
└── cpp_pubsub
├── CMakeLists.txt
├── include
│ └── cpp_pubsub
├── package.xml
└── src
├── publisher_member_function.cpp
└── subscriber_member_function.cpp
5 directories, 4 files
構建
colcon build --packages-select cpp_pubsub
構建完成后,兩層目錄大抵如下
.
├── build
│ ├── COLCON_IGNORE
│ └── cpp_pubsub
├── install
│ ├── COLCON_IGNORE
│ ├── cpp_pubsub
│ ├── local_setup.bash
│ ├── local_setup.ps1
│ ├── local_setup.sh
│ ├── _local_setup_util_ps1.py
│ ├── _local_setup_util_sh.py
│ ├── local_setup.zsh
│ ├── setup.bash
│ ├── setup.ps1
│ ├── setup.sh
│ └── setup.zsh
├── log
│ ├── build_2021-05-03_15-41-52
│ ├── build_2021-05-03_15-42-25
│ ├── COLCON_IGNORE
│ ├── latest -> latest_build
│ └── latest_build -> build_2021-05-03_15-42-25
└── src
└── cpp_pubsub
11 directories, 13 files
再打開一個
terminal,兩個terminal都進入dev_ws目錄下,且source
. install/setup.bash
其中一個輸入
ros2 run cpp_pubsub talker
另一個輸入
ros2 run cpp_pubsub listener
觀察運行。
ctrl+c中斷運行

三、接下來
接下來可能嘗試用
Python實現一遍,但更大的可能是嘗試C++的服務示例。
四、總結
就熟悉了一下基本的調用以及
CMakeLists.txt和package.xml的編寫。