docs(nav): 阅读路径导航 + 数字统一
This commit is contained in:
@@ -21,12 +21,17 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
|
||||
# 必备: 找 ament_cmake + 依赖
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(std_msgs REQUIRED)
|
||||
|
||||
set(THIS_PACKAGE_INCLUDE_DEPENDS
|
||||
rclcpp
|
||||
std_msgs
|
||||
)
|
||||
|
||||
# 库:含 ChatterPublisher / ChatterSubscriber 实现
|
||||
# 库:只含 ChatterPublisher / ChatterSubscriber 类实现(无 main)
|
||||
add_library(${PROJECT_NAME}_core SHARED
|
||||
src/chatter_publisher.cpp
|
||||
src/chatter_subscriber.cpp
|
||||
@@ -37,11 +42,11 @@ target_include_directories(${PROJECT_NAME}_core PUBLIC
|
||||
)
|
||||
ament_target_dependencies(${PROJECT_NAME}_core ${THIS_PACKAGE_INCLUDE_DEPENDS})
|
||||
|
||||
# 可执行文件:两个独立 main(每个节点独立可执行)
|
||||
add_executable(chatter_publisher_cpp src/chatter_publisher.cpp)
|
||||
# 可执行文件:每个 main 单独编译
|
||||
add_executable(chatter_publisher_cpp src/chatter_publisher_main.cpp)
|
||||
target_link_libraries(chatter_publisher_cpp ${PROJECT_NAME}_core)
|
||||
|
||||
add_executable(chatter_subscriber_cpp src/chatter_subscriber.cpp)
|
||||
add_executable(chatter_subscriber_cpp src/chatter_subscriber_main.cpp)
|
||||
target_link_libraries(chatter_subscriber_cpp ${PROJECT_NAME}_core)
|
||||
|
||||
# 安装:可执行 + 库 + launch
|
||||
@@ -56,4 +61,12 @@ install(DIRECTORY launch
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
# 测试
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_cmake_gtest REQUIRED)
|
||||
ament_add_gtest(test_pub_sub test/test_pub_sub.cpp)
|
||||
ament_target_dependencies(test_pub_sub ${THIS_PACKAGE_INCLUDE_DEPENDS})
|
||||
target_link_libraries(test_pub_sub ${PROJECT_NAME}_core)
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
+369
-31
@@ -1,54 +1,392 @@
|
||||
# cpp_pubsub
|
||||
# cpp_pubsub — C++ Topic 发布订阅
|
||||
|
||||
ROS2 Topic pub/sub 演示包 (C++)。属于 Level 1 基础机制第 1-2 块。
|
||||
> 跟 `py_pubsub` 一样的东西,用 C++ 写。**只用 Python 的项目可以跳过这个**。
|
||||
>
|
||||
> 预计学习时间:1-2 小时。
|
||||
>
|
||||
> **前置知识**:读懂 py_pubsub 的 README + 基础 C++(知道 class / shared_ptr / std:: 是什么)。
|
||||
|
||||
## 功能
|
||||
---
|
||||
|
||||
- **`chatter_publisher_cpp`**: 周期性发布 `std_msgs/String` 到 `/chatter`
|
||||
- **`chatter_subscriber_cpp`**: 订阅 `/chatter`,打印消息
|
||||
## 这是什么?
|
||||
|
||||
与 [`py_pubsub`](../py_pubsub/) 配合,演示 Python ↔ C++ 跨语言互通。
|
||||
`py_pubsub` 的 C++ 版本。**同一个 Topic,Python 和 C++ 节点互通**(都用 `std_msgs/String` 类型)。
|
||||
|
||||
## C++ 节点类(库)
|
||||
**本包存在的意义**:
|
||||
- 看到 Python 节点发的消息,C++ 节点能收到(反之亦然)
|
||||
- 学会用 **rclcpp**(ROS2 C++ 客户端库) 写节点
|
||||
- 知道工业级 ROS2 项目的 C++ 代码长什么样
|
||||
|
||||
`cpp_pubsub_core` 共享库包含:
|
||||
---
|
||||
|
||||
- `cpp_pubsub::ChatterPublisher` — `rclcpp::Node` 派生
|
||||
- `cpp_pubsub::ChatterSubscriber` — `rclcpp::Node` 派生
|
||||
## 🤔 我已经会 py_pubsub 了,为什么还要学 C++ 版?
|
||||
|
||||
均位于 `namespace cpp_pubsub`,便于复用与测试。
|
||||
**实话**:Python 写 ROS2 简单 90%,**大多数项目用 Python 就够了**。但有些场景用 C++ 更合适:
|
||||
|
||||
## 运行
|
||||
| 场景 | 为什么可能用 C++ |
|
||||
|---|---|
|
||||
| 性能敏感(图像/点云/SLAM) | Python GC 不可控 + 速度慢 |
|
||||
| 已有 C++ 库要复用 | OpenCV、PCL、MoveIt2 全是 C++ |
|
||||
| 嵌入式部署 | 部分芯片只支持 C/C++ |
|
||||
|
||||
```bash
|
||||
# 单独启动
|
||||
ros2 run cpp_pubsub chatter_publisher_cpp
|
||||
ros2 run cpp_pubsub chatter_subscriber_cpp
|
||||
**如果你的项目不涉及上面这些,只用 Python 就行,不必学这个包。**
|
||||
|
||||
# launch
|
||||
ros2 launch cpp_pubsub pubsub_launch.py
|
||||
---
|
||||
|
||||
# 实时观察
|
||||
ros2 topic info /chatter -v
|
||||
ros2 topic echo /chatter
|
||||
## 🎯 学完之后你能做什么?
|
||||
|
||||
1. ✅ 用 C++ 写 ROS2 节点(知道 `rclcpp::Node` 是什么)
|
||||
2. ✅ 理解 Python ↔ C++ 跨语言互通(为什么能行?)
|
||||
3. ✅ 知道 `shared_ptr` / `std::bind` / `rclcpp::QoS` 这些 C++ 套路
|
||||
4. ✅ 读懂 ROS2 官方 C++ 示例代码
|
||||
|
||||
---
|
||||
|
||||
## 📁 文件结构
|
||||
|
||||
```
|
||||
src/cpp_pubsub/
|
||||
├── src/
|
||||
│ ├── chatter_publisher.cpp/hpp # Publisher 类(声明 + 实现)
|
||||
│ ├── chatter_publisher_main.cpp # Publisher 的 main 入口(单独文件)
|
||||
│ ├── chatter_subscriber.cpp/hpp # Subscriber 类(声明 + 实现)
|
||||
│ └── chatter_subscriber_main.cpp # Subscriber 的 main 入口
|
||||
├── launch/pubsub_cpp_launch.py # 启动两个 cpp 节点
|
||||
├── test/test_pub_sub.cpp # gtest 单元测试
|
||||
├── CMakeLists.txt # C++ 编译配置
|
||||
└── package.xml # ROS2 包元数据
|
||||
```
|
||||
|
||||
## 测试
|
||||
**C++ 项目比 Python 多一个东西**:`CMakeLists.txt`(告诉编译器怎么编译)。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 跑起来
|
||||
|
||||
```bash
|
||||
source /opt/ros/humble/setup.bash
|
||||
source /root/ros2_ws/install/setup.bash
|
||||
|
||||
# 只跑 C++ 的两个节点
|
||||
ros2 launch cpp_pubsub pubsub_cpp_launch.py
|
||||
```
|
||||
|
||||
**预期输出**:
|
||||
```
|
||||
[INFO] [chatter_publisher_cpp]: Publishing: "Hello World C++: 0"
|
||||
[INFO] [chatter_publisher_cpp]: Publishing: "Hello World C++: 1"
|
||||
...
|
||||
[INFO] [chatter_subscriber_cpp]: I heard: Hello World C++: 0
|
||||
```
|
||||
|
||||
**跨语言验证**(另开终端,跟 Python 互通):
|
||||
|
||||
```bash
|
||||
# 终端 1: C++ Publisher
|
||||
ros2 run cpp_pubsub chatter_publisher_cpp
|
||||
|
||||
# 终端 2: Python Subscriber(可以!因为都用 std_msgs/String)
|
||||
ros2 run py_pubsub chatter_subscriber
|
||||
```
|
||||
|
||||
**预期效果**:Python Subscriber 能收到 C++ Publisher 的消息(和反之)。
|
||||
|
||||
---
|
||||
|
||||
## 📖 C++ 黑魔法词典(新手必看)
|
||||
|
||||
读代码前先扫一眼这些符号:
|
||||
|
||||
| 符号 | 含义 | Python 对应 |
|
||||
|---|---|---|
|
||||
| `class Foo : public rclcpp::Node` | Foo 继承 rclcpp::Node | `class Foo(Node):` |
|
||||
| `rclcpp::Node` | ROS2 C++ 节点基类 | `rclpy.node.Node` |
|
||||
| `std::shared_ptr<T>` | 共享指针(自动内存管理) | 不用写,GC 自动管 |
|
||||
| `::SharedPtr` | rclcpp 的共享指针类型 | 无 |
|
||||
| `std::make_shared<T>(...)` | 创建 shared_ptr 的工厂函数 | 无 |
|
||||
| `std::bind(&Foo::method, this)` | 把方法 + this 绑成一个可调用对象 | lambda 即可 |
|
||||
| `std::placeholders::_1` | bind 的占位符,代表"第一个参数" | lambda 参数 |
|
||||
| `std::chrono::seconds(1)` | 1 秒的 std 写法(可简写为 `1s`) | `1.0` |
|
||||
| `create_wall_timer(...)` | 创建定时器 | `create_timer(...)` |
|
||||
| `create_publisher<T>(topic, qos)` | 模板参数 T 是消息类型 | `create_publisher(T, topic, qos)` |
|
||||
| `<std_msgs::msg::String>` | 嵌套命名空间,ROS 消息类型 | `String`(从 std_msgs.msg 导入) |
|
||||
|
||||
**`std::bind` 是 C++ 的"高阶函数"**,把方法 + 对象绑在一起,让定时器能回调。Python 里 lambda 就能搞定。
|
||||
|
||||
---
|
||||
|
||||
## 📖 Publisher 完整代码
|
||||
|
||||
```cpp
|
||||
// chatter_publisher.hpp - 类声明
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/string.hpp"
|
||||
|
||||
namespace cpp_pubsub {
|
||||
|
||||
class ChatterPublisher : public rclcpp::Node {
|
||||
public:
|
||||
explicit ChatterPublisher(const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
|
||||
|
||||
private:
|
||||
void timer_callback();
|
||||
|
||||
// shared_ptr:节点销毁时自动释放这些对象(不用手动 delete)
|
||||
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
|
||||
rclcpp::TimerBase::SharedPtr timer_;
|
||||
std::size_t count_;
|
||||
};
|
||||
|
||||
} // namespace cpp_pubsub
|
||||
|
||||
// chatter_publisher.cpp - 类实现
|
||||
#include "cpp_pubsub/chatter_publisher.hpp"
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
namespace cpp_pubsub {
|
||||
|
||||
ChatterPublisher::ChatterPublisher(const rclcpp::NodeOptions & options)
|
||||
: rclcpp::Node("chatter_publisher_cpp", options), // 调父类构造函数,节点名 "chatter_publisher_cpp"
|
||||
count_(0)
|
||||
{
|
||||
// 1) 声明参数(C++ 用 ParameterDescriptor + set__description)
|
||||
// ⚠️ 注意:是 set__description(双下划线),不是 set_description
|
||||
// 双下划线 = ROS2 自动生成的 setter(类似 Python 的 description 属性)
|
||||
this->declare_parameter<std::string>(
|
||||
"message_prefix", "Hello World C++: ",
|
||||
rcl_interfaces::msg::ParameterDescriptor().set__description("消息前缀"));
|
||||
this->declare_parameter<double>(
|
||||
"publish_rate_hz", 1.0,
|
||||
rcl_interfaces::msg::ParameterDescriptor().set__description("发布频率 (Hz)"));
|
||||
|
||||
// 2) 读参数
|
||||
const std::string prefix = this->get_parameter("message_prefix").as_string();
|
||||
const double rate = this->get_parameter("publish_rate_hz").as_double();
|
||||
|
||||
// 3) 创建 Publisher
|
||||
// 模板参数 <std_msgs::msg::String> 表示消息类型
|
||||
// 第二个参数 10 是队列大小(跟 Python 一样)
|
||||
publisher_ = this->create_publisher<std_msgs::msg::String>("chatter_cpp", 10);
|
||||
|
||||
// 4) 创建定时器
|
||||
// std::bind 把方法 + this 绑成一个"函数对象"
|
||||
// 让定时器能调用 ChatterPublisher::timer_callback
|
||||
timer_ = this->create_wall_timer(
|
||||
std::chrono::milliseconds(static_cast<int>(1000.0 / rate)),
|
||||
std::bind(&ChatterPublisher::timer_callback, this));
|
||||
}
|
||||
|
||||
void ChatterPublisher::timer_callback() {
|
||||
auto msg = std_msgs::msg::String();
|
||||
msg.data = "Hello World C++: " + std::to_string(count_++);
|
||||
publisher_->publish(msg); // 发布!
|
||||
}
|
||||
|
||||
} // namespace cpp_pubsub
|
||||
```
|
||||
|
||||
**`set__description` 双下划线**:这是 ROS2 的 IDL(接口描述语言)生成器自动生成的 setter 命名约定。**返回自身引用,可以链式调用**:
|
||||
```cpp
|
||||
ParameterDescriptor().set__description("...") // 链式风格
|
||||
// 等价于
|
||||
auto d = ParameterDescriptor();
|
||||
d.set__description("...");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 Subscriber 完整代码
|
||||
|
||||
```cpp
|
||||
// chatter_subscriber.hpp
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "std_msgs/msg/string.hpp"
|
||||
|
||||
namespace cpp_pubsub {
|
||||
|
||||
class ChatterSubscriber : public rclcpp::Node {
|
||||
public:
|
||||
explicit ChatterSubscriber(const rclcpp::NodeOptions & options = rclcpp::NodeOptions());
|
||||
|
||||
private:
|
||||
void message_callback(const std_msgs::msg::String::SharedPtr msg);
|
||||
|
||||
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
|
||||
std::size_t received_count_;
|
||||
};
|
||||
|
||||
} // namespace cpp_pubsub
|
||||
|
||||
// chatter_subscriber.cpp
|
||||
#include "cpp_pubsub/chatter_subscriber.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace cpp_pubsub {
|
||||
|
||||
ChatterSubscriber::ChatterSubscriber(const rclcpp::NodeOptions & options)
|
||||
: rclcpp::Node("chatter_subscriber_cpp", options), received_count_(0)
|
||||
{
|
||||
this->declare_parameter<std::string>(
|
||||
"topic_name", "chatter",
|
||||
rcl_interfaces::msg::ParameterDescriptor().set__description("订阅话题名"));
|
||||
|
||||
const std::string topic_name = this->get_parameter("topic_name").as_string();
|
||||
|
||||
// create_subscription<消息类型>(话题名, 队列大小, callback)
|
||||
// _1 是 std::bind 的占位符,代表"收到的 msg"(传给 message_callback 的第一个参数)
|
||||
subscription_ = this->create_subscription<std_msgs::msg::String>(
|
||||
topic_name, 10,
|
||||
std::bind(&ChatterSubscriber::message_callback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void ChatterSubscriber::message_callback(const std_msgs::msg::String::SharedPtr msg) {
|
||||
received_count_++;
|
||||
if (received_count_ % 10 == 0) {
|
||||
RCLCPP_INFO(this->get_logger(),
|
||||
"recv #%zu: \"%s\"", received_count_, msg->data.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cpp_pubsub
|
||||
```
|
||||
|
||||
**对比 Python Subscriber**:
|
||||
```python
|
||||
self.subscription = self.create_subscription(
|
||||
String, 'chatter', self.listener_callback, 10)
|
||||
|
||||
def listener_callback(self, msg):
|
||||
self.get_logger().info(f'I heard: {msg.data}')
|
||||
```
|
||||
|
||||
**主要区别**:
|
||||
- C++ 用 `std::bind` + `std::placeholders::_1` 占位符
|
||||
- C++ 消息是 `SharedPtr`(智能指针)
|
||||
- C++ 字符串访问用 `msg->data.c_str()`(指针解引用 + C 字符串转换)
|
||||
|
||||
---
|
||||
|
||||
## 📖 main 函数(单独文件)
|
||||
|
||||
```cpp
|
||||
// chatter_publisher_main.cpp
|
||||
int main(int argc, char * argv[]) {
|
||||
rclcpp::init(argc, argv); // 初始化 ROS2
|
||||
rclcpp::spin(std::make_shared<cpp_pubsub::ChatterPublisher>()); // 创建节点 + spin
|
||||
rclcpp::shutdown(); // 关闭 ROS2
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
**为什么 main 单独一个文件**:类实现要单独编译给测试用(`gtest` 链接时会出冲突,如果 main 在类实现里)。
|
||||
|
||||
**`spin` 是什么**:死循环,反复调用所有 callback(定时器、订阅者、Service...)。按 `Ctrl+C` 退出。
|
||||
|
||||
---
|
||||
|
||||
## 📖 CMakeLists.txt 怎么读?
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(cpp_pubsub LANGUAGES CXX)
|
||||
|
||||
# 1) 找依赖
|
||||
find_package(ament_cmake REQUIRED) # ROS2 构建系统
|
||||
find_package(rclcpp REQUIRED) # ROS2 C++ 客户端
|
||||
find_package(std_msgs REQUIRED) # 标准消息
|
||||
find_package(ament_cmake_gtest REQUIRED) # gtest 支持(test_depend)
|
||||
|
||||
# 2) 类实现编译成共享库(给 gtest 用)
|
||||
add_library(${PROJECT_NAME}_core SHARED
|
||||
src/chatter_publisher.cpp
|
||||
src/chatter_subscriber.cpp)
|
||||
target_include_directories(${PROJECT_NAME}_core PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>)
|
||||
ament_target_dependencies(${PROJECT_NAME}_core rclcpp std_msgs)
|
||||
|
||||
# 3) 可执行文件(链接共享库 + main 单独文件)
|
||||
add_executable(chatter_publisher_cpp src/chatter_publisher_main.cpp)
|
||||
target_link_libraries(chatter_publisher_cpp ${PROJECT_NAME}_core)
|
||||
|
||||
add_executable(chatter_subscriber_cpp src/chatter_subscriber_main.cpp)
|
||||
target_link_libraries(chatter_subscriber_cpp ${PROJECT_NAME}_core)
|
||||
|
||||
# 4) 安装 + 测试
|
||||
install(TARGETS chatter_publisher_cpp chatter_subscriber_cpp ${PROJECT_NAME}_core
|
||||
DESTINATION lib/${PROJECT_NAME})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
ament_add_gtest(test_pub_sub test/test_pub_sub.cpp)
|
||||
target_link_libraries(test_pub_sub ${PROJECT_NAME}_core)
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
```
|
||||
|
||||
**跟 Python setup.py 对比**:CMakeLists.txt 更复杂,但更精确控制编译(链接、include 路径、依赖)。
|
||||
|
||||
---
|
||||
|
||||
## 🧪 跑测试
|
||||
|
||||
```bash
|
||||
colcon test --packages-select cpp_pubsub
|
||||
colcon test-result --all --verbose
|
||||
```
|
||||
|
||||
测试覆盖(在 `test/test_pub_sub.cpp`):
|
||||
**预期**:`cpp_pubsub: gtest 3/3 ✓` 全部通过。
|
||||
|
||||
| 用例 | 内容 |
|
||||
---
|
||||
|
||||
## 🔧 自己改代码
|
||||
|
||||
1. 改 `src/chatter_publisher.cpp`(比如改默认消息前缀)
|
||||
2. **必须重编译**(C++ 不会自动生效):
|
||||
```bash
|
||||
colcon build --packages-select cpp_pubsub
|
||||
```
|
||||
3. 重跑 launch(旧的先 `Ctrl+C` 停掉)
|
||||
|
||||
---
|
||||
|
||||
## 🧠 为什么 Python ↔ C++ 能互通?
|
||||
|
||||
**秘密在 `.msg` 文件**。`std_msgs/String.msg` 定义了消息结构,`rosidl` 自动生成 Python 类和 C++ 类,**接口完全对应**:
|
||||
|
||||
| Python | C++ |
|
||||
|---|---|
|
||||
| `PublisherConstructsWithDefaults` | 节点名 + 默认参数正确 |
|
||||
| `SubscriberConstructsWithDefaults` | 节点名 + 默认参数正确 |
|
||||
| `PublisherSpinSomeWorks` | spin_some 1s 不崩溃 |
|
||||
| `String()` | `std_msgs::msg::String()` |
|
||||
| `msg.data` | `msg.data` |
|
||||
| 字符串类型 | `std::string` |
|
||||
|
||||
## 深度学习
|
||||
DDS 传输的是**二进制数据**(按 `.msg` 定义序列化),不关心语言。所以 py 发 cpp 收完全没问题。
|
||||
|
||||
- 编程规范:[`doc/CODING_STYLE.md`](../doc/CODING_STYLE.md) §3
|
||||
- Topic 深度:[`doc/20-topics.md`](../doc/20-topics.md)
|
||||
- C++ Style: [ROS2 C++ Style Guide](https://docs.ros.org/en/humble/Contributing/Code-Style-Language-Versions.html)
|
||||
---
|
||||
|
||||
## 📚 深入学习
|
||||
|
||||
- [ROS2 C++ 教程](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html)
|
||||
- [doc/CODING_STYLE.md](../../doc/CODING_STYLE.md) — C++ 编码规范
|
||||
- [py_pubsub README](../py_pubsub/README.md) — 对比看效果
|
||||
|
||||
---
|
||||
|
||||
## ⏭️ 下一个包
|
||||
|
||||
学完这个,继续学 **[py_srv](../py_srv/README.md)** — 学习 Service(请求-响应)模式。
|
||||
|
||||
---
|
||||
|
||||
## 📍 学习路径导航
|
||||
|
||||
| ⏮ 上一个 | 🏠 当前位置 | ⏭ 下一个 |
|
||||
|---|---|---|
|
||||
| [py_pubsub — Python Topic](../py_pubsub/README.md) | **cpp_pubsub — C++ Topic** | [py_srv — Python Service](../py_srv/README.md) |
|
||||
|
||||
📍 完整 12 包学习顺序见 [主 README](../../README.md#-12-包推荐学习顺序)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<depend>rclcpp</depend>
|
||||
<depend>std_msgs</depend>
|
||||
|
||||
<test_depend>ament_cmake_gtest</test_depend>
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
// chatter_publisher.cpp - C++ ChatterPublisher 实现
|
||||
// chatter_publisher.cpp - ChatterPublisher 类实现(不含 main)
|
||||
//
|
||||
// 设计思想:
|
||||
// - 把节点类实现放 _core 库,main() 单独文件
|
||||
// - 库不包含 main,避免多定义错误
|
||||
|
||||
#include "cpp_pubsub/chatter_publisher.hpp"
|
||||
|
||||
#include <chrono>
|
||||
@@ -15,10 +20,10 @@ ChatterPublisher::ChatterPublisher(const rclcpp::NodeOptions & options)
|
||||
// 1) 声明参数(类型模板版 + 描述符)
|
||||
this->declare_parameter<int>(
|
||||
"publish_rate_hz", 2,
|
||||
rcl_interfaces::msg::ParameterDescriptor().set_description("发布频率 (Hz)"));
|
||||
rcl_interfaces::msg::ParameterDescriptor().set__description("发布频率 (Hz)"));
|
||||
this->declare_parameter<std::string>(
|
||||
"topic_name", "chatter",
|
||||
rcl_interfaces::msg::ParameterDescriptor().set_description("发布话题名"));
|
||||
rcl_interfaces::msg::ParameterDescriptor().set__description("发布话题名"));
|
||||
|
||||
// 2) 读参数
|
||||
const int publish_rate_hz = this->get_parameter("publish_rate_hz").as_int();
|
||||
@@ -27,7 +32,6 @@ ChatterPublisher::ChatterPublisher(const rclcpp::NodeOptions & options)
|
||||
// 3) 构造发布者 + 定时器
|
||||
publisher_ = this->create_publisher<std_msgs::msg::String>(topic_name, 10);
|
||||
|
||||
// 防零除
|
||||
const auto period = (publish_rate_hz > 0) ?
|
||||
std::chrono::milliseconds(1000 / publish_rate_hz) :
|
||||
std::chrono::milliseconds(1000);
|
||||
@@ -52,12 +56,4 @@ void ChatterPublisher::timer_callback()
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cpp_pubsub
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<cpp_pubsub::ChatterPublisher>());
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
} // namespace cpp_pubsub
|
||||
@@ -0,0 +1,10 @@
|
||||
// chatter_publisher_main.cpp - ChatterPublisher 节点入口
|
||||
#include "cpp_pubsub/chatter_publisher.hpp"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<cpp_pubsub::ChatterPublisher>());
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// chatter_subscriber.cpp - C++ ChatterSubscriber 实现
|
||||
// chatter_subscriber.cpp - ChatterSubscriber 类实现(不含 main)
|
||||
#include "cpp_pubsub/chatter_subscriber.hpp"
|
||||
|
||||
#include <string>
|
||||
@@ -11,7 +11,7 @@ ChatterSubscriber::ChatterSubscriber(const rclcpp::NodeOptions & options)
|
||||
{
|
||||
this->declare_parameter<std::string>(
|
||||
"topic_name", "chatter",
|
||||
rcl_interfaces::msg::ParameterDescriptor().set_description("订阅话题名"));
|
||||
rcl_interfaces::msg::ParameterDescriptor().set__description("订阅话题名"));
|
||||
|
||||
const std::string topic_name = this->get_parameter("topic_name").as_string();
|
||||
|
||||
@@ -25,18 +25,14 @@ ChatterSubscriber::ChatterSubscriber(const rclcpp::NodeOptions & options)
|
||||
void ChatterSubscriber::message_callback(const std_msgs::msg::String::SharedPtr msg)
|
||||
{
|
||||
try {
|
||||
RCLCPP_INFO(this->get_logger(), "recv #%zu: \"%s\"", received_count_++, msg->data.c_str());
|
||||
received_count_++;
|
||||
if (received_count_ % 10 == 0) {
|
||||
RCLCPP_INFO(this->get_logger(),
|
||||
"recv #%zu: \"%s\"", received_count_, msg->data.c_str());
|
||||
}
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(this->get_logger(), "callback failed: %s", exc.what());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cpp_pubsub
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<cpp_pubsub::ChatterSubscriber>());
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
} // namespace cpp_pubsub
|
||||
@@ -0,0 +1,10 @@
|
||||
// chatter_subscriber_main.cpp - ChatterSubscriber 节点入口
|
||||
#include "cpp_pubsub/chatter_subscriber.hpp"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::spin(std::make_shared<cpp_pubsub::ChatterSubscriber>());
|
||||
rclcpp::shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
# test/test_pub_sub.cpp - cpp_pubsub 单元测试
|
||||
#
|
||||
# 设计思想:
|
||||
# - SetUpTestSuite / TearDownTestSuite 共享 rclcpp::init / shutdown
|
||||
# - 用 spin_some(50ms) 代替 spin() 控制超时
|
||||
# - 不依赖 launch_testing(避免环境耦合)
|
||||
// test/test_pub_sub.cpp - cpp_pubsub 单元测试
|
||||
//
|
||||
// 设计思想:
|
||||
// - SetUpTestSuite / TearDownTestSuite 共享 rclcpp::init / shutdown
|
||||
// - 用 spin_some(50ms) 代替 spin() 控制超时
|
||||
// - 不依赖 launch_testing(避免环境耦合)
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
Reference in New Issue
Block a user