Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/en/qgc-user-guide/plan_view/plan_view.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ The plan tools are a vertical tool strip on the left side of the map, used for a

Inserts a takeoff command into the mission. This tool is available for all vehicle types except rovers.

For PX4 VTOL vehicles, choose **VTOL takeoff** to climb and transition to fixed-wing flight, or
**Multicopter takeoff** to remain in multicopter mode. The VTOL takeoff remains the default choice.

### Pattern

The [Pattern](pattern.md) tool simplifies the creation of missions for flying complex geometries, including [surveys](../plan_view/pattern_survey.md) and [structure scans](../plan_view/pattern_structure_scan_v2.md).
Expand Down
1 change: 1 addition & 0 deletions src/FirmwarePlugin/FirmwarePlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class FirmwarePlugin : public QObject
ROIModeCapability = 1 << 5, ///< Vehicle supports ROI (both in Fly guided mode and from Plan creation)
ChangeHeadingCapability = 1 << 6, ///< Vehicle supports changing heading at current location
GuidedTakeoffCapability = 1 << 7, ///< Vehicle supports guided takeoff
VTOLMulticopterTakeoffCapability = 1 << 8, ///< VTOL supports MAV_CMD_NAV_TAKEOFF while remaining in multicopter mode
};

/// Parameter name remapping support:
Expand Down
5 changes: 5 additions & 0 deletions src/FirmwarePlugin/PX4/PX4-MavCmdInfoVTOL.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,10 @@
"fileType": "MavCmdInfo",

"mavCmdInfo": [
{
"id": 22,
"comment": "MAV_CMD_NAV_TAKEOFF",
"description": "Take off vertically and continue the mission in multicopter mode."
}
]
}
3 changes: 3 additions & 0 deletions src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ bool PX4FirmwarePlugin::isCapable(const Vehicle *vehicle, FirmwareCapabilities c
if (vehicle->multiRotor() || vehicle->vtol()) {
available |= TakeoffVehicleCapability | GuidedTakeoffCapability | OrbitModeCapability;
}
if (vehicle->vtol()) {
available |= VTOLMulticopterTakeoffCapability;
}
if (vehicle->fixedWing()) {
available |= TakeoffVehicleCapability | OrbitModeCapability;
}
Expand Down
54 changes: 43 additions & 11 deletions src/MissionManager/MissionController.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "TakeoffMissionItem.h"
#include "PlanViewSettings.h"
#include "MissionCommandTree.h"
#include "AppMessages.h"
#include "QGCMath.h"
#include "QGCLoggingCategory.h"

Expand Down Expand Up @@ -71,7 +72,7 @@ MissionController::~MissionController()

void MissionController::_resetMissionFlightStatus(void)
{
_flightStatusCalc.reset(_controllerVehicle, _managerVehicle, _missionContainsVTOLTakeoff);
_flightStatusCalc.reset(_controllerVehicle, _managerVehicle, _missionStartsInVTOLMulticopterMode);
_missionFlightStatus = _flightStatusCalc.status();

emit missionPlannedDistanceChanged(_missionFlightStatus.plannedDistance);
Expand Down Expand Up @@ -313,9 +314,33 @@ VisualMissionItem* MissionController::insertSimpleMissionItem(QGeoCoordinate coo
}

VisualMissionItem* MissionController::insertTakeoffItem(QGeoCoordinate /*coordinate*/, int visualItemIndex, bool makeCurrentItem)
{
return _insertTakeoffItemWorker(
_controllerVehicle->vtol() ? MAV_CMD_NAV_VTOL_TAKEOFF : MAV_CMD_NAV_TAKEOFF,
visualItemIndex,
makeCurrentItem);
}

VisualMissionItem* MissionController::insertVTOLMulticopterTakeoffItem(
QGeoCoordinate /*coordinate*/, int visualItemIndex, bool makeCurrentItem)
{
const FirmwarePlugin* firmwarePlugin = _controllerVehicle->firmwarePlugin();
const QList<MAV_CMD> supportedCommands = firmwarePlugin->supportedMissionCommands(_controllerVehicle->vehicleClass());
const bool commandSupported = supportedCommands.isEmpty() || supportedCommands.contains(MAV_CMD_NAV_TAKEOFF);
if (!_controllerVehicle->vtol()
|| !firmwarePlugin->isCapable(_controllerVehicle, FirmwarePlugin::VTOLMulticopterTakeoffCapability)
|| !commandSupported) {
QGC::showAppMessage(tr("Multicopter takeoff is not supported for this vehicle and firmware."));
return nullptr;
}

return _insertTakeoffItemWorker(MAV_CMD_NAV_TAKEOFF, visualItemIndex, makeCurrentItem);
}

VisualMissionItem* MissionController::_insertTakeoffItemWorker(MAV_CMD command, int visualItemIndex, bool makeCurrentItem)
{
int sequenceNumber = _nextSequenceNumber();
_takeoffMissionItem = new TakeoffMissionItem(_controllerVehicle->vtol() ? MAV_CMD_NAV_VTOL_TAKEOFF : MAV_CMD_NAV_TAKEOFF, _masterController, _flyView, _settingsItem, false /* forLoad */);
_takeoffMissionItem = new TakeoffMissionItem(command, _masterController, _flyView, _settingsItem, false /* forLoad */);
_takeoffMissionItem->setSequenceNumber(sequenceNumber);
_initVisualItem(_takeoffMissionItem);

Expand Down Expand Up @@ -996,7 +1021,7 @@ void MissionController::_recalcFlightPathSegments(void)

FlightPathSegmentHashTable oldSegmentTable = _flightPathSegmentHashTable;

_missionContainsVTOLTakeoff = false;
_missionStartsInVTOLMulticopterMode = false;
_flightPathSegmentHashTable.clear();

_simpleFlightPathSegments.beginResetModel();
Expand Down Expand Up @@ -1036,14 +1061,19 @@ void MissionController::_recalcFlightPathSegments(void)
MAV_CMD command = simpleItem->mavCommand();
switch (command) {
case MAV_CMD_NAV_TAKEOFF:
if (firstCoordinateNotFound
&& _controllerVehicle->firmwarePlugin()->isCapable(
_controllerVehicle, FirmwarePlugin::VTOLMulticopterTakeoffCapability)) {
_missionStartsInVTOLMulticopterMode = true;
}
if (!linkEndToHome && firstCoordinateNotFound) {
linkStartToHome = true;
}
break;
case MAV_CMD_NAV_VTOL_TAKEOFF:
_missionContainsVTOLTakeoff = command == MAV_CMD_NAV_VTOL_TAKEOFF;
if (!linkEndToHome) {
// If we still haven't found the first coordinate item and we hit a takeoff command this means the mission starts from the ground.
// Link the first item back to home to show that.
if (firstCoordinateNotFound) {
linkStartToHome = true;
}
if (!linkEndToHome && firstCoordinateNotFound) {
_missionStartsInVTOLMulticopterMode = true;
linkStartToHome = true;
}
break;
case MAV_CMD_NAV_RETURN_TO_LAUNCH:
Expand Down Expand Up @@ -1173,7 +1203,9 @@ void MissionController::_recalcMissionFlightStatus()

qCDebug(MissionControllerLog) << "_recalcMissionFlightStatus";

_flightStatusCalc.recalc(_visualItems, _settingsItem, _controllerVehicle, _managerVehicle, _appSettings, _planViewSettings, _missionContainsVTOLTakeoff);
_flightStatusCalc.recalc(
_visualItems, _settingsItem, _controllerVehicle, _managerVehicle, _appSettings, _planViewSettings,
_missionStartsInVTOLMulticopterMode);
_missionFlightStatus = _flightStatusCalc.status();
_minAMSLAltitude = _flightStatusCalc.minAMSLAltitude();
_maxAMSLAltitude = _flightStatusCalc.maxAMSLAltitude();
Expand Down
11 changes: 10 additions & 1 deletion src/MissionManager/MissionController.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@ class MissionController : public PlanElementController
/// @return Newly created item
Q_INVOKABLE VisualMissionItem* insertTakeoffItem(QGeoCoordinate coordinate, int visualItemIndex, bool makeCurrentItem = false);

/// Add a multicopter takeoff item for a VTOL which supports remaining in multicopter mode
/// @param coordinate: Coordinate for item
/// @param visualItemIndex: index to insert at, -1 for end of list
/// @param makeCurrentItem: true: Make this item the current item
/// @return Newly created item, or nullptr if the vehicle does not support this command behavior
Q_INVOKABLE VisualMissionItem* insertVTOLMulticopterTakeoffItem(
QGeoCoordinate coordinate, int visualItemIndex, bool makeCurrentItem = false);

/// Add a new land item to the list
/// @param coordinate: Coordinate for item
/// @param visualItemIndex: index to insert at, -1 for end of list
Expand Down Expand Up @@ -385,6 +393,7 @@ private slots:
void _initLoadedVisualItems (QmlObjectListModel* loadedVisualItems);
FlightPathSegment* _addFlightPathSegment (FlightPathSegmentHashTable& prevItemPairHashTable, VisualItemPair& pair, bool mavlinkTerrainFrame);
VisualMissionItem* _insertSimpleMissionItemWorker (QGeoCoordinate coordinate, MAV_CMD command, int visualItemIndex, bool makeCurrentItem);
VisualMissionItem* _insertTakeoffItemWorker (MAV_CMD command, int visualItemIndex, bool makeCurrentItem);
void _insertComplexMissionItemWorker (const QGeoCoordinate& mapCenterCoordinate, ComplexMissionItem* complexItem, int visualItemIndex, bool makeCurrentItem);
bool _isROIBeginItem (SimpleMissionItem* simpleItem);
bool _isROICancelItem (SimpleMissionItem* simpleItem);
Expand Down Expand Up @@ -450,7 +459,7 @@ private slots:
bool _isROIBeginCurrentItem = false;
double _minAMSLAltitude = 0;
double _maxAMSLAltitude = 0;
bool _missionContainsVTOLTakeoff = false;
bool _missionStartsInVTOLMulticopterMode = false;

QGroundControlQmlGlobal::AltitudeFrame _globalAltFrame = QGroundControlQmlGlobal::AltitudeFrameRelative;

Expand Down
18 changes: 13 additions & 5 deletions src/MissionManager/MissionFlightStatusCalculator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@

#include <QtMath>

void MissionFlightStatusCalculator::reset(Vehicle* controllerVehicle, Vehicle* managerVehicle, bool missionContainsVTOLTakeoff)
void MissionFlightStatusCalculator::reset(
Vehicle* controllerVehicle, Vehicle* managerVehicle, bool missionStartsInVTOLMulticopterMode)
{
_status.totalDistance = 0.0;
_status.plannedDistance = 0.0;
Expand All @@ -36,7 +37,9 @@ void MissionFlightStatusCalculator::reset(Vehicle* controllerVehicle, Vehicle* m
_status.cruiseAmpsTotal = 0;
_status.batteryChangePoint = -1;
_status.batteriesRequired = -1;
_status.vtolMode = missionContainsVTOLTakeoff ? QGCMAVLink::VehicleClassMultiRotor : QGCMAVLink::VehicleClassFixedWing;
_status.vtolMode = missionStartsInVTOLMulticopterMode
? QGCMAVLink::VehicleClassMultiRotor
: QGCMAVLink::VehicleClassFixedWing;

controllerVehicle->firmwarePlugin()->batteryConsumptionData(controllerVehicle, _status.mAhBattery, _status.hoverAmps, _status.cruiseAmps);
if (_status.mAhBattery != 0) {
Expand All @@ -51,7 +54,7 @@ void MissionFlightStatusCalculator::recalc(QmlObjectListModel* visualItems,
Vehicle* managerVehicle,
AppSettings* appSettings,
PlanViewSettings* planViewSettings,
bool missionContainsVTOLTakeoff)
bool missionStartsInVTOLMulticopterMode)
{
bool firstCoordinateItem = true;
VisualMissionItem* lastFlyThroughVI = qobject_cast<VisualMissionItem*>(visualItems->get(0));
Expand All @@ -70,7 +73,7 @@ void MissionFlightStatusCalculator::recalc(QmlObjectListModel* visualItems,

_minAMSLAltitude = _maxAMSLAltitude = qQNaN();

reset(controllerVehicle, managerVehicle, missionContainsVTOLTakeoff);
reset(controllerVehicle, managerVehicle, missionStartsInVTOLMulticopterMode);

bool linkStartToHome = false;
bool foundRTL = false;
Expand Down Expand Up @@ -242,7 +245,12 @@ void MissionFlightStatusCalculator::recalc(QmlObjectListModel* visualItems,
// Update VTOL state
if (simpleItem && controllerVehicle->vtol()) {
switch (simpleItem->command()) {
case MAV_CMD_NAV_TAKEOFF: // This will do a fixed wing style takeoff
case MAV_CMD_NAV_TAKEOFF:
if (!controllerVehicle->firmwarePlugin()->isCapable(
controllerVehicle, FirmwarePlugin::VTOLMulticopterTakeoffCapability)) {
_status.vtolMode = QGCMAVLink::VehicleClassFixedWing;
}
break;
case MAV_CMD_NAV_VTOL_TAKEOFF: // Vehicle goes straight up and then transitions to FW
case MAV_CMD_NAV_LAND:
_status.vtolMode = QGCMAVLink::VehicleClassFixedWing;
Expand Down
4 changes: 2 additions & 2 deletions src/MissionManager/MissionFlightStatusCalculator.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class MissionFlightStatusCalculator
{
public:
/// Resets the flight status fields to defaults based on vehicle properties.
void reset(Vehicle* controllerVehicle, Vehicle* managerVehicle, bool missionContainsVTOLTakeoff);
void reset(Vehicle* controllerVehicle, Vehicle* managerVehicle, bool missionStartsInVTOLMulticopterMode);

/// Runs the full recalculation over all visual items, updating per-item
/// display properties and computing aggregate flight statistics.
Expand All @@ -30,7 +30,7 @@ class MissionFlightStatusCalculator
Vehicle* managerVehicle,
AppSettings* appSettings,
PlanViewSettings* planViewSettings,
bool missionContainsVTOLTakeoff);
bool missionStartsInVTOLMulticopterMode);

const MissionFlightStatus_t& status() const { return _status; }
double minAMSLAltitude() const { return _minAMSLAltitude; }
Expand Down
17 changes: 12 additions & 5 deletions src/MissionManager/TakeoffMissionItem.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#include "TakeoffMissionItem.h"

#include "FirmwarePlugin.h"
#include "MissionCommandTree.h"
#include "QGroundControlQmlGlobal.h"
#include "SettingsManager.h"
#include "PlanViewSettings.h"
#include "PlanMasterController.h"
#include "MissionSettingsItem.h"
#include "MultiVehicleManager.h"
#include "PlanMasterController.h"
#include "PlanViewSettings.h"
#include "QGroundControlQmlGlobal.h"
#include "SettingsManager.h"
#include "Vehicle.h"

TakeoffMissionItem::TakeoffMissionItem(PlanMasterController* masterController, bool flyView, MissionSettingsItem* settingsItem, bool forLoad)
Expand Down Expand Up @@ -112,7 +114,12 @@ bool TakeoffMissionItem::isTakeoffCommand(MAV_CMD command)
void TakeoffMissionItem::_initLaunchTakeoffAtSameLocation(void)
{
if (specifiesCoordinate()) {
if (_controllerVehicle->fixedWing() || _controllerVehicle->vtol()) {
const bool isVTOLMulticopterTakeoff =
_controllerVehicle->vtol()
&& mavCommand() == MAV_CMD_NAV_TAKEOFF
&& _controllerVehicle->firmwarePlugin()->isCapable(
_controllerVehicle, FirmwarePlugin::VTOLMulticopterTakeoffCapability);
if (_controllerVehicle->fixedWing() || (_controllerVehicle->vtol() && !isVTOLMulticopterTakeoff)) {
_setLaunchTakeoffAtSameLocation(false);
} else {
// PX4 specifies a coordinate for takeoff even for multi-rotor. But it makes more sense to not have a coordinate
Expand Down
41 changes: 41 additions & 0 deletions src/PlanView/PlanView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ Item {
_missionController.insertTakeoffItem(mapCenter(), nextIndex, true /* makeCurrentItem */)
}

function insertVTOLMulticopterTakeoffItemAfterCurrent() {
var nextIndex = _missionController.currentPlanViewVIIndex + 1
_missionController.insertVTOLMulticopterTakeoffItem(mapCenter(), nextIndex, true /* makeCurrentItem */)
}

function insertLandItemAfterCurrent() {
var nextIndex = _missionController.currentPlanViewVIIndex + 1
_missionController.insertLandItem(mapCenter(), nextIndex, true /* makeCurrentItem */)
Expand Down Expand Up @@ -454,6 +459,9 @@ Item {
iconSource: "/res/takeoff.svg"
enabled: _missionController.isInsertTakeoffValid
visible: toolStrip._isMissionLayer && !_planMasterController.controllerVehicle.rover
dropPanelComponent: _planMasterController.controllerVehicle.supports.vtolMulticopterTakeoff
? vtolTakeoffDropPanel
: null
onTriggered: {
insertTakeoffItemAfterCurrent()
}
Expand Down Expand Up @@ -808,6 +816,39 @@ Item {
} // Column
}

Component {
id: vtolTakeoffDropPanel

ColumnLayout {
spacing: ScreenTools.defaultFontPixelWidth * 0.5

QGCLabel { text: qsTr("Choose takeoff mode:") }

QGCButton {
objectName: "planTakeoff_vtolButton"
text: qsTr("VTOL takeoff")
Layout.fillWidth: true
primary: true

onClicked: {
insertTakeoffItemAfterCurrent()
dropPanel.hide()
}
}

QGCButton {
objectName: "planTakeoff_mcButton"
text: qsTr("Multicopter takeoff")
Layout.fillWidth: true

onClicked: {
insertVTOLMulticopterTakeoffItemAfterCurrent()
dropPanel.hide()
}
}
}
}

QGCPopupDialogFactory {
id: promptForPlanUsageOnVehicleChangePopupFactory

Expand Down
5 changes: 5 additions & 0 deletions src/Vehicle/VehicleSupports.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,8 @@ bool VehicleSupports::changeHeading() const
{
return _vehicle->firmwarePlugin()->isCapable(_vehicle, FirmwarePlugin::ChangeHeadingCapability);
}

bool VehicleSupports::vtolMulticopterTakeoff() const
{
return _vehicle->firmwarePlugin()->isCapable(_vehicle, FirmwarePlugin::VTOLMulticopterTakeoffCapability);
}
2 changes: 2 additions & 0 deletions src/Vehicle/VehicleSupports.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class VehicleSupports : public QObject
Q_PROPERTY(bool guidedTakeoffWithAltitude READ guidedTakeoffWithAltitude CONSTANT)
Q_PROPERTY(bool guidedTakeoffWithoutAltitude READ guidedTakeoffWithoutAltitude CONSTANT)
Q_PROPERTY(bool changeHeading READ changeHeading CONSTANT)
Q_PROPERTY(bool vtolMulticopterTakeoff READ vtolMulticopterTakeoff CONSTANT)

bool throttleModeCenterZero() const;
bool negativeThrust() const;
Expand All @@ -46,6 +47,7 @@ class VehicleSupports : public QObject
bool guidedTakeoffWithAltitude() const;
bool guidedTakeoffWithoutAltitude() const;
bool changeHeading() const;
bool vtolMulticopterTakeoff() const;

signals:
void terrainFrameChanged();
Expand Down
10 changes: 10 additions & 0 deletions test/MissionManager/MissionCommandTreeTest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ void MissionCommandTreeTest::testOverride()
delete vehicle;
}

void MissionCommandTreeTest::testPX4VTOLTakeoffOverride()
{
MissionCommandList commandList(QStringLiteral(":/json/PX4-MavCmdInfoVTOL.json"), false /* baseCommandList */);
const MissionCommandUIInfo* const takeoffInfo = commandList.getUIInfo(MAV_CMD_NAV_TAKEOFF);

QVERIFY(takeoffInfo);
QCOMPARE(takeoffInfo->description(),
QStringLiteral("Take off vertically and continue the mission in multicopter mode."));
}

void MissionCommandTreeTest::testAllTrees()
{
ignoreLogMessage("FirmwarePlugin.ParameterMetaData", QtWarningMsg, QRegularExpression("Skipping invalid enum value"));
Expand Down
1 change: 1 addition & 0 deletions test/MissionManager/MissionCommandTreeTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ private slots:

void testJsonLoad();
void testOverride();
void testPX4VTOLTakeoffOverride();
void testAllTrees();
void testUnknownCommandFallbacks();

Expand Down
Loading
Loading