diff --git a/ArduCopter/defines.h b/ArduCopter/defines.h index 828cfde8b4..82b1a39ca3 100644 --- a/ArduCopter/defines.h +++ b/ArduCopter/defines.h @@ -145,6 +145,7 @@ enum AutoMode { Auto_Spline, Auto_NavGuided, Auto_Loiter, + Auto_LoiterToAlt, Auto_NavPayloadPlace, }; diff --git a/ArduCopter/mode.h b/ArduCopter/mode.h index e58253e7d9..0159e3bdf0 100644 --- a/ArduCopter/mode.h +++ b/ArduCopter/mode.h @@ -338,6 +338,7 @@ private: void circle_run(); void nav_guided_run(); void loiter_run(); + void loiter_to_alt_run(); Location_Class loc_from_cmd(const AP_Mission::Mission_Command& cmd) const; @@ -358,6 +359,7 @@ private: void do_loiter_unlimited(const AP_Mission::Mission_Command& cmd); void do_circle(const AP_Mission::Mission_Command& cmd); void do_loiter_time(const AP_Mission::Mission_Command& cmd); + void do_loiter_to_alt(const AP_Mission::Mission_Command& cmd); void do_spline_wp(const AP_Mission::Mission_Command& cmd); #if NAV_GUIDED == ENABLED void do_nav_guided_enable(const AP_Mission::Mission_Command& cmd); @@ -385,6 +387,7 @@ private: bool verify_payload_place(); bool verify_loiter_unlimited(); bool verify_loiter_time(); + bool verify_loiter_to_alt(); bool verify_RTL(); bool verify_wait_delay(); bool verify_within_distance(); @@ -403,6 +406,14 @@ private: uint16_t loiter_time_max; // How long we should stay in Loiter Mode for mission scripting (time in seconds) uint32_t loiter_time; // How long have we been loitering - The start time in millis + struct { + bool reached_destination_xy : 1; + bool loiter_start_done : 1; + bool reached_alt : 1; + float alt_error_cm; + int32_t alt; + } loiter_to_alt; + // Delay the next navigation command int32_t nav_delay_time_max; // used for delaying the navigation commands (eg land,takeoff etc.) uint32_t nav_delay_time_start; diff --git a/ArduCopter/mode_auto.cpp b/ArduCopter/mode_auto.cpp index 6b12db78d3..f60501a348 100644 --- a/ArduCopter/mode_auto.cpp +++ b/ArduCopter/mode_auto.cpp @@ -94,6 +94,10 @@ void Copter::ModeAuto::run() loiter_run(); break; + case Auto_LoiterToAlt: + loiter_to_alt_run(); + break; + case Auto_NavPayloadPlace: payload_place_run(); break; @@ -412,6 +416,10 @@ bool Copter::ModeAuto::start_command(const AP_Mission::Mission_Command& cmd) do_loiter_time(cmd); break; + case MAV_CMD_NAV_LOITER_TO_ALT: + do_loiter_to_alt(cmd); + break; + case MAV_CMD_NAV_RETURN_TO_LAUNCH: //20 do_RTL(); break; @@ -633,6 +641,9 @@ bool Copter::ModeAuto::verify_command(const AP_Mission::Mission_Command& cmd) case MAV_CMD_NAV_LOITER_TIME: return verify_loiter_time(); + case MAV_CMD_NAV_LOITER_TO_ALT: + return verify_loiter_to_alt(); + case MAV_CMD_NAV_RETURN_TO_LAUNCH: return verify_RTL(); @@ -858,6 +869,58 @@ void Copter::ModeAuto::loiter_run() attitude_control->input_euler_angle_roll_pitch_euler_rate_yaw(wp_nav->get_roll(), wp_nav->get_pitch(), target_yaw_rate); } +// auto_loiter_run - loiter to altitude in AUTO flight mode +// called by auto_run at 100hz or more +void Copter::ModeAuto::loiter_to_alt_run() +{ + // if not auto armed or motor interlock not enabled set throttle to zero and exit immediately + if (!motors->armed() || !ap.auto_armed || ap.land_complete || !motors->get_interlock()) { + zero_throttle_and_relax_ac(); + return; + } + + // possibly just run the waypoint controller: + if (!loiter_to_alt.reached_destination_xy) { + loiter_to_alt.reached_destination_xy = wp_nav->reached_wp_destination_xy(); + if (!loiter_to_alt.reached_destination_xy) { + wp_run(); + return; + } + } + + if (!loiter_to_alt.loiter_start_done) { + loiter_nav->init_target(); + _mode = Auto_LoiterToAlt; + loiter_to_alt.loiter_start_done = true; + } + const float alt_error_cm = copter.current_loc.alt - loiter_to_alt.alt; + if (fabsf(alt_error_cm) < 5.0) { // random numbers R US + loiter_to_alt.reached_alt = true; + } else if (alt_error_cm * loiter_to_alt.alt_error_cm < 0) { + // we were above and are now below, or vice-versa + loiter_to_alt.reached_alt = true; + } + loiter_to_alt.alt_error_cm = alt_error_cm; + + // loiter... + + land_run_horizontal_control(); + + // Compute a vertical velocity demand such that the vehicle + // approaches the desired altitude. + float target_climb_rate = AC_AttitudeControl::sqrt_controller( + -alt_error_cm, + pos_control->get_pos_z_p().kP(), + pos_control->get_max_accel_z(), + G_Dt); + + // get avoidance adjusted climb rate + target_climb_rate = get_avoidance_adjusted_climbrate(target_climb_rate); + + pos_control->set_alt_target_from_climb_rate_ff(target_climb_rate, G_Dt, false); + pos_control->update_z_controller(); +} + // auto_payload_place_start - initialises controller to implement placement of a load void Copter::ModeAuto::payload_place_start(const Vector3f& destination) { @@ -1112,6 +1175,43 @@ void Copter::ModeAuto::do_loiter_time(const AP_Mission::Mission_Command& cmd) loiter_time_max = cmd.p1; // units are (seconds) } +// do_loiter_alt - initiate loitering at a point until a given altitude is reached +// note: caller should set yaw_mode +void Copter::ModeAuto::do_loiter_to_alt(const AP_Mission::Mission_Command& cmd) +{ + // re-use loiter unlimited + do_loiter_unlimited(cmd); + _mode = Auto_LoiterToAlt; + + // if we aren't navigating to a location then we have to adjust + // altitude for current location + Location_Class target_loc(cmd.content.location); + const Location_Class ¤t_loc = copter.current_loc; + if (target_loc.lat == 0 && target_loc.lng == 0) { + target_loc.lat = current_loc.lat; + target_loc.lng = current_loc.lng; + } + + if (!target_loc.get_alt_cm(Location_Class::ALT_FRAME_ABOVE_HOME, loiter_to_alt.alt)) { + loiter_to_alt.reached_destination_xy = true; + loiter_to_alt.reached_alt = true; + gcs().send_text(MAV_SEVERITY_INFO, "bad do_loiter_to_alt"); + return; + } + loiter_to_alt.reached_destination_xy = false; + loiter_to_alt.loiter_start_done = false; + loiter_to_alt.reached_alt = false; + loiter_to_alt.alt_error_cm = 0; + + pos_control->set_max_accel_z(wp_nav->get_accel_z()); + pos_control->set_max_speed_z(wp_nav->get_speed_down(), + wp_nav->get_speed_up()); + + if (pos_control->is_active_z()) { + pos_control->freeze_ff_z(); + } +} + // do_spline_wp - initiate move to next waypoint void Copter::ModeAuto::do_spline_wp(const AP_Mission::Mission_Command& cmd) { @@ -1585,6 +1685,17 @@ bool Copter::ModeAuto::verify_loiter_time() return (((millis() - loiter_time) / 1000) >= loiter_time_max); } +// verify_loiter_to_alt - check if we have reached both destination +// (roughly) and altitude (precisely) +bool Copter::ModeAuto::verify_loiter_to_alt() +{ + if (loiter_to_alt.reached_destination_xy && + loiter_to_alt.reached_alt) { + return true; + } + return false; +} + // verify_RTL - handles any state changes required to implement RTL // do_RTL should have been called once first to initialise all variables // returns true with RTL has completed successfully