-
Notifications
You must be signed in to change notification settings - Fork 16.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
plane: tail-sitter interpolate gains with airspeed and enable Q_Assist #9167
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,27 +56,27 @@ void QuadPlane::tailsitter_output(void) | |
return; | ||
} | ||
|
||
float tilt_left = 0.0f; | ||
float tilt_right = 0.0f; | ||
uint16_t mask = tailsitter.motor_mask; | ||
|
||
// handle forward flight modes and transition to VTOL modes | ||
if (!tailsitter_active() || in_tailsitter_vtol_transition()) { | ||
// in forward flight: set motor tilt servos and throttles using FW controller | ||
if (tailsitter.vectored_forward_gain > 0) { | ||
// thrust vectoring in fixed wing flight | ||
float aileron = SRV_Channels::get_output_scaled(SRV_Channel::k_aileron); | ||
float elevator = SRV_Channels::get_output_scaled(SRV_Channel::k_elevator); | ||
tilt_left = (elevator + aileron) * tailsitter.vectored_forward_gain; | ||
tilt_right = (elevator - aileron) * tailsitter.vectored_forward_gain; | ||
} | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, tilt_left); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, tilt_right); | ||
// record plane outputs in case they are needed for interpolation | ||
float fw_aileron = SRV_Channels::get_output_scaled(SRV_Channel::k_aileron); | ||
float fw_elevator = SRV_Channels::get_output_scaled(SRV_Channel::k_elevator); | ||
float fw_rudder = SRV_Channels::get_output_scaled(SRV_Channel::k_rudder); | ||
|
||
// thrust vectoring in fixed wing flight | ||
float fw_tilt_left = 0; | ||
float fw_tilt_right = 0; | ||
if (tailsitter.vectored_forward_gain > 0) { | ||
fw_tilt_left = (fw_elevator + fw_aileron) * tailsitter.vectored_forward_gain; | ||
fw_tilt_right = (fw_elevator - fw_aileron) * tailsitter.vectored_forward_gain; | ||
} | ||
|
||
if ((!tailsitter_active() || in_tailsitter_vtol_transition()) && !assisted_flight) { | ||
// output tilts for forward flight | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, fw_tilt_left); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, fw_tilt_right); | ||
|
||
// get FW controller throttle demand and mask of motors enabled during forward flight | ||
// get FW controller throttle demand and mask of motors enabled during forward flight | ||
float throttle = SRV_Channels::get_output_scaled(SRV_Channel::k_throttle); | ||
if (hal.util->get_soft_armed()) { | ||
if (in_tailsitter_vtol_transition() && !throttle_wait && is_flying()) { | ||
if (in_tailsitter_vtol_transition() && !throttle_wait && is_flying() && hal.util->get_soft_armed()) { | ||
/* | ||
during transitions to vtol mode set the throttle to | ||
hover thrust, center the rudder and set the altitude controller | ||
|
@@ -85,44 +85,49 @@ void QuadPlane::tailsitter_output(void) | |
throttle = motors->get_throttle_hover() * 100; | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, 0); | ||
pos_control->get_accel_z_pid().set_integrator(throttle*10); | ||
|
||
if (mask == 0) { | ||
// override AP_MotorsTailsitter throttles during back transition | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, throttle); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttleLeft, throttle); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttleRight, throttle); | ||
} | ||
|
||
// override AP_MotorsTailsitter throttles during back transition | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, throttle); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttleLeft, throttle); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttleRight, throttle); | ||
} | ||
if (mask != 0) { | ||
if (tailsitter.motor_mask != 0) { | ||
// set AP_MotorsMatrix throttles enabled for forward flight | ||
motors->output_motor_mask(throttle * 0.01f, mask, plane.rudder_dt); | ||
} | ||
motors->output_motor_mask(throttle * 0.01f, tailsitter.motor_mask, plane.rudder_dt); | ||
} | ||
return; | ||
} | ||
|
||
// handle VTOL modes | ||
// the MultiCopter rate controller has already been run in an earlier call | ||
// to motors_output() from quadplane.update() | ||
motors_output(false); | ||
|
||
if (assisted_flight) { | ||
control_stabilize(); | ||
motors_output(true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're calling motors_output more than once in each pass through set_servos, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is for Q-assist, it we're in a normal vtol mode this is already done in the quadplane code, altough i'm not sure why I need to call control stabalise again |
||
} else { | ||
motors_output(false); | ||
} | ||
|
||
// if in Q assist still a good idea to use copter I term and zero plane I to prevent windup | ||
plane.pitchController.reset_I(); | ||
plane.rollController.reset_I(); | ||
|
||
// pull in copter control outputs | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_aileron, (motors->get_yaw())*-SERVO_MAX); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_elevator, (motors->get_pitch())*SERVO_MAX); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, (motors->get_roll())*SERVO_MAX); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, (motors->get_throttle()) * 100); | ||
float aileron = motors->get_yaw()*-SERVO_MAX; | ||
float elevator = motors->get_pitch()*SERVO_MAX; | ||
float rudder = motors->get_roll()*SERVO_MAX; | ||
float throttle = motors->get_throttle() * 100; | ||
float tilt_left = SRV_Channels::get_output_scaled(SRV_Channel::k_tiltMotorLeft); | ||
float tilt_right = SRV_Channels::get_output_scaled(SRV_Channel::k_tiltMotorRight); | ||
|
||
// scale surfaces for throttle | ||
if (hal.util->get_soft_armed()) { | ||
// scale surfaces for throttle | ||
tailsitter_speed_scaling(); | ||
float scaling = get_tailsitter_speed_scaling(); | ||
rudder = constrain_float(rudder * scaling, -SERVO_MAX, SERVO_MAX); | ||
aileron = constrain_float(aileron * scaling, -SERVO_MAX, SERVO_MAX); | ||
elevator = constrain_float(elevator * scaling, -SERVO_MAX, SERVO_MAX); | ||
tilt_left = constrain_float(tilt_left * scaling, -SERVO_MAX, SERVO_MAX); | ||
tilt_right = constrain_float(tilt_right * scaling, -SERVO_MAX, SERVO_MAX); | ||
} | ||
|
||
if (tailsitter.vectored_hover_gain > 0) { | ||
// thrust vectoring VTOL modes | ||
tilt_left = SRV_Channels::get_output_scaled(SRV_Channel::k_tiltMotorLeft); | ||
tilt_right = SRV_Channels::get_output_scaled(SRV_Channel::k_tiltMotorRight); | ||
/* | ||
apply extra elevator when at high pitch errors, using a | ||
power law. This allows the motors to point straight up for | ||
|
@@ -131,36 +136,78 @@ void QuadPlane::tailsitter_output(void) | |
int32_t pitch_error_cd = (plane.nav_pitch_cd - ahrs_view->pitch_sensor) * 0.5; | ||
float extra_pitch = constrain_float(pitch_error_cd, -SERVO_MAX, SERVO_MAX) / SERVO_MAX; | ||
float extra_sign = extra_pitch > 0?1:-1; | ||
float extra_elevator = extra_sign * powf(fabsf(extra_pitch), tailsitter.vectored_hover_power) * SERVO_MAX; | ||
// only apply extra elevator in Q modes | ||
float extra_elevator = 0.0f; | ||
if (!assisted_flight) { | ||
extra_elevator = extra_sign * powf(fabsf(extra_pitch), tailsitter.vectored_hover_power) * SERVO_MAX; | ||
} | ||
tilt_left = extra_elevator + tilt_left * tailsitter.vectored_hover_gain; | ||
tilt_right = extra_elevator + tilt_right * tailsitter.vectored_hover_gain; | ||
if (fabsf(tilt_left) >= SERVO_MAX || fabsf(tilt_right) >= SERVO_MAX) { | ||
// prevent integrator windup | ||
motors->limit.roll_pitch = 1; | ||
motors->limit.yaw = 1; | ||
} | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, tilt_left); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, tilt_right); | ||
} | ||
|
||
// apply speed scaling to interpolate between fixed wing and VTOL outputs based on airspeed | ||
float aspeed; | ||
bool have_airspeed = ahrs.airspeed_estimate(&aspeed); | ||
const float scaling_range = tailsitter.scaling_speed_max - tailsitter.scaling_speed_min; | ||
// only bother if it will change the output | ||
if (aspeed > tailsitter.scaling_speed_min && have_airspeed && !is_zero(scaling_range)) { | ||
|
||
// match the Q rates with plane controller | ||
if (!assisted_flight) { | ||
// no fixed wing yaw controller so cannot stabilize VTOL roll | ||
const float pitch_rate = attitude_control->get_rate_pitch_pid().get_pid_info().desired * 100; | ||
const float yaw_rate = attitude_control->get_rate_yaw_pid().get_pid_info().desired * 100; | ||
const float speed_scaler = plane.get_speed_scaler(); | ||
|
||
// due to reference frame change roll and yaw are swapped, use roll as rudder input and output direct as with plane | ||
fw_aileron = plane.rollController.get_rate_out(-yaw_rate, speed_scaler); | ||
fw_elevator = plane.pitchController.get_rate_out(pitch_rate, speed_scaler); | ||
fw_rudder = plane.channel_roll->get_control_in(); | ||
} | ||
|
||
// calculate ratio of gains | ||
float fw_ratio = (aspeed - tailsitter.scaling_speed_min) / scaling_range; | ||
fw_ratio = constrain_float(fw_ratio, 0.0f, 1.0f); | ||
const float VTOL_rato = 1.0f - fw_ratio; | ||
|
||
// calculate interpolated outputs | ||
aileron = aileron * VTOL_rato + fw_aileron * fw_ratio; | ||
elevator = elevator * VTOL_rato + fw_elevator * fw_ratio; | ||
rudder = rudder * VTOL_rato + fw_rudder * fw_ratio; | ||
tilt_left = tilt_left * VTOL_rato + fw_tilt_left * fw_ratio; | ||
tilt_right = tilt_right * VTOL_rato + fw_tilt_right * fw_ratio; | ||
} | ||
|
||
if (tailsitter.input_mask_chan > 0 && | ||
tailsitter.input_mask > 0 && | ||
RC_Channels::get_radio_in(tailsitter.input_mask_chan-1) > 1700) { | ||
// the user is learning to prop-hang | ||
if (tailsitter.input_mask & TAILSITTER_MASK_AILERON) { | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_aileron, plane.channel_roll->get_control_in_zero_dz()); | ||
aileron = plane.channel_roll->get_control_in_zero_dz(); | ||
} | ||
if (tailsitter.input_mask & TAILSITTER_MASK_ELEVATOR) { | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_elevator, plane.channel_pitch->get_control_in_zero_dz()); | ||
elevator = plane.channel_pitch->get_control_in_zero_dz(); | ||
} | ||
if (tailsitter.input_mask & TAILSITTER_MASK_THROTTLE) { | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, plane.get_throttle_input(true)); | ||
throttle = plane.get_throttle_input(true); | ||
} | ||
if (tailsitter.input_mask & TAILSITTER_MASK_RUDDER) { | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, plane.channel_rudder->get_control_in_zero_dz()); | ||
rudder = plane.channel_rudder->get_control_in_zero_dz(); | ||
} | ||
} | ||
|
||
// set outputs | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_aileron, aileron); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_elevator, elevator); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_rudder, rudder); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, tilt_left); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorRight, tilt_right); | ||
SRV_Channels::set_output_scaled(SRV_Channel::k_throttle, throttle); | ||
} | ||
|
||
|
||
|
@@ -232,21 +279,23 @@ bool QuadPlane::in_tailsitter_vtol_transition(void) const | |
} | ||
|
||
/* | ||
account for speed scaling of control surfaces in VTOL modes | ||
calculate speed scaler of control surfaces in VTOL modes | ||
*/ | ||
void QuadPlane::tailsitter_speed_scaling(void) | ||
float QuadPlane::get_tailsitter_speed_scaling(void) | ||
{ | ||
const float hover_throttle = motors->get_throttle_hover(); | ||
const float throttle = motors->get_throttle(); | ||
float spd_scaler = 1; | ||
|
||
// If throttle_scale_max is > 1, boost gains at low throttle | ||
if (tailsitter.throttle_scale_max > 1) { | ||
// If throttle_scale_max is => 1, boost gains at low throttle | ||
if (tailsitter.throttle_scale_max >= 1) { | ||
if (is_zero(throttle)) { | ||
spd_scaler = tailsitter.throttle_scale_max; | ||
} else { | ||
spd_scaler = constrain_float(hover_throttle / throttle, 0, tailsitter.throttle_scale_max); | ||
} | ||
return spd_scaler; | ||
|
||
} else { | ||
// reduce gains when flying at high speed in Q modes: | ||
|
||
|
@@ -287,39 +336,15 @@ void QuadPlane::tailsitter_speed_scaling(void) | |
spd_scaler *= throttle_atten; | ||
spd_scaler = constrain_float(spd_scaler, max_atten, 1.0f); | ||
} | ||
} | ||
// limit positive and negative slew rates of applied speed scaling | ||
constexpr float posTC = 5.0f; // seconds | ||
constexpr float negTC = 2.0f; // seconds | ||
const float posdelta = plane.G_Dt / posTC; | ||
const float negdelta = plane.G_Dt / negTC; | ||
static float last_scale = 0; | ||
static float scale = 0; | ||
if ((spd_scaler - last_scale) > 0) { | ||
if ((spd_scaler - last_scale) > posdelta) { | ||
scale += posdelta; | ||
} else { | ||
scale = spd_scaler; | ||
} | ||
} else { | ||
if ((spd_scaler - last_scale) < -negdelta) { | ||
scale -= negdelta; | ||
} else { | ||
scale = spd_scaler; | ||
} | ||
} | ||
last_scale = scale; | ||
|
||
const SRV_Channel::Aux_servo_function_t functions[4] = { | ||
SRV_Channel::Aux_servo_function_t::k_aileron, | ||
SRV_Channel::Aux_servo_function_t::k_elevator, | ||
SRV_Channel::Aux_servo_function_t::k_tiltMotorLeft, | ||
SRV_Channel::Aux_servo_function_t::k_tiltMotorRight}; | ||
for (uint8_t i=0; i<ARRAY_SIZE(functions); i++) { | ||
int32_t v = SRV_Channels::get_output_scaled(functions[i]); | ||
v *= scale; | ||
v = constrain_int32(v, -SERVO_MAX, SERVO_MAX); | ||
SRV_Channels::set_output_scaled(functions[i], v); | ||
|
||
// limit positive and negative slew rates of applied speed scaling | ||
constexpr float posTC = 5.0f; // seconds | ||
constexpr float negTC = 2.0f; // seconds | ||
const float posdelta = plane.G_Dt / posTC; | ||
const float negdelta = plane.G_Dt / negTC; | ||
static float last_scale = 0; | ||
const float scale = constrain_float(spd_scaler, last_scale - negdelta, last_scale + posdelta); | ||
last_scale = scale; | ||
return scale; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this change seems to have no effect, why change it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to enable assisted flight for tail sitters without entering airspeed wait transition state