我使用的是humble版本
打开终端,执行以下命令创建工作空间和 src
目录:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
在 src
目录下创建一个新的功能包,这里假设功能包名为 cpp_pubsub
,依赖项为 rclcpp
和 std_msgs
:
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake cpp_pubsub --dependencies rclcpp std_msgs
在 ~/ros2_ws/src/cpp_pubsub/src
目录下创建一个名为 talker.cpp
的文件:
touch ~/ros2_ws/src/cpp_pubsub/src/talker.cpp
使用文本编辑器打开 talker.cpp
文件,添加以下代码:
#include
#include
using namespace std::chrono_literals;
class Talker : public rclcpp::Node
{
public:
Talker() : Node("talker")
{
// 创建发布者,发布 std_msgs::msg::String 类型的消息到 'topic' 话题,队列大小为 10
publisher_ = this->create_publisher("topic", 10);
// 创建定时器,每 500 毫秒触发一次,调用 timer_callback 函数
timer_ = this->create_wall_timer(
500ms, std::bind(&Talker::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::Publisher::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
size_t count_ = 0;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
这段代码创建了一个名为 talker
的节点,它会每 500 毫秒发布一条消息到 topic
话题。
在 ~/ros2_ws/src/cpp_pubsub/src
目录下创建一个名为 listener.cpp
的文件:
touch ~/ros2_ws/src/cpp_pubsub/src/listener.cpp
使用文本编辑器打开 listener.cpp
文件,添加以下代码:
#include
#include
class Listener : public rclcpp::Node
{
public:
Listener() : Node("listener")
{
// 创建订阅者,订阅 'topic' 话题的 std_msgs::msg::String 类型消息,队列大小为 10
subscription_ = this->create_subscription(
"topic", 10, std::bind(&Listener::topic_callback, this, std::placeholders::_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::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = std::make_shared();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
这段代码创建了一个名为 listener
的节点,它会订阅 topic
话题,并在接收到消息时打印消息内容。
CMakeLists.txt
文件打开 ~/ros2_ws/src/cpp_pubsub/CMakeLists.txt
文件,添加以下内容:
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})
这些配置用于编译 talker.cpp
和 listener.cpp
文件,并将生成的可执行文件安装到指定目录。
再补充一下具体解释
- 作用:
这行代码是 CMake 指令,其功能是告知 CMake 要把add_executable(talker src/talker.cpp)
src/talker.cpp
文件编译成一个可执行文件,并且将这个可执行文件命名为talker
。在 ROS 2 的 C++ 功能包开发里,每个节点的代码一般都要编译成可执行文件,这样才能在 ROS 2 环境中运行。- 原因:在 ROS 2 中,节点是运行的基本单元,为了让节点能够运行,需要把节点的源代码编译成可执行文件。
add_executable
指令就负责完成这个编译任务。
ament_target_dependencies(talker rclcpp std_msgs)
- 作用:这是
ament_cmake
提供的指令,它的作用是为talker
可执行文件添加依赖项。这里的依赖项是rclcpp
和std_msgs
。rclcpp
是 ROS 2 的 C++ 客户端库,节点要与 ROS 2 系统进行交互(像发布和订阅消息)就需要用到它;std_msgs
则是 ROS 2 提供的标准消息类型库,在节点代码里可能会使用到其中定义的消息类型(例如std_msgs::msg::String
)。- 原因:在编译节点代码时,编译器需要知道这些依赖库的位置和相关信息,
ament_target_dependencies
指令会自动处理这些依赖关系,确保编译器能够找到并正确链接这些库。
install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME})
- 作用:这行代码是 CMake 的
install
指令,其目的是在编译完成后,将talker
和listener
这两个可执行文件安装到指定的目录。DESTINATION lib/${PROJECT_NAME}
表示安装到lib
目录下以功能包名称命名的子目录中。在 ROS 2 里,这样做可以让这些可执行文件在系统中被正确地找到和运行。- 原因:安装可执行文件到指定目录是为了方便管理和使用。当使用
ros2 run
命令来运行节点时,ROS 2 会从指定的安装目录中查找可执行文件。如果不进行安装操作,就无法直接使用ros2 run
命令来运行节点。
在 ~/ros2_ws
目录下执行以下命令编译工作空间:
cd ~/ros2_ws
colcon build
注意:每次打开新的终端运行节点前,都需要设置环境变量:
source install/setup.bash
打开一个新终端,运行发布者节点:
ros2 run cpp_pubsub talker
再打开一个新终端,运行订阅者节点:
ros2 run cpp_pubsub listener
此时,你会看到发布者节点每隔 500 毫秒发布一条消息,订阅者节点会接收到这些消息并打印出来。