QGC地面站Mavlink生成和MockLink模拟收发通讯
Posted 火山上的企鹅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QGC地面站Mavlink生成和MockLink模拟收发通讯相关的知识,希望对你有一定的参考价值。
Mavlink生成和收发通讯
所有的热爱都要不遗余力,真正喜欢它便给它更高的优先级,和更多的时间吧!
关于QGC地面站其它文章请点击这里: QGC地面站
本文源码地址: QGC_CCH地面站
我建了个QGC学习交流群,感兴趣的小伙伴们可以扫码加入哦,失效了,可以加我微信拉进群
1. 演示效果
测试的时候如下,连接MockLink中的PX4:
QGC发送数据: 需要手动点击发送,MockLink中会打印接收到的两个字节
MockLink发送数据: 1HZ的定时器发送
2. Mavlink 生成
主要流程参考官网:
2.1 安装python3
安装教程可以参考这个,Python3.7.3安装(Win10),现在已经是3.9.5版本了。安装完后测试下看是否装成功了
然后安装 pip3,直接打开cmd命令
pip3 install future
如果报这个:
可以升级一下,不升级应该也行吧~
python -m pip install --upgrade pip
然后查看版本:
pip show pip
2.2 Mavlink下载和运行
需要递归下载
git clone https://github.com/mavlink/mavlink.git --recursive
下载不完全会导致运行不了Mavlink生成器,需要安装包,关注后评论留邮箱私发
进入下载的Mavlink的根文件夹,执行
python mavgenerate.py
如下,弹出了Mavlink生成器:
2.3 xml文件修改
打开 ~\\libs\\mavlink\\include\\mavlink\\v2.0\\message_definitions\\ardupilotmega.xml
仿原有的格式,自定义236号ID,并添加在226号后,如下:
<message id="226" name="RPM">
<description>RPM sensor output.</description>
<field type="float" name="rpm1">RPM Sensor1.</field>
<field type="float" name="rpm2">RPM Sensor2.</field>
</message>
<message id="236" name="CSDN_TEST">
<description>CSDN TEST.</description>
<field type="uint8_t" name="byte1">FRIST BYTE.</field>
<field type="uint8_t" name="byte2">SECOND BYTE.</field>
</message>
定义了byte1、byte1两个字节的数据,此处就是你需要与无人机通讯的数据。
PX4中定义的话最好在 common.xml 中添加。
2.4 自定义Mavlink生成
如下:
XML: ~\\libs\\mavlink\\include\\mavlink\\v2.0\\message_definitions\\ardupilotmega.xml
Out: 可以新建个文件作为输出,也可以直接覆盖QGC中的mavlink库(不太推荐,但是此次测试就是直接覆盖的,覆盖的话记得先备份下原本的mavlink库)
这就是生成的mavlink库:
复制 ardupilotmega 下的 mavlink_msg_csdn_test.h到 qgc的mavlink对应位置下( 如果全选OUT下的文夹复制到qgc mavlink库中可跳过这些步骤)
覆盖 qgc的mavlink的ardupilotmega.h ,注意看其实就改了几处:
① 修改 #define MAVLINK_MESSAGE_CRCS 校验处,增加了236号ID的校验
② include mavlink_msg_csdn_test.h 头文件
③ 修改 MAVLINK_MESSAGE_INFO
④ 修改 MAVLINK_MESSAGE_NAMES
注意: 其中最重要的是第一处的 MAVLINK_MESSAGE_CRCS 校验,如果在Mocklink中能通讯,实际的飞控无法通讯多半是检验位没通过,就是此处有问题,可以分别检查下 ardupilotmega.h、common.h、standard中的MAVLINK_MESSAGE_CRCS,甚至可以都加上新增的校验ID
3. 业务逻辑实现
3.1 QML前端显示
添加在 FlightDisplayView.qml (有些版本是FlyView.qml)后:
3.1.1 发送部分
发送的关键就是 activeVehicle.testSendToVehicle(protectTextField1.text, protectTextField2.text) ,把两个用户输入数据的文本框发送给MockLink
//FlightDisplayView.qml (有些版本是FlyView.qml) 最后
Rectangle {
id: sendRect
anchors.top: parent.top
anchors.topMargin: 20
anchors.horizontalCenter: parent.horizontalCenter
height: col.height * 1.1
width: col.width * 1.1
radius: 2
color: "black"
Column {
id: col
spacing: 4
anchors.centerIn: parent
QGCLabel {
anchors.horizontalCenter: parent.horizontalCenter
text: "Mavlink 发送测试!"
font.pointSize: 12
font.bold: true
color: "red"
}
Row {
anchors.left: parent.left
anchors.right: parent.right
QGCLabel {
id: tipsLabel
anchors.verticalCenter: protectTextField1.verticalCenter
text: qsTr("第一个字节:")
font.pointSize: 11
font.bold: true
color: "#B7FF4A"
}
QGCTextField {
id: protectTextField1
width: parent.width - parent.spacing - tipsLabel.width
height: tipsLabel.implicitHeight * 1.5
text: "1"
inputMethodHints: Qt.ImhFormattedNumbersOnly
focus: true
onEditingFinished: {
if(text > 256) {
text = 256
mainWindow.showMessageDialog(qsTr("警告"), qsTr("输入必须小于等于256"))
}
else if(text < 0) {
text = 0
mainWindow.showMessageDialog(qsTr("警告"), qsTr("输入必须大于等于0"))
}
}
}
}
Row {
anchors.left: parent.left
anchors.right: parent.right
QGCLabel {
id: tipsLabel2
anchors.verticalCenter: protectTextField2.verticalCenter
text: qsTr("第二个字节:")
font.pointSize: 11
font.bold: true
color: "#B7FF4A"
}
//保护距离的输入
QGCTextField {
id: protectTextField2
width: parent.width - parent.spacing - tipsLabel2.width
height: tipsLabel2.implicitHeight * 1.5
text: "1"
inputMethodHints: Qt.ImhFormattedNumbersOnly
focus: true
onEditingFinished: {
if(text > 256) {
text = 256
mainWindow.showMessageDialog(qsTr("警告"), qsTr("输入必须小于等于256"))
}
else if(text < 0) {
text = 0
mainWindow.showMessageDialog(qsTr("警告"), qsTr("输入必须大于等于0"))
}
}
}
}
QGCButton {
id: mavTestButton
backRadius: height/2
text: qsTr("MAVLINK消息发送测试")
enabled: activeVehicle
onClicked: {
if(activeVehicle)
activeVehicle.testSendToVehicle(protectTextField1.text, protectTextField2.text)
}
}
}
}
3.1.2 接收显示
接收的关键就是:activeVehicle.rcvByte1 和 activeVehicle.rcvByte2,后续C++中还会提及。
//FlightDisplayView.qml (有些版本是FlyView.qml) 最后
Rectangle {
anchors.top: sendRect.bottom
anchors.topMargin: 20
anchors.horizontalCenter: parent.horizontalCenter
height: colSendRect.height * 1.2
width: colSendRect.width * 1.2
radius: 2
color: "black"
Column {
id: colSendRect
spacing: 4
anchors.centerIn: parent
QGCLabel {
anchors.horizontalCenter: parent.horizontalCenter
text: "Mavlink 接收测试!"
font.pointSize: 12
font.bold: true
color: "red"
}
Row {
id: row1
anchors.horizontalCenter: parent.horizontalCenter
height: label1.height
QGCLabel {
id: label1
anchors.verticalCenter: parent.verticalCenter
text: qsTr("接收的第一个字节:")
font.pointSize: 11
font.bold: true
color: "#B7FF4A"
}
QGCLabel {
anchors.verticalCenter: parent.verticalCenter
text: activeVehicle ? activeVehicle.rcvByte1 : ""
font.pointSize: 11
font.bold: true
color: "#B7FF4A"
}
}
Row {
anchors.left: row1.left
height: label2.height
QGCLabel {
id: label2
anchors.verticalCenter: parent.verticalCenter
text: qsTr("接收的第二个字节:")
font.pointSize: 11
font.bold: true
color: "#B7FF4A"
}
QGCLabel {
anchors.verticalCenter: parent.verticalCenter
text: activeVehicle ? activeVehicle.rcvByte2 : ""
font.pointSize: 11
font.bold: true
color: "#B7FF4A"
}
}
}
}
3.2 C++后端
使用QGC与MockLink的通讯可以模拟QGC地面站与PX4/APM飞控的通讯,来验证QGC地面站是否编写有问题,再与飞控对接这样能确保地面站是没问题的。再提醒下,MockLink和QGC本身用的是同一套mavlink,所以检验位肯定正确,如不正确,请看前文的分析。
以下代码配合 QGC_CCH地面站 饮用更佳
3.2.1 QGC发送
上述QML中发送的核心就是:
activeVehicle.testSendToVehicle(protectTextField1.text, protectTextField2.text)
protectTextField1.text, protectTextField2.text 为两个文本框中数据
QGC中发送函数:
//Vehicle.cc
void Vehicle::testSendToVehicle(quint8 byte1, quint8 byte2)
{
mavlink_message_t msg;
mavlink_csdn_test_t csdn_test_t;
//用户数据填充到结构体中
csdn_test_t.byte1 = byte1;
csdn_test_t.byte2 = byte2;
qDebug() << "csdn_test_t.byte1" << csdn_test_t.byte1; //QGC send cmd to xxx
qDebug() << "csdn_test_t.byte2" << csdn_test_t.byte2; //QGC send cmd to xxx
mavlink_msg_csdn_test_encode_chan(_mavlink->getSystemId(),
_mavlink->getComponentId(),
priorityLink()->mavlinkChannel(),
&msg,
&csdn_test_t);
sendMessageOnLink(priorityLink(), msg);
//有些版本可用以下方式发送
//sendMessageOnLinkThreadSafe(priorityLink(), msg);
}
Vehicle.h中声明:
public:
...
Q_INVOKABLE void testSendToVehicle(quint8 byte1, quint8 byte2);
3.2.2 MockLink接收
MockLink接收函数:
//MockLink.cc
void MockLink::_handleCSDNTest(const mavlink_message_t& msg)
{
mavlink_csdn_test_t csdn_test_t;
mavlink_msg_csdn_test_decode(&msg, &csdn_test_t);
qDebug() << "[MockLink] Rcv byte1: " << csdn_test_t.byte1;
qDebug() << "[MockLink] Rcv byte2: " << csdn_test_t.byte2;
}
调用MockLink接收函数:
//MockLink.cc
void MockLink::_handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes)
{
//最后加入:
case MAVLINK_MSG_ID_CSDN_TEST:
_handleCSDNTest(msg);
break;
}
接收函数声明:
//MockLink.h
class MockLink : public LinkInterface {
...
private:
...
void _handleCSDNTest(const mavlink_message_t& msg);
}
3.2.3 MockLink发送
发送两个字节数据的声明:
//MockLink.h
class MockLink : public LinkInterface {
...
private:
...
void _sendCSDNTest(void);
//发送的两个数据
uint8_t _testByte1;
uint8_t _testByte2;
}
数据初始化:
//MockLink.cc
MockLink::MockLink(SharedLinkConfigurationPointer& config) {
...
//初始化列表最后添加
, _testByte1 (0)
, _testByte2 (0)
}
发送函数:
void MockLink::_sendCSDNTest(void)
{
mavlink_message_t msg;
//发送的两个数据一个加,一个减
_testByte1++;
_testByte2--;
mavlink_msg_csdn_test_pack_chan(_vehicleSystemId,
_vehicleComponentId,
_mavlinkChannel,
&msg,
_testByte1, // MAV_TYPE
_testByte2 // MAV_AUTOPILOT
);
respondWithMavlinkMessage(msg);
}
模拟发送需要借助定时器,以下为1HZ发送的:
// MockLink.cc
void MockLink::_run1HzTasks(void)
{
...
_sendVibration();
_sendSysStatus();
_sendADSBVehicles();
//[修改]
_sendCSDNTest();
...
}
3.2.4 QGC接收
QGC中接收函数:
void Vehicle::_handleCSDNTestRcv(mavlink_message_t& message)
{
mavlink_csdn_test_t csdn_test_t;
mavlink_msg_csdn_test_decode(&message, &csdn_test_t);
//赋值
_rcvByte1 = csdn_test_t.byte1;
_rcvByte2 = csdn_test_t.byte2;
//触发信号,QML前端中接收,可搜 activeVehicle.rcvByte1
emit rcvByte1Changed(_rcvByte1);
emit rcvByte2Changed(_rcvByte2);
// qDebug() << "[Vehicle] Rcv RcvByte1:" << _rcvByte1;
// qDebug() << "[Vehicle] Rcv RcvByte2:" << _rcvByte2;
}
调用接收函数:
void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message) {
...
case MAVLINK_MSG_ID_WIND:
_handleWind(message);
break;
//[修改]
case MAVLINK_MSG_ID_CSDN_TEST:
_handleCSDNTestRcv(message);
break;
}
变量初始化:
Vehicle::Vehicle(...)
: FactGroup(_vehicleUIUpdateRateMSecs, ":/json/Vehicle/VehicleFact.json")
...
, _gitHash(versionNotSetValue)
, _uid(0)
//[修改]
, _rcvByte1(0)
, _rcvByte2(0)
{
}
变量和函数声明:
class Vehicle : public FactGroup
{
Q_OBJECT
public:
...
Q_PROPERTY(quint8 rcvByte1 READ rcvByte1 NOTIFY rcvByte1Changed)
Q_PROPERTY(quint8 rcvByte2 READ rcvByte2 NOTIFY rcvByte2Changed)
...
quint8 rcvByte1 () { return _rcvByte1; }
quint8 rcvByte2 () { return _rcvByte2; }
signals:
...
void rcvByte1Changed (quint8 rcvByte1);
void rcvByte2Changed (quint8 rcvByte2);
...
private:
void _handleCSDNTestRcv(mavlink_message_t& message);
...
quint8 _rcvByte1;
quint8 _rcvByte2;
...
}
看都看完了,如果对你有任何帮助,请不要吝啬你的一键三连哦,感谢各位~
关于QGC地面站其它文章请点击这里: QGC地面站
本文源码地址: QGC_CCH地面站
以上是关于QGC地面站Mavlink生成和MockLink模拟收发通讯的主要内容,如果未能解决你的问题,请参考以下文章
QGC地面站软件名和Logo修改(QGroundControl)
QGC地面站软件名和Logo修改(QGroundControl)