AP_Landing_Slope.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. This program is free software: you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation, either version 3 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. /*
  14. * AP_Landing_Slope.cpp - Landing logic handler for ArduPlane for STANDARD_GLIDE_SLOPE
  15. */
  16. #include "AP_Landing.h"
  17. #include <GCS_MAVLink/GCS.h>
  18. #include <AP_HAL/AP_HAL.h>
  19. #include <AP_LandingGear/AP_LandingGear.h>
  20. #include <AP_AHRS/AP_AHRS.h>
  21. #include <AP_GPS/AP_GPS.h>
  22. void AP_Landing::type_slope_do_land(const AP_Mission::Mission_Command& cmd, const float relative_altitude)
  23. {
  24. initial_slope = 0;
  25. slope = 0;
  26. // once landed, post some landing statistics to the GCS
  27. type_slope_flags.post_stats = false;
  28. type_slope_stage = SLOPE_STAGE_NORMAL;
  29. gcs().send_text(MAV_SEVERITY_INFO, "Landing approach start at %.1fm", (double)relative_altitude);
  30. }
  31. void AP_Landing::type_slope_verify_abort_landing(const Location &prev_WP_loc, Location &next_WP_loc, bool &throttle_suppressed)
  32. {
  33. // when aborting a landing, mimic the verify_takeoff with steering hold. Once
  34. // the altitude has been reached, restart the landing sequence
  35. throttle_suppressed = false;
  36. nav_controller->update_heading_hold(prev_WP_loc.get_bearing_to(next_WP_loc));
  37. }
  38. /*
  39. update navigation for landing. Called when on landing approach or
  40. final flare
  41. */
  42. bool AP_Landing::type_slope_verify_land(const Location &prev_WP_loc, Location &next_WP_loc, const Location &current_loc,
  43. const float height, const float sink_rate, const float wp_proportion, const uint32_t last_flying_ms, const bool is_armed, const bool is_flying, const bool rangefinder_state_in_range)
  44. {
  45. // we don't 'verify' landing in the sense that it never completes,
  46. // so we don't verify command completion. Instead we use this to
  47. // adjust final landing parameters
  48. // determine stage
  49. if (type_slope_stage == SLOPE_STAGE_NORMAL) {
  50. const bool heading_lined_up = abs(nav_controller->bearing_error_cd()) < 1000 && !nav_controller->data_is_stale();
  51. const bool on_flight_line = fabsf(nav_controller->crosstrack_error()) < 5.0f && !nav_controller->data_is_stale();
  52. const bool below_prev_WP = current_loc.alt < prev_WP_loc.alt;
  53. if ((mission.get_prev_nav_cmd_id() == MAV_CMD_NAV_LOITER_TO_ALT) ||
  54. (wp_proportion >= 0 && heading_lined_up && on_flight_line) ||
  55. (wp_proportion > 0.15f && heading_lined_up && below_prev_WP) ||
  56. (wp_proportion > 0.5f)) {
  57. type_slope_stage = SLOPE_STAGE_APPROACH;
  58. }
  59. }
  60. /* Set land_complete (which starts the flare) under 3 conditions:
  61. 1) we are within LAND_FLARE_ALT meters of the landing altitude
  62. 2) we are within LAND_FLARE_SEC of the landing point vertically
  63. by the calculated sink rate (if LAND_FLARE_SEC != 0)
  64. 3) we have gone past the landing point and don't have
  65. rangefinder data (to prevent us keeping throttle on
  66. after landing if we've had positive baro drift)
  67. */
  68. // flare check:
  69. // 1) below flare alt/sec requires approach stage check because if sec/alt are set too
  70. // large, and we're on a hard turn to line up for approach, we'll prematurely flare by
  71. // skipping approach phase and the extreme roll limits will make it hard to line up with runway
  72. // 2) passed land point and don't have an accurate AGL
  73. // 3) probably crashed (ensures motor gets turned off)
  74. const bool on_approach_stage = type_slope_is_on_approach();
  75. const bool below_flare_alt = (height <= flare_alt);
  76. const bool below_flare_sec = (flare_sec > 0 && height <= sink_rate * flare_sec);
  77. const bool probably_crashed = (aparm.crash_detection_enable && fabsf(sink_rate) < 0.2f && !is_flying);
  78. if ((on_approach_stage && below_flare_alt) ||
  79. (on_approach_stage && below_flare_sec && (wp_proportion > 0.5)) ||
  80. (!rangefinder_state_in_range && wp_proportion >= 1) ||
  81. probably_crashed) {
  82. const AP_GPS &gps = AP::gps();
  83. if (type_slope_stage != SLOPE_STAGE_FINAL) {
  84. type_slope_flags.post_stats = true;
  85. if (is_flying && (AP_HAL::millis()-last_flying_ms) > 3000) {
  86. gcs().send_text(MAV_SEVERITY_CRITICAL, "Flare crash detected: speed=%.1f", (double)gps.ground_speed());
  87. } else {
  88. gcs().send_text(MAV_SEVERITY_INFO, "Flare %.1fm sink=%.2f speed=%.1f dist=%.1f",
  89. (double)height, (double)sink_rate,
  90. (double)gps.ground_speed(),
  91. (double)current_loc.get_distance(next_WP_loc));
  92. }
  93. type_slope_stage = SLOPE_STAGE_FINAL;
  94. // Check if the landing gear was deployed before landing
  95. // If not - go around
  96. AP_LandingGear *LG_inst = AP_LandingGear::get_singleton();
  97. if (LG_inst != nullptr && !LG_inst->check_before_land()) {
  98. type_slope_request_go_around();
  99. gcs().send_text(MAV_SEVERITY_CRITICAL, "Landing gear was not deployed");
  100. }
  101. }
  102. if (gps.ground_speed() < 3) {
  103. // reload any airspeed or groundspeed parameters that may have
  104. // been set for landing. We don't do this till ground
  105. // speed drops below 3.0 m/s as otherwise we will change
  106. // target speeds too early.
  107. aparm.airspeed_cruise_cm.load();
  108. aparm.min_gndspeed_cm.load();
  109. aparm.throttle_cruise.load();
  110. }
  111. } else if (type_slope_stage == SLOPE_STAGE_APPROACH && pre_flare_airspeed > 0) {
  112. bool reached_pre_flare_alt = pre_flare_alt > 0 && (height <= pre_flare_alt);
  113. bool reached_pre_flare_sec = pre_flare_sec > 0 && (height <= sink_rate * pre_flare_sec);
  114. if (reached_pre_flare_alt || reached_pre_flare_sec) {
  115. type_slope_stage = SLOPE_STAGE_PREFLARE;
  116. }
  117. }
  118. /*
  119. when landing we keep the L1 navigation waypoint 200m ahead. This
  120. prevents sudden turns if we overshoot the landing point
  121. */
  122. struct Location land_WP_loc = next_WP_loc;
  123. int32_t land_bearing_cd = prev_WP_loc.get_bearing_to(next_WP_loc);
  124. land_WP_loc.offset_bearing(land_bearing_cd * 0.01f, prev_WP_loc.get_distance(current_loc) + 200);
  125. nav_controller->update_waypoint(prev_WP_loc, land_WP_loc);
  126. // once landed and stationary, post some statistics
  127. // this is done before disarm_if_autoland_complete() so that it happens on the next loop after the disarm
  128. if (type_slope_flags.post_stats && !is_armed) {
  129. type_slope_flags.post_stats = false;
  130. gcs().send_text(MAV_SEVERITY_INFO, "Distance from LAND point=%.2fm", (double)current_loc.get_distance(next_WP_loc));
  131. }
  132. // check if we should auto-disarm after a confirmed landing
  133. if (type_slope_stage == SLOPE_STAGE_FINAL) {
  134. disarm_if_autoland_complete_fn();
  135. }
  136. /*
  137. we return false as a landing mission item never completes
  138. we stay on this waypoint unless the GCS commands us to change
  139. mission item, reset the mission, command a go-around or finish
  140. a land_abort procedure.
  141. */
  142. return false;
  143. }
  144. void AP_Landing::type_slope_adjust_landing_slope_for_rangefinder_bump(AP_Vehicle::FixedWing::Rangefinder_State &rangefinder_state, Location &prev_WP_loc, Location &next_WP_loc, const Location &current_loc, const float wp_distance, int32_t &target_altitude_offset_cm)
  145. {
  146. // check the rangefinder correction for a large change. When found, recalculate the glide slope. This is done by
  147. // determining the slope from your current location to the land point then following that back up to the approach
  148. // altitude and moving the prev_wp to that location. From there
  149. float correction_delta = fabsf(rangefinder_state.last_stable_correction) - fabsf(rangefinder_state.correction);
  150. if (slope_recalc_shallow_threshold <= 0 ||
  151. fabsf(correction_delta) < slope_recalc_shallow_threshold) {
  152. return;
  153. }
  154. rangefinder_state.last_stable_correction = rangefinder_state.correction;
  155. float corrected_alt_m = (adjusted_altitude_cm_fn() - next_WP_loc.alt)*0.01f - rangefinder_state.correction;
  156. float total_distance_m = prev_WP_loc.get_distance(next_WP_loc);
  157. float top_of_glide_slope_alt_m = total_distance_m * corrected_alt_m / wp_distance;
  158. prev_WP_loc.alt = top_of_glide_slope_alt_m*100 + next_WP_loc.alt;
  159. // re-calculate auto_state.land_slope with updated prev_WP_loc
  160. setup_landing_glide_slope(prev_WP_loc, next_WP_loc, current_loc, target_altitude_offset_cm);
  161. if (rangefinder_state.correction >= 0) { // we're too low or object is below us
  162. // correction positive means we're too low so we should continue on with
  163. // the newly computed shallower slope instead of pitching/throttling up
  164. } else if (slope_recalc_steep_threshold_to_abort > 0 && !type_slope_flags.has_aborted_due_to_slope_recalc) {
  165. // correction negative means we're too high and need to point down (and speed up) to re-align
  166. // to land on target. A large negative correction means we would have to dive down a lot and will
  167. // generating way too much speed that we can not bleed off in time. It is better to remember
  168. // the large baro altitude offset and abort the landing to come around again with the correct altitude
  169. // offset and "perfect" slope.
  170. // calculate projected slope with projected alt
  171. float new_slope_deg = degrees(atanf(slope));
  172. float initial_slope_deg = degrees(atanf(initial_slope));
  173. // is projected slope too steep?
  174. if (new_slope_deg - initial_slope_deg > slope_recalc_steep_threshold_to_abort) {
  175. gcs().send_text(MAV_SEVERITY_INFO, "Landing slope too steep, aborting (%.0fm %.1fdeg)",
  176. (double)rangefinder_state.correction, (double)(new_slope_deg - initial_slope_deg));
  177. alt_offset = rangefinder_state.correction;
  178. flags.commanded_go_around = true;
  179. type_slope_flags.has_aborted_due_to_slope_recalc = true; // only allow this once.
  180. Log();
  181. }
  182. }
  183. }
  184. bool AP_Landing::type_slope_request_go_around(void)
  185. {
  186. flags.commanded_go_around = true;
  187. return true;
  188. }
  189. /*
  190. a special glide slope calculation for the landing approach
  191. During the land approach use a linear glide slope to a point
  192. projected through the landing point. We don't use the landing point
  193. itself as that leads to discontinuities close to the landing point,
  194. which can lead to erratic pitch control
  195. */
  196. void AP_Landing::type_slope_setup_landing_glide_slope(const Location &prev_WP_loc, const Location &next_WP_loc, const Location &current_loc, int32_t &target_altitude_offset_cm)
  197. {
  198. float total_distance = prev_WP_loc.get_distance(next_WP_loc);
  199. // If someone mistakenly puts all 0's in their LAND command then total_distance
  200. // will be calculated as 0 and cause a divide by 0 error below. Lets avoid that.
  201. if (total_distance < 1) {
  202. total_distance = 1;
  203. }
  204. // height we need to sink for this WP
  205. float sink_height = (prev_WP_loc.alt - next_WP_loc.alt)*0.01f;
  206. // current ground speed
  207. float groundspeed = ahrs.groundspeed();
  208. if (groundspeed < 0.5f) {
  209. groundspeed = 0.5f;
  210. }
  211. // calculate time to lose the needed altitude
  212. float sink_time = total_distance / groundspeed;
  213. if (sink_time < 0.5f) {
  214. sink_time = 0.5f;
  215. }
  216. // find the sink rate needed for the target location
  217. float sink_rate = sink_height / sink_time;
  218. // the height we aim for is the one to give us the right flare point
  219. float aim_height = flare_sec * sink_rate;
  220. if (aim_height <= 0) {
  221. aim_height = flare_alt;
  222. }
  223. // don't allow the aim height to be too far above LAND_FLARE_ALT
  224. if (flare_alt > 0 && aim_height > flare_alt*2) {
  225. aim_height = flare_alt*2;
  226. }
  227. // calculate slope to landing point
  228. bool is_first_calc = is_zero(slope);
  229. slope = (sink_height - aim_height) / total_distance;
  230. if (is_first_calc) {
  231. gcs().send_text(MAV_SEVERITY_INFO, "Landing glide slope %.1f degrees", (double)degrees(atanf(slope)));
  232. }
  233. // time before landing that we will flare
  234. float flare_time = aim_height / SpdHgt_Controller->get_land_sinkrate();
  235. // distance to flare is based on ground speed, adjusted as we
  236. // get closer. This takes into account the wind
  237. float flare_distance = groundspeed * flare_time;
  238. // don't allow the flare before half way along the final leg
  239. if (flare_distance > total_distance/2) {
  240. flare_distance = total_distance/2;
  241. }
  242. // project a point 500 meters past the landing point, passing
  243. // through the landing point
  244. const float land_projection = 500;
  245. int32_t land_bearing_cd = prev_WP_loc.get_bearing_to(next_WP_loc);
  246. // now calculate our aim point, which is before the landing
  247. // point and above it
  248. Location loc = next_WP_loc;
  249. loc.offset_bearing(land_bearing_cd * 0.01f, -flare_distance);
  250. loc.alt += aim_height*100;
  251. // calculate point along that slope 500m ahead
  252. loc.offset_bearing(land_bearing_cd * 0.01f, land_projection);
  253. loc.alt -= slope * land_projection * 100;
  254. // setup the offset_cm for set_target_altitude_proportion()
  255. target_altitude_offset_cm = loc.alt - prev_WP_loc.alt;
  256. // calculate the proportion we are to the target
  257. float land_proportion = current_loc.line_path_proportion(prev_WP_loc, loc);
  258. // now setup the glide slope for landing
  259. set_target_altitude_proportion_fn(loc, 1.0f - land_proportion);
  260. // stay within the range of the start and end locations in altitude
  261. constrain_target_altitude_location_fn(loc, prev_WP_loc);
  262. }
  263. int32_t AP_Landing::type_slope_get_target_airspeed_cm(void)
  264. {
  265. // we're landing, check for custom approach and
  266. // pre-flare airspeeds. Also increase for head-winds
  267. const float land_airspeed = SpdHgt_Controller->get_land_airspeed();
  268. int32_t target_airspeed_cm = aparm.airspeed_cruise_cm;
  269. switch (type_slope_stage) {
  270. case SLOPE_STAGE_APPROACH:
  271. if (land_airspeed >= 0) {
  272. target_airspeed_cm = land_airspeed * 100;
  273. }
  274. break;
  275. case SLOPE_STAGE_PREFLARE:
  276. case SLOPE_STAGE_FINAL:
  277. if (pre_flare_airspeed > 0) {
  278. // if we just preflared then continue using the pre-flare airspeed during final flare
  279. target_airspeed_cm = pre_flare_airspeed * 100;
  280. } else if (land_airspeed >= 0) {
  281. target_airspeed_cm = land_airspeed * 100;
  282. }
  283. break;
  284. default:
  285. break;
  286. }
  287. // when landing, add half of head-wind.
  288. const int32_t head_wind_compensation_cm = head_wind() * 0.5f * 100;
  289. // Do not lower it or exceed cruise speed
  290. return constrain_int32(target_airspeed_cm + head_wind_compensation_cm, target_airspeed_cm, aparm.airspeed_cruise_cm);
  291. }
  292. int32_t AP_Landing::type_slope_constrain_roll(const int32_t desired_roll_cd, const int32_t level_roll_limit_cd)
  293. {
  294. if (type_slope_stage == SLOPE_STAGE_FINAL) {
  295. return constrain_int32(desired_roll_cd, level_roll_limit_cd * -1, level_roll_limit_cd);
  296. } else {
  297. return desired_roll_cd;
  298. }
  299. }
  300. bool AP_Landing::type_slope_is_flaring(void) const
  301. {
  302. return (type_slope_stage == SLOPE_STAGE_FINAL);
  303. }
  304. bool AP_Landing::type_slope_is_on_approach(void) const
  305. {
  306. return (type_slope_stage == SLOPE_STAGE_APPROACH ||
  307. type_slope_stage == SLOPE_STAGE_PREFLARE);
  308. }
  309. bool AP_Landing::type_slope_is_expecting_impact(void) const
  310. {
  311. return (type_slope_stage == SLOPE_STAGE_PREFLARE ||
  312. type_slope_stage == SLOPE_STAGE_FINAL);
  313. }
  314. bool AP_Landing::type_slope_is_complete(void) const
  315. {
  316. return (type_slope_stage == SLOPE_STAGE_FINAL);
  317. }
  318. void AP_Landing::type_slope_log(void) const
  319. {
  320. // log to AP_Logger
  321. AP::logger().Write("LAND", "TimeUS,stage,f1,f2,slope,slopeInit,altO", "QBBBfff",
  322. AP_HAL::micros64(),
  323. type_slope_stage,
  324. flags,
  325. type_slope_flags,
  326. (double)slope,
  327. (double)initial_slope,
  328. (double)alt_offset);
  329. }
  330. bool AP_Landing::type_slope_is_throttle_suppressed(void) const
  331. {
  332. return type_slope_stage == SLOPE_STAGE_FINAL;
  333. }