前言
本系列博文是本人的学习笔记,自用为主,不是教程,学习请移步其他大佬的相关教程。主要学习途径为@鱼香ROS大佬的教程,欢迎各位大佬交流学习,若有错误,轻喷。
一、项目背景与准备
需制作系统状态监测与可视化工具 ,结合 ROS2 话题通信、Python(获取并发布系统信息)、C++/Qt(订阅并界面显示)实现。首先创建工作空间:在 chapt3
目录下执行:
bash
mkdir -p chapt3/topic_practice_ws/src
cd chapt3/topic_practice_ws
colcon build # 初始化工作空间
二、步骤 1:自定义通信接口(消息类型)
ROS2 内置接口无法满足 "系统状态(CPU、内存、网络等)传输" 需求,需自定义消息接口。
1. 创建接口功能包
在 topic_practice_ws/src
目录下执行:
bash
ros2 pkg create status_interfaces --build-type ament_cmake --dependencies rosidl_default_generators builtin_interfaces --license Apache-2.0
--build-type ament_cmake
:指定构建类型(适合 C++ 及接口生成)。--dependencies
:依赖rosidl_default_generators
(将自定义消息转为 C++/Python 代码)、builtin_interfaces
(内置接口,如时间类型)。
2. 编写消息定义文件
在 status_interfaces
功能包下创建 msg
目录,新建 SystemStatus.msg
,内容如下:
bash
builtin_interfaces/Time stamp # 记录时间戳
string host_name # 系统名称
float32 cpu_percent # CPU 使用率
float32 memory_percent # 内存使用率
float32 memory_total # 内存总量
float32 memory_available # 剩余有效内存
float64 net_sent # 网络发送数据总量
float64 net_recv # 网络接收数据总量
- 语法:类似 C++ 变量定义(
类型 名称 # 注释
)。 - 基础类型:ROS2 还支持
bool
、byte
、char
、int8/uint8
等 9 种基础类型。
3. 注册消息文件(修改 CMakeLists.txt
)
在 status_interfaces/CMakeLists.txt
中添加:
bash
# 查找依赖
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(builtin_interfaces REQUIRED)
# 声明消息接口文件,并指定依赖
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/SystemStatus.msg"
DEPENDENCIES builtin_interfaces
)
ament_package()
4. 声明接口包(修改 package.xml
)
在 status_interfaces/package.xml
中添加:
bash
<license>Apache-2.0</license>
<member_of_group>rosidl_interface_packages</member_of_group>
<buildtool_depend>ament_cmake</buildtool_depend>
<member_of_group>
:声明为 "消息接口功能包",让 ROS2 特殊处理。
5. 构建与验证接口
- 构建:在工作空间根目录执行
colcon build --packages-select status_interfaces
。 - 生效环境:
source install/setup.bash
。 - 验证:执行
ros2 interface show status_interfaces/msg/SystemStatus
,若输出消息结构(时间戳、各字段),则接口创建成功。
三、步骤 2:系统信息获取与发布(Python 节点)
创建 Python 节点,通过 psutil
获取系统 CPU、内存、网络信息,再通过话题发布。
1. 创建发布者功能包
在 topic_practice_ws/src
目录下执行:
bash
ros2 pkg create status_publisher --build-type ament_python --dependencies rclpy status_interfaces --license Apache-2.0
--build-type ament_python
:指定为 Python 类型功能包。--dependencies
:依赖rclpy
(Python 的 ROS2 客户端库)、status_interfaces
(自定义接口)。
2. 编写发布节点代码
在 status_publisher/status_publisher/
下新建 sys_status_pub.py
,代码如下:
python
import rclpy
from rclpy.node import Node
from status_interfaces.msg import SystemStatus # 导入自定义消息
import psutil # 系统信息获取库
import platform
class SysStatusPub(Node):
def __init__(self, node_name):
super().__init__(node_name)
# 创建发布者:话题名 sys_status,队列大小 10
self.status_publisher_ = self.create_publisher(SystemStatus, 'sys_status', 10)
# 创建定时器:1 秒调用一次 timer_callback
self.timer = self.create_timer(1, self.timer_callback)
def timer_callback(self):
# 获取系统信息
cpu_percent = psutil.cpu_percent()
memory_info = psutil.virtual_memory()
net_io_counters = psutil.net_io_counters()
# 构造 SystemStatus 消息
msg = SystemStatus()
msg.stamp = self.get_clock().now().to_msg() # 当前时间转成消息时间戳
msg.host_name = platform.node() # 主机名
msg.cpu_percent = cpu_percent
msg.memory_percent = memory_info.percent
msg.memory_total = memory_info.total / 1024 / 1024 # 字节转 MB
msg.memory_available = memory_info.available / 1024 / 1024
msg.net_sent = net_io_counters.bytes_sent / 1024 / 1024
msg.net_recv = net_io_counters.bytes_recv / 1024 / 1024
self.get_logger().info(f'发布:{str(msg)}') # 日志输出
self.status_publisher_.publish(msg) # 发布消息
def main():
rclpy.init() # 初始化 rclpy
node = SysStatusPub('sys_status_pub')
rclpy.spin(node) # 循环运行节点(等待回调)
rclpy.shutdown() # 关闭 rclpy
if __name__ == '__main__':
main()
在 ROS2 的 ament_python
类型包 中,setup.py
的 entry_points
是核心配置:它负责告诉 ROS2:
- 节点命令名 (比如
sys_status_pub
,用于ros2 run
调用); - 对应的 Python 文件 (比如
status_publisher/sys_status_pub.py
); - 文件中的入口函数 (比如
main
函数)。
默认用 ros2 pkg create
生成的 setup.py
中,entry_points
是空的(如下),必须手动补充:
python
# 默认生成的空entry_points(无法识别节点)
entry_points={
'console_scripts': [
],
},
3. 运行发布节点
- 构建:在工作空间根目录执行
colcon build --packages-select status_publisher
。 - 生效环境:
source install/setup.bash
。 - 运行:
ros2 run status_publisher sys_status_pub
。 - 验证:新开终端,执行
ros2 topic echo /sys_status
,若看到系统信息持续输出,说明发布成功。
四、步骤 3:Qt 界面显示(C++ 节点)
用 Qt 创建界面,订阅 /sys_status
话题并显示系统状态。
1. 创建显示功能包
在 topic_practice_ws/src
目录下执行:
python
ros2 pkg create status_display --build-type ament_cmake --dependencies rclcpp status_interfaces --license Apache-2.0
- 依赖
rclcpp
(C++ 的 ROS2 客户端库)、status_interfaces
(自定义接口)。
2. 简单 Qt 界面测试(hello_qt
)
在 status_display/src
下新建 hello_qt.cpp
,代码如下:
cpp
#include <QApplication>
#include <QLabel>
#include <QString>
int main(int argc, char* argv[]) {
QApplication app(argc, argv); // Qt 应用对象
QLabel* label = new QLabel(); // 文本标签组件
QString message = QString::fromStdString("Hello Qt!"); // 字符串转换
label->setText(message); // 设置显示文本
label->show(); // 显示组件
return app.exec(); // Qt 事件循环(阻塞,处理界面事件)
}
3. 配置 CMakeLists.txt
(hello_qt
)
在 status_display/CMakeLists.txt
中添加:
bash
cmake_minimum_required(VERSION 3.8)
project(status_display)
find_package(status_interfaces REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Widgets) # 查找 Qt 的 Widgets 组件
# 生成可执行文件 hello_qt
add_executable(hello_qt src/hello_qt.cpp)
# 链接 Qt 的 Widgets 库
target_link_libraries(hello_qt Qt5::Widgets)
install(TARGETS hello_qt
DESTINATION lib/${PROJECT_NAME})
ament_package()
4. 运行 hello_qt
- 构建:
colcon build --packages-select status_display
。 - 生效环境:
source install/setup.bash
。 - 运行:
ros2 run status_display hello_qt
,会看到显示 "Hello Qt!" 的小窗口。
5. 订阅话题并显示系统状态
在 status_display/src
下新建 sys_status_display.cpp
,代码分两部分:
(1)类定义与消息转换
cpp
#include <QApplication>
#include <QLabel>
#include <QString>
#include "rclcpp/rclcpp.hpp"
#include "status_interfaces/msg/system_status.hpp" // 自定义消息头文件
// 为自定义消息起别名,简化代码
using SystemStatus = status_interfaces::msg::SystemStatus;
class SysStatusDisplay : public rclcpp::Node {
public:
SysStatusDisplay() : Node("sys_status_display") {
// 创建订阅者:话题 sys_status,队列 10,Lambda 回调更新界面
subscription_ = this->create_subscription<SystemStatus>(
"sys_status", 10,
[this](const SystemStatus::SharedPtr msg) {
label_->setText(get_qstr_from_msg(msg));
});
// 初始化标签(先显示空消息结构)
label_ = new QLabel(get_qstr_from_msg(std::make_shared<SystemStatus>()));
label_->show();
}
// 从 SystemStatus 消息转成 QString(格式化显示)
QString get_qstr_from_msg(const SystemStatus::SharedPtr msg) {
std::stringstream show_str;
show_str << "=============系统状态可视化工具=============\n"
<< "数据时间:\t" << msg->stamp.sec << "\t\n"
<< "主机名:\t" << msg->host_name << "\t\n"
<< "CPU 使用率:\t" << msg->cpu_percent << "%\t\n"
<< "内存使用率:\t" << msg->memory_percent << "%\t\n"
<< "内存总大小:\t" << msg->memory_total << " MB\t\n"
<< "剩余有效内存:\t" << msg->memory_available << " MB\t\n"
<< "网络发送量:\t" << msg->net_sent << " MB\t\n"
<< "网络接收量:\t" << msg->net_recv << " MB\t\n"
<< "============================================";
return QString::fromStdString(show_str.str());
}
private:
rclcpp::Subscription<SystemStatus>::SharedPtr subscription_; // 订阅者对象
QLabel* label_; // 标签组件
};
(2)主函数(处理 ROS2 与 Qt 事件循环)
cpp
int main(int argc, char* argv[]) {
rclcpp::init(argc, argv); // ROS2 初始化
QApplication app(argc, argv); // Qt 应用初始化
// 创建显示节点
auto node = std::make_shared<SysStatusDisplay>();
// 多线程处理:ROS2 的 spin 放单独线程,避免阻塞 Qt 事件循环
std::thread spin_thread([node]() { rclcpp::spin(node); });
spin_thread.detach(); // 后台运行线程
int result = app.exec(); // Qt 事件循环(处理界面更新)
rclcpp::shutdown(); // 关闭 ROS2
return result;
}
6. 配置 CMakeLists.txt
(sys_status_display
)
在 status_display/CMakeLists.txt
中添加:
bash
# 生成 sys_status_display 可执行文件
add_executable(sys_status_display src/sys_status_display.cpp)
# 链接 Qt Widgets 库
target_link_libraries(sys_status_display Qt5::Widgets)
# 为 ROS2 节点添加依赖
ament_target_dependencies(sys_status_display rclcpp status_interfaces)
# 安装可执行文件
install(TARGETS
hello_qt
sys_status_display
DESTINATION lib/${PROJECT_NAME})
7. 运行显示节点
- 构建:
colcon build --packages-select status_display
。 - 生效环境:
source install/setup.bash
。 - 启动流程:
- 先启动发布节点 :
ros2 run status_publisher sys_status_pub
。 - 再启动显示节点 :
ros2 run status_display sys_status_display
,界面会实时显示系统状态数据。
- 先启动发布节点 :
五、整体流程回顾
- 自定义接口 :创建
status_interfaces
包,定义SystemStatus
消息,生成跨语言接口代码。 - 发布系统信息 :创建
status_publisher
包(Python),用psutil
采集信息,通过/sys_status
话题发布。 - 订阅并显示 :创建
status_display
包(C++/Qt),订阅/sys_status
话题,用 Qt 界面实时展示系统状态。
每个环节核心是功能包创建、代码编写、CMake/package 配置、构建与运行,多实践即可熟悉 ROS2 开发流程