话题通信实践
使用话题通信实现一个系统信息监视工具
消息接口是定义通信数据结构的规则,消息类型是根据规则生成的具体代码实现,供节点使用,这里由于没有对应的消息类型所以我们需要先自定义一个消息接口。
需要两个基础包
builtin_interfaces:ROS 2 自带的一个接口包,里面定义了一些基础消息类型。
rosidl_default_generators:一个构建工具包,提供生成消息/服务/动作接口代码的 CMake 宏和逻辑。
自定义的消息接口如下
builtin_interfaces/Time stamp # 记录时间戳
string host_name # 主机名字
float32 cpu_percent # CPu使用率
float32 memory_percent # 内存使用率
float32 memory_total # 内存总大小
float32 memory_available # 内存总大小
float64 net_sent # 网络发送数据总量imp8Mb
float64 net_recv # 网络数据接受总量M
Cmake函数,将消息接口转换为库或头文件,这样编译时生成对应的 C++/Python 类型
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/SystemStatus.msg"
DEPENDENCIES builtin_interfaces
)
<member_of_group>rosidl_interface_packages</member_of_group> :它声明当前包是一个 ROS 2 接口包,用于提供消息、服务或动作定义。

以下是项目将自定义的消息接口生成对应的头文件


ros2 pkg create status_publisher --build-type ament_python --dependencies rclpy status_interfaces --license Apache-2.0创建发布者功能包,上面创建的是消息接口的功能包
每次打开新的终端都要刷新环境变量
import rclpy
import psutil
import platform
from status_interfaces.msg import SystemStatus
from rclpy.node import Node
class StatusPublisher(Node):
def __init__(self,node_name):
super().__init__(node_name)
# 话题名称为sys_status,发布者为节点内部成员
self.status_publisher = self.create_publisher(
SystemStatus,'sys_status',10
)
self.timer_ = self.create_timer(1.0,self.timer_callback)
def timer_callback(self):
# 获取信息
cpu_percent = psutil.cpu_percent()
memory_info = psutil.virtual_memory()
net_io_counters = psutil.net_io_counters()
# 输入消息
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
# 将B转换为MB
msg.memory_total = memory_info.total / 1024/ 1024
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'publish {str(msg)}')
self.status_publisher.publish(msg)
def main():
rclpy.init()
#节点名称为statusPub
node = StatusPublisher('statusPub')
rclpy.spin(node)
rclpy.shutdown()

使用QT创建可视化窗口,将订阅者获取得到的消息进行可视化展示
cmake_minimum_required(VERSION 3.8)
project(status_display)
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)
find_package(rclcpp REQUIRED)
find_package(status_interfaces REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Widgets)
add_executable(statusDisplay src/status_display.cpp)
ament_target_dependencies(statusDisplay rclcpp status_interfaces)
target_link_libraries(statusDisplay Qt5::Widgets)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
install(TARGETS statusDisplay
DESTINATION lib/${PROJECT_NAME}
)
ament_package()
#include<QApplication>
#include <QLabel>
#include <QString>
#include <rclcpp/rclcpp.hpp>
#include <status_interfaces/msg/system_status.hpp>
using namespace std;
using namespace rclcpp;
using SystemStatus = status_interfaces::msg::SystemStatus;
class StatusDisplay: public Node{
private:
Subscription<SystemStatus>::SharedPtr subscriber;
QLabel *label;
public:
StatusDisplay():Node("statusDisplay"){
label =new QLabel();
subscriber = this->create_subscription<SystemStatus>(
// lambada表达式创建匿名函数,每获取到一条消息就转换为对应字符串打印到界面上
"sys_status",10,[&](const SystemStatus::SharedPtr msg) -> void{
label->setText(get_qstr_from_msg(msg));
}
);
label->setText(get_qstr_from_msg(std::make_shared<SystemStatus>()));
label->show();
};
// 将获取到的消息转为我们要打印的字符串
QString get_qstr_from_msg(const SystemStatus::SharedPtr msg){
stringstream show_str;
show_str
<< "===新提供状态可视化显示工具=====\n"
<< "数据 时间:\t" << msg->stamp.sec << "\ts\n"
<< "主机名字:\t" << msg->host_name << "\t\n"
<< "CPU使用率:\t" << msg->cpu_percent <<"\t%\n"
<< "内存使用率:\t" << msg->memory_percent << "\t%\n"
<< "内存总大小:\t" << msg->memory_total << "\tMB\n"
<< "剩余有效内存:\t" << msg->memory_available << "\tMB\n"
<< "网络发送量:\t" << msg->net_sent << "\tMB\n"
<< "网络接受量:\t" << msg->net_recv << "\tMB\n"
<< "===============================";
return QString::fromStdString(show_str.str());
};
};
int main(int argc,char *argv[]){
rclcpp::init(argc,argv);
QApplication app(argc,argv);
auto node = make_shared<StatusDisplay>();
// 由于执行ROS和Qt界面都会堵塞系统,需要再开一个线程进行异步执行
thread spin_thread([&]()->void{
rclcpp::spin(node);
rclcpp::shutdown();
});
spin_thread.detach();
app.exec();
return 0;
}


浙公网安备 33010602011771号