[学习笔记]在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
相关推荐
一隅论数智1 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
XiHongShi20161 天前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
爱吃苹果的日记本1 天前
离散数学第六课
学习·离散数学
AI职业加油站1 天前
AI智能体应用工程师证书:政策红利下的职业新风口
大数据·运维·人工智能·学习·职场发展
深圳老胡1 天前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
旖旎夜光1 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
奇思妙想聪明勤奋的小羊1 天前
DeepAgents第5章:子Agent 与上下文隔离—让 Agent学会委派
人工智能·python·学习·语言模型
Because_of_Her11 天前
并查集-听课笔记
笔记·算法·并查集
霍霍的袁1 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio