You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
325 lines
12 KiB
325 lines
12 KiB
/* ----------------------------------------------------------------------------------------- |
|
This is currently a SITL only function until the project is complete. |
|
To trial this in SITL you will need to use Real Flight 8. |
|
Instructions for how to set this up in SITL can be found here: |
|
https://discuss.ardupilot.org/t/autonomous-autorotation-gsoc-project-blog/42139 |
|
-----------------------------------------------------------------------------------------*/ |
|
|
|
#include "Copter.h" |
|
#include <AC_Autorotation/AC_Autorotation.h> |
|
#include "mode.h" |
|
|
|
#include <utility> |
|
|
|
#if MODE_AUTOROTATE_ENABLED == ENABLED |
|
|
|
#define AUTOROTATE_ENTRY_TIME 2.0f // (s) number of seconds that the entry phase operates for |
|
#define BAILOUT_MOTOR_RAMP_TIME 1.0f // (s) time set on bailout ramp up timer for motors - See AC_MotorsHeli_Single |
|
#define HEAD_SPEED_TARGET_RATIO 1.0f // Normalised target main rotor head speed (unit: -) |
|
|
|
bool ModeAutorotate::init(bool ignore_checks) |
|
{ |
|
#if FRAME_CONFIG != HELI_FRAME |
|
// Only allow trad heli to use autorotation mode |
|
return false; |
|
#endif |
|
|
|
// Check that mode is enabled |
|
if (!g2.arot.is_enable()) { |
|
gcs().send_text(MAV_SEVERITY_INFO, "Autorot Mode Not Enabled"); |
|
return false; |
|
} |
|
|
|
// Check that interlock is disengaged |
|
if (motors->get_interlock()) { |
|
gcs().send_text(MAV_SEVERITY_INFO, "Autorot Mode Change Fail: Interlock Engaged"); |
|
return false; |
|
} |
|
|
|
// Initialise controllers |
|
// This must be done before RPM value is fetched |
|
g2.arot.init_hs_controller(); |
|
g2.arot.init_fwd_spd_controller(); |
|
|
|
// Retrive rpm and start rpm sensor health checks |
|
_initial_rpm = g2.arot.get_rpm(true); |
|
|
|
// Display message |
|
gcs().send_text(MAV_SEVERITY_INFO, "Autorotation initiated"); |
|
|
|
// Set all inial flags to on |
|
_flags.entry_initial = 1; |
|
_flags.ss_glide_initial = 1; |
|
_flags.flare_initial = 1; |
|
_flags.touch_down_initial = 1; |
|
_flags.level_initial = 1; |
|
_flags.break_initial = 1; |
|
_flags.straight_ahead_initial = 1; |
|
_flags.bail_out_initial = 1; |
|
_msg_flags.bad_rpm = true; |
|
|
|
// Setting default starting switches |
|
phase_switch = Autorotation_Phase::ENTRY; |
|
|
|
// Set entry timer |
|
_entry_time_start = millis(); |
|
|
|
// The decay rate to reduce the head speed from the current to the target |
|
_hs_decay = ((_initial_rpm/g2.arot.get_hs_set_point()) - HEAD_SPEED_TARGET_RATIO) / AUTOROTATE_ENTRY_TIME; |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
void ModeAutorotate::run() |
|
{ |
|
// Check if interlock becomes engaged |
|
if (motors->get_interlock() && !copter.ap.land_complete) { |
|
phase_switch = Autorotation_Phase::BAIL_OUT; |
|
} else if (motors->get_interlock() && copter.ap.land_complete) { |
|
// Aircraft is landed and no need to bail out |
|
set_mode(copter.prev_control_mode, ModeReason::AUTOROTATION_BAILOUT); |
|
} |
|
|
|
// Current time |
|
now = millis(); //milliseconds |
|
|
|
// Initialise internal variables |
|
float curr_vel_z = inertial_nav.get_velocity().z; // Current vertical descent |
|
|
|
//---------------------------------------------------------------- |
|
// State machine logic |
|
//---------------------------------------------------------------- |
|
|
|
// Setting default phase switch positions |
|
nav_pos_switch = Navigation_Decision::USER_CONTROL_STABILISED; |
|
|
|
// Timer from entry phase to progress to glide phase |
|
if (phase_switch == Autorotation_Phase::ENTRY){ |
|
|
|
if ((now - _entry_time_start)/1000.0f > AUTOROTATE_ENTRY_TIME) { |
|
// Flight phase can be progressed to steady state glide |
|
phase_switch = Autorotation_Phase::SS_GLIDE; |
|
} |
|
|
|
} |
|
|
|
|
|
//---------------------------------------------------------------- |
|
// State machine actions |
|
//---------------------------------------------------------------- |
|
switch (phase_switch) { |
|
|
|
case Autorotation_Phase::ENTRY: |
|
{ |
|
// Entry phase functions to be run only once |
|
if (_flags.entry_initial == 1) { |
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
gcs().send_text(MAV_SEVERITY_INFO, "Entry Phase"); |
|
#endif |
|
|
|
// Set following trim low pass cut off frequency |
|
g2.arot.set_col_cutoff_freq(g2.arot.get_col_entry_freq()); |
|
|
|
// Target head speed is set to rpm at initiation to prevent unwanted changes in attitude |
|
_target_head_speed = _initial_rpm/g2.arot.get_hs_set_point(); |
|
|
|
// Set desired forward speed target |
|
g2.arot.set_desired_fwd_speed(); |
|
|
|
// Prevent running the initial entry functions again |
|
_flags.entry_initial = 0; |
|
|
|
} |
|
|
|
// Slowly change the target head speed until the target head speed matches the parameter defined value |
|
if (g2.arot.get_rpm() > HEAD_SPEED_TARGET_RATIO*1.005f || g2.arot.get_rpm() < HEAD_SPEED_TARGET_RATIO*0.995f) { |
|
_target_head_speed -= _hs_decay*G_Dt; |
|
} else { |
|
_target_head_speed = HEAD_SPEED_TARGET_RATIO; |
|
} |
|
|
|
// Set target head speed in head speed controller |
|
g2.arot.set_target_head_speed(_target_head_speed); |
|
|
|
// Run airspeed/attitude controller |
|
g2.arot.set_dt(G_Dt); |
|
g2.arot.update_forward_speed_controller(); |
|
|
|
// Retrieve pitch target |
|
_pitch_target = g2.arot.get_pitch(); |
|
|
|
// Update controllers |
|
_flags.bad_rpm = g2.arot.update_hs_glide_controller(G_Dt); //run head speed/ collective controller |
|
|
|
break; |
|
} |
|
|
|
case Autorotation_Phase::SS_GLIDE: |
|
{ |
|
// Steady state glide functions to be run only once |
|
if (_flags.ss_glide_initial == 1) { |
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
gcs().send_text(MAV_SEVERITY_INFO, "SS Glide Phase"); |
|
#endif |
|
|
|
// Set following trim low pass cut off frequency |
|
g2.arot.set_col_cutoff_freq(g2.arot.get_col_glide_freq()); |
|
|
|
// Set desired forward speed target |
|
g2.arot.set_desired_fwd_speed(); |
|
|
|
// Set target head speed in head speed controller |
|
_target_head_speed = HEAD_SPEED_TARGET_RATIO; //Ensure target hs is set to glide incase hs hasent reached target for glide |
|
g2.arot.set_target_head_speed(_target_head_speed); |
|
|
|
// Prevent running the initial glide functions again |
|
_flags.ss_glide_initial = 0; |
|
} |
|
|
|
// Run airspeed/attitude controller |
|
g2.arot.set_dt(G_Dt); |
|
g2.arot.update_forward_speed_controller(); |
|
|
|
// Retrieve pitch target |
|
_pitch_target = g2.arot.get_pitch(); |
|
|
|
// Update head speed/ collective controller |
|
_flags.bad_rpm = g2.arot.update_hs_glide_controller(G_Dt); |
|
// Attitude controller is updated in navigation switch-case statements |
|
|
|
break; |
|
} |
|
|
|
case Autorotation_Phase::FLARE: |
|
case Autorotation_Phase::TOUCH_DOWN: |
|
{ |
|
break; |
|
} |
|
|
|
case Autorotation_Phase::BAIL_OUT: |
|
{ |
|
if (_flags.bail_out_initial == 1) { |
|
// Functions and settings to be done once are done here. |
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL |
|
gcs().send_text(MAV_SEVERITY_INFO, "Bailing Out of Autorotation"); |
|
#endif |
|
|
|
// Set bail out timer remaining equal to the paramter value, bailout time |
|
// cannot be less than the motor spool-up time: BAILOUT_MOTOR_RAMP_TIME. |
|
_bail_time = MAX(g2.arot.get_bail_time(),BAILOUT_MOTOR_RAMP_TIME+0.1f); |
|
|
|
// Set bail out start time |
|
_bail_time_start = now; |
|
|
|
// Set initial target vertical speed |
|
_desired_v_z = curr_vel_z; |
|
|
|
// Initialise position and desired velocity |
|
if (!pos_control->is_active_z()) { |
|
pos_control->relax_alt_hold_controllers(g2.arot.get_last_collective()); |
|
} |
|
|
|
// Get pilot parameter limits |
|
const float pilot_spd_dn = -get_pilot_speed_dn(); |
|
const float pilot_spd_up = g.pilot_speed_up; |
|
|
|
// Set speed limit |
|
pos_control->set_max_speed_z(curr_vel_z, pilot_spd_up); |
|
|
|
float pilot_des_v_z = get_pilot_desired_climb_rate(channel_throttle->get_control_in()); |
|
pilot_des_v_z = constrain_float(pilot_des_v_z, pilot_spd_dn, pilot_spd_up); |
|
|
|
// Calculate target climb rate adjustment to transition from bail out decent speed to requested climb rate on stick. |
|
_target_climb_rate_adjust = (curr_vel_z - pilot_des_v_z)/(_bail_time - BAILOUT_MOTOR_RAMP_TIME); //accounting for 0.5s motor spool time |
|
|
|
// Calculate pitch target adjustment rate to return to level |
|
_target_pitch_adjust = _pitch_target/_bail_time; |
|
|
|
// Set acceleration limit |
|
pos_control->set_max_accel_z(abs(_target_climb_rate_adjust)); |
|
|
|
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED); |
|
|
|
_flags.bail_out_initial = 0; |
|
} |
|
|
|
if ((now - _bail_time_start)/1000.0f >= BAILOUT_MOTOR_RAMP_TIME) { |
|
// Update desired vertical speed and pitch target after the bailout motor ramp timer has completed |
|
_desired_v_z -= _target_climb_rate_adjust*G_Dt; |
|
_pitch_target -= _target_pitch_adjust*G_Dt; |
|
} |
|
// Set position controller |
|
pos_control->set_alt_target_from_climb_rate(_desired_v_z, G_Dt, false); |
|
|
|
// Update controllers |
|
pos_control->update_z_controller(); |
|
|
|
if ((now - _bail_time_start)/1000.0f >= _bail_time) { |
|
// Bail out timer complete. Change flight mode. Do not revert back to auto. Prevent aircraft |
|
// from continuing mission and potentially flying further away after a power failure. |
|
if (copter.prev_control_mode == Mode::Number::AUTO) { |
|
set_mode(Mode::Number::ALT_HOLD, ModeReason::AUTOROTATION_BAILOUT); |
|
} else { |
|
set_mode(copter.prev_control_mode, ModeReason::AUTOROTATION_BAILOUT); |
|
} |
|
} |
|
|
|
break; |
|
} |
|
} |
|
|
|
|
|
switch (nav_pos_switch) { |
|
|
|
case Navigation_Decision::USER_CONTROL_STABILISED: |
|
{ |
|
// Operator is in control of roll and yaw. Controls act as if in stabilise flight mode. Pitch |
|
// is controlled by speed-height controller. |
|
float pilot_roll, pilot_pitch; |
|
get_pilot_desired_lean_angles(pilot_roll, pilot_pitch, copter.aparm.angle_max, copter.aparm.angle_max); |
|
|
|
// Get pilot's desired yaw rate |
|
float pilot_yaw_rate = get_pilot_desired_yaw_rate(channel_yaw->get_control_in()); |
|
|
|
// Pitch target is calculated in autorotation phase switch above |
|
attitude_control->input_euler_angle_roll_pitch_euler_rate_yaw(pilot_roll, _pitch_target, pilot_yaw_rate); |
|
break; |
|
} |
|
|
|
case Navigation_Decision::STRAIGHT_AHEAD: |
|
case Navigation_Decision::INTO_WIND: |
|
case Navigation_Decision::NEAREST_RALLY: |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
// Output warning messaged if rpm signal is bad |
|
if (_flags.bad_rpm) { |
|
warning_message(1); |
|
} |
|
|
|
} // End function run() |
|
|
|
void ModeAutorotate::warning_message(uint8_t message_n) |
|
{ |
|
switch (message_n) { |
|
case 1: |
|
{ |
|
if (_msg_flags.bad_rpm) { |
|
// Bad rpm sensor health. |
|
gcs().send_text(MAV_SEVERITY_INFO, "Warning: Poor RPM Sensor Health"); |
|
gcs().send_text(MAV_SEVERITY_INFO, "Action: Minimum Collective Applied"); |
|
_msg_flags.bad_rpm = false; |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
|
|
#endif
|
|
|