Plane: fixed tilt vectoring to cope with large tilt angles

This uses vectoring for both roll and yaw when tilted, and uses
differential thrust for yaw when tilted
This commit is contained in:
Andrew Tridgell 2020-12-03 17:42:39 +11:00
parent c504e2db04
commit ad14e153e8
3 changed files with 84 additions and 101 deletions

View File

@ -735,6 +735,15 @@ bool QuadPlane::setup(void)
if (!motors->initialised_ok()) { if (!motors->initialised_ok()) {
AP_BoardConfig::config_error("unknown Q_FRAME_TYPE %u", (unsigned)frame_type.get()); AP_BoardConfig::config_error("unknown Q_FRAME_TYPE %u", (unsigned)frame_type.get());
} }
if (motors_var_info == AP_MotorsMatrix::var_info &&
tilt.tilt_mask != 0 &&
tilt.tilt_type == TILT_TYPE_VECTORED_YAW) {
// we will be using vectoring for yaw
motors->disable_yaw_torque();
}
motors->set_throttle_range(thr_min_pwm, thr_max_pwm); motors->set_throttle_range(thr_min_pwm, thr_max_pwm);
motors->set_update_rate(rc_speed); motors->set_update_rate(rc_speed);
motors->set_interlock(true); motors->set_interlock(true);
@ -1728,11 +1737,18 @@ void QuadPlane::update_transition(void)
} }
hold_hover(climb_rate_cms); hold_hover(climb_rate_cms);
// set desired yaw to current yaw in both desired angle and if (tilt.tilt_mask == 0 ||
// rate request. This reduces wing twist in transition due to tilt.tilt_type != TILT_TYPE_VECTORED_YAW ||
// multicopter yaw demands now - transition_start_ms < 100) {
attitude_control->set_yaw_target_to_current_heading(); // set desired yaw to current yaw in both desired angle
attitude_control->rate_bf_yaw_target(ahrs.get_gyro().z); // and rate request. This reduces wing twist in transition
// due to multicopter yaw demands. This is disabled when
// using vectored yaw for tilt-rotors as the yaw control
// is needed to maintain good control in forward
// transitions
attitude_control->set_yaw_target_to_current_heading();
attitude_control->rate_bf_yaw_target(ahrs.get_gyro().z);
}
last_throttle = motors->get_throttle(); last_throttle = motors->get_throttle();

View File

@ -480,6 +480,7 @@ private:
float current_tilt; float current_tilt;
float current_throttle; float current_throttle;
bool motors_active:1; bool motors_active:1;
float transition_yaw;
} tilt; } tilt;
// bit 0 enables plane mode and bit 1 enables body-frame roll mode // bit 0 enables plane mode and bit 1 enables body-frame roll mode
@ -542,8 +543,7 @@ private:
void tiltrotor_binary_update(void); void tiltrotor_binary_update(void);
void tiltrotor_vectored_yaw(void); void tiltrotor_vectored_yaw(void);
void tiltrotor_bicopter(void); void tiltrotor_bicopter(void);
void tilt_compensate_up(float *thrust, uint8_t num_motors); void tilt_compensate_angle(float *thrust, uint8_t num_motors, float non_tilted_mul, float tilted_mul);
void tilt_compensate_down(float *thrust, uint8_t num_motors);
void tilt_compensate(float *thrust, uint8_t num_motors); void tilt_compensate(float *thrust, uint8_t num_motors);
bool is_motor_tilting(uint8_t motor) const { bool is_motor_tilting(uint8_t motor) const {
return (((uint8_t)tilt.tilt_mask.get()) & (1U<<motor)); return (((uint8_t)tilt.tilt_mask.get()) & (1U<<motor));

View File

@ -213,78 +213,66 @@ void QuadPlane::tiltrotor_update(void)
} }
} }
/* /*
compensate for tilt in a set of motor outputs tilt compensation for angle of tilt. When the rotors are tilted the
roll effect of differential thrust on the tilted rotors is decreased
and the yaw effect increased
We have two factors we apply.
Compensation is of two forms. The first is to apply _tilt_factor, 1) when we are transitioning to fwd flight we scale the tilted rotors by 1/cos(angle). This pushes us towards more flight speed
which is a compensation for the reduces vertical thrust when
tilted. This is supplied by set_motor_tilt_factor().
The second compensation is to use equal thrust on all tilted motors 2) when we are transitioning to hover we scale the non-tilted rotors by cos(angle). This pushes us towards lower fwd thrust
when _tilt_equal_thrust is true. This is used when the motors are
tilted by a large angle to prevent the roll and yaw controllers from
causing instability. Typically this would be used when the motors
are tilted beyond 45 degrees. At this angle it is assumed that roll
control can be achieved using fixed wing control surfaces and yaw
control with the remaining multicopter motors (eg. tricopter tail).
By applying _tilt_equal_thrust the tilted motors effectively become We also apply an equalisation to the tilted motors in proportion to
a single pitch control motor. how much tilt we have. This smoothly reduces the impact of the roll
gains as we tilt further forward.
Note that we use a different strategy for when we are transitioning For yaw, we apply differential thrust in proportion to the demanded
into VTOL as compared to from VTOL flight. The reason for that is yaw control and sin of the tilt angle
we want to lean towards higher tilted motor throttle when
transitioning to fixed wing flight, in order to gain airspeed, Finally we ensure no requested thrust is over 1 by scaling back all
whereas when transitioning to VTOL flight we want to lean to towards motors so the largest thrust is at most 1.0
lower fwd throttle. So we raise the throttle on the tilted motors
when transitioning to fixed wing, and lower throttle on tilted
motors when transitioning to VTOL
*/ */
void QuadPlane::tilt_compensate_down(float *thrust, uint8_t num_motors) void QuadPlane::tilt_compensate_angle(float *thrust, uint8_t num_motors, float non_tilted_mul, float tilted_mul)
{ {
float inv_tilt_factor;
if (tilt.current_tilt > 0.98f) {
inv_tilt_factor = 1.0 / cosf(radians(0.98f*90));
} else {
inv_tilt_factor = 1.0 / cosf(radians(tilt.current_tilt*90));
}
// when we got past Q_TILT_MAX we gang the tilted motors together
// to generate equal thrust. This makes them act as a single pitch
// control motor while preventing them trying to do roll and yaw
// control while angled over. This greatly improves the stability
// of the last phase of transitions
float tilt_threshold = (tilt.max_angle_deg/90.0f);
bool equal_thrust = (tilt.current_tilt > tilt_threshold);
float tilt_total = 0; float tilt_total = 0;
uint8_t tilt_count = 0; uint8_t tilt_count = 0;
// apply inv_tilt_factor first // apply tilt_factors first
for (uint8_t i=0; i<num_motors; i++) { for (uint8_t i=0; i<num_motors; i++) {
if (is_motor_tilting(i)) { if (!is_motor_tilting(i)) {
thrust[i] *= inv_tilt_factor; thrust[i] *= non_tilted_mul;
} else {
thrust[i] *= tilted_mul;
tilt_total += thrust[i]; tilt_total += thrust[i];
tilt_count++; tilt_count++;
} }
} }
float largest_tilted = 0; float largest_tilted = 0;
const float sin_tilt = sinf(radians(tilt.current_tilt*90));
// yaw_gain relates the amount of differential thrust we get from
// tilt, so that the scaling of the yaw control is the same at any
// tilt angle
const float yaw_gain = sinf(radians(tilt.tilt_yaw_angle));
const float avg_tilt_thrust = tilt_total / tilt_count;
// now constrain and apply _tilt_equal_thrust if enabled
for (uint8_t i=0; i<num_motors; i++) { for (uint8_t i=0; i<num_motors; i++) {
if (is_motor_tilting(i)) { if (is_motor_tilting(i)) {
if (equal_thrust) { // as we tilt we need to reduce the impact of the roll
thrust[i] = tilt_total / tilt_count; // controller. This simple method keeps the same average,
} // but moves us to no roll control as the angle increases
thrust[i] = tilt.current_tilt * avg_tilt_thrust + thrust[i] * (1-tilt.current_tilt);
// add in differential thrust for yaw control, scaled by tilt angle
const float diff_thrust = motors->get_roll_factor(i) * motors->get_yaw() * sin_tilt * yaw_gain;
thrust[i] += diff_thrust;
largest_tilted = MAX(largest_tilted, thrust[i]); largest_tilted = MAX(largest_tilted, thrust[i]);
} }
} }
// if we are saturating one of the tilted motors then reduce all // if we are saturating one of the motors then reduce all motors
// motors to keep them in proportion to the original thrust. This // to keep them in proportion to the original thrust. This helps
// helps maintain stability when tilted at a large angle // maintain stability when tilted at a large angle
if (largest_tilted > 1.0f) { if (largest_tilted > 1.0f) {
float scale = 1.0f / largest_tilted; float scale = 1.0f / largest_tilted;
for (uint8_t i=0; i<num_motors; i++) { for (uint8_t i=0; i<num_motors; i++) {
@ -293,45 +281,6 @@ void QuadPlane::tilt_compensate_down(float *thrust, uint8_t num_motors)
} }
} }
/*
tilt compensation when transitioning to VTOL flight
*/
void QuadPlane::tilt_compensate_up(float *thrust, uint8_t num_motors)
{
float tilt_factor = cosf(radians(tilt.current_tilt*90));
// when we got past Q_TILT_MAX we gang the tilted motors together
// to generate equal thrust. This makes them act as a single pitch
// control motor while preventing them trying to do roll and yaw
// control while angled over. This greatly improves the stability
// of the last phase of transitions
float tilt_threshold = (tilt.max_angle_deg/90.0f);
bool equal_thrust = (tilt.current_tilt > tilt_threshold);
float tilt_total = 0;
uint8_t tilt_count = 0;
// apply tilt_factor first
for (uint8_t i=0; i<num_motors; i++) {
if (!is_motor_tilting(i)) {
thrust[i] *= tilt_factor;
} else {
tilt_total += thrust[i];
tilt_count++;
}
}
// now constrain and apply _tilt_equal_thrust if enabled
for (uint8_t i=0; i<num_motors; i++) {
if (is_motor_tilting(i)) {
if (equal_thrust) {
thrust[i] = tilt_total / tilt_count;
}
}
}
}
/* /*
choose up or down tilt compensation based on flight mode When going choose up or down tilt compensation based on flight mode When going
to a fixed wing mode we use tilt_compensate_down, when going to a to a fixed wing mode we use tilt_compensate_down, when going to a
@ -345,9 +294,16 @@ void QuadPlane::tilt_compensate(float *thrust, uint8_t num_motors)
} }
if (in_vtol_mode()) { if (in_vtol_mode()) {
// we are transitioning to VTOL flight // we are transitioning to VTOL flight
tilt_compensate_up(thrust, num_motors); const float tilt_factor = cosf(radians(tilt.current_tilt*90));
tilt_compensate_angle(thrust, num_motors, tilt_factor, 1);
} else { } else {
tilt_compensate_down(thrust, num_motors); float inv_tilt_factor;
if (tilt.current_tilt > 0.98f) {
inv_tilt_factor = 1.0 / cosf(radians(0.98f*90));
} else {
inv_tilt_factor = 1.0 / cosf(radians(tilt.current_tilt*90));
}
tilt_compensate_angle(thrust, num_motors, 1, inv_tilt_factor);
} }
} }
@ -386,8 +342,8 @@ void QuadPlane::tiltrotor_vectored_yaw(void)
yaw_out /= plane.channel_rudder->get_range(); yaw_out /= plane.channel_rudder->get_range();
float yaw_range = zero_out; float yaw_range = zero_out;
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * (base_output + yaw_out * yaw_range)); SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * constrain_float(base_output + yaw_out * yaw_range,0,1));
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, 1000 * (base_output - yaw_out * yaw_range)); SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, 1000 * constrain_float(base_output - yaw_out * yaw_range,0,1));
} }
return; return;
} }
@ -398,11 +354,22 @@ void QuadPlane::tiltrotor_vectored_yaw(void)
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * base_output); SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * base_output);
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, 1000 * base_output); SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, 1000 * base_output);
} else { } else {
float yaw_out = motors->get_yaw(); const float yaw_out = motors->get_yaw();
const float roll_out = motors->get_roll();
float yaw_range = zero_out; float yaw_range = zero_out;
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * (base_output + yaw_out * yaw_range)); // now apply vectored thrust for yaw and roll.
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, 1000 * (base_output - yaw_out * yaw_range)); const float tilt_rad = radians(tilt.current_tilt*90);
const float sin_tilt = sinf(tilt_rad);
const float cos_tilt = cosf(tilt_rad);
// the MotorsMatrix library normalises roll factor to 0.5, so
// we need to use the same factor here to keep the same roll
// gains when tilted as we have when not tilted
const float avg_roll_factor = 0.5;
const float tilt_offset = constrain_float(yaw_out * cos_tilt + avg_roll_factor * roll_out * sin_tilt, -1, 1);
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * constrain_float(base_output + tilt_offset * yaw_range,0,1));
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, 1000 * constrain_float(base_output - tilt_offset * yaw_range,0,1));
} }
} }