█ 系列文章目录
提示:这里是收集了无人机的相关文章
- 【无人机学习】无人机基础知识
- 【无人机学习】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."
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#通道
- 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.消息协议格式
- 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 Name | Type | Units | Description |
---|---|---|---|
time_boot_ms | uint32_t | ms | 时间戳(自系统启动以来的时间) |
port | uint8_t | 伺服输出端口(8 个输出组 = 1 个端口); 在 Pixhawk 上:0 = MAIN,1 = AUX。 | |
chan1_raw | uint16_t | us | RC channel 1 value. |
chan2_raw | uint16_t | us | RC channel 2 value. |
chan3_raw | uint16_t | us | RC channel 3 value. |
chan4_raw | uint16_t | us | RC channel 4 value. |
chan5_raw | uint16_t | us | RC channel 5 value. |
chan6_raw | uint16_t | us | RC channel 6 value. |
chan7_raw | uint16_t | us | RC channel 7 value. |
chan8_raw | uint16_t | us | RC channel 8 value. |
rssi | uint8_t | 接收信号强度,取值范围:[0-254],UINT8_MAX:无效/未知。 |
- 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%.
- 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 Name | Type | Units | Description |
---|---|---|---|
time_boot_ms | uint32_t | ms | 时间戳(自系统启动以来的时间) |
chancount | uint8_t | 正在接收的 RC 通道总数。 这可以大于 18,表示有更多频道可用但未在此消息中给出 | |
chan1_raw | uint16_t | us | RC channel 1 value. |
chan2_raw | uint16_t | us | RC channel 2 value. |
chan3_raw | uint16_t | us | RC channel 3 value. |
chan4_raw | uint16_t | us | RC channel 4 value. |
chan5_raw | uint16_t | us | RC channel 5 value. |
chan6_raw | uint16_t | us | RC channel 6 value. |
chan7_raw | uint16_t | us | RC channel 7 value. |
chan8_raw | uint16_t | us | RC channel 8 value. |
chan9_raw | uint16_t | us | RC channel 9 value. |
chan10_raw | uint16_t | us | RC channel 10 value. |
chan11_raw | uint16_t | us | RC channel 11 value. |
chan12_raw | uint16_t | us | RC channel 12 value. |
chan13_raw | uint16_t | us | RC channel 13 value. |
chan14_raw | uint16_t | us | RC channel 14 value. |
chan15_raw | uint16_t | us | RC channel 15 value. |
chan16_raw | uint16_t | us | RC channel 16 value. |
chan17_raw | uint16_t | us | RC channel 17 value. |
chan18_raw | uint16_t | us | RC channel 18 value. |
rssi | uint8_t | 接收信号强度,取值范围:[0-254],UINT8_MAX:无效/未知。 |
- 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;
}
- 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;
}
█ 代码分析
- 创建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));
}
。。。。。。
}
}
}
- RC#通道:channel monitor( 没有将16路全部展示)
一共有16 个(RC1-RC16 )通道,每个通道有6个参数可设置:
- 姿态控制:Attitude Controls
“RCMAP_ROLL”、“RCMAP_PITCH”、“RCMAP_YAW”、RCMAP_THROTTLE"映射到上面16个通道中的四个(正常为前4个通道):
-
取值范围
| 参数 |RC#_MIN|RC#_MAX|RC#_TRIM|
|–|–|–|–|
|QGC| 800|2200|1500|
|MP| 1000|3000|1500|
其实遥控器校准就是 16个RC#_TRIM 值显示 -
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);
}
}*/
- 更新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版)
- UI界面:…\GCSViews\ConfigurationView\ConfigRadioInput.cs
- 绑定数据
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));
- 创建定时更新
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());
}
}
- 数据更新:…\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;
}
}
}
█ 相关资料
提示:这里是参考的相关文章
- 消息(常见) · MAVLink 开发者指南 - LOG_REQUEST_LIST ( #117 )
- 2021-11-13 QGC PlanView(任务规划功能) 界面加载顺序
- 2021-12-02 SetupView(设置功能) 界面加载顺序
- 2018-03-05 QGC SettingsView(设置功能) 界面加载顺序_Copy->Paste的博客-CSDN博客
- 2019-01-17 QGC 增加禁飞区显示功能_Copy->Paste的博客-CSDN博客_禁飞区数据
博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持! |
---|
提示:转载请注明出处:
https://blog.csdn.net/ljb568838953/article/details/124264485
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)