2015-05-29 23:12:49 -03:00
|
|
|
#include "Copter.h"
|
|
|
|
|
2013-11-03 01:26:19 -04:00
|
|
|
// Traditional helicopter variables and functions
|
|
|
|
|
|
|
|
#if FRAME_CONFIG == HELI_FRAME
|
|
|
|
|
|
|
|
#ifndef HELI_DYNAMIC_FLIGHT_SPEED_MIN
|
2017-10-25 01:27:25 -03:00
|
|
|
#define HELI_DYNAMIC_FLIGHT_SPEED_MIN 500 // we are in "dynamic flight" when the speed is over 5m/s for 2 seconds
|
2013-11-03 01:26:19 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// counter to control dynamic flight profile
|
|
|
|
static int8_t heli_dynamic_flight_counter;
|
|
|
|
|
2013-11-10 08:05:58 -04:00
|
|
|
// heli_init - perform any special initialisation required for the tradheli
|
2015-05-29 23:12:49 -03:00
|
|
|
void Copter::heli_init()
|
2013-11-10 08:05:58 -04:00
|
|
|
{
|
2015-10-12 22:05:49 -03:00
|
|
|
// pre-load stab col values as mode is initialized as Stabilize, but stabilize_init() function is not run on start-up.
|
|
|
|
input_manager.set_use_stab_col(true);
|
|
|
|
input_manager.set_stab_col_ramp(1.0);
|
2013-11-09 01:25:06 -04:00
|
|
|
}
|
|
|
|
|
2013-11-03 01:26:19 -04:00
|
|
|
// heli_check_dynamic_flight - updates the dynamic_flight flag based on our horizontal velocity
|
|
|
|
// should be called at 50hz
|
2015-05-29 23:12:49 -03:00
|
|
|
void Copter::check_dynamic_flight(void)
|
2013-11-03 01:26:19 -04:00
|
|
|
{
|
2019-04-09 09:16:58 -03:00
|
|
|
if (motors->get_spool_state() != AP_Motors::SpoolState::THROTTLE_UNLIMITED ||
|
2017-12-11 01:43:27 -04:00
|
|
|
control_mode == LAND || (control_mode==RTL && mode_rtl.state() == RTL_Land) || (control_mode == AUTO && mode_auto.mode() == Auto_Land)) {
|
2013-11-03 01:26:19 -04:00
|
|
|
heli_dynamic_flight_counter = 0;
|
|
|
|
heli_flags.dynamic_flight = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool moving = false;
|
|
|
|
|
|
|
|
// with GPS lock use inertial nav to determine if we are moving
|
2015-01-02 07:43:50 -04:00
|
|
|
if (position_ok()) {
|
2017-10-13 00:42:09 -03:00
|
|
|
// get horizontal speed
|
|
|
|
const float speed = inertial_nav.get_speed_xy();
|
|
|
|
moving = (speed >= HELI_DYNAMIC_FLIGHT_SPEED_MIN);
|
2013-11-03 01:26:19 -04:00
|
|
|
}else{
|
|
|
|
// with no GPS lock base it on throttle and forward lean angle
|
2017-01-09 03:31:26 -04:00
|
|
|
moving = (motors->get_throttle() > 0.8f || ahrs.pitch_sensor < -1500);
|
2013-11-03 01:26:19 -04:00
|
|
|
}
|
|
|
|
|
2017-02-09 07:30:59 -04:00
|
|
|
if (!moving && rangefinder_state.enabled && rangefinder.status_orient(ROTATION_PITCH_270) == RangeFinder::RangeFinder_Good) {
|
2015-09-03 23:06:00 -03:00
|
|
|
// when we are more than 2m from the ground with good
|
|
|
|
// rangefinder lock consider it to be dynamic flight
|
2017-02-09 07:30:59 -04:00
|
|
|
moving = (rangefinder.distance_cm_orient(ROTATION_PITCH_270) > 200);
|
2015-09-03 23:06:00 -03:00
|
|
|
}
|
|
|
|
|
2013-11-03 01:26:19 -04:00
|
|
|
if (moving) {
|
|
|
|
// if moving for 2 seconds, set the dynamic flight flag
|
|
|
|
if (!heli_flags.dynamic_flight) {
|
|
|
|
heli_dynamic_flight_counter++;
|
|
|
|
if (heli_dynamic_flight_counter >= 100) {
|
|
|
|
heli_flags.dynamic_flight = true;
|
|
|
|
heli_dynamic_flight_counter = 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
// if not moving for 2 seconds, clear the dynamic flight flag
|
|
|
|
if (heli_flags.dynamic_flight) {
|
|
|
|
if (heli_dynamic_flight_counter > 0) {
|
|
|
|
heli_dynamic_flight_counter--;
|
|
|
|
}else{
|
|
|
|
heli_flags.dynamic_flight = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-09 18:07:52 -03:00
|
|
|
// update_heli_control_dynamics - pushes several important factors up into AP_MotorsHeli.
|
|
|
|
// should be run between the rate controller and the servo updates.
|
2015-05-29 23:12:49 -03:00
|
|
|
void Copter::update_heli_control_dynamics(void)
|
2014-05-09 18:07:52 -03:00
|
|
|
{
|
|
|
|
// Use Leaky_I if we are not moving fast
|
2017-01-09 03:31:26 -04:00
|
|
|
attitude_control->use_leaky_i(!heli_flags.dynamic_flight);
|
2015-10-23 19:41:01 -03:00
|
|
|
|
2017-01-09 03:31:26 -04:00
|
|
|
if (ap.land_complete || (is_zero(motors->get_desired_rotor_speed()))){
|
2015-10-23 19:41:01 -03:00
|
|
|
// if we are landed or there is no rotor power demanded, decrement slew scalar
|
|
|
|
hover_roll_trim_scalar_slew--;
|
|
|
|
} else {
|
|
|
|
// if we are not landed and motor power is demanded, increment slew scalar
|
|
|
|
hover_roll_trim_scalar_slew++;
|
|
|
|
}
|
2016-06-04 22:21:31 -03:00
|
|
|
hover_roll_trim_scalar_slew = constrain_int16(hover_roll_trim_scalar_slew, 0, scheduler.get_loop_rate_hz());
|
2015-10-23 19:41:01 -03:00
|
|
|
|
|
|
|
// set hover roll trim scalar, will ramp from 0 to 1 over 1 second after we think helicopter has taken off
|
2018-11-16 11:48:42 -04:00
|
|
|
attitude_control->set_hover_roll_trim_scalar((float) hover_roll_trim_scalar_slew/(float) scheduler.get_loop_rate_hz());
|
2014-05-09 18:07:52 -03:00
|
|
|
}
|
|
|
|
|
2013-11-06 08:39:39 -04:00
|
|
|
// heli_update_landing_swash - sets swash plate flag so higher minimum is used when landed or landing
|
|
|
|
// should be called soon after update_land_detector in main code
|
2015-05-29 23:12:49 -03:00
|
|
|
void Copter::heli_update_landing_swash()
|
2013-11-06 08:39:39 -04:00
|
|
|
{
|
2018-12-23 22:18:10 -04:00
|
|
|
switch (control_mode) {
|
2014-02-03 08:06:34 -04:00
|
|
|
case ACRO:
|
|
|
|
case STABILIZE:
|
|
|
|
case DRIFT:
|
|
|
|
case SPORT:
|
2013-11-06 08:39:39 -04:00
|
|
|
// manual modes always uses full swash range
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_collective_for_landing(false);
|
2013-11-06 08:39:39 -04:00
|
|
|
break;
|
|
|
|
|
2014-02-03 08:06:34 -04:00
|
|
|
case LAND:
|
2013-11-06 08:39:39 -04:00
|
|
|
// landing always uses limit swash range
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_collective_for_landing(true);
|
2013-11-06 08:39:39 -04:00
|
|
|
break;
|
|
|
|
|
2014-02-03 08:06:34 -04:00
|
|
|
case RTL:
|
2017-09-08 23:45:31 -03:00
|
|
|
case SMART_RTL:
|
2017-12-11 01:43:27 -04:00
|
|
|
if (mode_rtl.state() == RTL_Land) {
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_collective_for_landing(true);
|
2014-02-03 08:06:34 -04:00
|
|
|
}else{
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_collective_for_landing(!heli_flags.dynamic_flight || ap.land_complete || !ap.auto_armed);
|
2014-02-03 08:06:34 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AUTO:
|
2017-12-11 01:43:27 -04:00
|
|
|
if (mode_auto.mode() == Auto_Land) {
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_collective_for_landing(true);
|
2014-02-03 08:06:34 -04:00
|
|
|
}else{
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_collective_for_landing(!heli_flags.dynamic_flight || ap.land_complete || !ap.auto_armed);
|
2014-02-03 08:06:34 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-11-06 08:39:39 -04:00
|
|
|
default:
|
|
|
|
// auto and hold use limited swash when landed
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_collective_for_landing(!heli_flags.dynamic_flight || ap.land_complete || !ap.auto_armed);
|
2013-11-06 08:39:39 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-08 04:29:54 -04:00
|
|
|
// heli_update_rotor_speed_targets - reads pilot input and passes new rotor speed targets to heli motors object
|
2015-05-29 23:12:49 -03:00
|
|
|
void Copter::heli_update_rotor_speed_targets()
|
2013-11-08 04:29:54 -04:00
|
|
|
{
|
2015-06-16 23:00:48 -03:00
|
|
|
|
|
|
|
static bool rotor_runup_complete_last = false;
|
|
|
|
|
2013-11-08 04:29:54 -04:00
|
|
|
// get rotor control method
|
2017-01-09 03:31:26 -04:00
|
|
|
uint8_t rsc_control_mode = motors->get_rsc_mode();
|
2019-01-26 10:33:32 -04:00
|
|
|
float rsc_control_deglitched = 0.0f;
|
2019-04-03 13:25:47 -03:00
|
|
|
RC_Channel *rc_ptr = rc().find_channel_for_option(RC_Channel::AUX_FUNC::MOTOR_INTERLOCK);
|
2019-01-26 10:33:32 -04:00
|
|
|
if (rc_ptr != nullptr) {
|
|
|
|
rsc_control_deglitched = rotor_speed_deglitch_filter.apply((float)rc_ptr->get_control_in()) * 0.001f;
|
|
|
|
}
|
2015-08-12 19:57:33 -03:00
|
|
|
switch (rsc_control_mode) {
|
2015-08-28 03:23:58 -03:00
|
|
|
case ROTOR_CONTROL_MODE_SPEED_PASSTHROUGH:
|
2015-08-12 19:57:33 -03:00
|
|
|
// pass through pilot desired rotor speed if control input is higher than 10, creating a deadband at the bottom
|
2019-01-26 10:33:32 -04:00
|
|
|
if (motors->get_interlock()) {
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_desired_rotor_speed(rsc_control_deglitched);
|
2015-08-12 19:57:33 -03:00
|
|
|
} else {
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_desired_rotor_speed(0.0f);
|
2015-08-12 19:57:33 -03:00
|
|
|
}
|
|
|
|
break;
|
2015-08-28 03:23:58 -03:00
|
|
|
case ROTOR_CONTROL_MODE_SPEED_SETPOINT:
|
|
|
|
case ROTOR_CONTROL_MODE_OPEN_LOOP_POWER_OUTPUT:
|
|
|
|
case ROTOR_CONTROL_MODE_CLOSED_LOOP_POWER_OUTPUT:
|
2015-08-12 19:57:33 -03:00
|
|
|
// pass setpoint through as desired rotor speed, this is almost pointless as the Setpoint serves no function in this mode
|
|
|
|
// other than being used to create a crude estimate of rotor speed
|
2019-01-26 10:33:32 -04:00
|
|
|
if (motors->get_interlock()) {
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_desired_rotor_speed(motors->get_rsc_setpoint());
|
2015-08-12 19:57:33 -03:00
|
|
|
}else{
|
2017-01-09 03:31:26 -04:00
|
|
|
motors->set_desired_rotor_speed(0.0f);
|
2015-08-12 19:57:33 -03:00
|
|
|
}
|
|
|
|
break;
|
2013-11-08 04:29:54 -04:00
|
|
|
}
|
2015-06-16 23:00:48 -03:00
|
|
|
|
|
|
|
// when rotor_runup_complete changes to true, log event
|
2017-01-09 03:31:26 -04:00
|
|
|
if (!rotor_runup_complete_last && motors->rotor_runup_complete()){
|
2015-06-16 23:00:48 -03:00
|
|
|
Log_Write_Event(DATA_ROTOR_RUNUP_COMPLETE);
|
2017-01-09 03:31:26 -04:00
|
|
|
} else if (rotor_runup_complete_last && !motors->rotor_runup_complete()){
|
2015-06-18 20:39:58 -03:00
|
|
|
Log_Write_Event(DATA_ROTOR_SPEED_BELOW_CRITICAL);
|
2015-06-16 23:00:48 -03:00
|
|
|
}
|
2017-01-09 03:31:26 -04:00
|
|
|
rotor_runup_complete_last = motors->rotor_runup_complete();
|
2013-11-08 04:29:54 -04:00
|
|
|
}
|
|
|
|
|
2014-01-13 04:23:09 -04:00
|
|
|
#endif // FRAME_CONFIG == HELI_FRAME
|