control_allocator: major refactoring & additions

- allow effectiveness matrix to select control allocator method
  (desaturation algorithm)
- add actuator_servos publication
- add support for multiple matrices (for vtol)
- add updateSetpoint callback method to actuator effectiveness to allow it
  to manipulate the actuator setpoint after allocation
- handle motor stopping & reversal
- add control surfaces & tilt servos
- handle standard vtol + tiltrotor
- rename MC rotors params & class to be more generically usable
- fixes and enables ActuatorEffectivenessRotorsTest
This commit is contained in:
Beat Küng 2021-11-24 15:12:09 +01:00 committed by Daniel Agar
parent a81f11acdd
commit 70e46a194f
28 changed files with 1936 additions and 775 deletions

View File

@ -14,19 +14,19 @@ param set-default SYS_CTRL_ALLOC 1
param set-default CA_AIRFRAME 0
param set-default CA_MC_R_COUNT 4
param set-default CA_MC_R0_PX 0.1515
param set-default CA_MC_R0_PY 0.245
param set-default CA_MC_R0_KM 0.05
param set-default CA_MC_R1_PX -0.1515
param set-default CA_MC_R1_PY -0.1875
param set-default CA_MC_R1_KM 0.05
param set-default CA_MC_R2_PX 0.1515
param set-default CA_MC_R2_PY -0.245
param set-default CA_MC_R2_KM -0.05
param set-default CA_MC_R3_PX -0.1515
param set-default CA_MC_R3_PY 0.1875
param set-default CA_MC_R3_KM -0.05
param set-default CA_ROTOR_COUNT 4
param set-default CA_ROTOR0_PX 0.1515
param set-default CA_ROTOR0_PY 0.245
param set-default CA_ROTOR0_KM 0.05
param set-default CA_ROTOR1_PX -0.1515
param set-default CA_ROTOR1_PY -0.1875
param set-default CA_ROTOR1_KM 0.05
param set-default CA_ROTOR2_PX 0.1515
param set-default CA_ROTOR2_PY -0.245
param set-default CA_ROTOR2_KM -0.05
param set-default CA_ROTOR3_PX -0.1515
param set-default CA_ROTOR3_PY 0.1875
param set-default CA_ROTOR3_KM -0.05
param set-default PWM_MAIN_FUNC1 101
param set-default PWM_MAIN_FUNC2 102

View File

@ -30,26 +30,26 @@ param set-default MNT_MODE_IN 0
param set-default MAV_PROTO_VER 2
param set-default CA_AIRFRAME 0
param set-default CA_MC_R_COUNT 6
param set-default CA_ROTOR_COUNT 6
param set-default CA_MC_R0_PX 0.0
param set-default CA_MC_R0_PY 1.0
param set-default CA_MC_R0_KM -0.05
param set-default CA_MC_R1_PX 0.0
param set-default CA_MC_R1_PY -1.0
param set-default CA_MC_R1_KM 0.05
param set-default CA_MC_R2_PX 0.866025
param set-default CA_MC_R2_PY -0.5
param set-default CA_MC_R2_KM -0.05
param set-default CA_MC_R3_PX -0.866025
param set-default CA_MC_R3_PY 0.5
param set-default CA_MC_R3_KM 0.05
param set-default CA_MC_R4_PX 0.866025
param set-default CA_MC_R4_PY 0.5
param set-default CA_MC_R4_KM 0.05
param set-default CA_MC_R5_PX -0.866025
param set-default CA_MC_R5_PY -0.5
param set-default CA_MC_R5_KM -0.05
param set-default CA_ROTOR0_PX 0.0
param set-default CA_ROTOR0_PY 1.0
param set-default CA_ROTOR0_KM -0.05
param set-default CA_ROTOR1_PX 0.0
param set-default CA_ROTOR1_PY -1.0
param set-default CA_ROTOR1_KM 0.05
param set-default CA_ROTOR2_PX 0.866025
param set-default CA_ROTOR2_PY -0.5
param set-default CA_ROTOR2_KM -0.05
param set-default CA_ROTOR3_PX -0.866025
param set-default CA_ROTOR3_PY 0.5
param set-default CA_ROTOR3_KM 0.05
param set-default CA_ROTOR4_PX 0.866025
param set-default CA_ROTOR4_PY 0.5
param set-default CA_ROTOR4_KM 0.05
param set-default CA_ROTOR5_PX -0.866025
param set-default CA_ROTOR5_PY -0.5
param set-default CA_ROTOR5_KM -0.05
param set-default PWM_MAIN_FUNC1 101
param set-default PWM_MAIN_FUNC2 102

View File

@ -19,19 +19,19 @@ param set-default SYS_CTRL_ALLOC 1
param set-default CA_AIRFRAME 0
param set-default CA_MC_R_COUNT 4
param set-default CA_MC_R0_PX 0.177
param set-default CA_MC_R0_PY 0.177
param set-default CA_MC_R0_KM 0.05
param set-default CA_MC_R1_PX -0.177
param set-default CA_MC_R1_PY -0.177
param set-default CA_MC_R1_KM 0.05
param set-default CA_MC_R2_PX 0.177
param set-default CA_MC_R2_PY -0.177
param set-default CA_MC_R2_KM -0.05
param set-default CA_MC_R3_PX -0.177
param set-default CA_MC_R3_PY 0.177
param set-default CA_MC_R3_KM -0.05
param set-default CA_ROTOR_COUNT 4
param set-default CA_ROTOR0_PX 0.177
param set-default CA_ROTOR0_PY 0.177
param set-default CA_ROTOR0_KM 0.05
param set-default CA_ROTOR1_PX -0.177
param set-default CA_ROTOR1_PY -0.177
param set-default CA_ROTOR1_KM 0.05
param set-default CA_ROTOR2_PX 0.177
param set-default CA_ROTOR2_PY -0.177
param set-default CA_ROTOR2_KM -0.05
param set-default CA_ROTOR3_PX -0.177
param set-default CA_ROTOR3_PY 0.177
param set-default CA_ROTOR3_KM -0.05
param set-default PWM_MAIN_FUNC1 101
param set-default PWM_MAIN_FUNC2 102

View File

@ -15,31 +15,31 @@
param set-default SYS_CTRL_ALLOC 1
param set-default CA_AIRFRAME 0
param set-default CA_MC_R_COUNT 6
param set-default CA_ROTOR_COUNT 6
param set-default CA_MC_R0_PX 0.0
param set-default CA_MC_R0_PY 0.275
param set-default CA_MC_R0_KM -0.05
param set-default CA_ROTOR0_PX 0.0
param set-default CA_ROTOR0_PY 0.275
param set-default CA_ROTOR0_KM -0.05
param set-default CA_MC_R1_PX 0.0
param set-default CA_MC_R1_PY -0.275
param set-default CA_MC_R1_KM 0.05
param set-default CA_ROTOR1_PX 0.0
param set-default CA_ROTOR1_PY -0.275
param set-default CA_ROTOR1_KM 0.05
param set-default CA_MC_R2_PX 0.238
param set-default CA_MC_R2_PY -0.1375
param set-default CA_MC_R2_KM -0.05
param set-default CA_ROTOR2_PX 0.238
param set-default CA_ROTOR2_PY -0.1375
param set-default CA_ROTOR2_KM -0.05
param set-default CA_MC_R3_PX -0.238
param set-default CA_MC_R3_PY 0.1375
param set-default CA_MC_R3_KM 0.05
param set-default CA_ROTOR3_PX -0.238
param set-default CA_ROTOR3_PY 0.1375
param set-default CA_ROTOR3_KM 0.05
param set-default CA_MC_R4_PX 0.238
param set-default CA_MC_R4_PY 0.1375
param set-default CA_MC_R4_KM 0.05
param set-default CA_ROTOR4_PX 0.238
param set-default CA_ROTOR4_PY 0.1375
param set-default CA_ROTOR4_KM 0.05
param set-default CA_MC_R5_PX -0.238
param set-default CA_MC_R5_PY -0.1375
param set-default CA_MC_R5_KM -0.05
param set-default CA_ROTOR5_PX -0.238
param set-default CA_ROTOR5_PY -0.1375
param set-default CA_ROTOR5_KM -0.05
param set-default PWM_MAIN_FUNC1 101
param set-default PWM_MAIN_FUNC2 102

View File

@ -0,0 +1,86 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#include "ActuatorEffectiveness.hpp"
#include "../ControlAllocation/ControlAllocation.hpp"
#include <px4_platform_common/log.h>
int ActuatorEffectiveness::Configuration::addActuator(ActuatorType type, const matrix::Vector3f &torque,
const matrix::Vector3f &thrust)
{
int actuator_idx = num_actuators_matrix[selected_matrix];
if (actuator_idx >= NUM_ACTUATORS) {
PX4_ERR("Too many actuators");
return -1;
}
if ((int)type < (int)ActuatorType::COUNT - 1 && num_actuators[(int)type + 1] > 0) {
PX4_ERR("Trying to add actuators in the wrong order (add motors first, then servos)");
return -1;
}
effectiveness_matrices[selected_matrix](ControlAllocation::ControlAxis::ROLL, actuator_idx) = torque(0);
effectiveness_matrices[selected_matrix](ControlAllocation::ControlAxis::PITCH, actuator_idx) = torque(1);
effectiveness_matrices[selected_matrix](ControlAllocation::ControlAxis::YAW, actuator_idx) = torque(2);
effectiveness_matrices[selected_matrix](ControlAllocation::ControlAxis::THRUST_X, actuator_idx) = thrust(0);
effectiveness_matrices[selected_matrix](ControlAllocation::ControlAxis::THRUST_Y, actuator_idx) = thrust(1);
effectiveness_matrices[selected_matrix](ControlAllocation::ControlAxis::THRUST_Z, actuator_idx) = thrust(2);
matrix_selection_indexes[totalNumActuators()] = selected_matrix;
++num_actuators[(int)type];
return num_actuators_matrix[selected_matrix]++;
}
void ActuatorEffectiveness::Configuration::actuatorsAdded(ActuatorType type, int count)
{
int total_count = totalNumActuators();
for (int i = 0; i < count; ++i) {
matrix_selection_indexes[i + total_count] = selected_matrix;
}
num_actuators[(int)type] += count;
num_actuators_matrix[selected_matrix] += count;
}
int ActuatorEffectiveness::Configuration::totalNumActuators() const
{
int total_count = 0;
for (int i = 0; i < MAX_NUM_MATRICES; ++i) {
total_count += num_actuators_matrix[i];
}
return total_count;
}

View File

@ -41,18 +41,36 @@
#pragma once
#include <ControlAllocation/ControlAllocation.hpp>
#include <matrix/matrix/math.hpp>
enum class AllocationMethod {
NONE = -1,
PSEUDO_INVERSE = 0,
SEQUENTIAL_DESATURATION = 1,
AUTO = 2,
};
enum class ActuatorType {
MOTORS = 0,
SERVOS,
COUNT
};
class ActuatorEffectiveness
{
public:
ActuatorEffectiveness() = default;
virtual ~ActuatorEffectiveness() = default;
static constexpr uint8_t NUM_ACTUATORS = ControlAllocation::NUM_ACTUATORS;
static constexpr uint8_t NUM_AXES = ControlAllocation::NUM_AXES;
static constexpr int NUM_ACTUATORS = 16;
static constexpr int NUM_AXES = 6;
static constexpr int MAX_NUM_MATRICES = 2;
using EffectivenessMatrix = matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS>;
using ActuatorVector = matrix::Vector<float, NUM_ACTUATORS>;
enum class FlightPhase {
HOVER_FLIGHT = 0,
@ -61,6 +79,31 @@ public:
TRANSITION_FF_TO_HF = 3
};
struct Configuration {
/**
* Add an actuator to the selected matrix, returning the index, or -1 on error
*/
int addActuator(ActuatorType type, const matrix::Vector3f &torque, const matrix::Vector3f &thrust);
/**
* Call this after manually adding N actuators to the selected matrix
*/
void actuatorsAdded(ActuatorType type, int count);
int totalNumActuators() const;
/// Configured effectiveness matrix. Actuators are expected to be filled in order, motors first, then servos
EffectivenessMatrix effectiveness_matrices[MAX_NUM_MATRICES];
int num_actuators_matrix[MAX_NUM_MATRICES]; ///< current amount, and next actuator index to fill in to effectiveness_matrices
ActuatorVector trim[MAX_NUM_MATRICES];
int selected_matrix;
uint8_t matrix_selection_indexes[NUM_ACTUATORS * MAX_NUM_MATRICES];
int num_actuators[(int)ActuatorType::COUNT];
};
/**
* Set the current flight phase
*
@ -71,22 +114,28 @@ public:
_flight_phase = flight_phase;
}
/**
* Get the number of effectiveness matrices. Must be <= MAX_NUM_MATRICES.
* This is expected to stay constant.
*/
virtual int numMatrices() const { return 1; }
/**
* Get the desired allocation method(s) for each matrix, if configured as AUTO
*/
virtual void getDesiredAllocationMethod(AllocationMethod allocation_method_out[MAX_NUM_MATRICES]) const
{
for (int i = 0; i < MAX_NUM_MATRICES; ++i) {
allocation_method_out[i] = AllocationMethod::PSEUDO_INVERSE;
}
}
/**
* Get the control effectiveness matrix if updated
*
* @return true if updated and matrix is set
*/
virtual bool getEffectivenessMatrix(matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &matrix, bool force) = 0;
/**
* Get the actuator trims
*
* @return Actuator trims
*/
const matrix::Vector<float, NUM_ACTUATORS> &getActuatorTrim() const
{
return _trim;
}
virtual bool getEffectivenessMatrix(Configuration &configuration, bool force) = 0;
/**
* Get the current flight phase
@ -98,17 +147,26 @@ public:
return _flight_phase;
}
/**
* Get the number of actuators
*/
virtual int numActuators() const = 0;
/**
* Display name
*/
virtual const char *name() const = 0;
/**
* Callback from the control allocation, allowing to manipulate the setpoint.
* This can be used to e.g. add non-linear or external terms.
* It is called after the matrix multiplication and before final clipping.
* @param actuator_sp input & output setpoint
*/
virtual void updateSetpoint(const matrix::Vector<float, NUM_AXES> &control_sp,
int matrix_index, ActuatorVector &actuator_sp) {}
/**
* Get a bitmask of motors to be stopped
*/
virtual uint32_t getStoppedMotors() const { return 0; }
protected:
matrix::Vector<float, NUM_ACTUATORS> _trim; ///< Actuator trim
FlightPhase _flight_phase{FlightPhase::HOVER_FLIGHT}; ///< Current flight phase
};

View File

@ -0,0 +1,159 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#include <px4_platform_common/log.h>
#include "ActuatorEffectivenessControlSurfaces.hpp"
using namespace matrix;
ActuatorEffectivenessControlSurfaces::ActuatorEffectivenessControlSurfaces(ModuleParams *parent)
: ModuleParams(parent)
{
for (int i = 0; i < MAX_COUNT; ++i) {
char buffer[17];
snprintf(buffer, sizeof(buffer), "CA_SV_CS%u_TYPE", i);
_param_handles[i].type = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_SV_CS%u_TRQ_R", i);
_param_handles[i].torque[0] = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_SV_CS%u_TRQ_P", i);
_param_handles[i].torque[1] = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_SV_CS%u_TRQ_Y", i);
_param_handles[i].torque[2] = param_find(buffer);
}
_count_handle = param_find("CA_SV_CS_COUNT");
updateParams();
}
void ActuatorEffectivenessControlSurfaces::updateParams()
{
ModuleParams::updateParams();
int32_t count = 0;
if (param_get(_count_handle, &count) != 0) {
PX4_ERR("param_get failed");
return;
}
_count = count;
for (int i = 0; i < _count; i++) {
param_get(_param_handles[i].type, (int32_t *)&_params[i].type);
Vector3f &torque = _params[i].torque;
for (int n = 0; n < 3; ++n) {
param_get(_param_handles[i].torque[n], &torque(n));
}
// TODO: enforce limits?
switch (_params[i].type) {
case Type::Elevator:
break;
case Type::Rudder:
break;
case Type::LeftElevon:
break;
case Type::RightElevon:
break;
case Type::LeftVTail:
break;
case Type::RightVTail:
break;
case Type::LeftFlaps:
case Type::RightFlaps:
torque.setZero();
break;
case Type::LeftAileron:
break;
case Type::RightAileron:
break;
case Type::Airbrakes:
torque.setZero();
break;
case Type::Custom:
break;
}
}
}
bool ActuatorEffectivenessControlSurfaces::getEffectivenessMatrix(Configuration &configuration, bool force)
{
if (!force) {
return false;
}
for (int i = 0; i < _count; i++) {
configuration.addActuator(ActuatorType::SERVOS, _params[i].torque, Vector3f{});
}
return true;
}
void ActuatorEffectivenessControlSurfaces::applyFlapsAndAirbrakes(float flaps_control, float airbrakes_control,
int first_actuator_idx,
ActuatorVector &actuator_sp) const
{
for (int i = 0; i < _count; ++i) {
switch (_params[i].type) {
// TODO: check sign
case ActuatorEffectivenessControlSurfaces::Type::LeftFlaps:
actuator_sp(i + first_actuator_idx) += flaps_control;
break;
case ActuatorEffectivenessControlSurfaces::Type::RightFlaps:
actuator_sp(i + first_actuator_idx) -= flaps_control;
break;
case ActuatorEffectivenessControlSurfaces::Type::Airbrakes:
actuator_sp(i + first_actuator_idx) += airbrakes_control;
break;
default:
break;
}
}
}

View File

@ -0,0 +1,95 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#pragma once
#include "ActuatorEffectiveness.hpp"
#include <px4_platform_common/module_params.h>
class ActuatorEffectivenessControlSurfaces : public ModuleParams, public ActuatorEffectiveness
{
public:
static constexpr int MAX_COUNT = 8;
enum class Type : int32_t {
// This matches with the parameter
Elevator = 1,
Rudder = 2,
LeftElevon = 3,
RightElevon = 4,
LeftVTail = 5,
RightVTail = 6,
LeftFlaps = 7,
RightFlaps = 8,
LeftAileron = 9,
RightAileron = 10,
Airbrakes = 11,
Custom = 12,
};
struct Params {
Type type;
matrix::Vector3f torque;
};
ActuatorEffectivenessControlSurfaces(ModuleParams *parent);
virtual ~ActuatorEffectivenessControlSurfaces() = default;
bool getEffectivenessMatrix(Configuration &configuration, bool force) override;
const char *name() const override { return "Control Surfaces"; }
int count() const { return _count; }
const Params &config(int idx) const { return _params[idx]; }
void applyFlapsAndAirbrakes(float flaps_control, float airbrakes_control, int first_actuator_idx,
ActuatorVector &actuator_sp) const;
private:
void updateParams() override;
struct ParamHandles {
param_t type;
param_t torque[3];
};
ParamHandles _param_handles[MAX_COUNT];
param_t _count_handle;
Params _params[MAX_COUNT] {};
int _count{0};
};

View File

@ -1,198 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file ActuatorEffectivenessMultirotor.hpp
*
* Actuator effectiveness computed from rotors position and orientation
*
* @author Julien Lecoeur <julien.lecoeur@gmail.com>
*/
#include "ActuatorEffectivenessMultirotor.hpp"
ActuatorEffectivenessMultirotor::ActuatorEffectivenessMultirotor(ModuleParams *parent):
ModuleParams(parent)
{
}
bool
ActuatorEffectivenessMultirotor::getEffectivenessMatrix(matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &matrix,
bool force)
{
if (_updated || force) {
_updated = false;
// Get multirotor geometry
MultirotorGeometry geometry = {};
geometry.rotors[0].position_x = _param_ca_mc_r0_px.get();
geometry.rotors[0].position_y = _param_ca_mc_r0_py.get();
geometry.rotors[0].position_z = _param_ca_mc_r0_pz.get();
geometry.rotors[0].axis_x = _param_ca_mc_r0_ax.get();
geometry.rotors[0].axis_y = _param_ca_mc_r0_ay.get();
geometry.rotors[0].axis_z = _param_ca_mc_r0_az.get();
geometry.rotors[0].thrust_coef = _param_ca_mc_r0_ct.get();
geometry.rotors[0].moment_ratio = _param_ca_mc_r0_km.get();
geometry.rotors[1].position_x = _param_ca_mc_r1_px.get();
geometry.rotors[1].position_y = _param_ca_mc_r1_py.get();
geometry.rotors[1].position_z = _param_ca_mc_r1_pz.get();
geometry.rotors[1].axis_x = _param_ca_mc_r1_ax.get();
geometry.rotors[1].axis_y = _param_ca_mc_r1_ay.get();
geometry.rotors[1].axis_z = _param_ca_mc_r1_az.get();
geometry.rotors[1].thrust_coef = _param_ca_mc_r1_ct.get();
geometry.rotors[1].moment_ratio = _param_ca_mc_r1_km.get();
geometry.rotors[2].position_x = _param_ca_mc_r2_px.get();
geometry.rotors[2].position_y = _param_ca_mc_r2_py.get();
geometry.rotors[2].position_z = _param_ca_mc_r2_pz.get();
geometry.rotors[2].axis_x = _param_ca_mc_r2_ax.get();
geometry.rotors[2].axis_y = _param_ca_mc_r2_ay.get();
geometry.rotors[2].axis_z = _param_ca_mc_r2_az.get();
geometry.rotors[2].thrust_coef = _param_ca_mc_r2_ct.get();
geometry.rotors[2].moment_ratio = _param_ca_mc_r2_km.get();
geometry.rotors[3].position_x = _param_ca_mc_r3_px.get();
geometry.rotors[3].position_y = _param_ca_mc_r3_py.get();
geometry.rotors[3].position_z = _param_ca_mc_r3_pz.get();
geometry.rotors[3].axis_x = _param_ca_mc_r3_ax.get();
geometry.rotors[3].axis_y = _param_ca_mc_r3_ay.get();
geometry.rotors[3].axis_z = _param_ca_mc_r3_az.get();
geometry.rotors[3].thrust_coef = _param_ca_mc_r3_ct.get();
geometry.rotors[3].moment_ratio = _param_ca_mc_r3_km.get();
geometry.rotors[4].position_x = _param_ca_mc_r4_px.get();
geometry.rotors[4].position_y = _param_ca_mc_r4_py.get();
geometry.rotors[4].position_z = _param_ca_mc_r4_pz.get();
geometry.rotors[4].axis_x = _param_ca_mc_r4_ax.get();
geometry.rotors[4].axis_y = _param_ca_mc_r4_ay.get();
geometry.rotors[4].axis_z = _param_ca_mc_r4_az.get();
geometry.rotors[4].thrust_coef = _param_ca_mc_r4_ct.get();
geometry.rotors[4].moment_ratio = _param_ca_mc_r4_km.get();
geometry.rotors[5].position_x = _param_ca_mc_r5_px.get();
geometry.rotors[5].position_y = _param_ca_mc_r5_py.get();
geometry.rotors[5].position_z = _param_ca_mc_r5_pz.get();
geometry.rotors[5].axis_x = _param_ca_mc_r5_ax.get();
geometry.rotors[5].axis_y = _param_ca_mc_r5_ay.get();
geometry.rotors[5].axis_z = _param_ca_mc_r5_az.get();
geometry.rotors[5].thrust_coef = _param_ca_mc_r5_ct.get();
geometry.rotors[5].moment_ratio = _param_ca_mc_r5_km.get();
geometry.rotors[6].position_x = _param_ca_mc_r6_px.get();
geometry.rotors[6].position_y = _param_ca_mc_r6_py.get();
geometry.rotors[6].position_z = _param_ca_mc_r6_pz.get();
geometry.rotors[6].axis_x = _param_ca_mc_r6_ax.get();
geometry.rotors[6].axis_y = _param_ca_mc_r6_ay.get();
geometry.rotors[6].axis_z = _param_ca_mc_r6_az.get();
geometry.rotors[6].thrust_coef = _param_ca_mc_r6_ct.get();
geometry.rotors[6].moment_ratio = _param_ca_mc_r6_km.get();
geometry.rotors[7].position_x = _param_ca_mc_r7_px.get();
geometry.rotors[7].position_y = _param_ca_mc_r7_py.get();
geometry.rotors[7].position_z = _param_ca_mc_r7_pz.get();
geometry.rotors[7].axis_x = _param_ca_mc_r7_ax.get();
geometry.rotors[7].axis_y = _param_ca_mc_r7_ay.get();
geometry.rotors[7].axis_z = _param_ca_mc_r7_az.get();
geometry.rotors[7].thrust_coef = _param_ca_mc_r7_ct.get();
geometry.rotors[7].moment_ratio = _param_ca_mc_r7_km.get();
geometry.num_rotors = _param_ca_mc_r_count.get();
_num_actuators = computeEffectivenessMatrix(geometry, matrix);
return true;
}
return false;
}
int
ActuatorEffectivenessMultirotor::computeEffectivenessMatrix(const MultirotorGeometry &geometry,
matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &effectiveness)
{
int num_actuators = 0;
effectiveness.setZero();
for (int i = 0; i < math::min(NUM_ROTORS_MAX, geometry.num_rotors); i++) {
// Get rotor axis
matrix::Vector3f axis(
geometry.rotors[i].axis_x,
geometry.rotors[i].axis_y,
geometry.rotors[i].axis_z
);
// Normalize axis
float axis_norm = axis.norm();
if (axis_norm > FLT_EPSILON) {
axis /= axis_norm;
} else {
// Bad axis definition, ignore this rotor
continue;
}
// Get rotor position
matrix::Vector3f position(
geometry.rotors[i].position_x,
geometry.rotors[i].position_y,
geometry.rotors[i].position_z
);
// Get coefficients
float ct = geometry.rotors[i].thrust_coef;
float km = geometry.rotors[i].moment_ratio;
if (fabsf(ct) < FLT_EPSILON) {
continue;
}
// Compute thrust generated by this rotor
matrix::Vector3f thrust = ct * axis;
// Compute moment generated by this rotor
matrix::Vector3f moment = ct * position.cross(axis) - ct * km * axis;
// Fill corresponding items in effectiveness matrix
for (size_t j = 0; j < 3; j++) {
effectiveness(j, i) = moment(j);
effectiveness(j + 3, i) = thrust(j);
}
}
num_actuators = geometry.num_rotors;
return num_actuators;
}

View File

@ -1,163 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file ActuatorEffectivenessMultirotor.hpp
*
* Actuator effectiveness computed from rotors position and orientation
*
* @author Julien Lecoeur <julien.lecoeur@gmail.com>
*/
#pragma once
#include "ActuatorEffectiveness.hpp"
#include <px4_platform_common/module_params.h>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionInterval.hpp>
using namespace time_literals;
class ActuatorEffectivenessMultirotor: public ModuleParams, public ActuatorEffectiveness
{
public:
ActuatorEffectivenessMultirotor(ModuleParams *parent);
virtual ~ActuatorEffectivenessMultirotor() = default;
static constexpr int NUM_ROTORS_MAX = 8;
struct RotorGeometry {
float position_x;
float position_y;
float position_z;
float axis_x;
float axis_y;
float axis_z;
float thrust_coef;
float moment_ratio;
};
struct MultirotorGeometry {
RotorGeometry rotors[NUM_ROTORS_MAX];
int num_rotors{0};
};
static int computeEffectivenessMatrix(const MultirotorGeometry &geometry,
matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &effectiveness);
bool getEffectivenessMatrix(matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &matrix, bool force) override;
int numActuators() const override { return _num_actuators; }
const char *name() const override { return "Multirotor"; }
private:
bool _updated{true};
int _num_actuators{0};
DEFINE_PARAMETERS(
(ParamFloat<px4::params::CA_MC_R0_PX>) _param_ca_mc_r0_px,
(ParamFloat<px4::params::CA_MC_R0_PY>) _param_ca_mc_r0_py,
(ParamFloat<px4::params::CA_MC_R0_PZ>) _param_ca_mc_r0_pz,
(ParamFloat<px4::params::CA_MC_R0_AX>) _param_ca_mc_r0_ax,
(ParamFloat<px4::params::CA_MC_R0_AY>) _param_ca_mc_r0_ay,
(ParamFloat<px4::params::CA_MC_R0_AZ>) _param_ca_mc_r0_az,
(ParamFloat<px4::params::CA_MC_R0_CT>) _param_ca_mc_r0_ct,
(ParamFloat<px4::params::CA_MC_R0_KM>) _param_ca_mc_r0_km,
(ParamFloat<px4::params::CA_MC_R1_PX>) _param_ca_mc_r1_px,
(ParamFloat<px4::params::CA_MC_R1_PY>) _param_ca_mc_r1_py,
(ParamFloat<px4::params::CA_MC_R1_PZ>) _param_ca_mc_r1_pz,
(ParamFloat<px4::params::CA_MC_R1_AX>) _param_ca_mc_r1_ax,
(ParamFloat<px4::params::CA_MC_R1_AY>) _param_ca_mc_r1_ay,
(ParamFloat<px4::params::CA_MC_R1_AZ>) _param_ca_mc_r1_az,
(ParamFloat<px4::params::CA_MC_R1_CT>) _param_ca_mc_r1_ct,
(ParamFloat<px4::params::CA_MC_R1_KM>) _param_ca_mc_r1_km,
(ParamFloat<px4::params::CA_MC_R2_PX>) _param_ca_mc_r2_px,
(ParamFloat<px4::params::CA_MC_R2_PY>) _param_ca_mc_r2_py,
(ParamFloat<px4::params::CA_MC_R2_PZ>) _param_ca_mc_r2_pz,
(ParamFloat<px4::params::CA_MC_R2_AX>) _param_ca_mc_r2_ax,
(ParamFloat<px4::params::CA_MC_R2_AY>) _param_ca_mc_r2_ay,
(ParamFloat<px4::params::CA_MC_R2_AZ>) _param_ca_mc_r2_az,
(ParamFloat<px4::params::CA_MC_R2_CT>) _param_ca_mc_r2_ct,
(ParamFloat<px4::params::CA_MC_R2_KM>) _param_ca_mc_r2_km,
(ParamFloat<px4::params::CA_MC_R3_PX>) _param_ca_mc_r3_px,
(ParamFloat<px4::params::CA_MC_R3_PY>) _param_ca_mc_r3_py,
(ParamFloat<px4::params::CA_MC_R3_PZ>) _param_ca_mc_r3_pz,
(ParamFloat<px4::params::CA_MC_R3_AX>) _param_ca_mc_r3_ax,
(ParamFloat<px4::params::CA_MC_R3_AY>) _param_ca_mc_r3_ay,
(ParamFloat<px4::params::CA_MC_R3_AZ>) _param_ca_mc_r3_az,
(ParamFloat<px4::params::CA_MC_R3_CT>) _param_ca_mc_r3_ct,
(ParamFloat<px4::params::CA_MC_R3_KM>) _param_ca_mc_r3_km,
(ParamFloat<px4::params::CA_MC_R4_PX>) _param_ca_mc_r4_px,
(ParamFloat<px4::params::CA_MC_R4_PY>) _param_ca_mc_r4_py,
(ParamFloat<px4::params::CA_MC_R4_PZ>) _param_ca_mc_r4_pz,
(ParamFloat<px4::params::CA_MC_R4_AX>) _param_ca_mc_r4_ax,
(ParamFloat<px4::params::CA_MC_R4_AY>) _param_ca_mc_r4_ay,
(ParamFloat<px4::params::CA_MC_R4_AZ>) _param_ca_mc_r4_az,
(ParamFloat<px4::params::CA_MC_R4_CT>) _param_ca_mc_r4_ct,
(ParamFloat<px4::params::CA_MC_R4_KM>) _param_ca_mc_r4_km,
(ParamFloat<px4::params::CA_MC_R5_PX>) _param_ca_mc_r5_px,
(ParamFloat<px4::params::CA_MC_R5_PY>) _param_ca_mc_r5_py,
(ParamFloat<px4::params::CA_MC_R5_PZ>) _param_ca_mc_r5_pz,
(ParamFloat<px4::params::CA_MC_R5_AX>) _param_ca_mc_r5_ax,
(ParamFloat<px4::params::CA_MC_R5_AY>) _param_ca_mc_r5_ay,
(ParamFloat<px4::params::CA_MC_R5_AZ>) _param_ca_mc_r5_az,
(ParamFloat<px4::params::CA_MC_R5_CT>) _param_ca_mc_r5_ct,
(ParamFloat<px4::params::CA_MC_R5_KM>) _param_ca_mc_r5_km,
(ParamFloat<px4::params::CA_MC_R6_PX>) _param_ca_mc_r6_px,
(ParamFloat<px4::params::CA_MC_R6_PY>) _param_ca_mc_r6_py,
(ParamFloat<px4::params::CA_MC_R6_PZ>) _param_ca_mc_r6_pz,
(ParamFloat<px4::params::CA_MC_R6_AX>) _param_ca_mc_r6_ax,
(ParamFloat<px4::params::CA_MC_R6_AY>) _param_ca_mc_r6_ay,
(ParamFloat<px4::params::CA_MC_R6_AZ>) _param_ca_mc_r6_az,
(ParamFloat<px4::params::CA_MC_R6_CT>) _param_ca_mc_r6_ct,
(ParamFloat<px4::params::CA_MC_R6_KM>) _param_ca_mc_r6_km,
(ParamFloat<px4::params::CA_MC_R7_PX>) _param_ca_mc_r7_px,
(ParamFloat<px4::params::CA_MC_R7_PY>) _param_ca_mc_r7_py,
(ParamFloat<px4::params::CA_MC_R7_PZ>) _param_ca_mc_r7_pz,
(ParamFloat<px4::params::CA_MC_R7_AX>) _param_ca_mc_r7_ax,
(ParamFloat<px4::params::CA_MC_R7_AY>) _param_ca_mc_r7_ay,
(ParamFloat<px4::params::CA_MC_R7_AZ>) _param_ca_mc_r7_az,
(ParamFloat<px4::params::CA_MC_R7_CT>) _param_ca_mc_r7_ct,
(ParamFloat<px4::params::CA_MC_R7_KM>) _param_ca_mc_r7_km,
(ParamInt<px4::params::CA_MC_R_COUNT>) _param_ca_mc_r_count
)
};

View File

@ -0,0 +1,247 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file ActuatorEffectivenessRotors.hpp
*
* Actuator effectiveness computed from rotors position and orientation
*
* @author Julien Lecoeur <julien.lecoeur@gmail.com>
*/
#include "ActuatorEffectivenessRotors.hpp"
#include "ActuatorEffectivenessTilts.hpp"
using namespace matrix;
ActuatorEffectivenessRotors::ActuatorEffectivenessRotors(ModuleParams *parent, AxisConfiguration axis_config,
bool tilt_support)
: ModuleParams(parent), _axis_config(axis_config), _tilt_support(tilt_support)
{
for (int i = 0; i < NUM_ROTORS_MAX; ++i) {
char buffer[17];
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_PX", i);
_param_handles[i].position_x = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_PY", i);
_param_handles[i].position_y = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_PZ", i);
_param_handles[i].position_z = param_find(buffer);
if (_axis_config == AxisConfiguration::Configurable) {
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_AX", i);
_param_handles[i].axis_x = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_AY", i);
_param_handles[i].axis_y = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_AZ", i);
_param_handles[i].axis_z = param_find(buffer);
}
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_CT", i);
_param_handles[i].thrust_coef = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_KM", i);
_param_handles[i].moment_ratio = param_find(buffer);
if (_tilt_support) {
snprintf(buffer, sizeof(buffer), "CA_ROTOR%u_TILT", i);
_param_handles[i].tilt_index = param_find(buffer);
}
}
_count_handle = param_find("CA_ROTOR_COUNT");
updateParams();
}
void ActuatorEffectivenessRotors::updateParams()
{
ModuleParams::updateParams();
int32_t count = 0;
if (param_get(_count_handle, &count) != 0) {
PX4_ERR("param_get failed");
return;
}
_geometry.num_rotors = count;
for (int i = 0; i < _geometry.num_rotors; ++i) {
Vector3f &position = _geometry.rotors[i].position;
param_get(_param_handles[i].position_x, &position(0));
param_get(_param_handles[i].position_y, &position(1));
param_get(_param_handles[i].position_z, &position(2));
Vector3f &axis = _geometry.rotors[i].axis;
switch (_axis_config) {
case AxisConfiguration::Configurable:
param_get(_param_handles[i].axis_x, &axis(0));
param_get(_param_handles[i].axis_y, &axis(1));
param_get(_param_handles[i].axis_z, &axis(2));
break;
case AxisConfiguration::FixedForward:
axis = Vector3f(1.f, 0.f, 0.f);
break;
case AxisConfiguration::FixedUpwards:
axis = Vector3f(0.f, 0.f, -1.f);
break;
}
param_get(_param_handles[i].thrust_coef, &_geometry.rotors[i].thrust_coef);
param_get(_param_handles[i].moment_ratio, &_geometry.rotors[i].moment_ratio);
if (_tilt_support) {
int32_t tilt_param{0};
param_get(_param_handles[i].tilt_index, &tilt_param);
_geometry.rotors[i].tilt_index = tilt_param - 1;
} else {
_geometry.rotors[i].tilt_index = -1;
}
}
}
bool
ActuatorEffectivenessRotors::getEffectivenessMatrix(Configuration &configuration, bool force)
{
if (_updated || force) {
_updated = false;
if (configuration.num_actuators[(int)ActuatorType::SERVOS] > 0) {
PX4_ERR("Wrong actuator ordering: servos need to be after motors");
return false;
}
int num_actuators = computeEffectivenessMatrix(_geometry,
configuration.effectiveness_matrices[configuration.selected_matrix],
configuration.num_actuators_matrix[configuration.selected_matrix]);
configuration.actuatorsAdded(ActuatorType::MOTORS, num_actuators);
return true;
}
return false;
}
int
ActuatorEffectivenessRotors::computeEffectivenessMatrix(const Geometry &geometry,
EffectivenessMatrix &effectiveness, int actuator_start_index)
{
int num_actuators = 0;
for (int i = 0; i < math::min(NUM_ROTORS_MAX, geometry.num_rotors); i++) {
if (i + actuator_start_index >= NUM_ACTUATORS) {
break;
}
++num_actuators;
// Get rotor axis
Vector3f axis = geometry.rotors[i].axis;
// Normalize axis
float axis_norm = axis.norm();
if (axis_norm > FLT_EPSILON) {
axis /= axis_norm;
} else {
// Bad axis definition, ignore this rotor
continue;
}
// Get rotor position
const Vector3f &position = geometry.rotors[i].position;
// Get coefficients
float ct = geometry.rotors[i].thrust_coef;
float km = geometry.rotors[i].moment_ratio;
if (geometry.yaw_disabled) {
km = 0.f;
}
if (fabsf(ct) < FLT_EPSILON) {
continue;
}
// Compute thrust generated by this rotor
matrix::Vector3f thrust = ct * axis;
// Compute moment generated by this rotor
matrix::Vector3f moment = ct * position.cross(axis) - ct * km * axis;
// Fill corresponding items in effectiveness matrix
for (size_t j = 0; j < 3; j++) {
effectiveness(j, i + actuator_start_index) = moment(j);
effectiveness(j + 3, i + actuator_start_index) = thrust(j);
}
}
return num_actuators;
}
uint32_t ActuatorEffectivenessRotors::updateAxisFromTilts(const ActuatorEffectivenessTilts &tilts, float tilt_control)
{
if (!PX4_ISFINITE(tilt_control)) {
tilt_control = -1.f;
}
uint32_t nontilted_motors = 0;
for (int i = 0; i < _geometry.num_rotors; ++i) {
int tilt_index = _geometry.rotors[i].tilt_index;
if (tilt_index == -1 || tilt_index >= tilts.count()) {
nontilted_motors |= 1u << i;
continue;
}
const ActuatorEffectivenessTilts::Params &tilt = tilts.config(tilt_index);
float tilt_angle = math::lerp(tilt.min_angle, tilt.max_angle, (tilt_control + 1.f) / 2.f);
float tilt_direction = math::radians((float)tilt.tilt_direction);
_geometry.rotors[i].axis = tiltedAxis(tilt_angle, tilt_direction);
}
return nontilted_motors;
}
Vector3f ActuatorEffectivenessRotors::tiltedAxis(float tilt_angle, float tilt_direction)
{
Vector3f axis{0.f, 0.f, -1.f};
return Dcmf{Eulerf{0.f, -tilt_angle, tilt_direction}} * axis;
}

View File

@ -0,0 +1,135 @@
/****************************************************************************
*
* Copyright (c) 2020 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file ActuatorEffectivenessRotors.hpp
*
* Actuator effectiveness computed from rotors position and orientation
*
* @author Julien Lecoeur <julien.lecoeur@gmail.com>
*/
#pragma once
#include "ActuatorEffectiveness.hpp"
#include <px4_platform_common/module_params.h>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionInterval.hpp>
class ActuatorEffectivenessTilts;
using namespace time_literals;
class ActuatorEffectivenessRotors : public ModuleParams, public ActuatorEffectiveness
{
public:
enum class AxisConfiguration {
Configurable, ///< axis can be configured
FixedForward, ///< axis is fixed, pointing forwards (positive X)
FixedUpwards, ///< axis is fixed, pointing upwards (negative Z)
};
static constexpr int NUM_ROTORS_MAX = 8;
struct RotorGeometry {
matrix::Vector3f position;
matrix::Vector3f axis;
float thrust_coef;
float moment_ratio;
int tilt_index;
};
struct Geometry {
RotorGeometry rotors[NUM_ROTORS_MAX];
int num_rotors{0};
bool yaw_disabled{false};
};
ActuatorEffectivenessRotors(ModuleParams *parent, AxisConfiguration axis_config = AxisConfiguration::Configurable,
bool tilt_support = false);
virtual ~ActuatorEffectivenessRotors() = default;
void getDesiredAllocationMethod(AllocationMethod allocation_method_out[MAX_NUM_MATRICES]) const override
{
allocation_method_out[0] = AllocationMethod::SEQUENTIAL_DESATURATION;
}
static int computeEffectivenessMatrix(const Geometry &geometry,
EffectivenessMatrix &effectiveness, int actuator_start_index = 0);
bool getEffectivenessMatrix(Configuration &configuration, bool force) override;
const char *name() const override { return "Multirotor"; }
/**
* Sets the motor axis from tilt configurations and current tilt control.
* @param tilts configured tilt servos
* @param tilt_control current tilt control in [-1, 1] (can be NAN)
* @return the motors as bitset which are not tiltable
*/
uint32_t updateAxisFromTilts(const ActuatorEffectivenessTilts &tilts, float tilt_control);
const Geometry &geometry() const { return _geometry; }
/**
* Get the tilted axis {0, 0, -1} rotated by -tilt_angle around y, then
* rotated by tilt_direction around z.
*/
static matrix::Vector3f tiltedAxis(float tilt_angle, float tilt_direction);
void enableYawControl(bool enable) { _geometry.yaw_disabled = !enable; }
private:
void updateParams() override;
bool _updated{true};
const AxisConfiguration _axis_config;
const bool _tilt_support; ///< if true, tilt servo assignment params are loaded
struct ParamHandles {
param_t position_x;
param_t position_y;
param_t position_z;
param_t axis_x;
param_t axis_y;
param_t axis_z;
param_t thrust_coef;
param_t moment_ratio;
param_t tilt_index;
};
ParamHandles _param_handles[NUM_ROTORS_MAX];
param_t _count_handle;
Geometry _geometry{};
};

View File

@ -32,7 +32,7 @@
****************************************************************************/
/**
* @file ActuatorEffectivenessMultirotorTest.cpp
* @file ActuatorEffectivenessRotors.cpp
*
* Tests for Control Allocation Algorithms
*
@ -40,52 +40,55 @@
*/
#include <gtest/gtest.h>
#include <ActuatorEffectivenessMultirotor.hpp>
#include "ActuatorEffectivenessRotors.hpp"
using namespace matrix;
TEST(ActuatorEffectivenessMultirotorTest, AllZeroCase)
TEST(ActuatorEffectivenessRotors, AllZeroCase)
{
// Quad wide geometry
ActuatorEffectivenessMultirotor::MultirotorGeometry geometry = {};
geometry.rotors[0].position_x = 1.0f;
geometry.rotors[0].position_y = 1.0f;
geometry.rotors[0].position_z = 0.0f;
geometry.rotors[0].axis_x = 0.0f;
geometry.rotors[0].axis_y = 0.0f;
geometry.rotors[0].axis_z = -1.0f;
ActuatorEffectivenessRotors::Geometry geometry = {};
geometry.rotors[0].position(0) = 1.0f;
geometry.rotors[0].position(1) = 1.0f;
geometry.rotors[0].position(2) = 0.0f;
geometry.rotors[0].axis(0) = 0.0f;
geometry.rotors[0].axis(1) = 0.0f;
geometry.rotors[0].axis(2) = -1.0f;
geometry.rotors[0].thrust_coef = 1.0f;
geometry.rotors[0].moment_ratio = 0.05f;
geometry.rotors[1].position_x = -1.0f;
geometry.rotors[1].position_y = -1.0f;
geometry.rotors[1].position_z = 0.0f;
geometry.rotors[1].axis_x = 0.0f;
geometry.rotors[1].axis_y = 0.0f;
geometry.rotors[1].axis_z = -1.0f;
geometry.rotors[1].position(0) = -1.0f;
geometry.rotors[1].position(1) = -1.0f;
geometry.rotors[1].position(2) = 0.0f;
geometry.rotors[1].axis(0) = 0.0f;
geometry.rotors[1].axis(1) = 0.0f;
geometry.rotors[1].axis(2) = -1.0f;
geometry.rotors[1].thrust_coef = 1.0f;
geometry.rotors[1].moment_ratio = 0.05f;
geometry.rotors[2].position_x = 1.0f;
geometry.rotors[2].position_y = -1.0f;
geometry.rotors[2].position_z = 0.0f;
geometry.rotors[2].axis_x = 0.0f;
geometry.rotors[2].axis_y = 0.0f;
geometry.rotors[2].axis_z = -1.0f;
geometry.rotors[2].position(0) = 1.0f;
geometry.rotors[2].position(1) = -1.0f;
geometry.rotors[2].position(2) = 0.0f;
geometry.rotors[2].axis(0) = 0.0f;
geometry.rotors[2].axis(1) = 0.0f;
geometry.rotors[2].axis(2) = -1.0f;
geometry.rotors[2].thrust_coef = 1.0f;
geometry.rotors[2].moment_ratio = -0.05f;
geometry.rotors[3].position_x = -1.0f;
geometry.rotors[3].position_y = 1.0f;
geometry.rotors[3].position_z = 0.0f;
geometry.rotors[3].axis_x = 0.0f;
geometry.rotors[3].axis_y = 0.0f;
geometry.rotors[3].axis_z = -1.0f;
geometry.rotors[3].position(0) = -1.0f;
geometry.rotors[3].position(1) = 1.0f;
geometry.rotors[3].position(2) = 0.0f;
geometry.rotors[3].axis(0) = 0.0f;
geometry.rotors[3].axis(1) = 0.0f;
geometry.rotors[3].axis(2) = -1.0f;
geometry.rotors[3].thrust_coef = 1.0f;
geometry.rotors[3].moment_ratio = -0.05f;
matrix::Matrix<float, ActuatorEffectiveness::NUM_AXES, ActuatorEffectiveness::NUM_ACTUATORS> effectiveness =
ActuatorEffectivenessMultirotor::computeEffectivenessMatrix(geometry);
geometry.num_rotors = 4;
ActuatorEffectiveness::EffectivenessMatrix effectiveness;
effectiveness.setZero();
ActuatorEffectivenessRotors::computeEffectivenessMatrix(geometry, effectiveness);
const float expected[ActuatorEffectiveness::NUM_AXES][ActuatorEffectiveness::NUM_ACTUATORS] = {
{-1.0f, 1.0f, 1.0f, -1.0f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
@ -95,8 +98,38 @@ TEST(ActuatorEffectivenessMultirotorTest, AllZeroCase)
{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{-1.0f, -1.0f, -1.0f, -1.0f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}
};
matrix::Matrix<float, ActuatorEffectiveness::NUM_AXES, ActuatorEffectiveness::NUM_ACTUATORS> effectiveness_expected(
expected);
ActuatorEffectiveness::EffectivenessMatrix effectiveness_expected(expected);
EXPECT_EQ(effectiveness, effectiveness_expected);
}
TEST(ActuatorEffectivenessRotors, Tilt)
{
Vector3f axis_expected{0.f, 0.f, -1.f};
Vector3f axis = ActuatorEffectivenessRotors::tiltedAxis(0.f, 0.f);
EXPECT_EQ(axis, axis_expected);
axis_expected = Vector3f{1.f, 0.f, 0.f};
axis = ActuatorEffectivenessRotors::tiltedAxis(M_PI_F / 2.f, 0.f);
EXPECT_EQ(axis, axis_expected);
axis_expected = Vector3f{1.f / sqrtf(2.f), 0.f, -1.f / sqrtf(2.f)};
axis = ActuatorEffectivenessRotors::tiltedAxis(M_PI_F / 2.f / 2.f, 0.f);
EXPECT_EQ(axis, axis_expected);
axis_expected = Vector3f{-1.f, 0.f, 0.f};
axis = ActuatorEffectivenessRotors::tiltedAxis(-M_PI_F / 2.f, 0.f);
EXPECT_EQ(axis, axis_expected);
axis_expected = Vector3f{0.f, 0.f, -1.f};
axis = ActuatorEffectivenessRotors::tiltedAxis(0.f, M_PI_F / 2.f);
EXPECT_EQ(axis, axis_expected);
axis_expected = Vector3f{0.f, 1.f, 0.f};
axis = ActuatorEffectivenessRotors::tiltedAxis(M_PI_F / 2.f, M_PI_F / 2.f);
EXPECT_EQ(axis, axis_expected);
axis_expected = Vector3f{0.f, -1.f, 0.f};
axis = ActuatorEffectivenessRotors::tiltedAxis(-M_PI_F / 2.f, M_PI_F / 2.f);
EXPECT_EQ(axis, axis_expected);
}

View File

@ -31,79 +31,76 @@
*
****************************************************************************/
/**
* @file ActuatorEffectivenessStandardVTOL.hpp
*
* Actuator effectiveness for standard VTOL
*
* @author Julien Lecoeur <julien.lecoeur@gmail.com>
*/
#include "ActuatorEffectivenessStandardVTOL.hpp"
#include <ControlAllocation/ControlAllocation.hpp>
ActuatorEffectivenessStandardVTOL::ActuatorEffectivenessStandardVTOL()
using namespace matrix;
ActuatorEffectivenessStandardVTOL::ActuatorEffectivenessStandardVTOL(ModuleParams *parent)
: ModuleParams(parent), _mc_rotors(this), _control_surfaces(this)
{
setFlightPhase(FlightPhase::HOVER_FLIGHT);
}
bool
ActuatorEffectivenessStandardVTOL::getEffectivenessMatrix(matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &matrix,
bool force)
ActuatorEffectivenessStandardVTOL::getEffectivenessMatrix(Configuration &configuration, bool force)
{
if (!(_updated || force)) {
if (!force) {
return false;
}
switch (_flight_phase) {
case FlightPhase::HOVER_FLIGHT: {
const float standard_vtol[NUM_AXES][NUM_ACTUATORS] = {
{-0.5f, 0.5f, 0.5f, -0.5f, 0.f, 0.0f, 0.0f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.5f, -0.5f, 0.5f, -0.5f, 0.f, 0.f, 0.f, 0.0f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.25f, 0.25f, -0.25f, -0.25f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{-0.25f, -0.25f, -0.25f, -0.25f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}
};
matrix = matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS>(standard_vtol);
break;
}
// MC motors
configuration.selected_matrix = 0;
_mc_rotors.getEffectivenessMatrix(configuration, true);
_mc_motors_mask = (1u << _mc_rotors.geometry().num_rotors) - 1;
case FlightPhase::FORWARD_FLIGHT: {
const float standard_vtol[NUM_AXES][NUM_ACTUATORS] = {
{ 0.f, 0.f, 0.f, 0.f, 0.f, -0.5f, 0.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}
};
matrix = matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS>(standard_vtol);
break;
}
// Pusher/Puller
configuration.selected_matrix = 1;
case FlightPhase::TRANSITION_HF_TO_FF:
case FlightPhase::TRANSITION_FF_TO_HF: {
const float standard_vtol[NUM_AXES][NUM_ACTUATORS] = {
{ -0.5f, 0.5f, 0.5f, -0.5f, 0.f, -0.5f, 0.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.5f, -0.5f, 0.5f, -0.5f, 0.f, 0.f, 0.f, 0.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.25f, 0.25f, -0.25f, -0.25f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{-0.25f, -0.25f, -0.25f, -0.25f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}
};
matrix = matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS>(standard_vtol);
break;
}
for (int i = 0; i < _param_ca_stdvtol_n_p.get(); ++i) {
configuration.addActuator(ActuatorType::MOTORS, Vector3f{}, Vector3f{1.f, 0.f, 0.f});
}
_updated = false;
// Control Surfaces
configuration.selected_matrix = 1;
_first_control_surface_idx = configuration.num_actuators_matrix[configuration.selected_matrix];
_control_surfaces.getEffectivenessMatrix(configuration, true);
return true;
}
void
ActuatorEffectivenessStandardVTOL::setFlightPhase(const FlightPhase &flight_phase)
void ActuatorEffectivenessStandardVTOL::updateSetpoint(const matrix::Vector<float, NUM_AXES> &control_sp,
int matrix_index, ActuatorVector &actuator_sp)
{
ActuatorEffectiveness::setFlightPhase(flight_phase);
_updated = true;
// apply flaps
if (matrix_index == 1) {
actuator_controls_s actuator_controls_1;
if (_actuator_controls_1_sub.copy(&actuator_controls_1)) {
float control_flaps = actuator_controls_1.control[actuator_controls_s::INDEX_FLAPS];
float airbrakes_control = actuator_controls_1.control[actuator_controls_s::INDEX_AIRBRAKES];
_control_surfaces.applyFlapsAndAirbrakes(control_flaps, airbrakes_control, _first_control_surface_idx, actuator_sp);
}
}
}
void ActuatorEffectivenessStandardVTOL::setFlightPhase(const FlightPhase &flight_phase)
{
if (_flight_phase == flight_phase) {
return;
}
ActuatorEffectiveness::setFlightPhase(flight_phase);
// update stopped motors
switch (flight_phase) {
case FlightPhase::FORWARD_FLIGHT:
_stopped_motors = _mc_motors_mask;
break;
case FlightPhase::HOVER_FLIGHT:
case FlightPhase::TRANSITION_FF_TO_HF:
case FlightPhase::TRANSITION_HF_TO_FF:
_stopped_motors = 0;
break;
}
}

View File

@ -42,25 +42,50 @@
#pragma once
#include "ActuatorEffectiveness.hpp"
#include "ActuatorEffectivenessRotors.hpp"
#include "ActuatorEffectivenessControlSurfaces.hpp"
class ActuatorEffectivenessStandardVTOL: public ActuatorEffectiveness
#include <uORB/topics/actuator_controls.h>
class ActuatorEffectivenessStandardVTOL : public ModuleParams, public ActuatorEffectiveness
{
public:
ActuatorEffectivenessStandardVTOL();
ActuatorEffectivenessStandardVTOL(ModuleParams *parent);
virtual ~ActuatorEffectivenessStandardVTOL() = default;
bool getEffectivenessMatrix(matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &matrix, bool force) override;
/**
* Set the current flight phase
*
* @param Flight phase
*/
void setFlightPhase(const FlightPhase &flight_phase) override;
int numActuators() const override { return 7; }
bool getEffectivenessMatrix(Configuration &configuration, bool force) override;
const char *name() const override { return "Standard VTOL"; }
protected:
bool _updated{true};
int numMatrices() const override { return 2; }
void getDesiredAllocationMethod(AllocationMethod allocation_method_out[MAX_NUM_MATRICES]) const override
{
static_assert(MAX_NUM_MATRICES >= 2, "expecting at least 2 matrices");
allocation_method_out[0] = AllocationMethod::SEQUENTIAL_DESATURATION;
allocation_method_out[1] = AllocationMethod::PSEUDO_INVERSE;
}
void updateSetpoint(const matrix::Vector<float, NUM_AXES> &control_sp, int matrix_index,
ActuatorVector &actuator_sp) override;
void setFlightPhase(const FlightPhase &flight_phase) override;
uint32_t getStoppedMotors() const override { return _stopped_motors; }
private:
ActuatorEffectivenessRotors _mc_rotors;
ActuatorEffectivenessControlSurfaces _control_surfaces;
uint32_t _mc_motors_mask{}; ///< mc motors (stopped during forward flight)
uint32_t _stopped_motors{}; ///< currently stopped motors
int _first_control_surface_idx{0}; ///< applies to matrix 1
uORB::Subscription _actuator_controls_1_sub{ORB_ID(actuator_controls_0)};
DEFINE_PARAMETERS(
(ParamInt<px4::params::CA_STDVTOL_N_P>) _param_ca_stdvtol_n_p ///< number of pushers
)
};

View File

@ -41,76 +41,98 @@
#include "ActuatorEffectivenessTiltrotorVTOL.hpp"
ActuatorEffectivenessTiltrotorVTOL::ActuatorEffectivenessTiltrotorVTOL()
using namespace matrix;
ActuatorEffectivenessTiltrotorVTOL::ActuatorEffectivenessTiltrotorVTOL(ModuleParams *parent)
: ModuleParams(parent),
_mc_rotors(this, ActuatorEffectivenessRotors::AxisConfiguration::Configurable, true),
_control_surfaces(this), _tilts(this)
{
setFlightPhase(FlightPhase::HOVER_FLIGHT);
}
bool
ActuatorEffectivenessTiltrotorVTOL::getEffectivenessMatrix(matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &matrix,
bool force)
ActuatorEffectivenessTiltrotorVTOL::getEffectivenessMatrix(Configuration &configuration, bool force)
{
if (!(_updated || force)) {
if (!_updated && !force) {
return false;
}
// Trim
float tilt = 0.0f;
// MC motors
configuration.selected_matrix = 0;
_mc_rotors.enableYawControl(!_tilts.hasYawControl());
_nontilted_motors = _mc_rotors.updateAxisFromTilts(_tilts, _last_tilt_control)
<< configuration.num_actuators[(int)ActuatorType::MOTORS];
_mc_rotors.getEffectivenessMatrix(configuration, true);
switch (_flight_phase) {
case FlightPhase::HOVER_FLIGHT: {
tilt = 0.0f;
break;
}
case FlightPhase::FORWARD_FLIGHT: {
tilt = 1.5f;
break;
}
case FlightPhase::TRANSITION_FF_TO_HF:
case FlightPhase::TRANSITION_HF_TO_FF: {
tilt = 1.0f;
break;
}
}
// Trim: half throttle, tilted motors
_trim(0) = 0.5f;
_trim(1) = 0.5f;
_trim(2) = 0.5f;
_trim(3) = 0.5f;
_trim(4) = tilt;
_trim(5) = tilt;
_trim(6) = tilt;
_trim(7) = tilt;
// Effectiveness
const float tiltrotor_vtol[NUM_AXES][NUM_ACTUATORS] = {
{-0.5f * cosf(_trim(4)), 0.5f * cosf(_trim(5)), 0.5f * cosf(_trim(6)), -0.5f * cosf(_trim(7)), 0.5f * _trim(0) *sinf(_trim(4)), -0.5f * _trim(1) *sinf(_trim(5)), -0.5f * _trim(2) *sinf(_trim(6)), 0.5f * _trim(3) *sinf(_trim(7)), -0.5f, 0.5f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.5f * cosf(_trim(4)), -0.5f * cosf(_trim(5)), 0.5f * cosf(_trim(6)), -0.5f * cosf(_trim(7)), -0.5f * _trim(0) *sinf(_trim(4)), 0.5f * _trim(1) *sinf(_trim(5)), -0.5f * _trim(2) *sinf(_trim(6)), 0.5f * _trim(3) *sinf(_trim(7)), 0.f, 0.f, 0.5f, 0.f, 0.f, 0.f, 0.f, 0.f},
{-0.5f * sinf(_trim(4)), 0.5f * sinf(_trim(5)), 0.5f * sinf(_trim(6)), -0.5f * sinf(_trim(7)), -0.5f * _trim(0) *cosf(_trim(4)), 0.5f * _trim(1) *cosf(_trim(5)), 0.5f * _trim(2) *cosf(_trim(6)), -0.5f * _trim(3) *cosf(_trim(7)), 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.25f * sinf(_trim(4)), 0.25f * sinf(_trim(5)), 0.25f * sinf(_trim(6)), 0.25f * sinf(_trim(7)), 0.25f * _trim(0) *cosf(_trim(4)), 0.25f * _trim(1) *cosf(_trim(5)), 0.25f * _trim(2) *cosf(_trim(6)), 0.25f * _trim(3) *cosf(_trim(7)), 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{ 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f},
{-0.25f * cosf(_trim(4)), -0.25f * cosf(_trim(5)), -0.25f * cosf(_trim(6)), -0.25f * cosf(_trim(7)), 0.25f * _trim(0) *sinf(_trim(4)), 0.25f * _trim(1) *sinf(_trim(5)), 0.25f * _trim(2) *sinf(_trim(6)), 0.25f * _trim(3) *sinf(_trim(7)), 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}
};
matrix = matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS>(tiltrotor_vtol);
// Temporarily disable a few controls (WIP)
for (size_t j = 4; j < 8; j++) {
matrix(3, j) = 0.0f;
matrix(4, j) = 0.0f;
matrix(5, j) = 0.0f;
}
// Control Surfaces
configuration.selected_matrix = 1;
_first_control_surface_idx = configuration.num_actuators_matrix[configuration.selected_matrix];
_control_surfaces.getEffectivenessMatrix(configuration, true);
// Tilts
configuration.selected_matrix = 0;
_first_tilt_idx = configuration.num_actuators_matrix[configuration.selected_matrix];
_tilts.updateYawSign(_mc_rotors.geometry());
_tilts.getEffectivenessMatrix(configuration, true);
_updated = false;
return true;
}
void
ActuatorEffectivenessTiltrotorVTOL::setFlightPhase(const FlightPhase &flight_phase)
void ActuatorEffectivenessTiltrotorVTOL::updateSetpoint(const matrix::Vector<float, NUM_AXES> &control_sp,
int matrix_index, ActuatorVector &actuator_sp)
{
// apply flaps
if (matrix_index == 1) {
actuator_controls_s actuator_controls_1;
if (_actuator_controls_1_sub.copy(&actuator_controls_1)) {
float control_flaps = -1.f; // For tilt-rotors INDEX_FLAPS is set as combined tilt. TODO: fix this
float airbrakes_control = actuator_controls_1.control[actuator_controls_s::INDEX_AIRBRAKES];
_control_surfaces.applyFlapsAndAirbrakes(control_flaps, airbrakes_control, _first_control_surface_idx, actuator_sp);
}
}
// apply tilt
if (matrix_index == 0) {
actuator_controls_s actuator_controls_1;
if (_actuator_controls_1_sub.copy(&actuator_controls_1)) {
float control_tilt = actuator_controls_1.control[4] * 2.f - 1.f;
if (fabsf(control_tilt - _last_tilt_control) > 0.01f && PX4_ISFINITE(_last_tilt_control)) {
_updated = true;
}
for (int i = 0; i < _tilts.count(); ++i) {
if (_tilts.config(i).tilt_direction == ActuatorEffectivenessTilts::TiltDirection::TowardsFront) {
actuator_sp(i + _first_tilt_idx) += control_tilt;
}
}
_last_tilt_control = control_tilt;
}
}
}
void ActuatorEffectivenessTiltrotorVTOL::setFlightPhase(const FlightPhase &flight_phase)
{
if (_flight_phase == flight_phase) {
return;
}
ActuatorEffectiveness::setFlightPhase(flight_phase);
_updated = true;
// update stopped motors
switch (flight_phase) {
case FlightPhase::FORWARD_FLIGHT:
_stopped_motors = _nontilted_motors;
break;
case FlightPhase::HOVER_FLIGHT:
case FlightPhase::TRANSITION_FF_TO_HF:
case FlightPhase::TRANSITION_HF_TO_FF:
_stopped_motors = 0;
break;
}
}

View File

@ -42,25 +42,51 @@
#pragma once
#include "ActuatorEffectiveness.hpp"
#include "ActuatorEffectivenessRotors.hpp"
#include "ActuatorEffectivenessControlSurfaces.hpp"
#include "ActuatorEffectivenessTilts.hpp"
class ActuatorEffectivenessTiltrotorVTOL: public ActuatorEffectiveness
#include <uORB/topics/actuator_controls.h>
#include <uORB/Subscription.hpp>
class ActuatorEffectivenessTiltrotorVTOL : public ModuleParams, public ActuatorEffectiveness
{
public:
ActuatorEffectivenessTiltrotorVTOL();
ActuatorEffectivenessTiltrotorVTOL(ModuleParams *parent);
virtual ~ActuatorEffectivenessTiltrotorVTOL() = default;
bool getEffectivenessMatrix(matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &matrix, bool force) override;
bool getEffectivenessMatrix(Configuration &configuration, bool force) override;
int numMatrices() const override { return 2; }
void getDesiredAllocationMethod(AllocationMethod allocation_method_out[MAX_NUM_MATRICES]) const override
{
static_assert(MAX_NUM_MATRICES >= 2, "expecting at least 2 matrices");
allocation_method_out[0] = AllocationMethod::SEQUENTIAL_DESATURATION;
allocation_method_out[1] = AllocationMethod::PSEUDO_INVERSE;
}
/**
* Set the current flight phase
*
* @param Flight phase
*/
void setFlightPhase(const FlightPhase &flight_phase) override;
int numActuators() const override { return 10; }
void updateSetpoint(const matrix::Vector<float, NUM_AXES> &control_sp, int matrix_index,
ActuatorVector &actuator_sp) override;
const char *name() const override { return "VTOL Tiltrotor"; }
uint32_t getStoppedMotors() const override { return _stopped_motors; }
protected:
bool _updated{true};
ActuatorEffectivenessRotors _mc_rotors;
ActuatorEffectivenessControlSurfaces _control_surfaces;
ActuatorEffectivenessTilts _tilts;
uint32_t _nontilted_motors{}; ///< motors that are not tiltable
uint32_t _stopped_motors{}; ///< currently stopped motors
int _first_control_surface_idx{0}; ///< applies to matrix 1
int _first_tilt_idx{0}; ///< applies to matrix 0
float _last_tilt_control{NAN};
uORB::Subscription _actuator_controls_1_sub{ORB_ID(actuator_controls_1)};
};

View File

@ -0,0 +1,142 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#include "ActuatorEffectivenessTilts.hpp"
#include <px4_platform_common/log.h>
#include <lib/mathlib/mathlib.h>
using namespace matrix;
ActuatorEffectivenessTilts::ActuatorEffectivenessTilts(ModuleParams *parent)
: ModuleParams(parent)
{
for (int i = 0; i < MAX_COUNT; ++i) {
char buffer[17];
snprintf(buffer, sizeof(buffer), "CA_SV_TL%u_CT", i);
_param_handles[i].control = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_SV_TL%u_MINA", i);
_param_handles[i].min_angle = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_SV_TL%u_MAXA", i);
_param_handles[i].max_angle = param_find(buffer);
snprintf(buffer, sizeof(buffer), "CA_SV_TL%u_TD", i);
_param_handles[i].tilt_direction = param_find(buffer);
}
_count_handle = param_find("CA_SV_TL_COUNT");
updateParams();
}
void ActuatorEffectivenessTilts::updateParams()
{
ModuleParams::updateParams();
int32_t count = 0;
if (param_get(_count_handle, &count) != 0) {
PX4_ERR("param_get failed");
return;
}
_count = count;
for (int i = 0; i < count; i++) {
param_get(_param_handles[i].control, (int32_t *)&_params[i].control);
param_get(_param_handles[i].tilt_direction, (int32_t *)&_params[i].tilt_direction);
param_get(_param_handles[i].min_angle, &_params[i].min_angle);
param_get(_param_handles[i].max_angle, &_params[i].max_angle);
_params[i].min_angle = math::radians(_params[i].min_angle);
_params[i].max_angle = math::radians(_params[i].max_angle);
_yaw_torque[i] = 0.f;
}
}
bool ActuatorEffectivenessTilts::getEffectivenessMatrix(Configuration &configuration, bool force)
{
if (!force) {
return false;
}
for (int i = 0; i < _count; i++) {
Vector3f torque{};
if (_params[i].control == Control::Yaw) {
torque(2) = _yaw_torque[i];
}
configuration.addActuator(ActuatorType::SERVOS, torque, Vector3f{});
}
return true;
}
void ActuatorEffectivenessTilts::updateYawSign(const ActuatorEffectivenessRotors::Geometry &geometry)
{
for (int i = 0; i < geometry.num_rotors; ++i) {
int tilt_index = geometry.rotors[i].tilt_index;
if (tilt_index == -1 || tilt_index >= _count) {
continue;
}
if (_params[tilt_index].control != Control::Yaw) {
continue;
}
// Find the yaw torque sign by checking the motor position and tilt direction.
// Rotate position by -tilt_direction around z, then check the sign of y pos
float tilt_direction = math::radians((float)_params[tilt_index].tilt_direction);
Vector3f rotated_pos = Dcmf{Eulerf{0.f, 0.f, -tilt_direction}} * geometry.rotors[i].position;
if (rotated_pos(1) < -0.01f) { // add minimal margin
_yaw_torque[tilt_index] = 1.f;
} else if (rotated_pos(1) > 0.01f) {
_yaw_torque[tilt_index] = -1.f;
}
}
}
bool ActuatorEffectivenessTilts::hasYawControl() const
{
for (int i = 0; i < _count; i++) {
if (_params[i].control == Control::Yaw) {
return true;
}
}
return false;
}

View File

@ -0,0 +1,97 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
#pragma once
#include "ActuatorEffectiveness.hpp"
#include "ActuatorEffectivenessRotors.hpp"
#include <px4_platform_common/module_params.h>
class ActuatorEffectivenessTilts : public ModuleParams, public ActuatorEffectiveness
{
public:
static constexpr int MAX_COUNT = 4;
enum class Control : int32_t {
// This matches with the parameter
None = 0,
Yaw = 1,
};
enum class TiltDirection : int32_t {
// This matches with the parameter
TowardsFront = 0,
TowardsRight = 90,
};
struct Params {
Control control;
float min_angle;
float max_angle;
TiltDirection tilt_direction;
};
ActuatorEffectivenessTilts(ModuleParams *parent);
virtual ~ActuatorEffectivenessTilts() = default;
bool getEffectivenessMatrix(Configuration &configuration, bool force) override;
const char *name() const override { return "Tilts"; }
int count() const { return _count; }
const Params &config(int idx) const { return _params[idx]; }
void updateYawSign(const ActuatorEffectivenessRotors::Geometry &geometry);
bool hasYawControl() const;
private:
void updateParams() override;
struct ParamHandles {
param_t control;
param_t min_angle;
param_t max_angle;
param_t tilt_direction;
};
ParamHandles _param_handles[MAX_COUNT];
param_t _count_handle;
Params _params[MAX_COUNT] {};
int _count{0};
float _yaw_torque[MAX_COUNT] {};
};

View File

@ -32,9 +32,14 @@
############################################################################
px4_add_library(ActuatorEffectiveness
ActuatorEffectiveness.cpp
ActuatorEffectiveness.hpp
ActuatorEffectivenessMultirotor.cpp
ActuatorEffectivenessMultirotor.hpp
ActuatorEffectivenessControlSurfaces.cpp
ActuatorEffectivenessControlSurfaces.hpp
ActuatorEffectivenessTilts.cpp
ActuatorEffectivenessTilts.hpp
ActuatorEffectivenessRotors.cpp
ActuatorEffectivenessRotors.hpp
ActuatorEffectivenessStandardVTOL.cpp
ActuatorEffectivenessStandardVTOL.hpp
ActuatorEffectivenessTiltrotorVTOL.cpp
@ -46,7 +51,6 @@ target_include_directories(ActuatorEffectiveness PUBLIC ${CMAKE_CURRENT_SOURCE_D
target_link_libraries(ActuatorEffectiveness
PRIVATE
mathlib
ControlAllocation
)
# px4_add_unit_gtest(SRC ActuatorEffectivenessMultirotorTest.cpp LINKLIBS ActuatorEffectiveness)
px4_add_functional_gtest(SRC ActuatorEffectivenessRotorsTest.cpp LINKLIBS ActuatorEffectiveness)

View File

@ -71,14 +71,16 @@
#include <matrix/matrix/math.hpp>
#include "ActuatorEffectiveness/ActuatorEffectiveness.hpp"
class ControlAllocation
{
public:
ControlAllocation();
virtual ~ControlAllocation() = default;
static constexpr uint8_t NUM_ACTUATORS = 16;
static constexpr uint8_t NUM_AXES = 6;
static constexpr uint8_t NUM_ACTUATORS = ActuatorEffectiveness::NUM_ACTUATORS;
static constexpr uint8_t NUM_AXES = ActuatorEffectiveness::NUM_AXES;
typedef matrix::Vector<float, NUM_ACTUATORS> ActuatorVector;
@ -93,8 +95,6 @@ public:
/**
* Allocate control setpoint to actuators
*
* @param control_setpoint Desired control setpoint vector (input)
*/
virtual void allocate() = 0;
@ -190,6 +190,8 @@ public:
*/
void clipActuatorSetpoint(matrix::Vector<float, NUM_ACTUATORS> &actuator) const;
void clipActuatorSetpoint() { clipActuatorSetpoint(_actuator_sp); }
/**
* Normalize the actuator setpoint between minimum and maximum values.
*
@ -207,6 +209,8 @@ public:
int numConfiguredActuators() const { return _num_actuators; }
protected:
friend class ControlAllocator; // for _actuator_sp
matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> _effectiveness; //< Effectiveness matrix
matrix::Vector<float, NUM_AXES> _control_allocation_scale; //< Scaling applied during allocation
matrix::Vector<float, NUM_ACTUATORS> _actuator_trim; //< Neutral actuator values

View File

@ -122,7 +122,4 @@ ControlAllocationPseudoInverse::allocate()
// Allocate
_actuator_sp = _actuator_trim + _mix * (_control_sp - _control_trim);
// Clip
clipActuatorSetpoint(_actuator_sp);
}

View File

@ -53,9 +53,9 @@ public:
ControlAllocationPseudoInverse() = default;
virtual ~ControlAllocationPseudoInverse() = default;
virtual void allocate() override;
virtual void setEffectivenessMatrix(const matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &effectiveness,
const matrix::Vector<float, NUM_ACTUATORS> &actuator_trim, int num_actuators) override;
void allocate() override;
void setEffectivenessMatrix(const matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &effectiveness,
const matrix::Vector<float, NUM_ACTUATORS> &actuator_trim, int num_actuators) override;
protected:
matrix::Matrix<float, NUM_ACTUATORS, NUM_AXES> _mix;

View File

@ -59,6 +59,7 @@ TEST(ControlAllocationTest, AllZeroCase)
method.setEffectivenessMatrix(effectiveness, actuator_trim, 16);
method.setControlSetpoint(control_sp);
method.allocate();
method.clipActuatorSetpoint();
actuator_sp = method.getActuatorSetpoint();
control_allocated_expected = method.getAllocatedControl();

View File

@ -60,9 +60,6 @@ ControlAllocationSequentialDesaturation::allocate()
mixAirmodeDisabled();
break;
}
// Clip
clipActuatorSetpoint(_actuator_sp);
}
void ControlAllocationSequentialDesaturation::desaturateActuators(

View File

@ -59,7 +59,10 @@ ControlAllocator::ControlAllocator() :
ControlAllocator::~ControlAllocator()
{
delete _control_allocation;
for (int i = 0; i < ActuatorEffectiveness::MAX_NUM_MATRICES; ++i) {
delete _control_allocation[i];
}
delete _actuator_effectiveness;
perf_free(_loop_perf);
@ -86,69 +89,85 @@ ControlAllocator::parameters_updated()
{
// Allocation method & effectiveness source
// Do this first: in case a new method is loaded, it will be configured below
update_effectiveness_source();
update_allocation_method();
bool updated = update_effectiveness_source();
update_allocation_method(updated); // must be called after update_effectiveness_source()
if (_control_allocation == nullptr) {
if (_num_control_allocation == 0) {
return;
}
_control_allocation->updateParameters();
for (int i = 0; i < _num_control_allocation; ++i) {
_control_allocation[i]->updateParameters();
}
update_effectiveness_matrix_if_needed(true);
}
void
ControlAllocator::update_allocation_method()
ControlAllocator::update_allocation_method(bool force)
{
AllocationMethod method = (AllocationMethod)_param_ca_method.get();
AllocationMethod configured_method = (AllocationMethod)_param_ca_method.get();
if (_allocation_method_id != method) {
if (!_actuator_effectiveness) {
PX4_ERR("_actuator_effectiveness null");
return;
}
// Save current state
matrix::Vector<float, NUM_ACTUATORS> actuator_sp;
if (_allocation_method_id != configured_method || force) {
if (_control_allocation != nullptr) {
actuator_sp = _control_allocation->getActuatorSetpoint();
matrix::Vector<float, NUM_ACTUATORS> actuator_sp[ActuatorEffectiveness::MAX_NUM_MATRICES];
// Cleanup first
for (int i = 0; i < ActuatorEffectiveness::MAX_NUM_MATRICES; ++i) {
// Save current state
if (_control_allocation[i] != nullptr) {
actuator_sp[i] = _control_allocation[i]->getActuatorSetpoint();
}
delete _control_allocation[i];
_control_allocation[i] = nullptr;
}
// try to instanciate new allocation method
ControlAllocation *tmp = nullptr;
_num_control_allocation = _actuator_effectiveness->numMatrices();
switch (method) {
case AllocationMethod::PSEUDO_INVERSE:
tmp = new ControlAllocationPseudoInverse();
break;
AllocationMethod desired_methods[ActuatorEffectiveness::MAX_NUM_MATRICES];
_actuator_effectiveness->getDesiredAllocationMethod(desired_methods);
case AllocationMethod::SEQUENTIAL_DESATURATION:
tmp = new ControlAllocationSequentialDesaturation();
break;
for (int i = 0; i < _num_control_allocation; ++i) {
AllocationMethod method = configured_method;
default:
PX4_ERR("Unknown allocation method");
break;
if (configured_method == AllocationMethod::AUTO) {
method = desired_methods[i];
}
switch (method) {
case AllocationMethod::PSEUDO_INVERSE:
_control_allocation[i] = new ControlAllocationPseudoInverse();
break;
case AllocationMethod::SEQUENTIAL_DESATURATION:
_control_allocation[i] = new ControlAllocationSequentialDesaturation();
break;
default:
PX4_ERR("Unknown allocation method");
break;
}
if (_control_allocation[i] == nullptr) {
PX4_ERR("alloc failed");
_num_control_allocation = 0;
} else {
_control_allocation[i]->setActuatorSetpoint(actuator_sp[i]);
}
}
// Replace previous method with new one
if (tmp == nullptr) {
// It did not work, forget about it
PX4_ERR("Control allocation init failed");
_param_ca_method.set((int)_allocation_method_id);
} else {
// Swap allocation methods
delete _control_allocation;
_control_allocation = tmp;
// Save method id
_allocation_method_id = method;
// Configure new allocation method
_control_allocation->setActuatorSetpoint(actuator_sp);
}
_allocation_method_id = configured_method;
}
}
void
bool
ControlAllocator::update_effectiveness_source()
{
EffectivenessSource source = (EffectivenessSource)_param_ca_airframe.get();
@ -161,15 +180,15 @@ ControlAllocator::update_effectiveness_source()
switch (source) {
case EffectivenessSource::NONE:
case EffectivenessSource::MULTIROTOR:
tmp = new ActuatorEffectivenessMultirotor(this);
tmp = new ActuatorEffectivenessRotors(this);
break;
case EffectivenessSource::STANDARD_VTOL:
tmp = new ActuatorEffectivenessStandardVTOL();
tmp = new ActuatorEffectivenessStandardVTOL(this);
break;
case EffectivenessSource::TILTROTOR_VTOL:
tmp = new ActuatorEffectivenessTiltrotorVTOL();
tmp = new ActuatorEffectivenessTiltrotorVTOL(this);
break;
default:
@ -191,7 +210,11 @@ ControlAllocator::update_effectiveness_source()
// Save source id
_effectiveness_source_id = source;
}
return true;
}
return false;
}
void
@ -216,7 +239,7 @@ ControlAllocator::Run()
parameters_updated();
}
if (_control_allocation == nullptr || _actuator_effectiveness == nullptr) {
if (_num_control_allocation == 0 || _actuator_effectiveness == nullptr) {
return;
}
@ -281,18 +304,35 @@ ControlAllocator::Run()
update_effectiveness_matrix_if_needed();
// Set control setpoint vector
matrix::Vector<float, NUM_AXES> c;
c(0) = _torque_sp(0);
c(1) = _torque_sp(1);
c(2) = _torque_sp(2);
c(3) = _thrust_sp(0);
c(4) = _thrust_sp(1);
c(5) = _thrust_sp(2);
_control_allocation->setControlSetpoint(c);
// Set control setpoint vector(s)
matrix::Vector<float, NUM_AXES> c[ActuatorEffectiveness::MAX_NUM_MATRICES];
c[0](0) = _torque_sp(0);
c[0](1) = _torque_sp(1);
c[0](2) = _torque_sp(2);
c[0](3) = _thrust_sp(0);
c[0](4) = _thrust_sp(1);
c[0](5) = _thrust_sp(2);
// Do allocation
_control_allocation->allocate();
if (_num_control_allocation > 1) {
_vehicle_torque_setpoint1_sub.copy(&vehicle_torque_setpoint);
_vehicle_thrust_setpoint1_sub.copy(&vehicle_thrust_setpoint);
c[1](0) = vehicle_torque_setpoint.xyz[0];
c[1](1) = vehicle_torque_setpoint.xyz[1];
c[1](2) = vehicle_torque_setpoint.xyz[2];
c[1](3) = vehicle_thrust_setpoint.xyz[0];
c[1](4) = vehicle_thrust_setpoint.xyz[1];
c[1](5) = vehicle_thrust_setpoint.xyz[2];
}
for (int i = 0; i < _num_control_allocation; ++i) {
_control_allocation[i]->setControlSetpoint(c[i]);
// Do allocation
_control_allocation[i]->allocate();
_actuator_effectiveness->updateSetpoint(c[i], i, _control_allocation[i]->_actuator_sp);
_control_allocation[i]->clipActuatorSetpoint();
}
// Publish actuator setpoint and allocator status
publish_actuator_controls();
@ -309,26 +349,61 @@ ControlAllocator::Run()
void
ControlAllocator::update_effectiveness_matrix_if_needed(bool force)
{
matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> effectiveness;
ActuatorEffectiveness::Configuration config{};
if (_actuator_effectiveness->getEffectivenessMatrix(effectiveness, force)) {
if (!force && hrt_elapsed_time(&_last_effectiveness_update) < 100_ms) { // rate-limit updates
return;
}
const matrix::Vector<float, NUM_ACTUATORS> &trim = _actuator_effectiveness->getActuatorTrim();
if (_actuator_effectiveness->getEffectivenessMatrix(config, force)) {
_last_effectiveness_update = hrt_absolute_time();
// Set 0 effectiveness for actuators that are disabled (act_min >= act_max)
matrix::Vector<float, NUM_ACTUATORS> actuator_max = _control_allocation->getActuatorMax();
matrix::Vector<float, NUM_ACTUATORS> actuator_min = _control_allocation->getActuatorMin();
memcpy(_control_allocation_selection_indexes, config.matrix_selection_indexes,
sizeof(_control_allocation_selection_indexes));
for (size_t j = 0; j < NUM_ACTUATORS; j++) {
if (actuator_min(j) >= actuator_max(j)) {
for (size_t i = 0; i < NUM_AXES; i++) {
effectiveness(i, j) = 0.0f;
// Get the minimum and maximum depending on type and configuration
ActuatorEffectiveness::ActuatorVector minimum[ActuatorEffectiveness::MAX_NUM_MATRICES];
ActuatorEffectiveness::ActuatorVector maximum[ActuatorEffectiveness::MAX_NUM_MATRICES];
int actuator_idx = 0;
int actuator_idx_matrix[ActuatorEffectiveness::MAX_NUM_MATRICES] {};
for (int actuator_type = 0; actuator_type < (int)ActuatorType::COUNT; ++actuator_type) {
_num_actuators[actuator_type] = config.num_actuators[actuator_type];
for (int actuator_type_idx = 0; actuator_type_idx < config.num_actuators[actuator_type]; ++actuator_type_idx) {
if (actuator_idx >= NUM_ACTUATORS) {
PX4_ERR("Too many actuators");
break;
}
int selected_matrix = _control_allocation_selection_indexes[actuator_idx];
if ((ActuatorType)actuator_type == ActuatorType::MOTORS) {
if (_param_r_rev.get() & (1u << actuator_type_idx)) {
minimum[selected_matrix](actuator_idx_matrix[selected_matrix]) = -1.f;
} else {
minimum[selected_matrix](actuator_idx_matrix[selected_matrix]) = 0.f;
}
} else {
minimum[selected_matrix](actuator_idx_matrix[selected_matrix]) = -1.f;
}
maximum[selected_matrix](actuator_idx_matrix[selected_matrix]) = 1.f;
++actuator_idx_matrix[selected_matrix];
++actuator_idx;
}
}
// Assign control effectiveness matrix
_control_allocation->setEffectivenessMatrix(effectiveness, trim, _actuator_effectiveness->numActuators());
for (int i = 0; i < _num_control_allocation; ++i) {
_control_allocation[i]->setActuatorMin(minimum[i]);
_control_allocation[i]->setActuatorMax(maximum[i]);
// Assign control effectiveness matrix
int total_num_actuators = config.num_actuators_matrix[i];
_control_allocation[i]->setEffectivenessMatrix(config.effectiveness_matrices[i], config.trim[i], total_num_actuators);
}
}
}
@ -338,8 +413,10 @@ ControlAllocator::publish_control_allocator_status()
control_allocator_status_s control_allocator_status{};
control_allocator_status.timestamp = hrt_absolute_time();
// TODO: handle multiple matrices & disabled motors (?)
// Allocated control
const matrix::Vector<float, NUM_AXES> &allocated_control = _control_allocation->getAllocatedControl();
const matrix::Vector<float, NUM_AXES> &allocated_control = _control_allocation[0]->getAllocatedControl();
control_allocator_status.allocated_torque[0] = allocated_control(0);
control_allocator_status.allocated_torque[1] = allocated_control(1);
control_allocator_status.allocated_torque[2] = allocated_control(2);
@ -348,7 +425,7 @@ ControlAllocator::publish_control_allocator_status()
control_allocator_status.allocated_thrust[2] = allocated_control(5);
// Unallocated control
matrix::Vector<float, NUM_AXES> unallocated_control = _control_allocation->getControlSetpoint() - allocated_control;
matrix::Vector<float, NUM_AXES> unallocated_control = _control_allocation[0]->getControlSetpoint() - allocated_control;
control_allocator_status.unallocated_torque[0] = unallocated_control(0);
control_allocator_status.unallocated_torque[1] = unallocated_control(1);
control_allocator_status.unallocated_torque[2] = unallocated_control(2);
@ -363,11 +440,11 @@ ControlAllocator::publish_control_allocator_status()
unallocated_control(5)).norm_squared() < 1e-6f);
// Actuator saturation
const matrix::Vector<float, NUM_ACTUATORS> &actuator_sp = _control_allocation->getActuatorSetpoint();
const matrix::Vector<float, NUM_ACTUATORS> &actuator_min = _control_allocation->getActuatorMin();
const matrix::Vector<float, NUM_ACTUATORS> &actuator_max = _control_allocation->getActuatorMax();
const matrix::Vector<float, NUM_ACTUATORS> &actuator_sp = _control_allocation[0]->getActuatorSetpoint();
const matrix::Vector<float, NUM_ACTUATORS> &actuator_min = _control_allocation[0]->getActuatorMin();
const matrix::Vector<float, NUM_ACTUATORS> &actuator_max = _control_allocation[0]->getActuatorMax();
for (size_t i = 0; i < NUM_ACTUATORS; i++) {
for (int i = 0; i < NUM_ACTUATORS; i++) {
if (actuator_sp(i) > (actuator_max(i) - FLT_EPSILON)) {
control_allocator_status.actuator_saturation[i] = control_allocator_status_s::ACTUATOR_SATURATION_UPPER;
@ -386,17 +463,57 @@ ControlAllocator::publish_actuator_controls()
actuator_motors.timestamp = hrt_absolute_time();
actuator_motors.timestamp_sample = _timestamp_sample;
const matrix::Vector<float, NUM_ACTUATORS> &actuator_sp = _control_allocation->getActuatorSetpoint();
matrix::Vector<float, NUM_ACTUATORS> actuator_sp_normalized = _control_allocation->normalizeActuatorSetpoint(
actuator_sp);
actuator_servos_s actuator_servos;
actuator_servos.timestamp = actuator_motors.timestamp;
actuator_servos.timestamp_sample = _timestamp_sample;
for (int i = 0; i < _control_allocation->numConfiguredActuators(); i++) {
actuator_motors.control[i] = PX4_ISFINITE(actuator_sp_normalized(i)) ? actuator_sp_normalized(i) : NAN;
actuator_motors.reversible_flags = _param_r_rev.get();
int actuator_idx = 0;
int actuator_idx_matrix[ActuatorEffectiveness::MAX_NUM_MATRICES] {};
uint32_t stopped_motors = _actuator_effectiveness->getStoppedMotors();
// motors
int motors_idx;
for (motors_idx = 0; motors_idx < _num_actuators[0] && motors_idx < actuator_motors_s::NUM_CONTROLS; motors_idx++) {
int selected_matrix = _control_allocation_selection_indexes[actuator_idx];
float actuator_sp = _control_allocation[selected_matrix]->getActuatorSetpoint()(actuator_idx_matrix[selected_matrix]);
actuator_motors.control[motors_idx] = PX4_ISFINITE(actuator_sp) ? actuator_sp : NAN;
if (stopped_motors & (1u << motors_idx)) {
actuator_motors.control[motors_idx] = NAN;
}
++actuator_idx_matrix[selected_matrix];
++actuator_idx;
}
for (int i = motors_idx; i < actuator_motors_s::NUM_CONTROLS; i++) {
actuator_motors.control[i] = NAN;
}
_actuator_motors_pub.publish(actuator_motors);
// TODO: servos
// servos
if (_num_actuators[1] > 0) {
int servos_idx;
for (servos_idx = 0; servos_idx < _num_actuators[1] && servos_idx < actuator_servos_s::NUM_CONTROLS; servos_idx++) {
int selected_matrix = _control_allocation_selection_indexes[actuator_idx];
float actuator_sp = _control_allocation[selected_matrix]->getActuatorSetpoint()(actuator_idx_matrix[selected_matrix]);
actuator_servos.control[servos_idx] = PX4_ISFINITE(actuator_sp) ? actuator_sp : NAN;
++actuator_idx_matrix[selected_matrix];
++actuator_idx;
}
for (int i = servos_idx; i < actuator_servos_s::NUM_CONTROLS; i++) {
actuator_servos.control[i] = NAN;
}
_actuator_servos_pub.publish(actuator_servos);
}
}
int ControlAllocator::task_spawn(int argc, char *argv[])
@ -439,6 +556,10 @@ int ControlAllocator::print_status()
case AllocationMethod::SEQUENTIAL_DESATURATION:
PX4_INFO("Method: Sequential desaturation");
break;
case AllocationMethod::AUTO:
PX4_INFO("Method: Auto");
break;
}
// Print current airframe
@ -447,11 +568,20 @@ int ControlAllocator::print_status()
}
// Print current effectiveness matrix
if (_control_allocation != nullptr) {
const matrix::Matrix<float, NUM_AXES, NUM_ACTUATORS> &effectiveness = _control_allocation->getEffectivenessMatrix();
PX4_INFO("Effectiveness.T =");
for (int i = 0; i < _num_control_allocation; ++i) {
const ActuatorEffectiveness::EffectivenessMatrix &effectiveness = _control_allocation[i]->getEffectivenessMatrix();
if (_num_control_allocation > 1) {
PX4_INFO("Instance: %i", i);
}
PX4_INFO(" Effectiveness.T =");
effectiveness.T().print();
PX4_INFO("Configured actuators: %i", _control_allocation->numConfiguredActuators());
PX4_INFO(" minimum =");
_control_allocation[i]->getActuatorMin().T().print();
PX4_INFO(" maximum =");
_control_allocation[i]->getActuatorMax().T().print();
PX4_INFO(" Configured actuators: %i", _control_allocation[i]->numConfiguredActuators());
}
// Print perf

View File

@ -42,7 +42,7 @@
#pragma once
#include <ActuatorEffectiveness.hpp>
#include <ActuatorEffectivenessMultirotor.hpp>
#include <ActuatorEffectivenessRotors.hpp>
#include <ActuatorEffectivenessStandardVTOL.hpp>
#include <ActuatorEffectivenessTiltrotorVTOL.hpp>
@ -61,8 +61,6 @@
#include <uORB/SubscriptionCallback.hpp>
#include <uORB/topics/actuator_motors.h>
#include <uORB/topics/actuator_servos.h>
#include <uORB/topics/airspeed.h>
#include <uORB/topics/battery_status.h>
#include <uORB/topics/control_allocator_status.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_torque_setpoint.h>
@ -72,6 +70,11 @@
class ControlAllocator : public ModuleBase<ControlAllocator>, public ModuleParams, public px4::WorkItem
{
public:
static constexpr int NUM_ACTUATORS = ControlAllocation::NUM_ACTUATORS;
static constexpr int NUM_AXES = ControlAllocation::NUM_AXES;
using ActuatorVector = ActuatorEffectiveness::ActuatorVector;
ControlAllocator();
virtual ~ControlAllocator();
@ -92,9 +95,6 @@ public:
bool init();
static constexpr uint8_t NUM_ACTUATORS = ControlAllocation::NUM_ACTUATORS;
static constexpr uint8_t NUM_AXES = ControlAllocation::NUM_AXES;
private:
/**
@ -102,8 +102,8 @@ private:
*/
void parameters_updated();
void update_allocation_method();
void update_effectiveness_source();
void update_allocation_method(bool force);
bool update_effectiveness_source();
void update_effectiveness_matrix_if_needed(bool force = false);
@ -111,14 +111,10 @@ private:
void publish_actuator_controls();
enum class AllocationMethod {
NONE = -1,
PSEUDO_INVERSE = 0,
SEQUENTIAL_DESATURATION = 1,
};
AllocationMethod _allocation_method_id{AllocationMethod::NONE};
ControlAllocation *_control_allocation{nullptr}; ///< class for control allocation calculations
ControlAllocation *_control_allocation[ActuatorEffectiveness::MAX_NUM_MATRICES] {}; ///< class for control allocation calculations
int _num_control_allocation{0};
hrt_abstime _last_effectiveness_update{0};
enum class EffectivenessSource {
NONE = -1,
@ -130,10 +126,16 @@ private:
EffectivenessSource _effectiveness_source_id{EffectivenessSource::NONE};
ActuatorEffectiveness *_actuator_effectiveness{nullptr}; ///< class providing actuator effectiveness
uint8_t _control_allocation_selection_indexes[NUM_ACTUATORS * ActuatorEffectiveness::MAX_NUM_MATRICES] {};
int _num_actuators[(int)ActuatorType::COUNT] {};
// Inputs
uORB::SubscriptionCallbackWorkItem _vehicle_torque_setpoint_sub{this, ORB_ID(vehicle_torque_setpoint)}; /**< vehicle torque setpoint subscription */
uORB::SubscriptionCallbackWorkItem _vehicle_thrust_setpoint_sub{this, ORB_ID(vehicle_thrust_setpoint)}; /**< vehicle thrust setpoint subscription */
uORB::Subscription _vehicle_torque_setpoint1_sub{ORB_ID(vehicle_torque_setpoint), 1}; /**< vehicle torque setpoint subscription (2. instance) */
uORB::Subscription _vehicle_thrust_setpoint1_sub{ORB_ID(vehicle_thrust_setpoint), 1}; /**< vehicle thrust setpoint subscription (2. instance) */
// Outputs
uORB::Publication<control_allocator_status_s> _control_allocator_status_pub{ORB_ID(control_allocator_status)}; /**< actuator setpoint publication */
@ -142,15 +144,11 @@ private:
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _airspeed_sub{ORB_ID(airspeed)}; /**< airspeed subscription */
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
matrix::Vector3f _torque_sp;
matrix::Vector3f _thrust_sp;
// float _battery_scale_factor{1.0f};
// float _airspeed_scale_factor{1.0f};
perf_counter_t _loop_perf; /**< loop duration performance counter */
hrt_abstime _last_run{0};
@ -159,7 +157,8 @@ private:
DEFINE_PARAMETERS(
(ParamInt<px4::params::CA_AIRFRAME>) _param_ca_airframe,
(ParamInt<px4::params::CA_METHOD>) _param_ca_method
(ParamInt<px4::params::CA_METHOD>) _param_ca_method,
(ParamInt<px4::params::CA_R_REV>) _param_r_rev
)
};

View File

@ -1,4 +1,6 @@
__max_num_mc_motors: &max_num_mc_motors 8
__max_num_servos: &max_num_servos 8
__max_num_tilts: &max_num_tilts 4
module_name: Control Allocation
@ -22,30 +24,50 @@ parameters:
description:
short: Control allocation method
long: |
Selects the algorithm and desaturation method
Selects the algorithm and desaturation method.
If set to Automtic, the selection is based on the airframe (CA_AIRFRAME).
type: enum
values:
0: Pseudo-inverse with output clipping
1: Pseudo-inverse with sequential desaturation technique
default: 1
2: Automatic
default: 2
# MC rotors
CA_MC_R_COUNT:
# Motor parameters
CA_R_REV:
description:
short: Reversible motors
long: |
Configure motors to be reversible. Note that the output driver needs to support this as well.
type: bitmask
bit:
0: Motor 1
1: Motor 2
2: Motor 3
3: Motor 4
4: Motor 5
5: Motor 6
6: Motor 7
7: Motor 8
default: 0
# (MC) Rotors
CA_ROTOR_COUNT:
description:
short: Total number of rotors
type: enum
values:
0: 0 Motors
1: 1 Motor
2: 2 Motors
3: 3 Motors
4: 4 Motors
5: 5 Motors
6: 6 Motors
7: 7 Motors
8: 8 Motors
0: '0'
1: '1'
2: '2'
3: '3'
4: '4'
5: '5'
6: '6'
7: '7'
8: '8'
default: 0
CA_MC_R${i}_PX:
CA_ROTOR${i}_PX:
description:
short: Position of rotor ${i} along X body axis
type: float
@ -56,7 +78,7 @@ parameters:
min: -100
max: 100
default: 0.0
CA_MC_R${i}_PY:
CA_ROTOR${i}_PY:
description:
short: Position of rotor ${i} along Y body axis
type: float
@ -67,7 +89,7 @@ parameters:
min: -100
max: 100
default: 0.0
CA_MC_R${i}_PZ:
CA_ROTOR${i}_PZ:
description:
short: Position of rotor ${i} along Z body axis
type: float
@ -79,7 +101,7 @@ parameters:
max: 100
default: 0.0
CA_MC_R${i}_AX:
CA_ROTOR${i}_AX:
description:
short: Axis of rotor ${i} thrust vector, X body axis component
long: Only the direction is considered (the vector is normalized).
@ -90,7 +112,7 @@ parameters:
min: -100
max: 100
default: 0.0
CA_MC_R${i}_AY:
CA_ROTOR${i}_AY:
description:
short: Axis of rotor ${i} thrust vector, Y body axis component
long: Only the direction is considered (the vector is normalized).
@ -101,7 +123,7 @@ parameters:
min: -100
max: 100
default: 0.0
CA_MC_R${i}_AZ:
CA_ROTOR${i}_AZ:
description:
short: Axis of rotor ${i} thrust vector, Z body axis component
long: Only the direction is considered (the vector is normalized).
@ -113,7 +135,7 @@ parameters:
max: 100
default: -1.0
CA_MC_R${i}_CT:
CA_ROTOR${i}_CT:
description:
short: Thrust coefficient of rotor ${i}
long: |
@ -128,7 +150,7 @@ parameters:
max: 100
default: 6.5
CA_MC_R${i}_KM:
CA_ROTOR${i}_KM:
description:
short: Moment coefficient of rotor ${i}
long: |
@ -143,6 +165,163 @@ parameters:
min: -1
max: 1
default: 0.05
CA_ROTOR${i}_TILT:
description:
short: Rotor ${i} tilt assignment
long: If not set to None, this motor is tilted by the configured tilt servo.
type: enum
values:
0: 'None'
1: 'Tilt 1'
2: 'Tilt 2'
3: 'Tilt 3'
4: 'Tilt 4'
num_instances: *max_num_mc_motors
instance_start: 0
default: 0
# control surfaces
CA_SV_CS_COUNT:
description:
short: Total number of Control Surfaces
type: enum
values:
0: '0'
1: '1'
2: '2'
3: '3'
4: '4'
5: '5'
6: '6'
7: '7'
8: '8'
default: 0
CA_SV_CS${i}_TYPE:
description:
short: Control Surface ${i} type
type: enum
values:
1: Elevator
2: Rudder
3: Left Elevon
4: Right Elevon
5: Left V-Tail
6: Right V-Tail
7: Left Flaps
8: Right Flaps
9: Left Aileron
10: Right Aileron
11: Airbrakes
12: Custom
num_instances: *max_num_servos
instance_start: 0
default: 1
CA_SV_CS${i}_TRQ_R:
description:
short: Control Surface ${i} roll torque scaling
type: float
decimal: 2
num_instances: *max_num_servos
instance_start: 0
default: 0.0
CA_SV_CS${i}_TRQ_P:
description:
short: Control Surface ${i} pitch torque scaling
type: float
decimal: 2
num_instances: *max_num_servos
instance_start: 0
default: 0.0
CA_SV_CS${i}_TRQ_Y:
description:
short: Control Surface ${i} yaw torque scaling
type: float
decimal: 2
num_instances: *max_num_servos
instance_start: 0
default: 0.0
# Tilts
CA_SV_TL_COUNT:
description:
short: Total number of Tilt Servos
type: enum
values:
0: '0'
1: '1'
2: '2'
3: '3'
4: '4'
default: 0
CA_SV_TL${i}_CT:
description:
short: Tilt ${i} is used for control
long: Define if this servo is used for additional control.
type: enum
values:
0: 'None'
1: 'Yaw'
num_instances: *max_num_tilts
instance_start: 0
default: 1
CA_SV_TL${i}_MINA:
description:
short: Tilt Servo ${i} Tilt Angle at Minimum
long: |
Defines the tilt angle when the servo is at the minimum.
An angle of zero means upwards.
type: float
decimal: 0
unit: 'deg'
num_instances: *max_num_tilts
instance_start: 0
min: -90.0
max: 90.0
default: 0.0
CA_SV_TL${i}_MAXA:
description:
short: Tilt Servo ${i} Tilt Angle at Maximum
long: |
Defines the tilt angle when the servo is at the maximum.
An angle of zero means upwards.
type: float
decimal: 0
unit: 'deg'
num_instances: *max_num_tilts
instance_start: 0
min: -90.0
max: 90.0
default: 90.0
CA_SV_TL${i}_TD:
description:
short: Tilt Servo ${i} Tilt Direction
long: |
Defines the direction the servo tilts towards when moving towards the maximum tilt angle.
For example if the minimum tilt angle is -90, the maximum 90, and the direction 'Towards Front',
the motor axis aligns with the XZ-plane, points towards -X at the minimum and +X at the maximum tilt.
type: enum
values:
0: 'Towards Front'
90: 'Towards Right'
num_instances: *max_num_tilts
instance_start: 0
default: 0
# Standard VTOL
CA_STDVTOL_N_P:
description:
short: Number of fixed wing (pusher/puller) motors
type: enum
values:
1: '1'
2: '2'
3: '3'
4: '4'
default: 1
# Mixer
mixer:
@ -153,6 +332,12 @@ mixer:
min: 0
max: 1
default_is_nan: true
per_item_parameters:
- name: 'CA_R_REV'
label: 'Reversible'
show_as: 'bitset'
index_offset: 0
advanced: true
servo:
functions: 'Servo'
actuator_testing_values:
@ -165,42 +350,125 @@ mixer:
max: 1
default: -1
rules:
- select_identifier: 'servo-type' # restrict torque based on servo type
apply_identifiers: ['servo-torque-roll', 'servo-torque-pitch', 'servo-torque-yaw']
items:
# TODO
1: # Elevator
- { 'hidden': True, 'default': 0.0 } # roll
- { 'min': 0.0, 'max': 1.0, 'default': 1.0 } # pitch
- { 'hidden': True, 'default': 0.0 } # yaw
2: # Rudder
- { 'hidden': True, 'default': 0.0 } # roll
- { 'hidden': True, 'default': 0.0 } # pitch
- { 'min': 0.0, 'max': 1.0, 'default': 1.0 } # yaw
3: # Left elevon
- { 'min': 0.0, 'max': 1.0, 'default': 1.0 } # roll
- { 'min': -1.0, 'max': 0.0, 'default': -1.0 } # pitch
- { 'hidden': True, 'default': 0.0 } # yaw
4: # Right elevon
- { 'min': 0.0, 'max': 1.0, 'default': 1.0 } # roll
- { 'min': 0.0, 'max': 1.0, 'default': 1.0 } # pitch
- { 'hidden': True, 'default': 0.0 } # yaw
config:
param: CA_AIRFRAME
types:
0: # Multirotor
type: "multirotor"
type: 'multirotor'
actuators:
- actuator_type: 'motor'
count: 'CA_MC_R_COUNT'
count: 'CA_ROTOR_COUNT'
per_item_parameters:
standard:
position: [ 'CA_MC_R${i}_PX', 'CA_MC_R${i}_PY', 'CA_MC_R${i}_PZ' ]
position: [ 'CA_ROTOR${i}_PX', 'CA_ROTOR${i}_PY', 'CA_ROTOR${i}_PZ' ]
extra:
- name: 'CA_MC_R${i}_KM'
- name: 'CA_ROTOR${i}_KM'
label: 'Direction CCW'
function: 'spin-dir'
show_as: 'true-if-positive'
2: # Tiltrotor VTOL example
1: # Standard VTOL
actuators:
- actuator_type: 'motor'
instances:
- name: 'Front Left Motor'
position: [ 0.3, -0.3, 0 ]
- name: 'Front Right Motor'
position: [ 0.3, 0.3, 0 ]
- name: 'Rear Motor'
position: [ -0.75, 0.3, 0 ]
group_label: 'MC Motors'
count: 'CA_ROTOR_COUNT'
per_item_parameters:
standard:
position: [ 'CA_ROTOR${i}_PX', 'CA_ROTOR${i}_PY', 'CA_ROTOR${i}_PZ' ]
extra:
- name: 'CA_ROTOR${i}_KM'
label: 'Direction CCW'
function: 'spin-dir'
show_as: 'true-if-positive'
- actuator_type: 'motor'
group_label: 'FW Motors'
count: 'CA_STDVTOL_N_P'
item_label_prefix: 'FW Motor ${i}'
- actuator_type: 'servo'
instances:
- name: 'Front Left Tilt'
position: [ 0.3, -0.3, 0 ]
- name: 'Front Right Tilt'
position: [ 0.3, 0.3, 0 ]
- name: 'Left Elevon'
position: [ 0, -0.3, 0 ]
- name: 'Right Elevon'
position: [ 0, 0.3, 0 ]
group_label: 'Control Surfaces'
count: 'CA_SV_CS_COUNT'
per_item_parameters:
extra:
- name: 'CA_SV_CS${i}_TYPE'
label: 'Type'
function: 'type'
identifier: 'servo-type'
- name: 'CA_SV_CS${i}_TRQ_R'
label: 'Roll Scale'
identifier: 'servo-torque-roll'
- name: 'CA_SV_CS${i}_TRQ_P'
label: 'Pitch Scale'
identifier: 'servo-torque-pitch'
- name: 'CA_SV_CS${i}_TRQ_Y'
label: 'Yaw Scale'
identifier: 'servo-torque-yaw'
2: # Tiltrotor VTOL
actuators:
- actuator_type: 'motor'
group_label: 'MC Motors'
count: 'CA_ROTOR_COUNT'
per_item_parameters:
standard:
position: [ 'CA_ROTOR${i}_PX', 'CA_ROTOR${i}_PY', 'CA_ROTOR${i}_PZ' ]
extra:
- name: 'CA_ROTOR${i}_TILT'
label: 'Tilted by'
- actuator_type: 'servo'
group_label: 'Control Surfaces'
count: 'CA_SV_CS_COUNT'
item_label_prefix: 'Surface ${i}'
per_item_parameters:
extra:
- name: 'CA_SV_CS${i}_TYPE'
label: 'Type'
function: 'type'
identifier: 'servo-type'
- name: 'CA_SV_CS${i}_TRQ_R'
label: 'Roll Torque'
identifier: 'servo-torque-roll'
- name: 'CA_SV_CS${i}_TRQ_P'
label: 'Pitch Torque'
identifier: 'servo-torque-pitch'
- name: 'CA_SV_CS${i}_TRQ_Y'
label: 'Yaw Torque'
identifier: 'servo-torque-yaw'
- actuator_type: 'servo'
group_label: 'Tilt Servos'
count: 'CA_SV_TL_COUNT'
item_label_prefix: 'Tilt ${i}'
per_item_parameters:
extra:
- name: 'CA_SV_TL${i}_MINA'
label: "Angle at Min\nTilt"
- name: 'CA_SV_TL${i}_MAXA'
label: "Angle at Max\nTilt"
- name: 'CA_SV_TL${i}_TD'
label: 'Tilt Direction'
- name: 'CA_SV_TL${i}_CT'
label: 'Use for Control'