QGC地面站Mavlink生成和MockLink模拟收发通讯


所有的热爱都要不遗余力,真正喜欢它便给它更高的优先级,和更多的时间吧!

关于QGC地面站其它文章请点击这里:     QGC地面站

本文源码地址:     QGC_CCH地面站

我建了个QGC学习交流群,感兴趣的小伙伴们可以扫码加入哦,失效了,可以加我微信拉进群
在这里插入图片描述


1. 演示效果

在这里插入图片描述
测试的时候如下,连接MockLink中的PX4:
在这里插入图片描述
QGC发送数据: 需要手动点击发送,MockLink中会打印接收到的两个字节
MockLink发送数据: 1HZ的定时器发送

2. Mavlink 生成

主要流程参考官网:

Installing MAVLink

Generating MAVLink Libraries

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地面站

  • 63
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 41
    评论
评论 41
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值