Browse Source

PID tuning: add Autotune UI for rate & attitude controllers

QGC4.4
Atanas Batinkov 4 years ago committed by GitHub
parent
commit
17cb9f48df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      custom-example/qgroundcontrol.qrc
  2. 2
      qgroundcontrol.pro
  3. 1
      qgroundcontrol.qrc
  4. 6
      src/FirmwarePlugin/FirmwarePlugin.cc
  5. 4
      src/FirmwarePlugin/FirmwarePlugin.h
  6. 4
      src/QGCApplication.cc
  7. 106
      src/QmlControls/AutotuneUI.qml
  8. 2
      src/QmlControls/CMakeLists.txt
  9. 126
      src/QmlControls/PIDTuning.qml
  10. 1
      src/QmlControls/QGroundControl/Controls/qmldir
  11. 177
      src/Vehicle/Autotune.cpp
  12. 67
      src/Vehicle/Autotune.h
  13. 3
      src/Vehicle/Vehicle.cc
  14. 4
      src/Vehicle/Vehicle.h

1
custom-example/qgroundcontrol.qrc

@ -125,6 +125,7 @@ @@ -125,6 +125,7 @@
<file alias="QGroundControl/Controls/PreFlightCheckGroup.qml">../src/QmlControls/PreFlightCheckGroup.qml</file>
<file alias="QGroundControl/Controls/PreFlightCheckModel.qml">../src/QmlControls/PreFlightCheckModel.qml</file>
<file alias="QGroundControl/Controls/QGCButton.qml">../src/QmlControls/QGCButton.qml</file>
<file alias="QGroundControl/Controls/AutotuneUI.qml">../src/QmlControls/AutotuneUI.qml</file>
<file alias="QGroundControl/Controls/QGCCheckBox.qml">../src/QmlControls/QGCCheckBox.qml</file>
<file alias="QGroundControl/Controls/QGCColoredImage.qml">../src/QmlControls/QGCColoredImage.qml</file>
<file alias="QGroundControl/Controls/QGCControlDebug.qml">../src/QmlControls/QGCControlDebug.qml</file>

2
qgroundcontrol.pro

@ -572,6 +572,7 @@ HEADERS += \ @@ -572,6 +572,7 @@ HEADERS += \
src/AnalyzeView/ULogParser.h \
src/AnalyzeView/MavlinkConsoleController.h \
src/Audio/AudioOutput.h \
src/Vehicle/Autotune.h \
src/Camera/QGCCameraControl.h \
src/Camera/QGCCameraIO.h \
src/Camera/QGCCameraManager.h \
@ -815,6 +816,7 @@ SOURCES += \ @@ -815,6 +816,7 @@ SOURCES += \
src/AnalyzeView/ULogParser.cc \
src/AnalyzeView/MavlinkConsoleController.cc \
src/Audio/AudioOutput.cc \
src/Vehicle/Autotune.cpp \
src/Camera/QGCCameraControl.cc \
src/Camera/QGCCameraIO.cc \
src/Camera/QGCCameraManager.cc \

1
qgroundcontrol.qrc

@ -126,6 +126,7 @@ @@ -126,6 +126,7 @@
<file alias="QGroundControl/Controls/PreFlightCheckGroup.qml">src/QmlControls/PreFlightCheckGroup.qml</file>
<file alias="QGroundControl/Controls/PreFlightCheckModel.qml">src/QmlControls/PreFlightCheckModel.qml</file>
<file alias="QGroundControl/Controls/QGCButton.qml">src/QmlControls/QGCButton.qml</file>
<file alias="QGroundControl/Controls/AutotuneUI.qml">src/QmlControls/AutotuneUI.qml</file>
<file alias="QGroundControl/Controls/QGCCheckBox.qml">src/QmlControls/QGCCheckBox.qml</file>
<file alias="QGroundControl/Controls/QGCColoredImage.qml">src/QmlControls/QGCColoredImage.qml</file>
<file alias="QGroundControl/Controls/QGCControlDebug.qml">src/QmlControls/QGCControlDebug.qml</file>

6
src/FirmwarePlugin/FirmwarePlugin.cc

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
#include "QGCFileDownload.h"
#include "QGCCameraManager.h"
#include "RadioComponentController.h"
#include "Autotune.h"
#include <QRegularExpression>
#include <QDebug>
@ -1125,3 +1126,8 @@ void FirmwarePlugin::sendGCSMotionReport(Vehicle* vehicle, FollowMe::GCSMotionRe @@ -1125,3 +1126,8 @@ void FirmwarePlugin::sendGCSMotionReport(Vehicle* vehicle, FollowMe::GCSMotionRe
vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), message);
}
}
Autotune* FirmwarePlugin::createAutotune(Vehicle *vehicle)
{
return new Autotune(vehicle);
}

4
src/FirmwarePlugin/FirmwarePlugin.h

@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
class Vehicle;
class QGCCameraControl;
class QGCCameraManager;
class Autotune;
/// This is the base class for Firmware specific plugins
///
@ -333,6 +334,9 @@ public: @@ -333,6 +334,9 @@ public:
// gets hobbs meter from autopilot. This should be reimplmeented for each firmware
virtual QString getHobbsMeter(Vehicle* vehicle) { Q_UNUSED(vehicle); return "Not Supported"; }
/// Creates Autotune object.
virtual Autotune* createAutotune(Vehicle *vehicle);
signals:
void toolIndicatorsChanged(void);
void modeIndicatorsChanged(void);

4
src/QGCApplication.cc

@ -101,6 +101,7 @@ @@ -101,6 +101,7 @@
#include "ToolStripActionList.h"
#include "QGCMAVLink.h"
#include "VehicleLinkManager.h"
#include "Autotune.h"
#if defined(QGC_ENABLE_PAIRING)
#include "PairingManager.h"
@ -523,6 +524,7 @@ void QGCApplication::_initCommon() @@ -523,6 +524,7 @@ void QGCApplication::_initCommon()
qmlRegisterUncreatableType<QGCVideoStreamInfo> (kQGCVehicle, 1, 0, "QGCVideoStreamInfo", kRefOnly);
qmlRegisterUncreatableType<LinkInterface> (kQGCVehicle, 1, 0, "LinkInterface", kRefOnly);
qmlRegisterUncreatableType<VehicleLinkManager> (kQGCVehicle, 1, 0, "VehicleLinkManager", kRefOnly);
qmlRegisterUncreatableType<Autotune> (kQGCVehicle, 1, 0, "Autotune", kRefOnly);
qmlRegisterUncreatableType<MissionController> (kQGCControllers, 1, 0, "MissionController", kRefOnly);
qmlRegisterUncreatableType<GeoFenceController> (kQGCControllers, 1, 0, "GeoFenceController", kRefOnly);
@ -530,7 +532,7 @@ void QGCApplication::_initCommon() @@ -530,7 +532,7 @@ void QGCApplication::_initCommon()
qmlRegisterUncreatableType<MissionItem> (kQGroundControl, 1, 0, "MissionItem", kRefOnly);
qmlRegisterUncreatableType<VisualMissionItem> (kQGroundControl, 1, 0, "VisualMissionItem", kRefOnly);
qmlRegisterUncreatableType<FlightPathSegment> (kQGroundControl, 1, 0, "FlightPathSegment", kRefOnly);
qmlRegisterUncreatableType<FlightPathSegment> (kQGroundControl, 1, 0, "FlightPathSegment", kRefOnly);
qmlRegisterUncreatableType<QmlObjectListModel> (kQGroundControl, 1, 0, "QmlObjectListModel", kRefOnly);
qmlRegisterUncreatableType<MissionCommandTree> (kQGroundControl, 1, 0, "MissionCommandTree", kRefOnly);
qmlRegisterUncreatableType<CameraCalc> (kQGroundControl, 1, 0, "CameraCalc", kRefOnly);

106
src/QmlControls/AutotuneUI.qml

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
/****************************************************************************
*
* (c) 2009-2021 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Controllers 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.ScreenTools 1.0
Item {
id: _root
property var _autotune: globals.activeVehicle.autotune
property real _margins: ScreenTools.defaultFontPixelHeight
readonly property string dialogTitle: qsTr("Autotune")
QGCPalette {
id: palette
colorGroupEnabled: enabled
}
Component {
id: autotuneConfirmationDialogComponent
QGCViewMessage {
message: qsTr("WARNING!\
\n\nThe auto-tuning procedure should be executed with caution and requires the vehicle to fly stable enough before \
attempting the procedure!\n\nBefore starting the auto-tuning process, make sure that: \
\n1. You have read the auto-tuning guide and have followed the preliminary steps \
\n2. The current control gains are good enough to stabilize the drone in presence of medium disturbances \
\n3. You are ready to abort the auto-tuning sequence by moving the RC sticks, if anything unexpected happens. \
\n\nClick Ok to start the auto-tuning process.\n")
function accept() {
hideDialog()
_autotune.autotuneRequest()
}
}
}
Rectangle {
width: _root.width
height: statusColumn.height + (2 * _margins)
color: palette.windowShade
enabled: _autotune.autotuneEnabled
QGCButton {
id: autotuneButton
primary: true
text: dialogTitle
enabled: !_autotune.autotuneInProgress
anchors {
left: parent.left
leftMargin: _margins
verticalCenter: parent.verticalCenter
}
onClicked: {
mainWindow.showComponentDialog(autotuneConfirmationDialogComponent,
dialogTitle,
mainWindow.showDialogDefaultWidth,
StandardButton.Ok | StandardButton.Cancel)
}
}
Column {
id: statusColumn
spacing: _margins
anchors {
left: autotuneButton.right
right: parent.right
leftMargin: _margins
rightMargin: _margins
verticalCenter: parent.verticalCenter
}
QGCLabel {
text: _autotune.autotuneStatus
anchors {
left: parent.left
}
}
ProgressBar {
value: _autotune.autotuneProgress
anchors {
left: parent.left
right: parent.right
}
}
}
}
}

2
src/QmlControls/CMakeLists.txt

@ -50,6 +50,7 @@ add_custom_target(QmlControlsQml @@ -50,6 +50,7 @@ add_custom_target(QmlControlsQml
EditPositionDialog.qml
ExclusiveGroupItem.qml
FactSliderPanel.qml
AutotuneUI.qml
FileButton.qml
FlightModeDropdown.qml
FlightModeMenu.qml
@ -81,6 +82,7 @@ add_custom_target(QmlControlsQml @@ -81,6 +82,7 @@ add_custom_target(QmlControlsQml
PreFlightCheckList.qml
PreFlightCheckModel.qml
QGCButton.qml
AutotuneUI.qml
QGCCheckBox.qml
QGCColoredImage.qml
QGCComboBox.qml

126
src/QmlControls/PIDTuning.qml

@ -177,77 +177,113 @@ RowLayout { @@ -177,77 +177,113 @@ RowLayout {
Layout.minimumWidth: contentWidth
Layout.maximumWidth: contentWidth
Layout.alignment: Qt.AlignTop
Column {
spacing: _margins
Layout.alignment: Qt.AlignTop
width: parent.width
id: rightColumn
Row {
id: _autotuneSelectRow
spacing: _margins
visible: tuningMode === Vehicle.ModeRateAndAttitude
Switch {
id: autotuningEnabled
checked: true
}
QGCLabel {
color: qgcPal.text
text: autotuningEnabled.checked ? qsTr("Autotune enabled") : qsTr("Autotune disabled")
}
}
Column {
width: parent.width
visible: _autotuneSelectRow.visible && autotuningEnabled.checked
RowLayout {
spacing: _margins
visible: axis.length > 1
AutotuneUI {
anchors {
top: parent.top
topMargin: _margins * 2
}
QGCLabel { text: qsTr("Select Tuning:") }
width: parent.width
}
}
Repeater {
model: axis
QGCRadioButton {
text: modelData.name
checked: index == _currentAxis
onClicked: _currentAxis = index
Column {
width: parent.width
visible: !_autotuneSelectRow.visible || !autotuningEnabled.checked
Column {
RowLayout {
spacing: _margins
visible: axis.length > 1
QGCLabel { text: qsTr("Select Tuning:") }
Repeater {
model: axis
QGCRadioButton {
text: modelData.name
checked: index == _currentAxis
onClicked: _currentAxis = index
}
}
}
}
}
// Instantiate all sliders (instead of switching the model), so that
// values are not changed unexpectedly if they do not match with a tick
// value
Repeater {
model: axis
FactSliderPanel {
width: parent.width
visible: _currentAxis === index
sliderModel: axis[index].params
// Instantiate all sliders (instead of switching the model), so that
// values are not changed unexpectedly if they do not match with a tick
// value
Repeater {
model: axis
FactSliderPanel {
width: parent.width
visible: _currentAxis === index
sliderModel: axis[index].params
}
}
}
Column {
QGCLabel { text: qsTr("Clipboard Values:") }
Column {
QGCLabel { text: qsTr("Clipboard Values:") }
GridLayout {
rows: savedRepeater.model.length
flow: GridLayout.TopToBottom
rowSpacing: 0
columnSpacing: _margins
GridLayout {
rows: savedRepeater.model.length
flow: GridLayout.TopToBottom
rowSpacing: 0
columnSpacing: _margins
Repeater {
model: axis[_currentAxis].params
Repeater {
model: axis[_currentAxis].params
QGCLabel { text: param }
}
QGCLabel { text: param }
}
Repeater {
id: savedRepeater
Repeater {
id: savedRepeater
QGCLabel { text: modelData }
QGCLabel { text: modelData }
}
}
}
}
RowLayout {
spacing: _margins
RowLayout {
spacing: _margins
QGCButton {
text: qsTr("Save To Clipboard")
onClicked: saveTuningParamValues()
}
QGCButton {
text: qsTr("Save To Clipboard")
onClicked: saveTuningParamValues()
}
QGCButton {
text: qsTr("Restore From Clipboard")
onClicked: resetToSavedTuningParamValues()
QGCButton {
text: qsTr("Restore From Clipboard")
onClicked: resetToSavedTuningParamValues()
}
}
}
}

1
src/QmlControls/QGroundControl/Controls/qmldir

@ -53,6 +53,7 @@ PreFlightCheckButton 1.0 PreFlightCheckButton.qml @@ -53,6 +53,7 @@ PreFlightCheckButton 1.0 PreFlightCheckButton.qml
PreFlightCheckGroup 1.0 PreFlightCheckGroup.qml
PreFlightCheckModel 1.0 PreFlightCheckModel.qml
QGCButton 1.0 QGCButton.qml
AutotuneUI 1.0 AutotuneUI.qml
QGCCheckBox 1.0 QGCCheckBox.qml
QGCColoredImage 1.0 QGCColoredImage.qml
QGCComboBox 1.0 QGCComboBox.qml

177
src/Vehicle/Autotune.cpp

@ -0,0 +1,177 @@ @@ -0,0 +1,177 @@
/****************************************************************************
*
* (c) 2009-2021 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include <QtGlobal>
#include "QGCApplication.h"
#include "Autotune.h"
//-----------------------------------------------------------------------------
Autotune::Autotune(Vehicle *vehicle) :
QObject(vehicle)
, _vehicle(vehicle)
{
connect(_vehicle, &Vehicle::flyingChanged, this, &Autotune::handleEnabled);
connect(_vehicle, &Vehicle::landingChanged, this, &Autotune::handleEnabled);
_pollTimer.setInterval(1000); // 1s for the polling interval
_pollTimer.setSingleShot(false);
connect(&_pollTimer, &QTimer::timeout, this, &Autotune::sendMavlinkRequest);
}
//-----------------------------------------------------------------------------
void Autotune::autotuneRequest()
{
sendMavlinkRequest();
startTimers();
_autotuneInProgress = true;
_autotuneStatus = tr("Autotune: In progress");
emit autotuneChanged();
}
//-----------------------------------------------------------------------------
void Autotune::ackHandler(void* resultHandlerData, int compId, MAV_RESULT commandResult, uint8_t progress, Vehicle::MavCmdResultFailureCode_t failureCode)
{
Q_UNUSED(compId);
Q_UNUSED(failureCode);
auto * autotune = static_cast<Autotune *>(resultHandlerData);
if (autotune->_autotuneInProgress) {
if ((commandResult == MAV_RESULT_IN_PROGRESS) || (commandResult == MAV_RESULT_ACCEPTED)) {
autotune->handleAckStatus(progress);
}
else if (commandResult == MAV_RESULT_FAILED) {
autotune->handleAckFailure();
}
else {
autotune->handleAckError(commandResult);
}
emit autotune->autotuneChanged();
}
else {
qWarning() << "Ack received for a command different from MAV_CMD_DO_AUTOTUNE_ENABLE ot wrong UI state.";
}
}
//-----------------------------------------------------------------------------
bool Autotune::autotuneEnabled()
{
return _vehicle->flying() || _autotuneInProgress;
}
//-----------------------------------------------------------------------------
void Autotune::handleEnabled()
{
emit autotuneChanged();
}
//-----------------------------------------------------------------------------
void Autotune::handleAckStatus(uint8_t ackProgress)
{
_autotuneProgress = ackProgress/100.f;
if (ackProgress < 20) {
_autotuneStatus = tr("Autotune: initializing");
}
else if (ackProgress < 40) {
_autotuneStatus = tr("Autotune: roll");
}
else if (ackProgress < 60) {
_autotuneStatus = tr("Autotune: pitch");
}
else if (ackProgress < 80) {
_autotuneStatus = tr("Autotune: yaw");
}
else if (ackProgress == 95) {
_autotuneStatus = tr("Wait for disarm");
if(!_disarmMessageDisplayed) {
qgcApp()->showAppMessage(tr("Land and disarm the vehicle in order to apply the parameters."));
_disarmMessageDisplayed = true;
}
}
else if (ackProgress < 100) {
_autotuneStatus = tr("Autotune: in progress");
}
else { // success or unknown error
stopTimers();
_autotuneInProgress = false;
if (ackProgress == 100) {
_autotuneStatus = tr("Autotune: Success");
qgcApp()->showAppMessage(tr("Autotune successful."));
}
else {
_autotuneStatus = tr("Autotune: Unknown error");
}
}
}
//-----------------------------------------------------------------------------
void Autotune::handleAckFailure()
{
stopTimers();
_autotuneInProgress = false;
_disarmMessageDisplayed = false;
_autotuneStatus = tr("Autotune: Failed");
}
//-----------------------------------------------------------------------------
void Autotune::handleAckError(uint8_t ackError)
{
stopTimers();
_autotuneInProgress = false;
_disarmMessageDisplayed = false;
_autotuneStatus = tr("Autotune: Ack error %1").arg(ackError);
}
//-----------------------------------------------------------------------------
void Autotune::startTimers()
{
_pollTimer.start();
}
//-----------------------------------------------------------------------------
void Autotune::stopTimers()
{
_pollTimer.stop();
}
//-----------------------------------------------------------------------------
void Autotune::sendMavlinkRequest()
{
_vehicle->sendMavCommandWithHandler(
ackHandler, // Ack callback
this, // Ack callback data
MAV_COMP_ID_AUTOPILOT1, // the ID of the autopilot
MAV_CMD_DO_AUTOTUNE_ENABLE, // the mavlink command
1, // request autotune
0, // unused parameter
0, // unused parameter
0, // unused parameter
0, // unused parameter
0, // unused parameter
0);
}

67
src/Vehicle/Autotune.h

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
/****************************************************************************
*
* (c) 2009-2021 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#pragma once
#include <QTimer>
#include "Vehicle.h"
#include "MAVLinkProtocol.h"
class Autotune : public QObject
{
Q_OBJECT
public:
explicit Autotune(Vehicle *vehicle);
Q_PROPERTY(bool autotuneEnabled READ autotuneEnabled NOTIFY autotuneChanged)
Q_PROPERTY(bool autotuneInProgress READ autotuneInProgress NOTIFY autotuneChanged)
Q_PROPERTY(float autotuneProgress READ autotuneProgress NOTIFY autotuneChanged)
Q_PROPERTY(QString autotuneStatus READ autotuneStatus NOTIFY autotuneChanged)
Q_INVOKABLE void autotuneRequest ();
static void ackHandler(void* resultHandlerData, int compId, MAV_RESULT commandResult, uint8_t progress, Vehicle::MavCmdResultFailureCode_t failureCode);
bool autotuneEnabled ();
bool autotuneInProgress () { return _autotuneInProgress; }
float autotuneProgress () { return _autotuneProgress; }
QString autotuneStatus () { return _autotuneStatus; }
public slots:
void handleEnabled ();
void sendMavlinkRequest();
private:
void handleAckStatus(uint8_t ackProgress);
void handleAckFailure();
void handleAckError(uint8_t ackError);
void startTimers();
void stopTimers();
private slots:
private:
Vehicle* _vehicle {nullptr};
bool _autotuneInProgress {false};
float _autotuneProgress {0.0};
QString _autotuneStatus {tr("Autotune: Not performed")};
bool _disarmMessageDisplayed {false};
QTimer _pollTimer; // the frequency at which the polling should be performed
signals:
void autotuneChanged ();
};

3
src/Vehicle/Vehicle.cc

@ -53,6 +53,7 @@ @@ -53,6 +53,7 @@
#ifdef QT_DEBUG
#include "MockLink.h"
#endif
#include "Autotune.h"
#if defined(QGC_AIRMAP_ENABLED)
#include "AirspaceVehicleManager.h"
@ -386,6 +387,8 @@ void Vehicle::_commonInit() @@ -386,6 +387,8 @@ void Vehicle::_commonInit()
_objectAvoidance = new VehicleObjectAvoidance(this, this);
_autotune = _firmwarePlugin->createAutotune(this);
// GeoFenceManager needs to access ParameterManager so make sure to create after
_geoFenceManager = new GeoFenceManager(this);
connect(_geoFenceManager, &GeoFenceManager::error, this, &Vehicle::_geoFenceManagerError);

4
src/Vehicle/Vehicle.h

@ -69,6 +69,7 @@ class RequestMessageTest; @@ -69,6 +69,7 @@ class RequestMessageTest;
class LinkInterface;
class LinkManager;
class InitialConnectStateMachine;
class Autotune;
#if defined(QGC_AIRMAP_ENABLED)
class AirspaceVehicleManager;
@ -272,6 +273,7 @@ public: @@ -272,6 +273,7 @@ public:
Q_PROPERTY(ParameterManager* parameterManager READ parameterManager CONSTANT)
Q_PROPERTY(VehicleLinkManager* vehicleLinkManager READ vehicleLinkManager CONSTANT)
Q_PROPERTY(VehicleObjectAvoidance* objectAvoidance READ objectAvoidance CONSTANT)
Q_PROPERTY(Autotune* autotune READ autotune CONSTANT)
// FactGroup object model properties
@ -664,6 +666,7 @@ public: @@ -664,6 +666,7 @@ public:
FTPManager* ftpManager () { return _ftpManager; }
ComponentInformationManager* compInfoManager () { return _componentInformationManager; }
VehicleObjectAvoidance* objectAvoidance () { return _objectAvoidance; }
Autotune* autotune () const { return _autotune; }
static const int cMaxRcChannels = 18;
@ -1105,6 +1108,7 @@ private: @@ -1105,6 +1108,7 @@ private:
JoystickManager* _joystickManager = nullptr;
ComponentInformationManager* _componentInformationManager = nullptr;
VehicleObjectAvoidance* _objectAvoidance = nullptr;
Autotune* _autotune = nullptr;
#if defined(QGC_AIRMAP_ENABLED)
AirspaceVehicleManager* _airspaceVehicleManager = nullptr;
#endif

Loading…
Cancel
Save