Files
ardupilot/ArduCopter/mode_throw.cpp
T
Peter Barker 8ccb77f708 Copter: wait for motors to spool up before trying to upright vehicle
This stops us from progressing through the whole throw mode if the
vehicle just happens to be in the right state - which is can be for a
drop.
2021-07-14 16:38:36 +09:00

324 lines
12 KiB
C++

#include "Copter.h"
#if MODE_THROW_ENABLED == ENABLED
// throw_init - initialise throw controller
bool ModeThrow::init(bool ignore_checks)
{
#if FRAME_CONFIG == HELI_FRAME
// do not allow helis to use throw to start
return false;
#endif
// do not enter the mode when already armed or when flying
if (motors->armed()) {
return false;
}
// init state
stage = Throw_Disarmed;
nextmode_attempted = false;
// initialise pos controller speed and acceleration
pos_control->set_max_speed_accel_xy(wp_nav->get_default_speed_xy(), BRAKE_MODE_DECEL_RATE);
pos_control->set_correction_speed_accel_xy(wp_nav->get_default_speed_xy(), BRAKE_MODE_DECEL_RATE);
// set vertical speed and acceleration limits
pos_control->set_max_speed_accel_z(BRAKE_MODE_SPEED_Z, BRAKE_MODE_SPEED_Z, BRAKE_MODE_DECEL_RATE);
pos_control->set_correction_speed_accel_z(BRAKE_MODE_SPEED_Z, BRAKE_MODE_SPEED_Z, BRAKE_MODE_DECEL_RATE);
return true;
}
// runs the throw to start controller
// should be called at 100hz or more
void ModeThrow::run()
{
/* Throw State Machine
Throw_Disarmed - motors are off
Throw_Detecting - motors are on and we are waiting for the throw
Throw_Uprighting - the throw has been detected and the copter is being uprighted
Throw_HgtStabilise - the copter is kept level and height is stabilised about the target height
Throw_PosHold - the copter is kept at a constant position and height
*/
if (!motors->armed()) {
// state machine entry is always from a disarmed state
stage = Throw_Disarmed;
} else if (stage == Throw_Disarmed && motors->armed()) {
gcs().send_text(MAV_SEVERITY_INFO,"waiting for throw");
stage = Throw_Detecting;
} else if (stage == Throw_Detecting && throw_detected()){
gcs().send_text(MAV_SEVERITY_INFO,"throw detected - spooling motors");
stage = Throw_Wait_Throttle_Unlimited;
// Cancel the waiting for throw tone sequence
AP_Notify::flags.waiting_for_throw = false;
} else if (stage == Throw_Wait_Throttle_Unlimited && throttle_is_unlimited()) {
gcs().send_text(MAV_SEVERITY_INFO,"throttle is unlimited - uprighting");
stage = Throw_Uprighting;
} else if (stage == Throw_Uprighting && throw_attitude_good()) {
gcs().send_text(MAV_SEVERITY_INFO,"uprighted - controlling height");
stage = Throw_HgtStabilise;
// initialise the z controller
pos_control->init_z_controller_no_descent();
// initialise the demanded height to 3m above the throw height
// we want to rapidly clear surrounding obstacles
if (g2.throw_type == ThrowType::Drop) {
pos_control->set_pos_target_z_cm(inertial_nav.get_altitude() - 100);
} else {
pos_control->set_pos_target_z_cm(inertial_nav.get_altitude() + 300);
}
// Set the auto_arm status to true to avoid a possible automatic disarm caused by selection of an auto mode with throttle at minimum
copter.set_auto_armed(true);
} else if (stage == Throw_HgtStabilise && throw_height_good()) {
gcs().send_text(MAV_SEVERITY_INFO,"height achieved - controlling position");
stage = Throw_PosHold;
// initialise position controller
pos_control->init_xy_controller();
// Set the auto_arm status to true to avoid a possible automatic disarm caused by selection of an auto mode with throttle at minimum
copter.set_auto_armed(true);
} else if (stage == Throw_PosHold && throw_position_good()) {
if (!nextmode_attempted) {
switch ((Mode::Number)g2.throw_nextmode.get()) {
case Mode::Number::AUTO:
case Mode::Number::GUIDED:
case Mode::Number::RTL:
case Mode::Number::LAND:
case Mode::Number::BRAKE:
case Mode::Number::LOITER:
set_mode((Mode::Number)g2.throw_nextmode.get(), ModeReason::THROW_COMPLETE);
break;
default:
// do nothing
break;
}
nextmode_attempted = true;
}
}
// Throw State Processing
switch (stage) {
case Throw_Disarmed:
// prevent motors from rotating before the throw is detected unless enabled by the user
if (g.throw_motor_start == PreThrowMotorState::RUNNING) {
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::GROUND_IDLE);
} else {
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::SHUT_DOWN);
}
// demand zero throttle (motors will be stopped anyway) and continually reset the attitude controller
attitude_control->reset_yaw_target_and_rate();
attitude_control->reset_rate_controller_I_terms();
attitude_control->set_throttle_out(0,true,g.throttle_filt);
break;
case Throw_Detecting:
// prevent motors from rotating before the throw is detected unless enabled by the user
if (g.throw_motor_start == PreThrowMotorState::RUNNING) {
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::GROUND_IDLE);
} else {
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::SHUT_DOWN);
}
// Hold throttle at zero during the throw and continually reset the attitude controller
attitude_control->reset_yaw_target_and_rate();
attitude_control->reset_rate_controller_I_terms();
attitude_control->set_throttle_out(0,true,g.throttle_filt);
// Play the waiting for throw tone sequence to alert the user
AP_Notify::flags.waiting_for_throw = true;
break;
case Throw_Wait_Throttle_Unlimited:
// set motors to full range
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
break;
case Throw_Uprighting:
// set motors to full range
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
// demand a level roll/pitch attitude with zero yaw rate
attitude_control->input_euler_angle_roll_pitch_euler_rate_yaw(0.0f, 0.0f, 0.0f);
// output 50% throttle and turn off angle boost to maximise righting moment
attitude_control->set_throttle_out(0.5f, false, g.throttle_filt);
break;
case Throw_HgtStabilise:
// set motors to full range
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
// call attitude controller
attitude_control->input_euler_angle_roll_pitch_euler_rate_yaw(0.0f, 0.0f, 0.0f);
// call height controller
pos_control->set_pos_target_z_from_climb_rate_cm(0.0f, false);
pos_control->update_z_controller();
break;
case Throw_PosHold:
// set motors to full range
motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
// use position controller to stop
Vector2f vel;
Vector2f accel;
pos_control->input_vel_accel_xy(vel, accel);
pos_control->update_xy_controller();
// call attitude controller
attitude_control->input_thrust_vector_rate_heading(pos_control->get_thrust_vector(), 0.0f);
// call height controller
pos_control->set_pos_target_z_from_climb_rate_cm(0.0f, false);
pos_control->update_z_controller();
break;
}
// log at 10hz or if stage changes
uint32_t now = AP_HAL::millis();
if ((stage != prev_stage) || (now - last_log_ms) > 100) {
prev_stage = stage;
last_log_ms = now;
const float velocity = inertial_nav.get_velocity().length();
const float velocity_z = inertial_nav.get_velocity().z;
const float accel = copter.ins.get_accel().length();
const float ef_accel_z = ahrs.get_accel_ef().z;
const bool throw_detect = (stage > Throw_Detecting) || throw_detected();
const bool attitude_ok = (stage > Throw_Uprighting) || throw_attitude_good();
const bool height_ok = (stage > Throw_HgtStabilise) || throw_height_good();
const bool pos_ok = (stage > Throw_PosHold) || throw_position_good();
// @LoggerMessage: THRO
// @Description: Throw Mode messages
// @URL: https://ardupilot.org/copter/docs/throw-mode.html
// @Field: TimeUS: Time since system startup
// @Field: Stage: Current stage of the Throw Mode
// @Field: Vel: Magnitude of the velocity vector
// @Field: VelZ: Vertical Velocity
// @Field: Acc: Magnitude of the vector of the current acceleration
// @Field: AccEfZ: Vertical earth frame accelerometer value
// @Field: Throw: True if a throw has been detected since entering this mode
// @Field: AttOk: True if the vehicle is upright
// @Field: HgtOk: True if the vehicle is within 50cm of the demanded height
// @Field: PosOk: True if the vehicle is within 50cm of the demanded horizontal position
AP::logger().Write(
"THRO",
"TimeUS,Stage,Vel,VelZ,Acc,AccEfZ,Throw,AttOk,HgtOk,PosOk",
"s-nnoo----",
"F-0000----",
"QBffffbbbb",
AP_HAL::micros64(),
(uint8_t)stage,
(double)velocity,
(double)velocity_z,
(double)accel,
(double)ef_accel_z,
throw_detect,
attitude_ok,
height_ok,
pos_ok);
}
}
bool ModeThrow::throw_detected()
{
// Check that we have a valid navigation solution
nav_filter_status filt_status = inertial_nav.get_filter_status();
if (!filt_status.flags.attitude || !filt_status.flags.horiz_pos_abs || !filt_status.flags.vert_pos) {
return false;
}
// Check for high speed (>500 cm/s)
bool high_speed = inertial_nav.get_velocity().length() > THROW_HIGH_SPEED;
// check for upwards or downwards trajectory (airdrop) of 50cm/s
bool changing_height;
if (g2.throw_type == ThrowType::Drop) {
changing_height = inertial_nav.get_velocity().z < -THROW_VERTICAL_SPEED;
} else {
changing_height = inertial_nav.get_velocity().z > THROW_VERTICAL_SPEED;
}
// Check the vertical acceleraton is greater than 0.25g
bool free_falling = ahrs.get_accel_ef().z > -0.25 * GRAVITY_MSS;
// Check if the accel length is < 1.0g indicating that any throw action is complete and the copter has been released
bool no_throw_action = copter.ins.get_accel().length() < 1.0f * GRAVITY_MSS;
// High velocity or free-fall combined with increasing height indicate a possible air-drop or throw release
bool possible_throw_detected = (free_falling || high_speed) && changing_height && no_throw_action;
// Record time and vertical velocity when we detect the possible throw
if (possible_throw_detected && ((AP_HAL::millis() - free_fall_start_ms) > 500)) {
free_fall_start_ms = AP_HAL::millis();
free_fall_start_velz = inertial_nav.get_velocity().z;
}
// Once a possible throw condition has been detected, we check for 2.5 m/s of downwards velocity change in less than 0.5 seconds to confirm
bool throw_condition_confirmed = ((AP_HAL::millis() - free_fall_start_ms < 500) && ((inertial_nav.get_velocity().z - free_fall_start_velz) < -250.0f));
// start motors and enter the control mode if we are in continuous freefall
return throw_condition_confirmed;
}
bool ModeThrow::throw_attitude_good() const
{
// Check that we have uprighted the copter
const Matrix3f &rotMat = ahrs.get_rotation_body_to_ned();
return (rotMat.c.z > 0.866f); // is_upright
}
bool ModeThrow::throw_height_good() const
{
// Check that we are within 0.5m of the demanded height
return (pos_control->get_pos_error_z_cm() < 50.0f);
}
bool ModeThrow::throw_position_good() const
{
// check that our horizontal position error is within 50cm
return (pos_control->get_pos_error_xy_cm() < 50.0f);
}
bool ModeThrow::throttle_is_unlimited() const
{
switch (motors->get_spool_state()) {
case AP_Motors::SpoolState::SHUT_DOWN:
case AP_Motors::SpoolState::GROUND_IDLE:
case AP_Motors::SpoolState::SPOOLING_UP:
return false;
case AP_Motors::SpoolState::THROTTLE_UNLIMITED:
return true;
case AP_Motors::SpoolState::SPOOLING_DOWN:
return false;
}
// compiler ensures we never get here
return true;
}
#endif