【无人机学习之QGroundControl】android端App初解4-遥控器通道

【无人机学习之QGroundControl】android端App初解4-遥控器通道,第1张

█ 【无人机学习之QGroundControl】android端App初解4-遥控器通道
█ 系列文章目录

提示:这里是收集了无人机的相关文章

  • 【无人机学习】无人机基础知识
  • 【无人机学习】Mission Planner(pc端)和QGroundControl(android端)
  • 【无人机学习之DroidPlanner】FlightActivity的启动过程
  • 【无人机学习之DroidPlanner】msg_heartbeat心跳处理(含MAVLink协议)
  • 【无人机学习之DroidPlanner】msg_sys_status系统状态
  • 【无人机学习之QGroundControl】android端App初解1
  • 【无人机学习之QGroundControl】android端App初解2-APMPowerComponent(含QML的介绍)
  • 【无人机学习之QGroundControl】android端App初解3-ParameterEditorController
  • 【无人机学习之QGroundControl】android端App初解4-遥控器通道

█ 文章目录

  • █ 【无人机学习之QGroundControl】android端App初解4-遥控器通道
  • █ 系列文章目录
  • █ 文章目录
  • █ 读前说明
  • █ 界面分析
    • 1.遥控器界面
    • 2.MissionPlanner界面参考
    • 3. 相关文件
    • 4. RadioComponent.qml
    • 5. APMRadioComponentSummary.qml
    • 6. RCChannelMonitor.qml
  • █ mavlink协议
    • 1.消息协议格式
    • 2.生成的Mavlink
  • █ 代码分析
  • █ 代码分析(MP版)
  • █ 相关资料
  • █ 免责声明


█ 读前说明
  • 本文通过学习别人写demo,学习一些课件,参考一些博客,学习相关知识,如有涉及侵权请告知
  • 本文可能只简单罗列了一些相关的代码实现过程,复制了一些大神的高论,如内容有误请自行辨别
  • 涉及到的逻辑以及说明可能只做了简单的介绍,主要当做笔记,了解过程而已,如有不同看法,欢迎下方评论
  • 本文源码:https://github.com/mavlink/qgroundcontrol
  • 本文UI:https://github.com/mavlink/qgroundcontrol/blob/master/src/ui
  • 本文的硬件测试是APM而不是PX4,因此代码走向都是走向APM方向
  • QGC中UI设计的主要模式是用QML编写的UI页面,多次与用C ++编写的定制“控制器”进行通信。类似MVC的设计模式。
  • QGC最新源码版本是v4.1.1(2021年1月28),使用QtCreator版本是5.12.6,语言是c++ 。

█ 界面分析 1.遥控器界面 The Radio Component is used to setup which channels on your RC Transmitter you will use for each vehicle control such as Roll, Pitch, Yaw and Throttle. "
"It also allows you to assign switches and dials to the various flight modes. "
"Prior to flight you must also calibrate the extents for all of your channels."
"Before calibrating you should zero all your trims and subtrims. Click Ok to start Calibration."


2.MissionPlanner界面参考



MAVLINK_MSG_ID_RC_CHANNELS - sysid:100 compid:1 time_boot_ms:555394 
chan1_raw:1500 chan2_raw:1500 chan3_raw:1500 chan4_raw:1500 
chan5_raw:1050 chan6_raw:1950 chan7_raw:1050 chan8_raw:1050 
chan9_raw:1050 chan10_raw:1050 chan11_raw:1950 chan12_raw:1950 
chan13_raw:874 chan14_raw:874 chan15_raw:874 chan16_raw:874 
chan17_raw:0 chan18_raw:0 chancount:16 rssi:0
3. 相关文件

RadioComponent.qml文件在qgroundcontrol-master\src\AutoPilotPlugins\Common\RadioComponent.qml

ui界面qgroundcontrol-master\src\AutoPilotPlugins\Common\RadioComponent.qml
遥控器逻辑qgroundcontrol-master\src\AutoPilotPlugins\Common\RadioComponentController.cc
遥控器组件类
负责按钮的图标和加载qml文件
qgroundcontrol-master\src\AutoPilotPlugins\APM\APMRadioComponent.cc
预览时的展示UI
读取相应参数值
qgroundcontrol-master\src\AutoPilotPlugins\APM\APMRadioComponentSummary.qmll
RC#进度条qgroundcontrol-master\src\QmlControls\RCChannelMonitor.qml

)

  • 遥控器

  • RC#通道
4. RadioComponent.qml
  • Attitude Controls 姿态控制
Column {
    width:      parent.width
    spacing:    5
    QGCLabel { text: qsTr("Attitude Controls") }

    Item {
        width:  parent.width
        height: globals.defaultTextHeight * 2
        QGCLabel {
            id:     rollLabel
            width:  globals.defaultTextWidth * 10
            text:   qsTr("Roll")
        }

        Loader {
            id:                 rollLoader
            anchors.left:       rollLabel.right
            anchors.right:      parent.right
            height:             globals.defaultTextHeight
            width:              100
            sourceComponent:    channelMonitorDisplayComponent

            property bool mapped:           controller.rollChannelMapped
            property bool reversed:         controller.rollChannelReversed
        }

        Connections {
            target: controller
            onRollChannelRCValueChanged: rollLoader.item.rcValue = rcValue
        }
    }
	。。。。。。
	// Command Buttons
    Row {
        spacing: 10

        QGCButton {
            id:         skipButton
            text:       qsTr("Skip")
            onClicked:  controller.skipButtonClicked()
        }

        QGCButton {
            id:         cancelButton
            text:       qsTr("Cancel")
            onClicked:  controller.cancelButtonClicked()
        }

        QGCButton {
            id:         nextButton
            primary:    true
            text:       qsTr("Calibrate")

            onClicked: {
                if (text === qsTr("Calibrate")) {
                    mainWindow.showComponentDialog(zeroTrimsDialogComponent, dialogTitle, mainWindow.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
                } else {
                    controller.nextButtonClicked()
                }
            }
        }
    } // Row - Buttons
}// Column - Attitude Control labels
  • Additional Radio setup 其他遥控器设置
 // Status Text
 QGCLabel { text: qsTr("Additional Radio setup:") }

 GridLayout {
     id:                 switchSettingsGrid
     anchors.left:       parent.left
     anchors.right:      parent.right
     columns:            2
     columnSpacing:      ScreenTools.defaultFontPixelWidth

     Repeater {
         model: QGroundControl.multiVehicleManager.activeVehicle.px4Firmware ?
                    (QGroundControl.multiVehicleManager.activeVehicle.multiRotor ?
                        [ "RC_MAP_AUX1", "RC_MAP_AUX2", "RC_MAP_PARAM1", "RC_MAP_PARAM2", "RC_MAP_PARAM3"] :
                        [ "RC_MAP_FLAPS", "RC_MAP_AUX1", "RC_MAP_AUX2", "RC_MAP_PARAM1", "RC_MAP_PARAM2", "RC_MAP_PARAM3"]) :
                    0

         RowLayout {
             Layout.fillWidth: true

             property Fact fact: controller.getParameterFact(-1, modelData)

             QGCLabel {
                 Layout.fillWidth:   true
                 text:               fact.shortDescription
             }
             FactComboBox {
                 width:      ScreenTools.defaultFontPixelWidth * 15
                 fact:       parent.fact
                 indexModel: false
             }
         }
     }
 }

 RowLayout {
     QGCButton {
         id:         bindButton
         text:       qsTr("Spektrum Bind")
         onClicked:  mainWindow.showComponentDialog(spektrumBindDialogComponent, dialogTitle, mainWindow.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
     }

     QGCButton {
         text:       qsTr("Copy Trims")
         onClicked:  mainWindow.showComponentDialog(copyTrimsDialogComponent, dialogTitle, mainWindow.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel)
     }
 }
  • channelMonitor 通道监控bar
// Right side column
Column {
    id:             rightColumn
    anchors.top:    parent.top
    anchors.right:  parent.right
    width:          ScreenTools.defaultFontPixelWidth * 40
    spacing:        ScreenTools.defaultFontPixelHeight / 2

    Row {
        spacing: ScreenTools.defaultFontPixelWidth

        QGCRadioButton {
            text:       qsTr("Mode 1")
            checked:    controller.transmitterMode == 1
            onClicked:  controller.transmitterMode = 1
        }

        QGCRadioButton {
            text:       qsTr("Mode 2")
            checked:    controller.transmitterMode == 2
            onClicked:  controller.transmitterMode = 2
        }
    }

    Image {
        width:      parent.width
        fillMode:   Image.PreserveAspectFit
        smooth:     true
        source:     controller.imageHelp
    }

    RCChannelMonitor {
        width:      parent.width
        twoColumn:  true
    }
} // Column - Right Column
5. APMRadioComponentSummary.qml
  • APMRadioComponentSummary.qml 姿态控制
	// 对应的配置通道
    property Fact mapRollFact:      controller.getParameterFact(-1, "RCMAP_ROLL")
    property Fact mapPitchFact:     controller.getParameterFact(-1, "RCMAP_PITCH")
    property Fact mapYawFact:       controller.getParameterFact(-1, "RCMAP_YAW")
    property Fact mapThrottleFact:  controller.getParameterFact(-1, "RCMAP_THROTTLE")
    Column {
        anchors.fill:       parent

        VehicleSummaryRow {
            labelText: qsTr("Roll")
            valueText: mapRollFact.value == 0 ? qsTr("Setup required") : qsTr("Channel %1").arg(mapRollFact.valueString)
        }

        VehicleSummaryRow {
            labelText: qsTr("Pitch")
            valueText: mapPitchFact.value == 0 ? qsTr("Setup required") : qsTr("Channel %1").arg(mapPitchFact.valueString)
        }
		......
    }
  • APMRadioComponent.cc:配置两个qml
    First check for all attitude controls mapped
    Next check RC#_MIN/MAX/TRIM all at defaults
QUrl APMRadioComponent::setupSource(void) const
{
    return QUrl::fromUserInput(QStringLiteral("qrc:/qml/RadioComponent.qml"));
}

QUrl APMRadioComponent::summaryQmlSource(void) const
{
    return QUrl::fromUserInput(QStringLiteral("qrc:/qml/APMRadioComponentSummary.qml"));
}
6. RCChannelMonitor.qml
  • RCChannelMonitor
Item {
    id:     _root
    height: monitorColumn.height

    property bool twoColumn: false

    readonly property int _pwmMin:      800
    readonly property int _pwmMax:      2200
    readonly property int _pwmRange:    _pwmMax - _pwmMin

    RCChannelMonitorController {
        id:             controller
    }

    // Live channel monitor control component
    Component {
        id: channelMonitorDisplayComponent
    } // Component - channelMonitorDisplayComponent

    GridLayout {
        id:         monitorColumn
        width:      parent.width
        columns:    twoColumn ? 2 : 1

        QGCLabel {
            Layout.columnSpan:  parent.columns
            text:               "Channel Monitor"
        }

        Connections {
            target: controller

            onChannelRCValueChanged: {
                if (channelMonitorRepeater.itemAt(channel)) {
                    channelMonitorRepeater.itemAt(channel).loader.item.rcValue = rcValue
                }
            }
        }

        Repeater {
            id:     channelMonitorRepeater
            model:  controller.channelCount

            RowLayout {
                // Need this to get to loader from Connections above
                property Item loader: theLoader

                QGCLabel {
                    id:     channelLabel
                    text:   modelData + 1
                }

                Loader {
                    id:                 theLoader
                    Layout.fillWidth:   true
                    //height:                 ScreenTools.defaultFontPixelHeight
                    //width:                  parent.width - anchors.leftMargin - ScreenTools.defaultFontPixelWidth
                    sourceComponent:        channelMonitorDisplayComponent

                    property bool mapped:               true
                    readonly property bool reversed:    false
                }
            }
        }
    }
}
  • channelMonitor 通道监控bar
// Live channel monitor control component
Component {
    id: channelMonitorDisplayComponent

    Item {
        property int    rcValue:    1500


        property int            __lastRcValue:      1500
        readonly property int   __rcValueMaxJitter: 2
        property color          __barColor:         qgcPal.windowShade

        readonly property int _pwmMin:      800
        readonly property int _pwmMax:      2200
        readonly property int _pwmRange:    _pwmMax - _pwmMin

        // Bar
        Rectangle {
            id:                     bar
            anchors.verticalCenter: parent.verticalCenter
            width:                  parent.width
            height:                 parent.height / 2
            color:                  __barColor
        }

        // Center point
        Rectangle {
            anchors.horizontalCenter:   parent.horizontalCenter
            width:                      globals.defaultTextWidth / 2
            height:                     parent.height
            color:                      qgcPal.window
        }

        // Indicator
        Rectangle {
            anchors.verticalCenter: parent.verticalCenter
            width:                  parent.height * 0.75
            height:                 width
            radius:                 width / 2
            color:                  qgcPal.text
            visible:                mapped
            x:                      (((reversed ? _pwmMax - rcValue : rcValue - _pwmMin) / _pwmRange) * parent.width) - (width / 2)
        }

        QGCLabel {
            anchors.fill:           parent
            horizontalAlignment:    Text.AlignHCenter
            verticalAlignment:      Text.AlignVCenter
            text:                   qsTr("Not Mapped")
            visible:                !mapped
        }

        ColorAnimation {
            id:         barAnimation
            target:     bar
            property:   "color"
            from:       "yellow"
            to:         __barColor
            duration:   1500
        }
    }
} // Component - channelMonitorDisplayComponent
█ mavlink协议 1.消息协议格式
  1. RC_CHANNELS_RAW ( #35 ):接收到的 RC 通道的 RAW 值

The RAW values of the RC channels received. The standard PPM modulation is as follows: 1000 microseconds: 0%, 2000 microseconds: 100%. A value of UINT16_MAX implies the channel is unused. Individual receivers/transmitters might violate this specification.

Field NameTypeUnitsDescription
time_boot_msuint32_tms时间戳(自系统启动以来的时间)
portuint8_t伺服输出端口(8 个输出组 = 1 个端口); 在 Pixhawk 上:0 = MAIN,1 = AUX。
chan1_rawuint16_tusRC channel 1 value.
chan2_rawuint16_tusRC channel 2 value.
chan3_rawuint16_tusRC channel 3 value.
chan4_rawuint16_tusRC channel 4 value.
chan5_rawuint16_tusRC channel 5 value.
chan6_rawuint16_tusRC channel 6 value.
chan7_rawuint16_tusRC channel 7 value.
chan8_rawuint16_tusRC channel 8 value.
rssiuint8_t接收信号强度,取值范围:[0-254],UINT8_MAX:无效/未知。
  1. SERVO_OUTPUT_RAW ( #36 ): 伺服输出的 RAW 值(来自遥控器的 RC 输入,使用 RC_CHANNELS 消息)

Superseded by ACTUATOR_OUTPUT_STATUS. The RAW values of the servo outputs (for RC input from the remote, use the RC_CHANNELS messages). The standard PPM modulation is as follows: 1000 microseconds: 0%, 2000 microseconds: 100%.

  1. RC_CHANNELS ( #65 ):接收到的 RC 通道的 PPM 值

The PPM values of the RC channels received. The standard PPM modulation is as follows: 1000 microseconds: 0%, 2000 microseconds: 100%. A value of UINT16_MAX implies the channel is unused. Individual receivers/transmitters might violate this specification.

Field NameTypeUnitsDescription
time_boot_msuint32_tms时间戳(自系统启动以来的时间)
chancountuint8_t正在接收的 RC 通道总数。 这可以大于 18,表示有更多频道可用但未在此消息中给出
chan1_rawuint16_tusRC channel 1 value.
chan2_rawuint16_tusRC channel 2 value.
chan3_rawuint16_tusRC channel 3 value.
chan4_rawuint16_tusRC channel 4 value.
chan5_rawuint16_tusRC channel 5 value.
chan6_rawuint16_tusRC channel 6 value.
chan7_rawuint16_tusRC channel 7 value.
chan8_rawuint16_tusRC channel 8 value.
chan9_rawuint16_tusRC channel 9 value.
chan10_rawuint16_tusRC channel 10 value.
chan11_rawuint16_tusRC channel 11 value.
chan12_rawuint16_tusRC channel 12 value.
chan13_rawuint16_tusRC channel 13 value.
chan14_rawuint16_tusRC channel 14 value.
chan15_rawuint16_tusRC channel 15 value.
chan16_rawuint16_tusRC channel 16 value.
chan17_rawuint16_tusRC channel 17 value.
chan18_rawuint16_tusRC channel 18 value.
rssiuint8_t接收信号强度,取值范围:[0-254],UINT8_MAX:无效/未知。
2.生成的Mavlink
  1. msg_rc_channels_raw
public class msg_rc_channels_raw extends MAVLinkMessage {
    public static final int MAVLINK_MSG_ID_RC_CHANNELS_RAW = 35;
    public static final int MAVLINK_MSG_LENGTH = 22;
    private static final long serialVersionUID = 35L;
    public long time_boot_ms;
    public int chan1_raw;
    public int chan2_raw;
    public int chan3_raw;
    public int chan4_raw;
    public int chan5_raw;
    public int chan6_raw;
    public int chan7_raw;
    public int chan8_raw;
    public short port;
    public short rssi;
}
  1. msg_rc_channels
public class msg_rc_channels extends MAVLinkMessage {
    public static final int MAVLINK_MSG_ID_RC_CHANNELS = 65;
    public static final int MAVLINK_MSG_LENGTH = 42;
    private static final long serialVersionUID = 65L;
    public long time_boot_ms;
    public int chan1_raw;
    public int chan2_raw;
    public int chan3_raw;
    public int chan4_raw;
    public int chan5_raw;
    public int chan6_raw;
    public int chan7_raw;
    public int chan8_raw;
    public int chan9_raw;
    public int chan10_raw;
    public int chan11_raw;
    public int chan12_raw;
    public int chan13_raw;
    public int chan14_raw;
    public int chan15_raw;
    public int chan16_raw;
    public int chan17_raw;
    public int chan18_raw;
    public short chancount;
    public short rssi;
}
█ 代码分析
  1. 创建APMRadioComponent
const QVariantList& APMAutoPilotPlugin::vehicleComponents(void)
{
    if (_components.count() == 0 && !_incorrectParameterVersion) {
        if (_vehicle->parameterManager()->parametersReady()) {
        	。。。。。。
            if ( _vehicle->supportsRadio() ) {
                _radioComponent = new APMRadioComponent(_vehicle, this);
                _radioComponent->setupTriggerSignals();
                _components.append(QVariant::fromValue((VehicleComponent*)_radioComponent));
            }
            。。。。。。
         }
    }
}
  1. RC#通道:channel monitor( 没有将16路全部展示)

    一共有16 个(RC1-RC16 )通道,每个通道有6个参数可设置:

  2. 姿态控制:Attitude Controls

    “RCMAP_ROLL”、“RCMAP_PITCH”、“RCMAP_YAW”、RCMAP_THROTTLE"映射到上面16个通道中的四个(正常为前4个通道):

  1. 取值范围
    | 参数 |RC#_MIN|RC#_MAX|RC#_TRIM|
    |–|–|–|–|
    |QGC| 800|2200|1500|
    |MP| 1000|3000|1500|
    其实遥控器校准就是 16个RC#_TRIM 值显示

  2. mavlinkMessage:接收到消息->rcChannelsChanged
    qgroundcontrol-master\src\Vehicle\Vehicle.cc

void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message)
{
    。。。。。。
    // Give the plugin a change to adjust the message contents
    if (!_firmwarePlugin->adjustIncomingMavlinkMessage(this, &message)) {// APMFirmwarePlugin
        return;
    }
    。。。。。。
    switch (message.msgid) {
	    case MAVLINK_MSG_ID_HOME_POSITION:
	        _handleHomePosition(message);
	        break;
	    case MAVLINK_MSG_ID_HEARTBEAT:
	        _handleHeartbeat(message);
	        break;
	    case MAVLINK_MSG_ID_RADIO_STATUS:
	        _handleRadioStatus(message);
	        break;
	    case MAVLINK_MSG_ID_RC_CHANNELS:
	        _handleRCChannels(message);
	        break;
        。。。。。。
    }
    // This must be emitted after the vehicle processes the message. This way the vehicle state is up to date when anyone else
    // does processing.
    emit mavlinkMessageReceived(message);

    _uas->receiveMessage(message);
}
void Vehicle::_handleRCChannels(mavlink_message_t& message)
{
    mavlink_rc_channels_t channels;

    mavlink_msg_rc_channels_decode(&message, &channels);

    uint16_t* _rgChannelvalues[cMaxRcChannels] = {
        &channels.chan1_raw,
        &channels.chan2_raw,
        &channels.chan3_raw,
        &channels.chan4_raw,
        &channels.chan5_raw,
        &channels.chan6_raw,
        &channels.chan7_raw,
        &channels.chan8_raw,
        &channels.chan9_raw,
        &channels.chan10_raw,
        &channels.chan11_raw,
        &channels.chan12_raw,
        &channels.chan13_raw,
        &channels.chan14_raw,
        &channels.chan15_raw,
        &channels.chan16_raw,
        &channels.chan17_raw,
        &channels.chan18_raw,
    };
    int pwmValues[cMaxRcChannels];

    for (int i=0; i<cMaxRcChannels; i++) {
        uint16_t channelValue = *_rgChannelvalues[i];

        if (i < channels.chancount) {
            pwmValues[i] = channelValue == UINT16_MAX ? -1 : channelValue;
        } else {
            pwmValues[i] = -1;
        }
    }

    emit remoteControlRSSIChanged(channels.rssi);
    emit rcChannelsChanged(channels.chancount, pwmValues);//  #########重点
}

qgroundcontrol-master\src\FirmwarePlugin\APM\APMFirmwarePlugin.cc

/// This is the base class for all stack specific APM firmware plugins
class APMFirmwarePlugin : public FirmwarePlugin{};
/*bool APMFirmwarePlugin::adjustIncomingMavlinkMessage(Vehicle* vehicle, mavlink_message_t* message)
{
    if (message->msgid == MAVLINK_MSG_ID_HEARTBEAT) {
        // We need to look at all heartbeats that go by from any component
        _handleIncomingHeartbeat(vehicle, message);
        return true;
    }

    // Only translate messages which come from ArduPilot code. All other components are expected to follow current mavlink spec.
    if (_ardupilotComponentMap[vehicle->id()][message->compid]) {
        switch (message->msgid) {
        case MAVLINK_MSG_ID_PARAM_VALUE:
            _handleIncomingParamValue(vehicle, message);
            break;
        case MAVLINK_MSG_ID_STATUSTEXT:
            return _handleIncomingStatusText(vehicle, message);
        case MAVLINK_MSG_ID_RC_CHANNELS:
            _handleRCChannels(vehicle, message);
            break;
        case MAVLINK_MSG_ID_RC_CHANNELS_RAW:
            _handleRCChannelsRaw(vehicle, message);
            break;
        }
    }
    return true;
}*/
/*void APMFirmwarePlugin::_handleRCChannels(Vehicle* vehicle, mavlink_message_t* message)
{
    WeakLinkInterfacePtr weakLink = vehicle->vehicleLinkManager()->primaryLink();
    if (!weakLink.expired()) {
        mavlink_rc_channels_t   channels;
        SharedLinkInterfacePtr  sharedLink = weakLink.lock();

        mavlink_msg_rc_channels_decode(message, &channels);
        //-- Ardupilot uses 0-255 to indicate 0-100% where QGC expects 0-100
        if(channels.rssi) {
            channels.rssi = static_cast(static_cast(channels.rssi) / 255.0 * 100.0);
        }
        MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol();
        mavlink_msg_rc_channels_encode_chan(
                    static_cast(mavlink->getSystemId()),
                    static_cast(mavlink->getComponentId()),
                    sharedLink->mavlinkChannel(),
                    message,
                    &channels);
    }
}*/
  1. 更新ui: RadioComponentController
RadioComponentController::RadioComponentController(void)
    : _currentStep(-1)
    , _transmitterMode(2)
    , _chanCount(0)
    , _rcCalState(rcCalStateChannelWait)
{
	。。。。。。
    connect(_vehicle, &Vehicle::rcChannelsChanged, this,
     			&RadioComponentController::_rcChannelsChanged);//  #########重点
}
/// Connected to Vehicle::rcChannelsChanged signal
void RadioComponentController::_rcChannelsChanged(int channelCount, int pwmValues[Vehicle::cMaxRcChannels])
{
    for (int channel=0; channel<channelCount; channel++) {
        int channelValue = pwmValues[channel];
        if (channelValue != -1) {
            qCDebug(RadioComponentControllerVerboseLog) << "Raw value" << channel << channelValue;
            _rcRawValue[channel] = channelValue;
            emit channelRCValueChanged(channel, channelValue);//  #########重点
            // Signal attitude rc values to Qml if mapped
            if (_rgChannelInfo[channel].function != rcCalFunctionMax) {
                switch (_rgChannelInfo[channel].function) {
	                case rcCalFunctionRoll:
	                    emit rollChannelRCValueChanged(channelValue);//  #########重点
	                    break;
	                case rcCalFunctionPitch:
	                    emit pitchChannelRCValueChanged(channelValue);//  #########重点
	                    break;
	                ......
                }
            }
			......
        }
    }
}

\qgroundcontrol-master\src\AutoPilotPlugins\Common\RadioComponent.qml

Item {
    width:  parent.width
    height: globals.defaultTextHeight * 2
    QGCLabel {
        id:     rollLabel
        width:  globals.defaultTextWidth * 10
        text:   qsTr("Roll")
    }

    Loader {
        id:                 rollLoader
        anchors.left:       rollLabel.right
        anchors.right:      parent.right
        height:             globals.defaultTextHeight
        width:              100
        sourceComponent:    channelMonitorDisplayComponent

        property bool mapped:           controller.rollChannelMapped
        property bool reversed:         controller.rollChannelReversed
    }

    Connections {
        target: controller

        onRollChannelRCValueChanged: rollLoader.item.rcValue = rcValue//  #########重点
    }
}

qgroundcontrol-master\src\QmlControls\RCChannelMonitor.qml

GridLayout {
    id:         monitorColumn
    width:      parent.width
    columns:    twoColumn ? 2 : 1

    QGCLabel {
        Layout.columnSpan:  parent.columns
        text:               "Channel Monitor"
    }

    Connections {
        target: controller

        onChannelRCValueChanged: {
            if (channelMonitorRepeater.itemAt(channel)) {
                channelMonitorRepeater.itemAt(channel).loader.item.rcValue = rcValue//  #########重点
            }
        }
    }

    Repeater {
        id:     channelMonitorRepeater
        model:  controller.channelCount

        RowLayout {
            // Need this to get to loader from Connections above
            property Item loader: theLoader

            QGCLabel {
                id:     channelLabel
                text:   modelData + 1
            }

            Loader {
                id:                 theLoader
                Layout.fillWidth:   true
                //height:                 ScreenTools.defaultFontPixelHeight
                //width:                  parent.width - anchors.leftMargin - ScreenTools.defaultFontPixelWidth
                sourceComponent:        channelMonitorDisplayComponent

                property bool mapped:               true
                readonly property bool reversed:    false
            }
        }
    }
}
█ 代码分析(MP版)
  1. UI界面:…\GCSViews\ConfigurationView\ConfigRadioInput.cs
  2. 绑定数据
    qgroundcontrol-master\src\Vehicle\Vehicle.cc
   //setup bindings
   chroll = (int)(float)MainV2.comPort.MAV.param["RCMAP_ROLL"];
   chpitch = (int)(float)MainV2.comPort.MAV.param["RCMAP_PITCH"];
   chthro = (int)(float)MainV2.comPort.MAV.param["RCMAP_THROTTLE"];
   chyaw = (int)(float)MainV2.comPort.MAV.param["RCMAP_YAW"];
  BARroll.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch" + chroll + "in", true));
  BARpitch.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch" + chpitch + "in", true));
  BARthrottle.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch" + chthro + "in", true));
  BARyaw.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch" + chyaw + "in", true));

  BAR5.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch5in", true));
  BAR6.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch6in", true));
  BAR7.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch7in", true));
  BAR8.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch8in", true));

  BAR9.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch9in", true));
  BAR10.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch10in", true));
  BAR11.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch11in", true));
  BAR12.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch12in", true));
  BAR13.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch13in", true));
  BAR14.DataBindings.Add(new Binding("Value", currentStateBindingSource, "ch14in", true));
  1. 创建定时更新
   private readonly Timer _timer = new Timer();
   // setup rc update
   _timer.Tick += timer_Tick;
   _timer.Enabled = true;
   _timer.Interval = 100;
   _timer.Start();
 private void timer_Tick(object sender, EventArgs e) {
     // update all linked controls - 10hz
     try {
         MainV2.comPort.MAV.cs.UpdateCurrentSettings(currentStateBindingSource.UpdateDataSource(MainV2.comPort.MAV.cs));
     } catch (Exception ex) {
         Console.WriteLine(ex.ToString());
     }
 }
  1. 数据更新:…\ExtLibs\ArduPilot\CurrentState.cs
namespace MissionPlanner
{
    public class CurrentState : ICloneable, IDisposable
    {
        public void UpdateCurrentSettings(Action<CurrentState> bs)
        {
            if (DateTime.Now > lastupdate.AddMilliseconds(50) || updatenow) // 20 hz
            {
                lastupdate = DateTime.Now;
	            。。。。。。
	            // re-request streams
	            if (!(lastdata.AddSeconds(8) > DateTime.Now) && mavinterface.BaseStream != null &&
	                mavinterface.BaseStream.IsOpen)
	            {
	                try
	                {
	                    mavinterface.requestDatastream(MAVLink.MAV_DATA_STREAM.EXTENDED_STATUS, MAV.cs.ratestatus,
	                        MAV.sysid, MAV.compid); // mode
	                    mavinterface.requestDatastream(MAVLink.MAV_DATA_STREAM.POSITION, MAV.cs.rateposition,
	                        MAV.sysid, MAV.compid); // request gps
	                    mavinterface.requestDatastream(MAVLink.MAV_DATA_STREAM.EXTRA1, MAV.cs.rateattitude,
	                        MAV.sysid, MAV.compid); // request attitude
	                    mavinterface.requestDatastream(MAVLink.MAV_DATA_STREAM.EXTRA2, MAV.cs.rateattitude,
	                        MAV.sysid, MAV.compid); // request vfr
	                    mavinterface.requestDatastream(MAVLink.MAV_DATA_STREAM.EXTRA3, MAV.cs.ratesensors,
	                        MAV.sysid,
	                        MAV.compid); // request extra stuff - tridge
	                    mavinterface.requestDatastream(MAVLink.MAV_DATA_STREAM.RAW_SENSORS, MAV.cs.ratesensors,
	                        MAV.sysid, MAV.compid); // request raw sensor
	                    mavinterface.requestDatastream(MAVLink.MAV_DATA_STREAM.RC_CHANNELS, MAV.cs.raterc,
	                        MAV.sysid,
	                        MAV.compid); // request rc info
	                }
	                catch
	                {
	                    log.Error("Failed to request rates");
	                }
	
	                lastdata = DateTime.Now.AddSeconds(30); // prevent flooding
	            }
	        }
        }
    }
}

MAVlink数据接收

		//radio
        [GroupText("RadioIn")] public float ch1in { get; set; }

        [GroupText("RadioIn")] public float ch2in { get; set; }

        [GroupText("RadioIn")] public float ch3in { get; set; }

        [GroupText("RadioIn")] public float ch4in { get; set; }

        [GroupText("RadioIn")] public float ch5in { get; set; }

        [GroupText("RadioIn")] public float ch6in { get; set; }

        [GroupText("RadioIn")] public float ch7in { get; set; }

        [GroupText("RadioIn")] public float ch8in { get; set; }

        [GroupText("RadioIn")] public float ch9in { get; set; }

        [GroupText("RadioIn")] public float ch10in { get; set; }

        [GroupText("RadioIn")] public float ch11in { get; set; }

        [GroupText("RadioIn")] public float ch12in { get; set; }

        [GroupText("RadioIn")] public float ch13in { get; set; }

        [GroupText("RadioIn")] public float ch14in { get; set; }

        [GroupText("RadioIn")] public float ch15in { get; set; }

        [GroupText("RadioIn")] public float ch16in { get; set; }

        // motors
        [GroupText("RadioOut")] public float ch1out { get; set; }

        [GroupText("RadioOut")] public float ch2out { get; set; }

        [GroupText("RadioOut")] public float ch3out { get; set; }

        [GroupText("RadioOut")] public float ch4out { get; set; }

        [GroupText("RadioOut")] public float ch5out { get; set; }

        [GroupText("RadioOut")] public float ch6out { get; set; }

        [GroupText("RadioOut")] public float ch7out { get; set; }

        [GroupText("RadioOut")] public float ch8out { get; set; }

        [GroupText("RadioOut")] public float ch9out { get; set; }

        [GroupText("RadioOut")] public float ch10out { get; set; }

        [GroupText("RadioOut")] public float ch11out { get; set; }

        [GroupText("RadioOut")] public float ch12out { get; set; }

        [GroupText("RadioOut")] public float ch13out { get; set; }

        [GroupText("RadioOut")] public float ch14out { get; set; }

        [GroupText("RadioOut")] public float ch15out { get; set; }

        [GroupText("RadioOut")] public float ch16out { get; set; }
        private void Parent_OnPacketReceived(object sender, MAVLink.MAVLinkMessage mavLinkMessage)
        {
            if (mavLinkMessage.sysid == parent.sysid && mavLinkMessage.compid == parent.compid
                || mavLinkMessage.msgid == (uint)MAVLink.MAVLINK_MSG_ID.RADIO // propagate the RADIO/RADIO_STATUS message across all devices on this link
                || mavLinkMessage.msgid == (uint)MAVLink.MAVLINK_MSG_ID.RADIO_STATUS)
            {
                switch (mavLinkMessage.msgid)
                {
                    case (uint)MAVLink.MAVLINK_MSG_ID.RC_CHANNELS_RAW:// RC_CHANNELS_RAW ( #35 )

                        {
                            var rcin = mavLinkMessage.ToStructure<MAVLink.mavlink_rc_channels_raw_t>();

                            ch1in = rcin.chan1_raw;
                            ch2in = rcin.chan2_raw;
                            ch3in = rcin.chan3_raw;
                            ch4in = rcin.chan4_raw;
                            ch5in = rcin.chan5_raw;
                            ch6in = rcin.chan6_raw;
                            ch7in = rcin.chan7_raw;
                            ch8in = rcin.chan8_raw;

                            //percent
                            rxrssi = (int)(rcin.rssi / 255.0 * 100.0);

                            //MAVLink.packets[(byte)MAVLink.MSG_NAMES.RC_CHANNELS_RAW);
                        }

                        break;
                    case (uint)MAVLink.MAVLINK_MSG_ID.RC_CHANNELS:// RC_CHANNELS ( #65 )

                        {
                            var rcin = mavLinkMessage.ToStructure<MAVLink.mavlink_rc_channels_t>();

                            ch1in = rcin.chan1_raw;
                            ch2in = rcin.chan2_raw;
                            ch3in = rcin.chan3_raw;
                            ch4in = rcin.chan4_raw;
                            ch5in = rcin.chan5_raw;
                            ch6in = rcin.chan6_raw;
                            ch7in = rcin.chan7_raw;
                            ch8in = rcin.chan8_raw;

                            ch9in = rcin.chan9_raw;
                            ch10in = rcin.chan10_raw;
                            ch11in = rcin.chan11_raw;
                            ch12in = rcin.chan12_raw;
                            ch13in = rcin.chan13_raw;
                            ch14in = rcin.chan14_raw;
                            ch15in = rcin.chan15_raw;
                            ch16in = rcin.chan16_raw;

                            //percent
                            rxrssi = (int)(rcin.rssi / 255.0 * 100.0);

                            //MAVLink.packets[(byte)MAVLink.MSG_NAMES.RC_CHANNELS_RAW);
                        }

                        break;
                    case (uint)MAVLink.MAVLINK_MSG_ID.SERVO_OUTPUT_RAW:// SERVO_OUTPUT_RAW ( #36 )

                        {
                            var servoout = mavLinkMessage.ToStructure<MAVLink.mavlink_servo_output_raw_t>();

                            ch1out = servoout.servo1_raw;
                            ch2out = servoout.servo2_raw;
                            ch3out = servoout.servo3_raw;
                            ch4out = servoout.servo4_raw;
                            ch5out = servoout.servo5_raw;
                            ch6out = servoout.servo6_raw;
                            ch7out = servoout.servo7_raw;
                            ch8out = servoout.servo8_raw;

                            // mavlink2 extension
                            ch9out = servoout.servo9_raw;
                            ch10out = servoout.servo10_raw;
                            ch11out = servoout.servo11_raw;
                            ch12out = servoout.servo12_raw;
                            ch13out = servoout.servo13_raw;
                            ch14out = servoout.servo14_raw;
                            ch15out = servoout.servo15_raw;
                            ch16out = servoout.servo16_raw;
                        }
                        break;
                }
            }
        }

█ 相关资料

提示:这里是参考的相关文章

  1. 消息(常见) · MAVLink 开发者指南 - LOG_REQUEST_LIST ( #117 )
  2. 2021-11-13 QGC PlanView(任务规划功能) 界面加载顺序
  3. 2021-12-02 SetupView(设置功能) 界面加载顺序
  4. 2018-03-05 QGC SettingsView(设置功能) 界面加载顺序_Copy->Paste的博客-CSDN博客
  5. 2019-01-17 QGC 增加禁飞区显示功能_Copy->Paste的博客-CSDN博客_禁飞区数据
█ 免责声明
博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持!

提示:转载请注明出处:
https://blog.csdn.net/ljb568838953/article/details/124264485

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/876679.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存