LCOV - code coverage report
Current view: top level - home/pbarker/rc/ardupilot/ArduCopter - takeoff.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 111 126 88.1 %
Date: 2026-09-09 11:02:08 Functions: 8 9 88.9 %

          Line data    Source code
       1             : #include "Copter.h"
       2             : 
       3             : Mode::_TakeOff Mode::takeoff;
       4             : _AutoTakeoff Mode::auto_takeoff;
       5             : 
       6             : // This file contains the high-level takeoff logic for Loiter, PosHold, AltHold, Sport modes.
       7             : //   The take-off can be initiated from a GCS NAV_TAKEOFF command which includes a takeoff altitude
       8             : //   A safe takeoff speed is calculated and used to calculate a time_ms
       9             : //   the pos_control target is then slowly increased until time_ms expires
      10             : 
      11           0 : bool Mode::do_user_takeoff_start_m(float takeoff_alt_m)
      12             : {
      13           0 :     copter.flightmode->takeoff.start_m(takeoff_alt_m);
      14           0 :     return true;
      15             : }
      16             : 
      17             : // initiate user takeoff - called when MAVLink TAKEOFF command is received
      18         147 : bool Mode::do_user_takeoff_U_m(float takeoff_alt_m, bool must_navigate)
      19             : {
      20         147 :     if (!copter.motors->armed()) {
      21           2 :         return false;
      22             :     }
      23         145 :     if (!copter.ap.land_complete) {
      24             :         // can't takeoff again!
      25           0 :         return false;
      26             :     }
      27         145 :     if (!has_user_takeoff(must_navigate)) {
      28             :         // this mode doesn't support user takeoff
      29           0 :         return false;
      30             :     }
      31         145 :     if (takeoff_alt_m <= copter.current_loc.alt * 0.01) {
      32             :         // can't takeoff downwards...
      33           0 :         return false;
      34             :     }
      35             : 
      36             :     // Vehicles using motor interlock should return false if motor interlock is disabled.
      37             :     // Interlock must be enabled to allow the controller to spool up the motor(s) for takeoff.
      38         145 :     if (!motors->get_interlock() && copter.ap.using_interlock) {
      39           0 :         return false;
      40             :     }
      41             : 
      42         145 :     if (!do_user_takeoff_start_m(takeoff_alt_m)) {
      43           0 :         return false;
      44             :     }
      45             : 
      46         145 :     copter.set_auto_armed(true);
      47         145 :     return true;
      48             : }
      49             : 
      50             : // start takeoff to specified altitude above home in meters
      51         165 : void Mode::_TakeOff::start_m(float alt_m)
      52             : {
      53             :     // initialise takeoff state
      54         165 :     _running = true;
      55         165 :     take_off_start_alt_m = copter.pos_control->get_pos_estimate_U_m();
      56         165 :     take_off_complete_alt_m  = take_off_start_alt_m + alt_m;
      57         165 : }
      58             : 
      59             : // stop takeoff
      60        1432 : void Mode::_TakeOff::stop()
      61             : {
      62        1432 :     _running = false;
      63             :     // Check if we have progressed far enough through the takeoff process that the
      64             :     // aircraft may have left the ground but not yet detected the climb.
      65        1432 :     if (copter.attitude_control->get_throttle_in() > copter.get_non_takeoff_throttle()) {
      66         755 :         copter.set_land_complete(false);
      67             :     }
      68        1432 : }
      69             : 
      70             : // do_pilot_takeoff_ms - controls the vertical position controller during the process of taking off
      71             : //  take off is complete when the vertical target reaches the take off altitude.
      72             : //  climb is cancelled if pilot_climb_rate_ms becomes negative
      73             : //  sets take off to complete when target altitude is within 1% of the take off altitude
      74       71751 : void Mode::_TakeOff::do_pilot_takeoff_ms(float pilot_climb_rate_ms)
      75             : {
      76             :     // return pilot_climb_rate if take-off inactive
      77       71751 :     if (!_running) {
      78           0 :         return;
      79             :     }
      80             : 
      81       71751 :     if (copter.ap.land_complete) {
      82             :         // send throttle to attitude controller with angle boost
      83       65824 :         float throttle_norm = constrain_float(copter.attitude_control->get_throttle_in() + copter.G_Dt / copter.g2.takeoff_throttle_slew_time, 0.0, 1.0);
      84       65824 :         copter.attitude_control->set_throttle_out(throttle_norm, true, 0.0);
      85             :         // tell position controller to reset alt target and reset I terms
      86       65824 :         copter.pos_control->D_init_controller();
      87       65824 :         if (throttle_norm >= MIN(copter.g2.takeoff_throttle_max, 0.9) || 
      88      131481 :             (copter.pos_control->get_estimated_accel_U_mss() >= 0.5 * copter.pos_control->D_get_max_accel_mss()) ||
      89      198206 :             (copter.pos_control->get_vel_estimate_U_ms() >= constrain_float(pilot_climb_rate_ms, copter.pos_control->get_max_speed_up_ms() * 0.1, copter.pos_control->get_max_speed_up_ms() * 0.5)) || 
      90       66560 :             (is_positive(take_off_complete_alt_m - take_off_start_alt_m) && copter.pos_control->get_pos_estimate_U_m() - take_off_start_alt_m > 0.5 * (take_off_complete_alt_m - take_off_start_alt_m))) {
      91             :             // throttle > 90%
      92             :             // acceleration > 50% maximum acceleration
      93             :             // velocity > 10% maximum velocity && commanded climb rate
      94             :             // velocity > 50% maximum velocity
      95             :             // altitude change greater than half complete alt from start off alt
      96         165 :             copter.set_land_complete(false);
      97             :         }
      98             :     } else {
      99        5927 :         float pos_d_m = -take_off_complete_alt_m;
     100        5927 :         float vel_d_ms = -pilot_climb_rate_ms;
     101             : 
     102             :         // command the aircraft to the take off altitude and current pilot climb rate
     103        5927 :         copter.pos_control->input_pos_vel_accel_D_m(pos_d_m, vel_d_ms, 0.0);
     104             : 
     105             :         // stop take off early and return if negative climb rate is commanded or we are within 0.1% of our take off altitude
     106       11854 :         if (is_negative(pilot_climb_rate_ms) ||
     107        5927 :             (take_off_complete_alt_m  - take_off_start_alt_m) * 0.999f < copter.pos_control->get_pos_desired_U_m() - take_off_start_alt_m) {
     108         165 :             stop();
     109             :         }
     110             :     }
     111             : }
     112             : 
     113             : // auto_takeoff_run - controls the vertical position controller during the process of taking off in auto modes
     114             : // auto_takeoff_complete set to true when target altitude is within 10% of the take off altitude and less than 50% max climb rate
     115     3150731 : void _AutoTakeoff::run()
     116             : {
     117     3150731 :     const auto &g2 = copter.g2;
     118     3150731 :     const auto &wp_nav = copter.wp_nav;
     119     3150731 :     auto *motors = copter.motors;
     120     3150731 :     auto *pos_control = copter.pos_control;
     121     3150731 :     auto *attitude_control = copter.attitude_control;
     122             : 
     123             :     // if not armed set throttle to zero and exit immediately
     124     3150731 :     if (!motors->armed() || !copter.ap.auto_armed) {
     125             :         // do not spool down tradheli when on the ground with motor interlock enabled
     126      244601 :         copter.flightmode->make_safe_ground_handling(copter.is_tradheli() && motors->get_interlock());
     127             :         // update auto_takeoff_no_nav_alt_m
     128      244601 :         no_nav_alt_m = pos_control->get_pos_estimate_U_m() + g2.wp_navalt_min_m;
     129      485617 :         return;
     130             :     }
     131             : 
     132             :     // get terrain offset
     133     2906130 :     float terrain_u_m = 0.0f;
     134     2906130 :     if (is_terrain_alt && !wp_nav->get_terrain_U_m(terrain_u_m)) {
     135             :         // trigger terrain failsafe
     136           0 :         copter.failsafe_terrain_on_event();
     137           0 :         return;
     138             :     }
     139             : 
     140             :     // set motors to full range
     141     2906130 :     motors->set_desired_spool_state(AP_Motors::DesiredSpoolState::THROTTLE_UNLIMITED);
     142             : 
     143             :     // aircraft stays in landed state until rotor speed run up has finished
     144     2906130 :     if (motors->get_spool_state() != AP_Motors::SpoolState::THROTTLE_UNLIMITED) {
     145             :         // motors have not completed spool up yet so relax navigation and position controllers
     146      170331 :         pos_control->NE_relax_velocity_controller();
     147      170331 :         pos_control->NE_update_controller();
     148      170331 :         pos_control->D_relax_controller(0.0f);   // forces throttle output to decay to zero
     149      170331 :         pos_control->D_update_controller();
     150      170331 :         attitude_control->reset_yaw_target_and_rate();
     151      170331 :         attitude_control->reset_rate_controller_I_terms();
     152      170331 :         attitude_control->input_thrust_vector_rate_heading_rads(pos_control->get_thrust_vector(), 0.0);
     153             :         // update auto_takeoff_no_nav_alt_m
     154      170331 :         no_nav_alt_m = pos_control->get_pos_estimate_U_m() + g2.wp_navalt_min_m;
     155      170331 :         return;
     156             :     }
     157             :     
     158             :     // aircraft stays in landed state until vertical movement is detected or 90% throttle is reached
     159     2735799 :     if (copter.ap.land_complete) {
     160             :         // send throttle to attitude controller with angle boost
     161       70685 :         float throttle = constrain_float(copter.attitude_control->get_throttle_in() + copter.G_Dt / copter.g2.takeoff_throttle_slew_time, 0.0, 1.0);
     162       70685 :         copter.attitude_control->set_throttle_out(throttle, true, 0.0);
     163             :         // tell position controller to reset alt target and reset I terms
     164       70685 :         copter.pos_control->D_init_controller();
     165       70685 :         pos_control->NE_relax_velocity_controller();
     166       70685 :         pos_control->NE_update_controller();
     167       70685 :         attitude_control->reset_rate_controller_I_terms();
     168       70685 :         attitude_control->input_thrust_vector_rate_heading_rads(pos_control->get_thrust_vector(), 0.0);
     169       70685 :         if (throttle >= MIN(copter.g2.takeoff_throttle_max, 0.9) || 
     170       70685 :             (copter.pos_control->get_estimated_accel_U_mss() >= 0.5 * copter.pos_control->D_get_max_accel_mss()) ||
     171      211836 :             (copter.pos_control->get_vel_estimate_U_ms() >= 0.1 * copter.pos_control->get_max_speed_up_ms()) || 
     172       70466 :             ( no_nav_active && (pos_control->get_pos_estimate_U_m() >= no_nav_alt_m))) {
     173             :             // throttle > 90%
     174             :             // acceleration > 50% maximum acceleration
     175             :             // velocity > 10% maximum velocity
     176             :             // altitude change greater than half auto_takeoff_no_nav_alt_cm
     177         219 :             copter.set_land_complete(false);
     178             :         }
     179       70685 :         return;
     180             :     }
     181             : 
     182             :     // check if we are not navigating because of low altitude
     183     2665114 :     if (no_nav_active) {
     184             :         // check if vehicle has reached no_nav_alt threshold
     185           0 :         if (pos_control->get_pos_estimate_U_m() >= no_nav_alt_m) {
     186           0 :             no_nav_active = false;
     187             :         }
     188           0 :         pos_control->NE_relax_velocity_controller();
     189             :     } else {
     190     2665114 :         Vector2f vel_ne_zero;
     191     2665114 :         Vector2f accel_ne_zero;
     192     2665114 :         pos_control->input_vel_accel_NE_m(vel_ne_zero, accel_ne_zero);
     193             :     }
     194     2665114 :     pos_control->NE_update_controller();
     195             : 
     196             :     // command the aircraft to the take off altitude
     197     2665114 :     float pos_d_m = -(complete_alt_m + terrain_u_m);
     198     2665114 :     float vel_d_zero = 0.0;
     199     2665114 :     copter.pos_control->input_pos_vel_accel_D_m(pos_d_m, vel_d_zero, 0.0);
     200             :     
     201             :     // run the vertical position controller and set output throttle
     202     2665114 :     pos_control->D_update_controller();
     203             : 
     204             :     // call attitude controller with auto yaw
     205     2665114 :     attitude_control->input_thrust_vector_heading(pos_control->get_thrust_vector(), copter.flightmode->auto_yaw.get_heading());
     206             : 
     207             :     // takeoff complete when we are less than 1% of the stopping distance from the target altitude
     208             :     // and 10% our maximum climb rate
     209     2665114 :     const float vel_threshold_fraction = 0.1;
     210             :     // stopping distance from vel_threshold_fraction * max velocity
     211     2665114 :     const float stop_distance_m = 0.5 * sq(vel_threshold_fraction * copter.pos_control->get_max_speed_up_ms()) / copter.pos_control->D_get_max_accel_mss();
     212     2665114 :     bool reached_altitude = copter.pos_control->get_pos_desired_U_m() >= -pos_d_m - stop_distance_m;
     213     2665114 :     bool reached_climb_rate = copter.pos_control->get_vel_desired_U_ms() < copter.pos_control->get_max_speed_up_ms() * vel_threshold_fraction;
     214     2665114 :     complete = reached_altitude && reached_climb_rate;
     215             : 
     216             :     // calculate completion for location in case it is needed for a smooth transition to wp_nav
     217     2665114 :     if (complete) {
     218     1564115 :         no_nav_active = false;
     219     1564115 :         const Vector3p& _complete_pos_ned_m = copter.pos_control->get_pos_desired_NED_m();
     220     1564115 :         complete_pos_ned_m = Vector3p{_complete_pos_ned_m.x, _complete_pos_ned_m.y, pos_d_m};
     221             :     }
     222             : }
     223             : 
     224         238 : void _AutoTakeoff::start_m(float _complete_alt_m, bool _is_terrain_alt)
     225             : {
     226             :     // auto_takeoff_complete_alt_m is a problem if equal to auto_takeoff_start_alt_m
     227         238 :     complete_alt_m = _complete_alt_m;
     228         238 :     is_terrain_alt = _is_terrain_alt;
     229         238 :     complete = false;
     230             :     // initialise auto_takeoff_no_nav_alt_m
     231         238 :     const auto &g2 = copter.g2;
     232         238 :     no_nav_alt_m = copter.pos_control->get_pos_estimate_U_m() + g2.wp_navalt_min_m;
     233         238 :     if ((g2.wp_navalt_min_m > 0) && (copter.flightmode->is_disarmed_or_landed() || !copter.motors->get_interlock())) {
     234             :         // we are not flying, climb with no navigation to current alt-above-ekf-origin + wp_navalt_min_m
     235           0 :         no_nav_active = true;
     236             :     } else {
     237         238 :         no_nav_active = false;
     238             :     }
     239         238 : }
     240             : 
     241             : // return takeoff final target position in m from the EKF origin if takeoff has completed successfully
     242          58 : bool _AutoTakeoff::get_completion_pos_ned_m(Vector3p& pos_ned_m)
     243             : {
     244             :     // only provide location if takeoff has completed
     245          58 :     if (!complete) {
     246           1 :         return false;
     247             :     }
     248             : 
     249          57 :     pos_ned_m = complete_pos_ned_m;
     250          57 :     return true;
     251             : }
     252             : 
     253     2996931 : bool Mode::is_taking_off() const
     254             : {
     255     2996931 :     if (!has_user_takeoff(false)) {
     256     1866449 :         return false;
     257             :     }
     258     1130482 :     return takeoff.running();
     259             : }

Generated by: LCOV version 1.14