[学习笔记]在ros humble里使用qt

0. 示例:使用qt控制ros小海龟

0.1 安装qt creator

0.2 创建ROS包

依赖只需要ros2的依赖即可,qt的依赖单独添加。创建一个resource文件夹,待会qt的ui文件放到这里

bash 复制代码
ros2 pkg create qt_test --build-type ament_cmake --dependencies rclcpp --dependencies rclcpp std_msgs geometry_msgs --node-name qt_test_node --license Apache-2.0

mkdir resource

0.3 使用Qt Designer创建UI界面

打开qt designer

选择Main Window窗口

创建一个按钮(Push Botton),一个输入框(Line Edit)。

在右边属性栏修改:

将按钮的 objectName 改为 rotate,text 改为"开始旋转";

将文本框的 objectName 改为angle;

将工程保存到ros2包的resource目录下,这将保存一个.ui文件

关闭QT设计器。

0.4 编写程序

cpp 复制代码
/* qt_test_node.hpp */
#pragma once

#include <QMainWindow>  //Qt头文件

#include <rclcpp/rclcpp.hpp>
#include <geometry_msgs/msg/twist.hpp> // 包含小乌龟的Twist消息类型
#include "ui_test.h"   //*添加生成的头文件 这个文件在编译后的build路径里

/* 前置声明 Ui::MainWindow 类。这样可以在头文件中使用 Ui::MainWindow *ui; 指针,而无需在头文件中完全包含 ui_main_window.h */
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

/* 创建自定义类MainWindow  继承自QT的类QMainWindow */
class MainWindow : public QMainWindow
{
    Q_OBJECT  /*QT模对象宏*/

public:
    /* 声明构造函数 explicit防止类型隐式转换   传递ros节点指针,便于在qt程序中调用ros通信函数   传递父母窗口指针 */
    explicit MainWindow(rclcpp::Node::SharedPtr node, QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onRotateButtonClicked();/* 按钮点击事件函数 */

private:
    Ui::MainWindow *ui;/* ui窗口指针 */
    rclcpp::Node::SharedPtr ros_node_;/* ros节点指针 */
    rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr twist_publisher_;/* 角度发布者 */
};
cpp 复制代码
//qt_test_node.cpp:
#include <QApplication> //Qt应用头文件
#include <QString> //Qt字符串类
#include <rclcpp/rclcpp.hpp>
#include <sstream>
#include "qt_test_node.hpp"


MainWindow::MainWindow(rclcpp::Node::SharedPtr node, QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow), ros_node_(node)
{ /* 新建ui窗口;传递ros节点 */

    ui->setupUi(this);/*创建构建并显示界面*/
    
    // 创建一个发布者,向 "/turtle1/cmd_vel" 话题发布速度指令
    twist_publisher_ = ros_node_->create_publisher<geometry_msgs::msg::Twist>("/turtle1/cmd_vel", 10);
    
    // 连接按钮的点击信号到我们的自定义槽函数
    /* 连接rotate按钮的 "信号" 到 名为的onRotateButtonClicked自定义槽函数.    */
    connect(ui->rotate, 
        &QPushButton::clicked, 
        this, 
        &MainWindow::onRotateButtonClicked
    );
}

MainWindow::~MainWindow()
{
    delete ui;/* 释放ui指针指向的内存区域 */
}

/* 自定义槽函数 点击按钮时调用 */
void MainWindow::onRotateButtonClicked()
{
    QString angle_text = ui->angle->text(); /*获取angle构建的文本*/
    bool ok;
    double angle = angle_text.toDouble(&ok);/*文本转换为浮点数 将结果输出到ok */
    

    
    if (ok)
    {
        geometry_msgs::msg::Twist twist_msg;
        twist_msg.angular.z = angle; // 设置旋转角速度
        
        twist_publisher_->publish(twist_msg);
        RCLCPP_INFO(ros_node_->get_logger(), "Published rotate command with angular velocity: %f", angle);
    }
    else
    {
        RCLCPP_WARN(ros_node_->get_logger(), "Invalid angle input: '%s'", angle_text.toStdString().c_str());
    }
}


int main(int argc, char *argv[])
{
    
    rclcpp::init(argc, argv);
    auto node = std::make_shared<rclcpp::Node>("qt_turtle_control_node");// 初始化 ROS2

    
    QApplication app(argc, argv);// 初始化 Qt 应用
    
    MainWindow window(node);//创建主窗口对象
    window.show();//显示对象

    // 在单独的线程中运行 ROS2 的 spin 函数,避免阻塞 Qt 的事件循环
    rclcpp::executors::SingleThreadedExecutor executor;
    executor.add_node(node);
    auto spin_thread = std::thread([&executor]() { executor.spin(); });

    // 进入 Qt 事件循环
    int result = app.exec();

    // 程序退出前的清理工作
    rclcpp::shutdown();
    spin_thread.join();
    
    return result;
}

CMakeLists.txt

cpp 复制代码
cmake_minimum_required(VERSION 3.8)
project(qt_test)

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(std_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(Qt5 REQUIRED COMPONENTS Widgets) #新增加依赖
set(CMAKE_AUTOMOC ON) #启用Qt的MOC(元对象编译器)
qt5_wrap_ui(UI_HEADERS resource/test.ui)# 从 .ui 文件生成对应的C++头文件

add_executable(qt_test_node 
  src/qt_test_node.cpp
  include/qt_test_node.hpp   # 必须添加
  ${UI_HEADERS}  # 必须添加  
)

target_include_directories(qt_test_node PUBLIC
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>
  ${CMAKE_CURRENT_BINARY_DIR}  #添加 以寻找build路径下的ui_test.h
)
target_compile_features(qt_test_node PUBLIC c_std_99 cxx_std_17)  # Require C99 and C++17
ament_target_dependencies(
  qt_test_node
  "rclcpp"
  "std_msgs"
  "geometry_msgs"
)
target_link_libraries(qt_test_node Qt5::Widgets)#链接到QT库

install(TARGETS qt_test_node
  DESTINATION lib/${PROJECT_NAME})

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()

ament_package()

0.5 测试

bash 复制代码
colcon build
source install/setup.bash

终端1:

bash 复制代码
source /opt/ros/humble/setup.bash
ros2 run turtlesim turtlesim_node

终端2:

bash 复制代码
source install/setup.bash
ros2 run qt_test qt_test_node
相关推荐
red_redemption2 小时前
自由学习记录(158)
学习
智慧化智能化数字化方案2 小时前
向华为学习——解读质量管理培训 IPD基础知识研发质量管理【附全文阅读】
学习·华为ipd流程·ipd基础知识·研发质量管理
Fanfffff7202 小时前
前端进阶:从请求竞态到并发控制(系统学习笔记)
前端·笔记·学习
山甫aa2 小时前
STL---常见数据结构总结
开发语言·数据结构·c++·学习
小琪爱学习2 小时前
项目学习代码
学习
Oll Correct2 小时前
实验十四:IPv4地址的无分类编址方法
网络·笔记
&&Citrus11 小时前
【CPN学习笔记(二)】Chap2 非分层颜色 Petri 网——从一个简单协议开始读懂 CPN
笔记·学习·php·cpn·petri网
HXQ_晴天11 小时前
Linux 磁盘清理 & 查看常用指令笔记
笔记
小橘子83112 小时前
(学习)Claude Code 源码架构深度解析
学习·程序人生·架构