QGC地图界面自定义数据面板开发实战
1. 理解QGC地图界面自定义数据面板的需求第一次接触QGroundControlQGC地图界面自定义数据面板开发时我完全被各种技术术语绕晕了。后来在实际项目中才发现这个功能对于无人机开发者来说简直是刚需。想象一下你正在开发一个农业植保无人机除了常规的飞行高度、速度外还需要实时显示喷洒量、作业面积等专业数据。这时候默认的QGC界面就显得力不从心了。QGC的地图界面默认显示的是基础飞行数据比如高度、速度、GPS信息等。但实际业务中我们往往需要展示更多自定义数据。比如环境监测无人机需要显示PM2.5、温湿度等传感器数据测绘无人机需要实时显示拍摄张数、覆盖面积物流无人机需要展示货物重量、配送状态这些需求都需要我们扩展QGC的地图数据显示能力。好消息是QGC提供了完善的扩展机制允许开发者通过FactGroup体系添加自定义数据。我去年给一个光伏巡检项目做开发时就成功实现了组件温度、缺陷数量等专业数据的实时展示。2. 搭建开发环境与基础准备工欲善其事必先利其器。在开始编码前我们需要准备好开发环境。根据我的经验最稳定的组合是Ubuntu 18.04 LTS20.04也行但需要处理更多依赖问题Qt 5.13.2开发套件QGC 4.1.16源码安装Qt时有个小技巧建议使用官方在线安装器勾选以下组件Qt 5.13.2 → Desktop gcc 64-bitQt ChartsQt Quick 2D Renderer我第一次搭建环境时因为漏装了Qt Charts导致编译时各种奇怪的链接错误。后来发现QGC的很多可视化组件都依赖这个模块。另外建议预留至少30GB磁盘空间因为Qt和QGC的编译会产生大量中间文件。获取QGC源码后先别急着修改。我建议先尝试编译原始版本确保基础环境没问题。编译命令很简单mkdir build cd build qmake ../qgroundcontrol.pro make -j$(nproc)如果编译成功你会看到生成的qgroundcontrol可执行文件。这时候运行它应该能看到完整的QGC界面。这个验证步骤很重要可以避免后续开发中遇到环境问题。3. 创建自定义FactGroup数据组FactGroup是QGC中管理数据的基本单元相当于一个数据容器。我们要显示自定义数据首先得创建自己的FactGroup。以显示四个测试数据为例我们需要创建两个文件TestInfoFactGroup.h和TestInfoFactGroup.cc。在头文件中我们定义数据结构和处理逻辑// TestInfoFactGroup.h #pragma once #include FactGroup.h #include QGCMAVLink.h class TestInfoFactGroup : public FactGroup { Q_OBJECT public: TestInfoFactGroup(QObject* parent nullptr); Q_PROPERTY(Fact* temperature READ temperature CONSTANT) Q_PROPERTY(Fact* humidity READ humidity CONSTANT) Q_PROPERTY(Fact* pressure READ pressure CONSTANT) Q_PROPERTY(Fact* airQuality READ airQuality CONSTANT) Fact* temperature() { return _temperatureFact; } Fact* humidity() { return _humidityFact; } Fact* pressure() { return _pressureFact; } Fact* airQuality() { return _airQualityFact; } void handleMessage(Vehicle* vehicle, mavlink_message_t message) override; private: Fact _temperatureFact; Fact _humidityFact; Fact _pressureFact; Fact _airQualityFact; static const char* _temperatureFactName; static const char* _humidityFactName; static const char* _pressureFactName; static const char* _airQualityFactName; };实现文件中我们需要初始化各个Fact并处理MAVLink消息// TestInfoFactGroup.cc #include TestInfoFactGroup.h #include Vehicle.h const char* TestInfoFactGroup::_temperatureFactName temperature; const char* TestInfoFactGroup::_humidityFactName humidity; const char* TestInfoFactGroup::_pressureFactName pressure; const char* TestInfoFactGroup::_airQualityFactName airQuality; TestInfoFactGroup::TestInfoFactGroup(QObject* parent) : FactGroup(1000, :/json/Vehicle/TestInfoFact.json, parent) , _temperatureFact(0, _temperatureFactName, FactMetaData::valueTypeDouble) , _humidityFact(0, _humidityFactName, FactMetaData::valueTypeDouble) , _pressureFact(0, _pressureFactName, FactMetaData::valueTypeDouble) , _airQualityFact(0, _airQualityFactName, FactMetaData::valueTypeUint32) { _addFact(_temperatureFact, _temperatureFactName); _addFact(_humidityFact, _humidityFactName); _addFact(_pressureFact, _pressureFactName); _addFact(_airQualityFact, _airQualityFactName); // 初始化默认值 _temperatureFact.setRawValue(qQNaN()); _humidityFact.setRawValue(qQNaN()); _pressureFact.setRawValue(qQNaN()); _airQualityFact.setRawValue(0); } void TestInfoFactGroup::handleMessage(Vehicle*, mavlink_message_t message) { if (message.msgid ! MAVLINK_MSG_ID_SCALED_PRESSURE) { return; } mavlink_scaled_pressure_t pressure; mavlink_msg_scaled_pressure_decode(message, pressure); temperature()-setRawValue(pressure.temperature / 100.0f); pressure()-setRawValue(pressure.press_abs); _setTelemetryAvailable(true); }这里有几个关键点需要注意Fact的valueType必须与数据类型匹配比如浮点数用valueTypeDouble整数用valueTypeUint32更新频率构造函数中的1000表示数据刷新间隔单位毫秒handleMessage方法中需要过滤和解析特定的MAVLink消息4. 集成FactGroup到QGC主框架创建好FactGroup后我们需要把它集成到QGC的主框架中。这个过程就像给房子安装新的水电线路需要找到正确的接入点。首先修改Vehicle.h添加我们的FactGroup// 在文件头部添加包含 #include TestInfoFactGroup.h // 在类定义中添加成员变量 TestInfoFactGroup _testInfoFactGroup; // 添加静态名称定义 static const char* _testInfoFactGroupName; // 添加访问方法 FactGroup* testInfoFactGroup() { return _testInfoFactGroup; } // 添加Q_PROPERTY声明 Q_PROPERTY(FactGroup* testInfo READ testInfoFactGroup CONSTANT)然后在Vehicle.cc中进行初始化// 定义静态名称 const char* Vehicle::_testInfoFactGroupName testInfo; // 在构造函数初始化列表中添加 , _testInfoFactGroup(this) // 在初始化方法中添加注册 _addFactGroup(_testInfoFactGroup, _testInfoFactGroupName);这里最容易出错的是初始化顺序。我遇到过因为初始化顺序不对导致的段错误。记住一个原则基础组件先初始化依赖组件后初始化。如果遇到崩溃问题可以检查构造函数初始化列表的顺序。最后别忘了在qgroundcontrol.pro中添加我们的源文件# 在HEADERS部分添加 HEADERS \ src/Vehicle/TestInfoFactGroup.h \ ... # 在SOURCES部分添加 SOURCES \ src/Vehicle/TestInfoFactGroup.cc \ ...5. 设计数据元信息与JSON配置QGC使用JSON文件来定义数据的显示属性这就像给数据穿衣服决定它们在前端如何展示。创建TestInfoFact.json文件{ version: 1, fileType: FactMetaData, QGC.MetaData.Facts: [ { name: temperature, shortDesc: 温度, type: double, decimalPlaces: 1, units: °C }, { name: humidity, shortDesc: 湿度, type: double, decimalPlaces: 1, units: % }, { name: pressure, shortDesc: 气压, type: double, decimalPlaces: 2, units: hPa }, { name: airQuality, shortDesc: 空气质量, type: uint32, units: AQI } ] }这个配置文件有几个关键字段name必须与代码中的Fact名称完全一致shortDesc前端显示的标签文本type数据类型与代码中的valueType对应decimalPlaces小数位数仅数值类型有效units单位符号会显示在值后面把JSON文件添加到资源系统中也很重要。打开qgroundcontrol.qrc文件添加我们的JSON文件qresource prefix/ ... file aliasVehicle/TestInfoFact.jsonsrc/Vehicle/TestInfoFact.json/file ... /qresource这里alias的路径必须与FactGroup构造函数中指定的路径一致。我遇到过因为路径大小写不一致导致找不到文件的问题在Linux环境下尤其要注意。6. 前端界面集成与数据绑定数据准备好了现在要让它们在地图界面上显示出来。QGC使用QML作为前端技术我们需要修改地图页面的QML文件。找到src/uis/Vehicle/VehicleValues.qml文件添加我们的数据显示组件// 在适当位置添加我们的数据显示项 VehicleValuesGroup { name: qsTr(环境数据) width: parent.width VehicleValuesRow { VehicleValuesColumn { FactValue { fact: vehicle.testInfo.temperature showUnits: true } Label { text: qsTr(温度) } } VehicleValuesColumn { FactValue { fact: vehicle.testInfo.humidity showUnits: true } Label { text: qsTr(湿度) } } } VehicleValuesRow { VehicleValuesColumn { FactValue { fact: vehicle.testInfo.pressure showUnits: true } Label { text: qsTr(气压) } } VehicleValuesColumn { FactValue { fact: vehicle.testInfo.airQuality showUnits: true } Label { text: qsTr(空气质量) } } } }QML的布局系统需要一些时间来适应。我的经验是VehicleValuesGroup是一个垂直容器VehicleValuesRow代表一行VehicleValuesColumn代表一行中的一列每列通常包含一个FactValue和一个Label调试QML界面时可以使用Qt Creator的QML调试器。设置环境变量QML_DEBUGtrue后启动QGC就能在Qt Creator中实时查看和修改QML属性。7. 测试与调试技巧完成代码编写后就是紧张的测试阶段了。我总结了一套有效的测试方法编译检查make clean qmake ../qgroundcontrol.pro make -j$(nproc)如果编译失败重点关注头文件包含是否正确初始化顺序是否合理是否有拼写错误运行时测试启动QGC并连接飞控或模拟器在地图界面检查自定义数据是否显示发送测试MAVLink消息验证数据更新常见问题排查数据不显示检查JSON文件路径是否正确Q_PROPERTY声明是否完整数据显示NaN检查handleMessage是否被调用数据解析是否正确界面布局错乱检查QML层次结构确保宽度设置合理一个实用的调试技巧是在代码中添加qDebug()输出qDebug() 收到MAVLink消息msgid: message.msgid;可以在启动QGC时加上参数查看日志./qgroundcontrol --logging:full8. 进阶优化与扩展思路基础功能实现后可以考虑进一步优化数据持久化// 在FactGroup中添加保存/加载方法 void saveToSettings(QSettings settings); void loadFromSettings(QSettings settings);动态数据更新// 使用ChartView实现实时曲线 ChartView { LineSeries { name: 温度变化 axisX: ValueAxis { min: 0; max: 100 } axisY: ValueAxis { min: -20; max: 50 } // 动态添加数据点 } }多语言支持{ shortDesc: { en: Temperature, zh: 温度 } }性能优化合理设置更新频率避免不必要的重绘使用Qt的模型/视图框架处理大量数据考虑使用OpenGL加速图形渲染在实际项目中我还遇到过需要动态显示/隐藏某些数据的需求。这可以通过QML的visible属性和Fact的可用性状态来实现FactValue { fact: vehicle.testInfo.temperature visible: fact.available }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2443658.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!