Compare commits

...

21 Commits

Author SHA1 Message Date
Balduin 93c0d19712 preflight check: enable mode via command 2025-02-20 14:37:42 +01:00
Balduin 276a7a2ab2 ActuatorEffectivenessTiltrotorVTOL: move comment 2025-02-20 11:44:35 +01:00
Balduin 6613272a70 Commander: remove unneeded function
The preflight check can be enabled by switching to nav_state
vehicle_status_s::NAVIGATION_STATE_CS_PREFLIGHT_CHECK using
VEHICLE_CMD_SET_NAV_STATE. Thus we can ditch this function and forgo
adding an extra vehicle command.
2025-02-20 10:10:52 +01:00
Balduin 6e6df2e47e preflight check: format 2025-02-20 09:12:15 +01:00
Balduin 9654ac6b20 ActuatorEffectivenessTiltrotorVTOL: refactor
put everything related to tiltrotor extra controls into own function to
clean up updateSetpoint
2025-02-20 09:11:35 +01:00
Balduin 009c300216 preflight check: replace argument with setter
this allows us to have *only* the setter in the base class
(ActuatorEffectiveness) with an empty implementation. Derived classes
can implement another implementation, or stay completely unchanged
w.r.t. previously. This may finally be the desired clean-ish OOP-ish
solution to this.
2025-02-20 08:50:50 +01:00
Balduin 10bcc52f13 preflight check: only change nav_mode once 2025-02-19 16:30:01 +01:00
Balduin dea58db757 preflight check: only conduct if pre-armed 2025-02-19 16:30:01 +01:00
Balduin 6e5f3a6099 preflight check: only conduct if not armed 2025-02-18 16:59:08 +01:00
Balduin c28d015d9c preflight check: replace uOrb msg with argument
Previously, the approach to modify collective tilt control was to send
the corresponding tiltrotor_extra_control uOrb message from
ControlAllocator, which then influences
ActuatorEffectivenessTiltrotorVTOL with minimal changes.

This was a bit hacky and introduced potentially conflicting uOrb
messages. So, with this new approach we pass the same information via
argument.

Specifically, the class ActuatorEffectiveness now declares
updateSetpoint with an extra argument, preflight_check_running. It is
only used in ActuatorEffectivenessTiltrotorVTOL, but has to be included
as a "dummy" in all other classes inheriting from ActuatorEffectiveness.

The argument can be used to bypass the collective tilt/thrust setpoints,
instead replacing them with values from public class member variables
which can be set from outside just before calling updateSetpoints.

Also, slight refactor in ControlAllocator by splitting up the functions
related to the preflight check into smaller parts
2025-02-18 15:57:56 +01:00
Balduin 96fc4a0673 preflight check: modify actuator_sp if running
this adds a new flag to ActuatorEffectivenessTiltrotorVTOL:
_preflight_check_running. We set it from ControlAllocator, and if true
we always add collective tilt to the actuator setpoint.
2025-02-18 13:03:45 +01:00
Balduin 122643af25 preflight check: fix range 2025-02-18 13:03:38 +01:00
Balduin 71afdb5680 preflight check: restore old nav_state only once
previously it would always change _user_mode_intention to
_prev_nav_state forever. now we change it only if we are in preflight
check mode to not interfere with anything else later.
2025-02-18 11:08:12 +01:00
Balduin 0253f9798e preflight check: send tilt control always
even if we are not currently testing the tilt axis. then we send zero
tilt rather than nothing.
2025-02-18 11:08:06 +01:00
Balduin ad42a5b2f9 preflight check: more self explanatory code 2025-02-17 17:46:39 +01:00
Balduin 761810884e preflight check: always send tiltrotor controls 2025-02-17 17:24:21 +01:00
Balduin 2a779663f6 preflight check: tilt collective actuation
we "intercept" the tiltrotor_extra_controls message, which is read again
right after in ActuatorEffectivenessTiltrotorVTOL::updateSetpoint.
2025-02-17 11:40:41 +01:00
Balduin 7816fd14b7 preflight check: restore prev nav state 2025-02-14 13:37:31 +01:00
Balduin 64d18f0a64 preflight check: first working version
modifies torque setpoints directly via the matrix c.
2025-02-14 12:17:10 +01:00
Balduin a3cca099a8 preflight check: set vehicle_control_mode flags
not sure if all of these are needed, but with all of them the allocator
runs, and with only flag_control_allocation_enabled it does not.
2025-02-14 10:59:42 +01:00
Balduin 01a65bfcf6 preflight check: base business logic
- currently triggered via param COM_DO_CS_CHECK. ultimately this will
   be replaced by a mavlink cmd
 - new VehicleStatus.nav_state, NAVIGATION_STATE_CS_PREFLIGHT_CHECK.
   this is how commander tells ControlAllocator to conduct the check
 - (todo) within ControlAllocator we overwrite torque setpoints to do
   the check. additionally we can also control servos directly from
   there, in a different mode if that is needed.
2025-02-14 09:45:53 +01:00
12 changed files with 281 additions and 79 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ uint8 NAVIGATION_STATE_DESCEND = 12 # Descend mode (no position cont
uint8 NAVIGATION_STATE_TERMINATION = 13 # Termination mode
uint8 NAVIGATION_STATE_OFFBOARD = 14
uint8 NAVIGATION_STATE_STAB = 15 # Stabilized mode
uint8 NAVIGATION_STATE_FREE1 = 16
uint8 NAVIGATION_STATE_CS_PREFLIGHT_CHECK = 16
uint8 NAVIGATION_STATE_AUTO_TAKEOFF = 17 # Takeoff
uint8 NAVIGATION_STATE_AUTO_LAND = 18 # Land
uint8 NAVIGATION_STATE_AUTO_FOLLOW_TARGET = 19 # Auto Follow
@@ -101,3 +101,8 @@ void ActuatorEffectiveness::stopMaskedMotorsWithZeroThrust(uint32_t stoppable_mo
}
}
}
void ActuatorEffectiveness::setBypassTiltrotorControls(bool bypass, float collective_tilt, float collective_thrust)
{
// empty implementation to be overridden if needed.
}
@@ -219,6 +219,18 @@ public:
*/
virtual void stopMaskedMotorsWithZeroThrust(uint32_t stoppable_motors_mask, ActuatorVector &actuator_sp);
/**
* If overridden by derived classes, optionally bypass the info usually
* contained in tiltrotor_extra_controls -- normalised collective thrust
* and tilt setpoints.
*
* Base class implementation is empty.
*
* @param bypass Flag indicating whether or not to use the other
* arguments to bypass setpoints
*/
virtual void setBypassTiltrotorControls(bool bypass, float collective_tilt, float collective_thrust);
protected:
FlightPhase _flight_phase{FlightPhase::HOVER_FLIGHT};
uint32_t _stopped_motors_mask{0};
+20 -2
View File
@@ -408,7 +408,6 @@ int Commander::custom_command(int argc, char *argv[])
} else if (!strcmp(argv[1], "auto:land")) {
send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,
PX4_CUSTOM_SUB_MODE_AUTO_LAND);
} else if (!strcmp(argv[1], "auto:precland")) {
send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,
PX4_CUSTOM_SUB_MODE_AUTO_PRECLAND);
@@ -416,7 +415,6 @@ int Commander::custom_command(int argc, char *argv[])
} else if (!strcmp(argv[1], "ext1")) {
send_vehicle_command(vehicle_command_s::VEHICLE_CMD_DO_SET_MODE, 1, PX4_CUSTOM_MAIN_MODE_AUTO,
PX4_CUSTOM_SUB_MODE_EXTERNAL1);
} else {
PX4_ERR("argument %s unsupported.", argv[1]);
}
@@ -844,6 +842,10 @@ Commander::handle_command(const vehicle_command_s &cmd)
desired_nav_state = vehicle_status_s::NAVIGATION_STATE_AUTO_PRECLAND;
break;
case PX4_CUSTOM_SUB_MODE_AUTO_CS_PREFLIGHT_CHECK:
desired_nav_state = vehicle_status_s::NAVIGATION_STATE_CS_PREFLIGHT_CHECK;
break;
case PX4_CUSTOM_SUB_MODE_EXTERNAL1...PX4_CUSTOM_SUB_MODE_EXTERNAL8:
desired_nav_state = vehicle_status_s::NAVIGATION_STATE_EXTERNAL1 + (custom_sub_mode - PX4_CUSTOM_SUB_MODE_EXTERNAL1);
break;
@@ -1835,6 +1837,22 @@ void Commander::run()
_status_changed = true;
}
// do the control surface preflight check only if pre-armed. this means we need:
// COM_PREARM_MODE = 1 (Safety Button) or 2 (Always).
if (_actuator_armed.prearmed) {
if (_param_com_do_cs_check.get()) {
if (_vehicle_status.nav_state != vehicle_status_s::NAVIGATION_STATE_CS_PREFLIGHT_CHECK) {
_prev_nav_state = _vehicle_status.nav_state;
_user_mode_intention.change(vehicle_status_s::NAVIGATION_STATE_CS_PREFLIGHT_CHECK);
}
} else {
if (_vehicle_status.nav_state == vehicle_status_s::NAVIGATION_STATE_CS_PREFLIGHT_CHECK) {
_user_mode_intention.change(_prev_nav_state);
}
}
}
modeManagementUpdate();
const hrt_abstime now = hrt_absolute_time();
+5
View File
@@ -157,6 +157,8 @@ private:
unsigned handleCommandActuatorTest(const vehicle_command_s &cmd);
unsigned handleCommandControlTest(const vehicle_command_s &cmd);
void executeActionRequest(const action_request_s &action_request);
void printRejectMode(uint8_t nav_state);
@@ -284,6 +286,8 @@ private:
vehicle_land_detected_s _vehicle_land_detected{};
uint8_t _prev_nav_state;
// commander publications
actuator_armed_s _actuator_armed{};
vehicle_control_mode_s _vehicle_control_mode{};
@@ -349,6 +353,7 @@ private:
(ParamFloat<px4::params::COM_SPOOLUP_TIME>) _param_com_spoolup_time,
(ParamInt<px4::params::COM_FLIGHT_UUID>) _param_com_flight_uuid,
(ParamInt<px4::params::COM_TAKEOFF_ACT>) _param_com_takeoff_act,
(ParamBool<px4::params::COM_DO_CS_CHECK>) _param_com_do_cs_check,
(ParamFloat<px4::params::COM_CPU_MAX>) _param_com_cpu_max
)
};
@@ -178,6 +178,21 @@ void getVehicleControlMode(uint8_t nav_state, uint8_t vehicle_type,
vehicle_control_mode.flag_control_allocation_enabled = true;
break;
// enabling literally all of these makes the allocator run nicely.
// enabling only allocator, it will not run.
// surely there is like one or two which we really need and the rest can be dropped...
case vehicle_status_s::NAVIGATION_STATE_CS_PREFLIGHT_CHECK:
vehicle_control_mode.flag_control_auto_enabled = true;
vehicle_control_mode.flag_control_position_enabled = true;
vehicle_control_mode.flag_control_velocity_enabled = true;
vehicle_control_mode.flag_control_altitude_enabled = true;
vehicle_control_mode.flag_control_climb_rate_enabled = true;
vehicle_control_mode.flag_control_attitude_enabled = true;
vehicle_control_mode.flag_control_rates_enabled = true;
vehicle_control_mode.flag_control_allocation_enabled = true;
break;
// vehicle_status_s::NAVIGATION_STATE_EXTERNALx: handled in ModeManagement
default:
break;
+3
View File
@@ -1029,3 +1029,6 @@ PARAM_DEFINE_INT32(COM_FLTT_LOW_ACT, 3);
*
*/
PARAM_DEFINE_INT32(COM_MODE_ARM_CHK, 0);
// temporary, to test control surface preflight check.
PARAM_DEFINE_INT32(COM_DO_CS_CHECK, 0);
+6
View File
@@ -65,6 +65,7 @@ enum PX4_CUSTOM_SUB_MODE_AUTO {
PX4_CUSTOM_SUB_MODE_AUTO_FOLLOW_TARGET,
PX4_CUSTOM_SUB_MODE_AUTO_PRECLAND,
PX4_CUSTOM_SUB_MODE_AUTO_VTOL_TAKEOFF,
PX4_CUSTOM_SUB_MODE_AUTO_CS_PREFLIGHT_CHECK,
PX4_CUSTOM_SUB_MODE_EXTERNAL1,
PX4_CUSTOM_SUB_MODE_EXTERNAL2,
PX4_CUSTOM_SUB_MODE_EXTERNAL3,
@@ -157,6 +158,11 @@ static inline union px4_custom_mode get_px4_custom_mode(uint8_t nav_state)
custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_STABILIZED;
break;
case vehicle_status_s::NAVIGATION_STATE_CS_PREFLIGHT_CHECK:
custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_AUTO;
custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_CS_PREFLIGHT_CHECK;
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_TAKEOFF:
custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_AUTO;
custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_TAKEOFF;
@@ -345,6 +345,7 @@ ControlAllocator::Run()
if (_vehicle_status_sub.update(&vehicle_status)) {
_armed = vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED;
_preflight_check_running = vehicle_status.nav_state == vehicle_status_s::NAVIGATION_STATE_CS_PREFLIGHT_CHECK;
ActuatorEffectiveness::FlightPhase flight_phase{ActuatorEffectiveness::FlightPhase::HOVER_FLIGHT};
@@ -437,12 +438,29 @@ ControlAllocator::Run()
}
}
if (_preflight_check_running) {
preflight_check_update_state();
preflight_check_overwrite_torque_sp(c);
}
for (int i = 0; i < _num_control_allocation; ++i) {
_control_allocation[i]->setControlSetpoint(c[i]);
// Do allocation
_control_allocation[i]->allocate();
bool is_tiltrotor = _effectiveness_source_id == EffectivenessSource::TILTROTOR_VTOL;
if (_preflight_check_running && is_tiltrotor) {
float preflight_check_tilt_sp = preflight_check_get_tilt_control();
_actuator_effectiveness->setBypassTiltrotorControls(true, preflight_check_tilt_sp, 0.0f);
} else {
_actuator_effectiveness->setBypassTiltrotorControls(false, 0.0f, 0.0f);
}
_actuator_effectiveness->allocateAuxilaryControls(dt, i, _control_allocation[i]->_actuator_sp); //flaps and spoilers
_actuator_effectiveness->updateSetpoint(c[i], i, _control_allocation[i]->_actuator_sp,
_control_allocation[i]->getActuatorMin(), _control_allocation[i]->getActuatorMax());
@@ -473,6 +491,74 @@ ControlAllocator::Run()
perf_end(_loop_perf);
}
void ControlAllocator::preflight_check_update_state()
{
// bool tiltrotor = dynamic_cast<ActuatorEffectivenessTiltrotorVTOL*>(_actuator_effectiveness) != nullptr;
bool tiltrotor = _effectiveness_source_id == EffectivenessSource::TILTROTOR_VTOL;
// cycle through roll, pitch, yaw, and for each one inject positive and
// negative torque setpoints.
int n_axes = 3;
if (tiltrotor) {
n_axes = 4;
}
int max_phase = 2 * n_axes;
hrt_abstime now = hrt_absolute_time();
if (now - _last_preflight_check_update >= 500_ms) {
_preflight_check_phase++;
_preflight_check_phase %= max_phase; // or quit once we did the whole thing once?
_last_preflight_check_update = now;
}
}
void ControlAllocator::preflight_check_overwrite_torque_sp(matrix::Vector<float, NUM_AXES>
(&c)[ActuatorEffectiveness::MAX_NUM_MATRICES])
{
int axis = _preflight_check_phase / 2;
int negative = _preflight_check_phase % 2;
if (axis < 3) {
c[0](0) = 0.;
c[0](1) = 0.;
c[0](2) = 0.;
c[0](axis) = negative ? -1.f : 1.f;
if (_num_control_allocation > 1) {
c[1](0) = 0.;
c[1](1) = 0.;
c[1](2) = 0.;
c[1](axis) = negative ? -1.f : 1.f;
}
}
}
float ControlAllocator::preflight_check_get_tilt_control()
{
int axis = _preflight_check_phase / 2;
int negative = _preflight_check_phase % 2;
float modified_tilt_control = 0.5f;
if (axis == 3) {
// axis 3 = tiltrotor.
// collective tilt normalised control goes from 0 to 1.
modified_tilt_control = negative ? 0.f : 1.f;
}
return modified_tilt_control;
}
void
ControlAllocator::update_effectiveness_matrix_if_needed(EffectivenessUpdateReason reason)
{
@@ -138,6 +138,10 @@ private:
void publish_actuator_controls();
void preflight_check_overwrite_torque_sp(matrix::Vector<float, NUM_AXES> (&c)[ActuatorEffectiveness::MAX_NUM_MATRICES]);
void preflight_check_update_state();
float preflight_check_get_tilt_control();
AllocationMethod _allocation_method_id{AllocationMethod::NONE};
ControlAllocation *_control_allocation[ActuatorEffectiveness::MAX_NUM_MATRICES] {}; ///< class for control allocation calculations
int _num_control_allocation{0};
@@ -201,6 +205,10 @@ private:
// For example, the system might report two motor failures, but only the first one is handled by CA
uint16_t _handled_motor_failure_bitmask{0};
bool _preflight_check_running{false};
int _preflight_check_phase{0};
hrt_abstime _last_preflight_check_update{0};
perf_counter_t _loop_perf; /**< loop duration performance counter */
bool _armed{false};
@@ -124,88 +124,121 @@ void ActuatorEffectivenessTiltrotorVTOL::allocateAuxilaryControls(const float dt
}
void ActuatorEffectivenessTiltrotorVTOL::setBypassTiltrotorControls(bool bypass, float collective_tilt,
float collective_thrust)
{
// if _bypass_tiltrotor_controls (used for control surface preflight
// check), we alter the behaviour of processTiltrotorControls in these
// two ways:
// - collective tilt and thrust setpoints are NOT taken from uOrb
// message, but from class member variable, which we can arbitrarily set
// before calling this function using setBypassTiltrotorControls
// - collective tilt is added to actuator_sp even if
// (throttleSpoolupFinished() || _flight_phase != FlightPhase::HOVER_FLIGHT)
// evaluates to false
_bypass_tiltrotor_controls = bypass;
_collective_tilt_normalized_setpoint = collective_tilt;
_collective_thrust_normalized_setpoint = collective_thrust;
}
void ActuatorEffectivenessTiltrotorVTOL::processTiltrotorControls(ActuatorVector &actuator_sp,
const matrix::Vector<float, NUM_ACTUATORS> &actuator_min,
const matrix::Vector<float, NUM_ACTUATORS> &actuator_max)
{
tiltrotor_extra_controls_s tiltrotor_extra_controls;
if (_tiltrotor_extra_controls_sub.copy(&tiltrotor_extra_controls) || _bypass_tiltrotor_controls) {
float control_collective_tilt = tiltrotor_extra_controls.collective_tilt_normalized_setpoint * 2.f - 1.f;
float control_collective_thrust = tiltrotor_extra_controls.collective_thrust_normalized_setpoint;
if (_bypass_tiltrotor_controls) {
control_collective_tilt = _collective_tilt_normalized_setpoint * 2.f - 1.f;
control_collective_thrust = _collective_thrust_normalized_setpoint;
}
// set control_collective_tilt to exactly -1 or 1 if close to these end points
control_collective_tilt = control_collective_tilt < -0.99f ? -1.f : control_collective_tilt;
control_collective_tilt = control_collective_tilt > 0.99f ? 1.f : control_collective_tilt;
// initialize _last_collective_tilt_control
if (!PX4_ISFINITE(_last_collective_tilt_control)) {
_last_collective_tilt_control = control_collective_tilt;
} else if (fabsf(control_collective_tilt - _last_collective_tilt_control) > 0.01f) {
_collective_tilt_updated = true;
_last_collective_tilt_control = control_collective_tilt;
}
// During transition to FF, only allow update thrust axis up to 45° as with a high tilt angle the effectiveness
// of the thrust axis in z is apporaching 0, and by that is increasing the motor output to max.
// Transition to HF: disable thrust axis tilting, and assume motors are vertical. This is to avoid
// a thrust spike when the transition is initiated (as then the tilt is fully forward).
if (_flight_phase == FlightPhase::TRANSITION_HF_TO_FF) {
_last_collective_tilt_control = math::constrain(_last_collective_tilt_control, -1.f, 0.f);
} else if (_flight_phase == FlightPhase::TRANSITION_FF_TO_HF) {
_last_collective_tilt_control = -1.f;
}
bool yaw_saturated_positive = true;
bool yaw_saturated_negative = true;
for (int i = 0; i < _tilts.count(); ++i) {
if (_tilts.config(i).tilt_direction == ActuatorEffectivenessTilts::TiltDirection::TowardsFront) {
// as long as throttle spoolup is not completed, leave the tilts in the disarmed position (in hover)
if (throttleSpoolupFinished() || _flight_phase != FlightPhase::HOVER_FLIGHT || _bypass_tiltrotor_controls) {
actuator_sp(i + _first_tilt_idx) += control_collective_tilt;
} else {
actuator_sp(i + _first_tilt_idx) = NAN; // NaN sets tilts to disarmed position
}
}
// custom yaw saturation logic: only declare yaw saturated if all tilts are at the negative or positive yawing limit
if (_tilts.getYawTorqueOfTilt(i) > FLT_EPSILON) {
if (yaw_saturated_positive && actuator_sp(i + _first_tilt_idx) < actuator_max(i + _first_tilt_idx) - FLT_EPSILON) {
yaw_saturated_positive = false;
}
if (yaw_saturated_negative && actuator_sp(i + _first_tilt_idx) > actuator_min(i + _first_tilt_idx) + FLT_EPSILON) {
yaw_saturated_negative = false;
}
} else if (_tilts.getYawTorqueOfTilt(i) < -FLT_EPSILON) {
if (yaw_saturated_negative && actuator_sp(i + _first_tilt_idx) < actuator_max(i + _first_tilt_idx) - FLT_EPSILON) {
yaw_saturated_negative = false;
}
if (yaw_saturated_positive && actuator_sp(i + _first_tilt_idx) > actuator_min(i + _first_tilt_idx) + FLT_EPSILON) {
yaw_saturated_positive = false;
}
}
}
_yaw_tilt_saturation_flags.tilt_yaw_neg = yaw_saturated_negative;
_yaw_tilt_saturation_flags.tilt_yaw_pos = yaw_saturated_positive;
// in FW directly use throttle sp
if (_flight_phase == FlightPhase::FORWARD_FLIGHT) {
for (int i = 0; i < _first_tilt_idx; ++i) {
actuator_sp(i) = control_collective_thrust;
}
}
}
}
void ActuatorEffectivenessTiltrotorVTOL::updateSetpoint(const matrix::Vector<float, NUM_AXES> &control_sp,
int matrix_index, ActuatorVector &actuator_sp, const matrix::Vector<float, NUM_ACTUATORS> &actuator_min,
const matrix::Vector<float, NUM_ACTUATORS> &actuator_max)
{
// apply tilt
if (matrix_index == 0) {
tiltrotor_extra_controls_s tiltrotor_extra_controls;
if (_tiltrotor_extra_controls_sub.copy(&tiltrotor_extra_controls)) {
float control_collective_tilt = tiltrotor_extra_controls.collective_tilt_normalized_setpoint * 2.f - 1.f;
// set control_collective_tilt to exactly -1 or 1 if close to these end points
control_collective_tilt = control_collective_tilt < -0.99f ? -1.f : control_collective_tilt;
control_collective_tilt = control_collective_tilt > 0.99f ? 1.f : control_collective_tilt;
// initialize _last_collective_tilt_control
if (!PX4_ISFINITE(_last_collective_tilt_control)) {
_last_collective_tilt_control = control_collective_tilt;
} else if (fabsf(control_collective_tilt - _last_collective_tilt_control) > 0.01f) {
_collective_tilt_updated = true;
_last_collective_tilt_control = control_collective_tilt;
}
// During transition to FF, only allow update thrust axis up to 45° as with a high tilt angle the effectiveness
// of the thrust axis in z is apporaching 0, and by that is increasing the motor output to max.
// Transition to HF: disable thrust axis tilting, and assume motors are vertical. This is to avoid
// a thrust spike when the transition is initiated (as then the tilt is fully forward).
if (_flight_phase == FlightPhase::TRANSITION_HF_TO_FF) {
_last_collective_tilt_control = math::constrain(_last_collective_tilt_control, -1.f, 0.f);
} else if (_flight_phase == FlightPhase::TRANSITION_FF_TO_HF) {
_last_collective_tilt_control = -1.f;
}
bool yaw_saturated_positive = true;
bool yaw_saturated_negative = true;
for (int i = 0; i < _tilts.count(); ++i) {
if (_tilts.config(i).tilt_direction == ActuatorEffectivenessTilts::TiltDirection::TowardsFront) {
// as long as throttle spoolup is not completed, leave the tilts in the disarmed position (in hover)
if (throttleSpoolupFinished() || _flight_phase != FlightPhase::HOVER_FLIGHT) {
actuator_sp(i + _first_tilt_idx) += control_collective_tilt;
} else {
actuator_sp(i + _first_tilt_idx) = NAN; // NaN sets tilts to disarmed position
}
}
// custom yaw saturation logic: only declare yaw saturated if all tilts are at the negative or positive yawing limit
if (_tilts.getYawTorqueOfTilt(i) > FLT_EPSILON) {
if (yaw_saturated_positive && actuator_sp(i + _first_tilt_idx) < actuator_max(i + _first_tilt_idx) - FLT_EPSILON) {
yaw_saturated_positive = false;
}
if (yaw_saturated_negative && actuator_sp(i + _first_tilt_idx) > actuator_min(i + _first_tilt_idx) + FLT_EPSILON) {
yaw_saturated_negative = false;
}
} else if (_tilts.getYawTorqueOfTilt(i) < -FLT_EPSILON) {
if (yaw_saturated_negative && actuator_sp(i + _first_tilt_idx) < actuator_max(i + _first_tilt_idx) - FLT_EPSILON) {
yaw_saturated_negative = false;
}
if (yaw_saturated_positive && actuator_sp(i + _first_tilt_idx) > actuator_min(i + _first_tilt_idx) + FLT_EPSILON) {
yaw_saturated_positive = false;
}
}
}
_yaw_tilt_saturation_flags.tilt_yaw_neg = yaw_saturated_negative;
_yaw_tilt_saturation_flags.tilt_yaw_pos = yaw_saturated_positive;
// in FW directly use throttle sp
if (_flight_phase == FlightPhase::FORWARD_FLIGHT) {
for (int i = 0; i < _first_tilt_idx; ++i) {
actuator_sp(i) = tiltrotor_extra_controls.collective_thrust_normalized_setpoint;
}
}
}
processTiltrotorControls(actuator_sp, actuator_min, actuator_max);
if (_flight_phase == FlightPhase::FORWARD_FLIGHT) {
stopMaskedMotorsWithZeroThrust(_motors & ~_untiltable_motors, actuator_sp);
@@ -88,6 +88,13 @@ public:
void getUnallocatedControl(int matrix_index, control_allocator_status_s &status) override;
void setBypassTiltrotorControls(bool bypass, float collective_tilt, float collective_thrust) override;
void processTiltrotorControls(ActuatorVector &actuator_sp,
const matrix::Vector<float, NUM_ACTUATORS> &actuator_min,
const matrix::Vector<float, NUM_ACTUATORS> &actuator_max);
protected:
bool _collective_tilt_updated{true};
ActuatorEffectivenessRotors _mc_rotors;
@@ -114,6 +121,10 @@ protected:
uORB::Subscription _tiltrotor_extra_controls_sub{ORB_ID(tiltrotor_extra_controls)};
bool _bypass_tiltrotor_controls{false};
float _collective_tilt_normalized_setpoint{0.5f};
float _collective_thrust_normalized_setpoint{0.0f};
private:
void updateParams() override;