尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Qt无人机地面站编译指南:qextserialport跨平台串口构建

Qt无人机地面站编译指南:qextserialport跨平台串口构建 简介本资源是一套基于Qt Creator开发的无人机地面站软件项目源码面向嵌入式开发初学者、飞控算法学习者及智能机器人方向实践者聚焦于解决地面站软件的跨平台编译、实时遥测通信与GUI人机交互等核心问题。压缩包共218个文件含55个C源文件如frameserial.cpp、qextserialport_win.cpp等串口通信与航电协议解析模块、50个头文件、38张UI资源图及23个Qt工程配置文件.pri/.pro辅以CSS样式、QRC资源描述及动态链接库等整体3.17MB结构完整、模块划分清晰。已有60人学习下载适合通过真实工业级地面站框架理解QT信号槽机制、多线程串口通信、飞行参数可视化gaugeplane.cpp等及ANOPROTOCOL数据解析package_ano_422.cpp等关键技术点代码注释充分、部署门槛低是深入掌握无人机系统软件层开发的优质实践入口。1. 这不是普通 Qt 项目一个真实无人机地面站的编译链路卡在 qextserialport_unix.cpp 就等于卡死整条遥控通路你手头这份“无人机地面站项目”表面看是 QT Creator 打开就能跑的 GUI 工程实际却是一条精密咬合的实时通信链——从frameserial.cpp的帧解析、到qextserialport_win.cpp/qextserialport_unix.cpp的跨平台串口抽象、再到package_ano_422.cpp对 ANO 协议常见于国产飞控如 STM32F4 系列的 422 总线打包每一行都在和毫秒级时延、字节序、波特率容错率搏斗。它不依赖 Qt Quick 或 QML 渲染动画而是靠gaugeplane.cpp实时刷新姿态角、progressbarwater.cpp动态渲染电池电量水位——这些控件背后是serialdownload.cpp持续吞吐的遥测流。用 QT Creator 编译它本质是在构建一个硬实时性要求下的跨平台串口通信中枢而非普通桌面应用。适合正在调试 PX4/ArduPilot 飞控数据链、需要自定义地面站协议栈、或想深入理解串口驱动层与 Qt 事件循环耦合机制的嵌入式开发者与飞控算法工程师。新手照着.pro文件点 Build 很可能失败因为qextserialport不是 Qt 官方模块它的 Unix/Linux 编译路径与 Windows 完全不同而cmake预编译的写法在此项目中几乎不可用——.pro qmake 才是唯一正解。2. 为什么必须用 qmake 而非 CMakeqextserialport 的平台条件编译逻辑深度绑定 Qt 构建系统2.1 qextserialport 的跨平台实现机制决定了构建工具选型qextserialport是一个已停止维护但仍在大量无人机地面站中服役的串口封装库其核心设计哲学是同一份头文件qextserialport.h通过宏开关控制底层实现分支。查看源码可发现qextserialport_unix.cpp依赖termios.h和fcntl.h使用open()/ioctl()直接操作/dev/ttyUSB0等设备节点qextserialport_win.cpp则调用 Windows APICreateFile()/SetCommState()qextserialport.cpp是通用接口层包含QextSerialPort类声明与虚函数定义。这种设计意味着编译时必须让构建系统识别当前目标平台并仅编译对应.cpp文件。Qt 的 qmake 通过win32/unix等作用域scope原生支持该逻辑而 CMake 需手动编写if(WIN32)/if(UNIX)判断并控制源文件列表——但本项目.pro文件中已固化了该逻辑强行切换构建系统将导致qextserialport_unix.cpp在 Windows 下被编译报termios.h: No such file or directory或qextserialport_win.cpp在 Linux 下被链接报undefined reference to CreateFileA。这是qt creator项目无法用cmake预编译的写法替代的根本原因。提示不要尝试用cmake -G Unix Makefiles导入.pro项目。qmake 生成的Makefile中包含QMAKE_TARGET、QMAKE_HOST等 Qt 特有变量CMake 无法解析。若需 CMake 支持必须重写整个构建脚本且需重新实现qextserialport的平台选择逻辑。2.2 正确配置 QT Creator 的 Kit 与 qmake 路径避免 “No valid kits found”QT Creator 不是编译器它只是调用 qmake 的前端。编译失败的第一道关卡往往是 Kit 配置错误。以 Ubuntu 22.04 Qt 5.15.2 为例# 1. 确认系统已安装 Qt5 开发包关键 sudo apt install qt5-default qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools # 2. 查找 qmake 路径注意不是 /usr/bin/qmake而是 Qt 安装目录下的 qmake ls /usr/lib/x86_64-linux-gnu/qt5/bin/qmake # 或若使用在线安装版 Qt/opt/Qt/5.15.2/gcc_64/bin/qmake # 3. 在 QT Creator 中配置 Kit # Tools → Options → Kits → Add Kit # - Name: Qt 5.15.2 GCC 64-bit # - Device type: Desktop # - Compiler: System GCC (x86_64-linux-gnu-g-11) # - Qt version: Browse → 选择上述 qmake 路径 # - Debugger: system gdb若 Kit 显示为灰色No valid kits found说明qmake返回的QT_INSTALL_PREFIX与系统 Qt 库路径不匹配。此时需检查# 运行 qmake -query 查看 Qt 安装路径 /usr/lib/x86_64-linux-gnu/qt5/bin/qmake -query # 输出应包含 # QT_INSTALL_PREFIX:/usr/lib/x86_64-linux-gnu/qt5 # QT_INSTALL_LIBS:/usr/lib/x86_64-linux-gnu/qt5/lib # 若 LIBS 路径为 /usr/lib/x86_64-linux-gnu而 qmake 报告为 /usr/lib则需软链接修复 sudo ln -sf /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/qt5/lib2.3 修改 .pro 文件以适配现代 Qt 版本解决QextSerialPort类未声明错误原始.pro文件通常基于 Qt 4 编写而 Qt 5 移除了QextSerialPort的内置支持需显式包含头文件路径并链接-lqextserialport。在项目根目录.pro文件末尾添加# --- BEGIN qextserialport 适配段 --- # 声明 qextserialport 源码位置假设与 main.cpp 同级 HEADERS \ qextserialport.h \ frameserial.h \ frameplanesetting.h \ serialdownload.h \ gaugeplane.h \ package_ano_422.h \ progressbarwater.h SOURCES \ qextserialport.cpp \ qextserialport_unix.cpp \ qextserialport_win.cpp \ frameserial.cpp \ frameplanesetting.cpp \ serialdownload.cpp \ gaugeplane.cpp \ package_ano_422.cpp \ progressbarwater.cpp # 关键为 Unix 平台启用 termios 支持 unix { DEFINES QEXTSERIALPORT_UNIX LIBS -lqextserialport # 若提示找不到 libqextserialport.so需先编译该库见 3.2 节 } win32 { DEFINES QEXTSERIALPORT_WIN LIBS -lqextserialport } # --- END qextserialport 适配段 ---注意LIBS -lqextserialport表示链接名为libqextserialport.soLinux或qextserialport.libWindows的静态/动态库。若项目未提供预编译库则必须先编译qextserialport源码见下一章。3. 编译 qextserialport 库从源码构建 libqextserialport.so 的完整流程与平台差异3.1 Linux 下编译 libqextserialport.so解决termios.h与QMutex冲突qextserialport源码包中通常包含qextserialport.pro文件。在 Ubuntu 下编译需分两步# 进入 qextserialport 源码目录假设路径为 ./3rdparty/qextserialport/ cd ./3rdparty/qextserialport/ # 1. 生成 Makefile指定 Qt5 qmake 路径 /opt/Qt/5.15.2/gcc_64/bin/qmake qextserialport.pro # 2. 编译关键添加 -fPIC 生成位置无关代码 make -j$(nproc) # 3. 检查生成物 ls -l libqextserialport.so* # 应输出libqextserialport.so.1.2.0 libqextserialport.so.1 libqextserialport.so若编译报错error: QMutex does not name a type说明qextserialport.h中缺少 Qt 头文件包含。需手动修改qextserialport.h// 在 #include QThread 下方添加 #include QMutex #include QWaitCondition #include QTimer注意qextserialport的QextSerialPort类继承自QIODevice而QIODevice在 Qt 5 中已移至QtCore模块因此必须确保qextserialport.pro中包含QT core。若缺失编辑该文件并添加QT core TARGET qextserialport TEMPLATE lib3.2 Windows 下编译 qextserialport.libMinGW 与 MSVC 工具链的选择陷阱Windows 用户常因工具链不匹配导致qextserialport_win.cpp编译失败。关键原则QT Creator 的 Kit 中选择的 Compiler 必须与 qmake 生成的 Makefile 一致。若 Kit 使用MinGW 11.2推荐兼容性好# 在 Qt MinGW 终端中执行非 cmd cd ./3rdparty/qextserialport/ qmake -spec win32-g qextserialport.pro mingw32-make -j4 # 生成 libqextserialport.a 和 qextserialport.dll若 Kit 使用MSVC 2019:: 在 x64 Native Tools Command Prompt for VS 2019 中执行 cd .\3rdparty\qextserialport\ qmake -spec win32-msvc qextserialport.pro nmake :: 生成 qextserialport.lib 和 qextserialport.dll提示qextserialport_win.cpp中的#include windows.h会与某些 Qt 版本的qglobal.h冲突。若报错error C2011: OVERLAPPED : struct type redefinition需在qextserialport_win.cpp开头添加#define WIN32_LEAN_AND_MEAN #include windows.h3.3 将 libqextserialport.so 部署到项目可链接路径编译成功后需让主项目.pro文件能定位到该库。两种可靠方式方式操作适用场景绝对路径链接在.pro中写LIBS -L/home/user/project/3rdparty/qextserialport/ -lqextserialport快速验证但路径硬编码不便于协作相对路径 INSTALLS在qextserialport.pro中添加target.path $$OUT_PWD/../libINSTALLS target然后运行make install推荐。$$OUT_PWD指向构建目录../lib即主项目根目录下的lib/文件夹验证链接是否生效# 编译主项目后检查可执行文件依赖 ldd ./build-groundstation/GroundStation | grep qext # 应输出libqextserialport.so.1 /path/to/libqextserialport.so.1 (0x...)4. 解决package_ano_422.cpp编译期协议校验失败ANOPacket 结构体对齐与字节序陷阱4.1 ANO 协议帧结构解析为什么#pragma pack(1)是刚需package_ano_422.cpp封装的是 APM/PIXHAWK 飞控常用的 ANOAnonymous协议其典型帧格式为字段长度字节说明Header1固定值 0xAALen1数据长度不含 Header/Len/ChecksumCmd1命令 ID如 0x01姿态0x02GPSDataLen变长负载含 float/int16 等类型Checksum1Header Len Cmd Data[0] ... Data[Len-1]的低 8 位问题在于C 结构体默认按 4 字节对齐而Data字段中的float pitch; int16_t roll;若未强制 1 字节对齐会导致sizeof(ANOPacket) 实际协议长度memcpy复制时溢出。必须在package_ano_422.h中声明#pragma pack(push, 1) // 关键强制 1 字节对齐 typedef struct { uint8_t header; // 0xAA uint8_t len; // Data 长度 uint8_t cmd; // 命令 uint8_t data[255]; // 最大负载 uint8_t checksum; // 校验和 } ANOPacket; #pragma pack(pop)注意#pragma pack(1)是 GCC/Clang/MSVC 通用指令但需确保所有包含该头文件的.cpp都受其影响。若frameserial.cpp也使用ANOPacket则必须在frameserial.h中#include package_ano_422.h而非重复定义。4.2 字节序转换Linux x86_64 与 STM32 ARM 的 endianness 差异ANO 协议规定Data中的float以little-endian存储。但 x86_64 主机与 ARM Cortex-M4 飞控均是小端看似无需转换。然而qextserialport在 Linux 下读取串口数据时QByteArray::data()返回的指针直接映射到内存若ANOPacket结构体未正确对齐reinterpret_castfloat*(pkt.data 4)会读取错误地址。安全做法是显式转换// 在 package_ano_422.cpp 中解析 float float parseFloat(const uint8_t* src) { union { uint32_t i; float f; } u; u.i (src[0]) | (src[1] 8) | (src[2] 16) | (src[3] 24); return u.f; } // 使用 float pitch parseFloat(pkt.data); // pkt.data[0..3] 是 pitch 的 4 字节此写法绕过结构体对齐风险且明确表达字节序意图避免qscintilla下载与编译等第三方库引入的隐式内存布局干扰。4.3 编译期校验用 static_assert 确保协议结构体尺寸在package_ano_422.h末尾添加编译期断言防止未来修改破坏协议// ANO 协议最大帧长 1(header) 1(len) 1(cmd) 255(data) 1(checksum) 259 static_assert(sizeof(ANOPacket) 259, ANOPacket size mismatch! Check #pragma pack and field order.); // 验证 header 偏移量为 0 static_assert(offsetof(ANOPacket, header) 0, ANOPacket header offset must be 0);若sizeof(ANOPacket)计算结果不为 259qmake 编译时将直接报错static assertion failed而非运行时崩溃。这是比qml编译错误更底层、更致命的协议一致性保障。5. 验证串口通信链路用stty与hexdump定位serialdownload.cpp数据流中断点5.1 在 Linux 下绕过 QT Creator用命令行复现串口初始化失败场景当serialdownload.cpp在 QT Creator 中点击 Run 时卡在port-open(QIODevice::ReadWrite)优先排除硬件与权限问题# 1. 确认设备存在且权限正确 ls -l /dev/ttyUSB0 # 应输出crw-rw---- 1 root dialout 188, 0 May 20 10:00 /dev/ttyUSB0 # 若用户不在 dialout 组执行 sudo usermod -a -G dialout $USER # 注销重登录生效 # 2. 用 stty 手动配置串口模拟 qextserialport 初始化 stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb -crtscts # 参数含义115200 波特率8 数据位1 停止位无校验无硬件流控 # 3. 发送测试帧模拟 groundstation 发送心跳 echo -ne \xaa\x00\x01\x00\xab | dd of/dev/ttyUSB0 bs1 convnotrunc # \xaaheader, \x00len, \x01cmd(heartbeat), \x00checksum, \xab校验和示例 # 4. 用 hexdump 监听飞控返回验证物理链路 hexdump -C /dev/ttyUSB0 | head -20 # 正常应看到连续的 ANO 帧如aa 10 01 00 00 00 00 00 ...若hexdump无输出说明飞控未响应或接线错误若输出乱码检查stty参数是否与飞控固件配置一致常见错误飞控设为 57600地面站设为 115200。5.2 在 QT Creator 中启用 qextserialport 调试日志捕获QextSerialPort::open()的 errnoqextserialport的open()方法内部调用open()系统调用失败时设置errno。在serialdownload.cpp的connectToPort()函数中插入调试bool SerialDownload::connectToPort(const QString portName) { port new QextSerialPort(portName, QextSerialPort::EventDriven); port-setBaudRate(BAUD115200); port-setFlowControl(FLOW_OFF); port-setParity(PAR_NONE); port-setDataBits(DATA_8); port-setStopBits(STOP_1); if (!port-open(QIODevice::ReadWrite)) { // 关键打印 errno qDebug() QextSerialPort open failed: port-errorString() errno: errno strerror: strerror(errno); // 常见 errno13Permission denied, 2No such file, 6Device busy return false; } return true; }注意errno是全局变量必须在port-open()紧接着读取否则后续 Qt 调用可能覆盖其值。qextserialport的errorString()仅返回 Qt 封装的字符串而strerror(errno)给出 POSIX 标准错误描述对定位debian如何源码编译git等系统级问题更精准。5.3 使用udevadm固定 USB 串口设备名避免/dev/ttyUSB0变成/dev/ttyUSB1当同时接入多个 USB 设备如 GPS 模块、数传电台Linux 可能将飞控分配为/dev/ttyUSB1导致地面站连接失败。解决方案是创建 udev 规则# 创建规则文件 sudo nano /etc/udev/rules.d/99-drone-serial.rules # 添加内容根据 lsusb 获取飞控的 idVendor/idProduct SUBSYSTEMtty, ATTRS{idVendor}0403, ATTRS{idProduct}6001, SYMLINKdrone_fc # 保存后重载规则 sudo udevadm control --reload-rules sudo udevadm trigger # 检查是否生效 ls -l /dev/drone_fc # 应输出lrwxrwxrwx 1 root root 7 May 20 10:00 /dev/drone_fc - ttyUSB0在serialdownload.cpp中将端口名改为/dev/drone_fc即可彻底规避设备名漂移问题。本文还有配套的精品资源点击获取
返回列表