2017-07-18 23:17:45 -03:00
|
|
|
#include "mode.h"
|
|
|
|
#include "Rover.h"
|
|
|
|
|
|
|
|
Mode::Mode() :
|
2017-08-03 05:08:09 -03:00
|
|
|
ahrs(rover.ahrs),
|
2017-07-18 23:17:45 -03:00
|
|
|
g(rover.g),
|
|
|
|
g2(rover.g2),
|
|
|
|
channel_steer(rover.channel_steer),
|
|
|
|
channel_throttle(rover.channel_throttle),
|
2018-05-10 04:10:34 -03:00
|
|
|
channel_lateral(rover.channel_lateral),
|
2017-08-08 02:37:21 -03:00
|
|
|
attitude_control(rover.g2.attitude_control)
|
2017-07-18 23:17:45 -03:00
|
|
|
{ }
|
|
|
|
|
|
|
|
void Mode::exit()
|
|
|
|
{
|
|
|
|
// call sub-classes exit
|
|
|
|
_exit();
|
|
|
|
}
|
|
|
|
|
2018-01-22 07:00:47 -04:00
|
|
|
bool Mode::enter()
|
2017-11-29 02:30:37 -04:00
|
|
|
{
|
2018-01-22 07:00:47 -04:00
|
|
|
const bool ignore_checks = !hal.util->get_soft_armed(); // allow switching to any mode if disarmed. We rely on the arming check to perform
|
|
|
|
if (!ignore_checks) {
|
2017-11-29 02:30:37 -04:00
|
|
|
|
2018-01-22 07:00:47 -04:00
|
|
|
// get EKF filter status
|
|
|
|
nav_filter_status filt_status;
|
|
|
|
rover.ahrs.get_filter_status(filt_status);
|
2017-11-29 02:30:37 -04:00
|
|
|
|
2018-01-22 07:00:47 -04:00
|
|
|
// check position estimate. requires origin and at least one horizontal position flag to be true
|
2018-12-18 06:28:20 -04:00
|
|
|
const bool position_ok = rover.ekf_position_ok() && !rover.failsafe.ekf;
|
2018-01-22 07:00:47 -04:00
|
|
|
if (requires_position() && !position_ok) {
|
2017-11-29 02:30:37 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-22 07:00:47 -04:00
|
|
|
// check velocity estimate (if we have position estimate, we must have velocity estimate)
|
|
|
|
if (requires_velocity() && !position_ok && !filt_status.flags.horiz_vel) {
|
2017-11-29 02:30:37 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-22 02:21:37 -03:00
|
|
|
bool ret = _enter();
|
|
|
|
|
|
|
|
// initialisation common to all modes
|
|
|
|
if (ret) {
|
|
|
|
set_reversed(false);
|
2018-09-14 04:09:07 -03:00
|
|
|
|
|
|
|
// clear sailboat tacking flags
|
|
|
|
rover.sailboat_clear_tack();
|
2018-08-22 02:21:37 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-07-18 23:17:45 -03:00
|
|
|
}
|
|
|
|
|
2018-05-05 22:47:59 -03:00
|
|
|
// decode pilot steering and throttle inputs and return in steer_out and throttle_out arguments
|
2018-05-03 05:57:40 -03:00
|
|
|
// steering_out is in the range -4500 ~ +4500 with positive numbers meaning rotate clockwise
|
|
|
|
// throttle_out is in the range -100 ~ +100
|
2018-05-05 22:47:59 -03:00
|
|
|
void Mode::get_pilot_input(float &steering_out, float &throttle_out)
|
2017-11-27 09:11:45 -04:00
|
|
|
{
|
2017-11-29 03:12:13 -04:00
|
|
|
// no RC input means no throttle and centered steering
|
|
|
|
if (rover.failsafe.bits & FAILSAFE_EVENT_THROTTLE) {
|
|
|
|
steering_out = 0;
|
|
|
|
throttle_out = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-27 09:11:45 -04:00
|
|
|
// apply RC skid steer mixing
|
|
|
|
switch ((enum pilot_steer_type_t)rover.g.pilot_steer_type.get())
|
|
|
|
{
|
|
|
|
case PILOT_STEER_TYPE_DEFAULT:
|
2018-05-03 05:57:40 -03:00
|
|
|
case PILOT_STEER_TYPE_DIR_REVERSED_WHEN_REVERSING:
|
2017-11-27 09:11:45 -04:00
|
|
|
default: {
|
|
|
|
// by default regular and skid-steering vehicles reverse their rotation direction when backing up
|
|
|
|
throttle_out = rover.channel_throttle->get_control_in();
|
2018-05-03 05:57:40 -03:00
|
|
|
const float steering_dir = is_negative(throttle_out) ? -1 : 1;
|
|
|
|
steering_out = steering_dir * rover.channel_steer->get_control_in();
|
2017-11-27 09:11:45 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PILOT_STEER_TYPE_TWO_PADDLES: {
|
|
|
|
// convert the two radio_in values from skid steering values
|
|
|
|
// left paddle from steering input channel, right paddle from throttle input channel
|
|
|
|
// steering = left-paddle - right-paddle
|
|
|
|
// throttle = average(left-paddle, right-paddle)
|
|
|
|
const float left_paddle = rover.channel_steer->norm_input();
|
|
|
|
const float right_paddle = rover.channel_throttle->norm_input();
|
|
|
|
|
|
|
|
throttle_out = 0.5f * (left_paddle + right_paddle) * 100.0f;
|
2018-05-03 05:57:40 -03:00
|
|
|
steering_out = (left_paddle - right_paddle) * 0.5f * 4500.0f;
|
2017-11-27 09:11:45 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PILOT_STEER_TYPE_DIR_UNCHANGED_WHEN_REVERSING: {
|
|
|
|
throttle_out = rover.channel_throttle->get_control_in();
|
2018-05-03 05:57:40 -03:00
|
|
|
steering_out = rover.channel_steer->get_control_in();
|
2017-11-27 09:11:45 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-05 22:47:59 -03:00
|
|
|
// decode pilot steering and throttle inputs and return in steer_out and throttle_out arguments
|
|
|
|
// steering_out is in the range -4500 ~ +4500 with positive numbers meaning rotate clockwise
|
|
|
|
// throttle_out is in the range -100 ~ +100
|
|
|
|
void Mode::get_pilot_desired_steering_and_throttle(float &steering_out, float &throttle_out)
|
|
|
|
{
|
|
|
|
// do basic conversion
|
|
|
|
get_pilot_input(steering_out, throttle_out);
|
|
|
|
|
|
|
|
// check for special case of input and output throttle being in opposite directions
|
|
|
|
float throttle_out_limited = g2.motors.get_slew_limited_throttle(throttle_out, rover.G_Dt);
|
|
|
|
if ((is_negative(throttle_out) != is_negative(throttle_out_limited)) &&
|
|
|
|
((g.pilot_steer_type == PILOT_STEER_TYPE_DEFAULT) ||
|
|
|
|
(g.pilot_steer_type == PILOT_STEER_TYPE_DIR_REVERSED_WHEN_REVERSING))) {
|
|
|
|
steering_out *= -1;
|
|
|
|
}
|
|
|
|
throttle_out = throttle_out_limited;
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode pilot steering and return steering_out and speed_out (in m/s)
|
|
|
|
void Mode::get_pilot_desired_steering_and_speed(float &steering_out, float &speed_out)
|
|
|
|
{
|
|
|
|
float desired_throttle;
|
|
|
|
get_pilot_input(steering_out, desired_throttle);
|
|
|
|
speed_out = desired_throttle * 0.01f * calc_speed_max(g.speed_cruise, g.throttle_cruise * 0.01f);
|
|
|
|
// check for special case of input and output throttle being in opposite directions
|
2018-05-21 22:05:20 -03:00
|
|
|
float speed_out_limited = g2.attitude_control.get_desired_speed_accel_limited(speed_out, rover.G_Dt);
|
2018-05-05 22:47:59 -03:00
|
|
|
if ((is_negative(speed_out) != is_negative(speed_out_limited)) &&
|
|
|
|
((g.pilot_steer_type == PILOT_STEER_TYPE_DEFAULT) ||
|
|
|
|
(g.pilot_steer_type == PILOT_STEER_TYPE_DIR_REVERSED_WHEN_REVERSING))) {
|
|
|
|
steering_out *= -1;
|
|
|
|
}
|
|
|
|
speed_out = speed_out_limited;
|
|
|
|
}
|
|
|
|
|
2018-05-10 04:10:34 -03:00
|
|
|
// decode pilot lateral movement input and return in lateral_out argument
|
|
|
|
void Mode::get_pilot_desired_lateral(float &lateral_out)
|
|
|
|
{
|
|
|
|
// no RC input means no lateral input
|
|
|
|
if (rover.failsafe.bits & FAILSAFE_EVENT_THROTTLE) {
|
|
|
|
lateral_out = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get pilot lateral input
|
|
|
|
lateral_out = rover.channel_lateral->get_control_in();
|
|
|
|
}
|
|
|
|
|
2018-07-02 04:21:37 -03:00
|
|
|
// decode pilot's input and return heading_out (in cd) and speed_out (in m/s)
|
|
|
|
void Mode::get_pilot_desired_heading_and_speed(float &heading_out, float &speed_out)
|
|
|
|
{
|
2018-09-10 01:45:06 -03:00
|
|
|
// get steering and throttle in the -1 to +1 range
|
2018-09-11 02:20:39 -03:00
|
|
|
const float desired_steering = constrain_float(rover.channel_steer->norm_input_dz(), -1.0f, 1.0f);
|
|
|
|
const float desired_throttle = constrain_float(rover.channel_throttle->norm_input_dz(), -1.0f, 1.0f);
|
2018-07-02 04:21:37 -03:00
|
|
|
|
|
|
|
// calculate angle of input stick vector
|
2018-09-10 01:45:06 -03:00
|
|
|
heading_out = wrap_360_cd(atan2f(desired_steering, desired_throttle) * DEGX100);
|
2018-07-02 04:21:37 -03:00
|
|
|
|
2018-09-11 02:20:39 -03:00
|
|
|
// calculate throttle using magnitude of input stick vector
|
|
|
|
const float throttle = MIN(safe_sqrt(sq(desired_throttle) + sq(desired_steering)), 1.0f);
|
2018-07-02 04:21:37 -03:00
|
|
|
speed_out = throttle * calc_speed_max(g.speed_cruise, g.throttle_cruise * 0.01f);
|
|
|
|
}
|
|
|
|
|
2018-12-11 10:04:50 -04:00
|
|
|
// return heading (in degrees) to target destination (aka waypoint)
|
|
|
|
float Mode::wp_bearing() const
|
|
|
|
{
|
|
|
|
if (!is_autopilot_mode()) {
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
return rover.nav_controller->target_bearing_cd() * 0.01f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return short-term target heading in degrees (i.e. target heading back to line between waypoints)
|
|
|
|
float Mode::nav_bearing() const
|
|
|
|
{
|
|
|
|
if (!is_autopilot_mode()) {
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
return rover.nav_controller->nav_bearing_cd() * 0.01f;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return cross track error (i.e. vehicle's distance from the line between waypoints)
|
|
|
|
float Mode::crosstrack_error() const
|
|
|
|
{
|
|
|
|
if (!is_autopilot_mode()) {
|
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
return rover.nav_controller->crosstrack_error();
|
|
|
|
}
|
|
|
|
|
2017-08-03 05:08:09 -03:00
|
|
|
// set desired location
|
2017-08-09 22:30:35 -03:00
|
|
|
void Mode::set_desired_location(const struct Location& destination, float next_leg_bearing_cd)
|
2017-07-18 23:17:45 -03:00
|
|
|
{
|
2018-03-13 09:06:13 -03:00
|
|
|
// set origin to last destination if waypoint controller active
|
|
|
|
if ((AP_HAL::millis() - last_steer_to_wp_ms < 100) && _reached_destination) {
|
|
|
|
_origin = _destination;
|
|
|
|
} else {
|
|
|
|
// otherwise use reasonable stopping point
|
|
|
|
calc_stopping_location(_origin);
|
|
|
|
}
|
2017-08-03 05:08:09 -03:00
|
|
|
_destination = destination;
|
|
|
|
|
|
|
|
// initialise distance
|
2017-08-03 06:41:47 -03:00
|
|
|
_distance_to_destination = get_distance(_origin, _destination);
|
2017-08-03 05:08:09 -03:00
|
|
|
_reached_destination = false;
|
2017-08-09 22:30:35 -03:00
|
|
|
|
|
|
|
// set final desired speed
|
|
|
|
_desired_speed_final = 0.0f;
|
|
|
|
if (!is_equal(next_leg_bearing_cd, MODE_NEXT_HEADING_UNKNOWN)) {
|
2018-03-13 23:58:54 -03:00
|
|
|
const float curr_leg_bearing_cd = get_bearing_cd(_origin, _destination);
|
|
|
|
const float turn_angle_cd = wrap_180_cd(next_leg_bearing_cd - curr_leg_bearing_cd);
|
2019-02-01 01:34:57 -04:00
|
|
|
if (fabsf(turn_angle_cd) < 10.0f) {
|
|
|
|
// if turning less than 0.1 degrees vehicle can continue at full speed
|
|
|
|
// we use 0.1 degrees instead of zero to avoid divide by zero in calcs below
|
2017-08-09 22:30:35 -03:00
|
|
|
_desired_speed_final = _desired_speed;
|
2018-07-10 18:40:51 -03:00
|
|
|
} else if (rover.use_pivot_steering_at_next_WP(turn_angle_cd)) {
|
2018-05-21 08:38:31 -03:00
|
|
|
// pivoting so we will stop
|
|
|
|
_desired_speed_final = 0.0f;
|
2017-08-09 22:30:35 -03:00
|
|
|
} else {
|
|
|
|
// calculate maximum speed that keeps overshoot within bounds
|
|
|
|
const float radius_m = fabsf(g.waypoint_overshoot / (cosf(radians(turn_angle_cd * 0.01f)) - 1.0f));
|
|
|
|
_desired_speed_final = MIN(_desired_speed, safe_sqrt(g.turn_max_g * GRAVITY_MSS * radius_m));
|
|
|
|
}
|
|
|
|
}
|
2017-08-03 05:08:09 -03:00
|
|
|
}
|
2017-07-18 23:17:45 -03:00
|
|
|
|
2017-11-30 00:08:49 -04:00
|
|
|
// set desired location as an offset from the EKF origin in NED frame
|
|
|
|
bool Mode::set_desired_location_NED(const Vector3f& destination, float next_leg_bearing_cd)
|
|
|
|
{
|
|
|
|
Location destination_ned;
|
|
|
|
// initialise destination to ekf origin
|
|
|
|
if (!ahrs.get_origin(destination_ned)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// apply offset
|
|
|
|
location_offset(destination_ned, destination.x, destination.y);
|
|
|
|
set_desired_location(destination_ned, next_leg_bearing_cd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-03 05:08:09 -03:00
|
|
|
// set desired heading and speed
|
|
|
|
void Mode::set_desired_heading_and_speed(float yaw_angle_cd, float target_speed)
|
|
|
|
{
|
|
|
|
// handle initialisation
|
|
|
|
_reached_destination = false;
|
2017-07-18 23:17:45 -03:00
|
|
|
|
2017-08-03 05:08:09 -03:00
|
|
|
// record targets
|
|
|
|
_desired_yaw_cd = yaw_angle_cd;
|
|
|
|
_desired_speed = target_speed;
|
|
|
|
}
|
2017-07-18 23:17:45 -03:00
|
|
|
|
2017-12-05 21:41:28 -04:00
|
|
|
// get default speed for this mode (held in (CRUISE_SPEED, WP_SPEED or RTL_SPEED)
|
|
|
|
float Mode::get_speed_default(bool rtl) const
|
|
|
|
{
|
|
|
|
if (rtl && is_positive(g2.rtl_speed)) {
|
|
|
|
return g2.rtl_speed;
|
|
|
|
} else if (is_positive(g2.wp_speed)) {
|
|
|
|
return g2.wp_speed;
|
|
|
|
} else {
|
|
|
|
return g.speed_cruise;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// restore desired speed to default from parameter values (CRUISE_SPEED or WP_SPEED)
|
|
|
|
void Mode::set_desired_speed_to_default(bool rtl)
|
|
|
|
{
|
|
|
|
_desired_speed = get_speed_default(rtl);
|
|
|
|
}
|
|
|
|
|
2018-06-08 22:19:41 -03:00
|
|
|
// set desired speed in m/s
|
|
|
|
bool Mode::set_desired_speed(float speed)
|
|
|
|
{
|
|
|
|
if (!is_negative(speed)) {
|
|
|
|
_desired_speed = speed;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-08 01:48:38 -03:00
|
|
|
// execute the mission in reverse (i.e. backing up)
|
|
|
|
void Mode::set_reversed(bool value)
|
|
|
|
{
|
2018-08-22 01:46:35 -03:00
|
|
|
_reversed = value;
|
2018-08-08 01:48:38 -03:00
|
|
|
}
|
|
|
|
|
2018-09-14 04:09:07 -03:00
|
|
|
// handle tacking request (from auxiliary switch) in sailboats
|
|
|
|
void Mode::handle_tack_request()
|
|
|
|
{
|
|
|
|
// autopilot modes handle tacking
|
|
|
|
if (is_autopilot_mode()) {
|
|
|
|
rover.sailboat_handle_tack_request_auto();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-21 03:45:12 -03:00
|
|
|
void Mode::calc_throttle(float target_speed, bool nudge_allowed, bool avoidance_enabled)
|
2017-08-03 05:08:09 -03:00
|
|
|
{
|
2017-08-08 21:24:30 -03:00
|
|
|
// add in speed nudging
|
|
|
|
if (nudge_allowed) {
|
2017-08-15 23:32:55 -03:00
|
|
|
target_speed = calc_speed_nudge(target_speed, g.speed_cruise, g.throttle_cruise * 0.01f);
|
2017-08-08 21:24:30 -03:00
|
|
|
}
|
2017-08-03 05:08:09 -03:00
|
|
|
|
2018-04-21 03:45:12 -03:00
|
|
|
// get acceleration limited target speed
|
2018-05-21 22:05:20 -03:00
|
|
|
target_speed = attitude_control.get_desired_speed_accel_limited(target_speed, rover.G_Dt);
|
2018-04-21 03:45:12 -03:00
|
|
|
|
2018-05-22 01:30:32 -03:00
|
|
|
// apply object avoidance to desired speed using half vehicle's maximum deceleration
|
2018-04-21 03:45:12 -03:00
|
|
|
if (avoidance_enabled) {
|
2018-05-22 01:30:32 -03:00
|
|
|
g2.avoid.adjust_speed(0.0f, 0.5f * attitude_control.get_decel_max(), ahrs.yaw, target_speed, rover.G_Dt);
|
2018-04-21 03:45:12 -03:00
|
|
|
}
|
|
|
|
|
2017-08-08 21:24:30 -03:00
|
|
|
// call throttle controller and convert output to -100 to +100 range
|
2018-02-18 21:29:49 -04:00
|
|
|
float throttle_out;
|
|
|
|
|
|
|
|
// call speed or stop controller
|
2018-12-13 03:54:02 -04:00
|
|
|
if (is_zero(target_speed) && !rover.is_balancebot()) {
|
2018-02-18 21:29:49 -04:00
|
|
|
bool stopped;
|
2018-05-21 22:05:20 -03:00
|
|
|
throttle_out = 100.0f * attitude_control.get_throttle_out_stop(g2.motors.limit.throttle_lower, g2.motors.limit.throttle_upper, g.speed_cruise, g.throttle_cruise * 0.01f, rover.G_Dt, stopped);
|
2018-02-18 21:29:49 -04:00
|
|
|
} else {
|
2018-05-21 22:05:20 -03:00
|
|
|
throttle_out = 100.0f * attitude_control.get_throttle_out_speed(target_speed, g2.motors.limit.throttle_lower, g2.motors.limit.throttle_upper, g.speed_cruise, g.throttle_cruise * 0.01f, rover.G_Dt);
|
2018-02-18 21:29:49 -04:00
|
|
|
}
|
2017-07-18 23:17:45 -03:00
|
|
|
|
2018-06-21 09:36:58 -03:00
|
|
|
// if vehicle is balance bot, calculate actual throttle required for balancing
|
|
|
|
if (rover.is_balancebot()) {
|
2018-08-04 04:25:07 -03:00
|
|
|
rover.balancebot_pitch_control(throttle_out);
|
2018-06-21 09:36:58 -03:00
|
|
|
}
|
|
|
|
|
2018-09-25 10:09:47 -03:00
|
|
|
// update mainsail position if present
|
|
|
|
rover.sailboat_update_mainsail(target_speed);
|
|
|
|
|
2017-08-15 22:32:56 -03:00
|
|
|
// send to motor
|
|
|
|
g2.motors.set_throttle(throttle_out);
|
2017-08-08 21:24:30 -03:00
|
|
|
}
|
2017-07-18 23:17:45 -03:00
|
|
|
|
2017-08-10 00:06:43 -03:00
|
|
|
// performs a controlled stop with steering centered
|
|
|
|
bool Mode::stop_vehicle()
|
|
|
|
{
|
|
|
|
// call throttle controller and convert output to -100 to +100 range
|
|
|
|
bool stopped = false;
|
2018-12-13 03:54:02 -04:00
|
|
|
float throttle_out;
|
2017-08-10 00:06:43 -03:00
|
|
|
|
2018-12-13 03:54:02 -04:00
|
|
|
// if vehicle is balance bot, calculate throttle required for balancing
|
2018-07-23 03:20:46 -03:00
|
|
|
if (rover.is_balancebot()) {
|
2018-12-13 03:54:02 -04:00
|
|
|
throttle_out = 100.0f * attitude_control.get_throttle_out_speed(0, g2.motors.limit.throttle_lower, g2.motors.limit.throttle_upper, g.speed_cruise, g.throttle_cruise * 0.01f, rover.G_Dt);
|
2018-08-04 04:25:07 -03:00
|
|
|
rover.balancebot_pitch_control(throttle_out);
|
2018-12-13 03:54:02 -04:00
|
|
|
} else {
|
|
|
|
throttle_out = 100.0f * attitude_control.get_throttle_out_stop(g2.motors.limit.throttle_lower, g2.motors.limit.throttle_upper, g.speed_cruise, g.throttle_cruise * 0.01f, rover.G_Dt, stopped);
|
2018-07-23 03:20:46 -03:00
|
|
|
}
|
|
|
|
|
2018-09-25 10:09:47 -03:00
|
|
|
// relax mainsail if present
|
|
|
|
g2.motors.set_mainsail(100.0f);
|
|
|
|
|
2017-08-15 22:32:56 -03:00
|
|
|
// send to motor
|
|
|
|
g2.motors.set_throttle(throttle_out);
|
2017-08-10 00:06:43 -03:00
|
|
|
|
|
|
|
// do not attempt to steer
|
|
|
|
g2.motors.set_steering(0.0f);
|
|
|
|
|
|
|
|
// return true once stopped
|
|
|
|
return stopped;
|
|
|
|
}
|
|
|
|
|
2017-08-08 21:24:30 -03:00
|
|
|
// estimate maximum vehicle speed (in m/s)
|
2017-12-31 23:46:46 -04:00
|
|
|
// cruise_speed is in m/s, cruise_throttle should be in the range -1 to +1
|
|
|
|
float Mode::calc_speed_max(float cruise_speed, float cruise_throttle) const
|
2017-08-08 21:24:30 -03:00
|
|
|
{
|
|
|
|
float speed_max;
|
2017-07-18 23:17:45 -03:00
|
|
|
|
2017-08-08 21:24:30 -03:00
|
|
|
// sanity checks
|
|
|
|
if (cruise_throttle > 1.0f || cruise_throttle < 0.05f) {
|
|
|
|
speed_max = cruise_speed;
|
2017-07-18 23:17:45 -03:00
|
|
|
} else {
|
2017-08-08 21:24:30 -03:00
|
|
|
// project vehicle's maximum speed
|
|
|
|
speed_max = (1.0f / cruise_throttle) * cruise_speed;
|
2017-07-18 23:17:45 -03:00
|
|
|
}
|
|
|
|
|
2017-08-08 21:24:30 -03:00
|
|
|
// constrain to 30m/s (108km/h) and return
|
|
|
|
return constrain_float(speed_max, 0.0f, 30.0f);
|
2017-07-18 23:17:45 -03:00
|
|
|
}
|
|
|
|
|
2017-08-08 21:24:30 -03:00
|
|
|
// calculate pilot input to nudge speed up or down
|
|
|
|
// target_speed should be in meters/sec
|
|
|
|
// cruise_speed is vehicle's cruising speed, cruise_throttle is the throttle (from -1 to +1) that achieves the cruising speed
|
|
|
|
// return value is a new speed (in m/s) which up to the projected maximum speed based on the cruise speed and cruise throttle
|
|
|
|
float Mode::calc_speed_nudge(float target_speed, float cruise_speed, float cruise_throttle)
|
2017-07-18 04:27:05 -03:00
|
|
|
{
|
2018-11-13 22:47:34 -04:00
|
|
|
// return immediately during RC/GCS failsafe
|
|
|
|
if (rover.failsafe.bits & FAILSAFE_EVENT_THROTTLE) {
|
|
|
|
return target_speed;
|
|
|
|
}
|
|
|
|
|
2017-08-08 21:24:30 -03:00
|
|
|
// return immediately if pilot is not attempting to nudge speed
|
|
|
|
// pilot can nudge up speed if throttle (in range -100 to +100) is above 50% of center in direction of travel
|
|
|
|
const int16_t pilot_throttle = constrain_int16(rover.channel_throttle->get_control_in(), -100, 100);
|
2017-10-25 05:29:44 -03:00
|
|
|
if (((pilot_throttle <= 50) && (target_speed >= 0.0f)) ||
|
|
|
|
((pilot_throttle >= -50) && (target_speed <= 0.0f))) {
|
2017-08-08 21:24:30 -03:00
|
|
|
return target_speed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sanity checks
|
|
|
|
if (cruise_throttle > 1.0f || cruise_throttle < 0.05f) {
|
|
|
|
return target_speed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// project vehicle's maximum speed
|
|
|
|
const float vehicle_speed_max = calc_speed_max(cruise_speed, cruise_throttle);
|
|
|
|
|
|
|
|
// return unadjusted target if already over vehicle's projected maximum speed
|
2018-01-18 20:23:33 -04:00
|
|
|
if (fabsf(target_speed) >= vehicle_speed_max) {
|
2017-08-08 21:24:30 -03:00
|
|
|
return target_speed;
|
|
|
|
}
|
|
|
|
|
|
|
|
const float speed_increase_max = vehicle_speed_max - fabsf(target_speed);
|
2017-08-16 05:35:36 -03:00
|
|
|
float speed_nudge = ((static_cast<float>(abs(pilot_throttle)) - 50.0f) * 0.02f) * speed_increase_max;
|
2017-08-08 21:24:30 -03:00
|
|
|
if (pilot_throttle < 0) {
|
|
|
|
speed_nudge = -speed_nudge;
|
2017-07-18 04:27:05 -03:00
|
|
|
}
|
|
|
|
|
2017-08-08 21:24:30 -03:00
|
|
|
return target_speed + speed_nudge;
|
2017-07-18 04:27:05 -03:00
|
|
|
}
|
|
|
|
|
2017-08-03 05:08:09 -03:00
|
|
|
// calculated a reduced speed(in m/s) based on yaw error and lateral acceleration and/or distance to a waypoint
|
|
|
|
// should be called after calc_lateral_acceleration and before calc_throttle
|
2018-06-02 23:44:37 -03:00
|
|
|
// relies on these internal members being updated: _yaw_error_cd, _distance_to_destination
|
2017-08-03 05:08:09 -03:00
|
|
|
float Mode::calc_reduced_speed_for_turn_or_distance(float desired_speed)
|
|
|
|
{
|
2018-06-02 23:44:37 -03:00
|
|
|
// reduce speed to zero during pivot turns
|
|
|
|
if (rover.use_pivot_steering(_yaw_error_cd)) {
|
|
|
|
return 0.0f;
|
|
|
|
}
|
2017-08-03 05:08:09 -03:00
|
|
|
|
2018-06-02 23:44:37 -03:00
|
|
|
// reduce speed to limit overshoot from line between origin and destination
|
|
|
|
// calculate number of degrees vehicle must turn to face waypoint
|
|
|
|
const float heading_cd = is_negative(desired_speed) ? wrap_180_cd(ahrs.yaw_sensor + 18000) : ahrs.yaw_sensor;
|
|
|
|
const float wp_yaw_diff = wrap_180_cd(rover.nav_controller->target_bearing_cd() - heading_cd);
|
|
|
|
const float turn_angle_rad = fabsf(radians(wp_yaw_diff * 0.01f));
|
|
|
|
|
|
|
|
// calculate distance from vehicle to line + wp_overshoot
|
|
|
|
const float line_yaw_diff = wrap_180_cd(get_bearing_cd(_origin, _destination) - heading_cd);
|
2018-12-11 10:04:50 -04:00
|
|
|
const float xtrack_error = rover.nav_controller->crosstrack_error();
|
|
|
|
const float dist_from_line = fabsf(xtrack_error);
|
|
|
|
const bool heading_away = is_positive(line_yaw_diff) == is_positive(xtrack_error);
|
2018-06-02 23:44:37 -03:00
|
|
|
const float wp_overshoot_adj = heading_away ? -dist_from_line : dist_from_line;
|
|
|
|
|
|
|
|
// calculate radius of circle that touches vehicle's current position and heading and target position and heading
|
|
|
|
float radius_m = 999.0f;
|
|
|
|
float radius_calc_denom = fabsf(1.0f - cosf(turn_angle_rad));
|
|
|
|
if (!is_zero(radius_calc_denom)) {
|
|
|
|
radius_m = MAX(0.0f, rover.g.waypoint_overshoot + wp_overshoot_adj) / radius_calc_denom;
|
2017-08-03 05:08:09 -03:00
|
|
|
}
|
|
|
|
|
2018-06-02 23:44:37 -03:00
|
|
|
// calculate and limit speed to allow vehicle to stay on circle
|
|
|
|
float overshoot_speed_max = safe_sqrt(g.turn_max_g * GRAVITY_MSS * MAX(g2.turn_radius, radius_m));
|
|
|
|
float speed_max = constrain_float(desired_speed, -overshoot_speed_max, overshoot_speed_max);
|
2017-08-09 22:30:35 -03:00
|
|
|
|
|
|
|
// limit speed based on distance to waypoint and max acceleration/deceleration
|
2018-06-02 23:44:37 -03:00
|
|
|
if (is_positive(_distance_to_destination) && is_positive(attitude_control.get_decel_max())) {
|
|
|
|
const float dist_speed_max = safe_sqrt(2.0f * _distance_to_destination * attitude_control.get_decel_max() + sq(_desired_speed_final));
|
|
|
|
speed_max = constrain_float(speed_max, -dist_speed_max, dist_speed_max);
|
2017-08-03 05:08:09 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
// return minimum speed
|
2018-06-02 23:44:37 -03:00
|
|
|
return speed_max;
|
2017-08-03 05:08:09 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
// calculate the lateral acceleration target to cause the vehicle to drive along the path from origin to destination
|
2017-12-23 02:17:49 -04:00
|
|
|
// this function updates the _yaw_error_cd value
|
2017-11-27 07:57:59 -04:00
|
|
|
void Mode::calc_steering_to_waypoint(const struct Location &origin, const struct Location &destination, bool reversed)
|
2017-07-18 23:17:45 -03:00
|
|
|
{
|
2018-03-13 09:06:13 -03:00
|
|
|
// record system time of call
|
|
|
|
last_steer_to_wp_ms = AP_HAL::millis();
|
|
|
|
|
2017-07-18 23:17:45 -03:00
|
|
|
// Calculate the required turn of the wheels
|
|
|
|
// negative error = left turn
|
|
|
|
// positive error = right turn
|
2017-08-03 03:19:57 -03:00
|
|
|
rover.nav_controller->set_reverse(reversed);
|
2018-03-14 23:30:41 -03:00
|
|
|
rover.nav_controller->update_waypoint(origin, destination, g.waypoint_radius);
|
2017-12-23 02:17:49 -04:00
|
|
|
float desired_lat_accel = rover.nav_controller->lateral_acceleration();
|
2017-12-23 02:29:51 -04:00
|
|
|
float desired_heading = rover.nav_controller->target_bearing_cd();
|
2017-08-03 03:19:57 -03:00
|
|
|
if (reversed) {
|
2018-05-06 01:41:37 -03:00
|
|
|
desired_heading = wrap_360_cd(desired_heading + 18000);
|
|
|
|
desired_lat_accel *= -1.0f;
|
2017-08-03 03:19:57 -03:00
|
|
|
}
|
2018-05-06 01:41:37 -03:00
|
|
|
_yaw_error_cd = wrap_180_cd(desired_heading - ahrs.yaw_sensor);
|
2017-12-23 02:29:51 -04:00
|
|
|
|
2018-09-14 04:09:07 -03:00
|
|
|
if (rover.sailboat_use_indirect_route(desired_heading)) {
|
|
|
|
// sailboats use heading controller when tacking upwind
|
|
|
|
desired_heading = rover.sailboat_calc_heading(desired_heading);
|
|
|
|
calc_steering_to_heading(desired_heading, g2.pivot_turn_rate);
|
|
|
|
} else if (rover.use_pivot_steering(_yaw_error_cd)) {
|
2017-12-23 02:29:51 -04:00
|
|
|
// for pivot turns use heading controller
|
2018-05-18 05:38:06 -03:00
|
|
|
calc_steering_to_heading(desired_heading, g2.pivot_turn_rate);
|
2017-12-23 02:29:51 -04:00
|
|
|
} else {
|
|
|
|
// call lateral acceleration to steering controller
|
|
|
|
calc_steering_from_lateral_acceleration(desired_lat_accel, reversed);
|
2017-07-18 23:17:45 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-11-27 07:57:59 -04:00
|
|
|
calculate steering output given lateral_acceleration
|
2017-07-18 23:17:45 -03:00
|
|
|
*/
|
2017-12-23 02:17:49 -04:00
|
|
|
void Mode::calc_steering_from_lateral_acceleration(float lat_accel, bool reversed)
|
2017-07-18 23:17:45 -03:00
|
|
|
{
|
2017-08-03 05:08:09 -03:00
|
|
|
// add obstacle avoidance response to lateral acceleration target
|
2018-05-06 01:41:37 -03:00
|
|
|
// ToDo: replace this type of object avoidance with path planning
|
2017-08-03 03:19:57 -03:00
|
|
|
if (!reversed) {
|
2017-12-23 02:17:49 -04:00
|
|
|
lat_accel += (rover.obstacle.turn_angle / 45.0f) * g.turn_max_g;
|
2017-07-18 23:17:45 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
// constrain to max G force
|
2017-12-23 02:17:49 -04:00
|
|
|
lat_accel = constrain_float(lat_accel, -g.turn_max_g * GRAVITY_MSS, g.turn_max_g * GRAVITY_MSS);
|
2017-07-18 23:17:45 -03:00
|
|
|
|
|
|
|
// send final steering command to motor library
|
2018-04-09 09:12:07 -03:00
|
|
|
const float steering_out = attitude_control.get_steering_out_lat_accel(lat_accel,
|
|
|
|
g2.motors.limit.steer_left,
|
2018-05-21 22:05:20 -03:00
|
|
|
g2.motors.limit.steer_right,
|
|
|
|
rover.G_Dt);
|
2017-08-08 02:37:21 -03:00
|
|
|
g2.motors.set_steering(steering_out * 4500.0f);
|
2017-07-18 23:17:45 -03:00
|
|
|
}
|
2017-12-23 01:54:35 -04:00
|
|
|
|
|
|
|
// calculate steering output to drive towards desired heading
|
2018-09-10 04:25:27 -03:00
|
|
|
// rate_max is a maximum turn rate in deg/s. set to zero to use default turn rate limits
|
|
|
|
void Mode::calc_steering_to_heading(float desired_heading_cd, float rate_max_degs)
|
2017-12-23 01:54:35 -04:00
|
|
|
{
|
2018-07-07 04:04:54 -03:00
|
|
|
// calculate yaw error so it can be used for reporting and slowing the vehicle
|
|
|
|
_yaw_error_cd = wrap_180_cd(desired_heading_cd - ahrs.yaw_sensor);
|
|
|
|
|
|
|
|
// call heading controller
|
2018-04-09 09:12:07 -03:00
|
|
|
const float steering_out = attitude_control.get_steering_out_heading(radians(desired_heading_cd*0.01f),
|
2018-09-10 04:25:27 -03:00
|
|
|
radians(rate_max_degs),
|
2018-04-09 09:12:07 -03:00
|
|
|
g2.motors.limit.steer_left,
|
2018-05-21 22:05:20 -03:00
|
|
|
g2.motors.limit.steer_right,
|
|
|
|
rover.G_Dt);
|
2017-12-23 01:54:35 -04:00
|
|
|
g2.motors.set_steering(steering_out * 4500.0f);
|
|
|
|
}
|
2018-03-13 09:06:13 -03:00
|
|
|
|
|
|
|
// calculate vehicle stopping point using current location, velocity and maximum acceleration
|
|
|
|
void Mode::calc_stopping_location(Location& stopping_loc)
|
|
|
|
{
|
|
|
|
// default stopping location
|
|
|
|
stopping_loc = rover.current_loc;
|
|
|
|
|
|
|
|
// get current velocity vector and speed
|
|
|
|
const Vector2f velocity = ahrs.groundspeed_vector();
|
|
|
|
const float speed = velocity.length();
|
|
|
|
|
|
|
|
// avoid divide by zero
|
|
|
|
if (!is_positive(speed)) {
|
|
|
|
stopping_loc = rover.current_loc;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get stopping distance in meters
|
|
|
|
const float stopping_dist = attitude_control.get_stopping_distance(speed);
|
|
|
|
|
|
|
|
// calculate stopping position from current location in meters
|
|
|
|
const Vector2f stopping_offset = velocity.normalized() * stopping_dist;
|
|
|
|
|
|
|
|
location_offset(stopping_loc, stopping_offset.x, stopping_offset.y);
|
|
|
|
}
|
2019-01-07 13:22:03 -04:00
|
|
|
|
|
|
|
Mode *Rover::mode_from_mode_num(const enum Mode::Number num)
|
|
|
|
{
|
|
|
|
Mode *ret = nullptr;
|
|
|
|
switch (num) {
|
|
|
|
case Mode::Number::MANUAL:
|
|
|
|
ret = &mode_manual;
|
|
|
|
break;
|
|
|
|
case Mode::Number::ACRO:
|
|
|
|
ret = &mode_acro;
|
|
|
|
break;
|
|
|
|
case Mode::Number::STEERING:
|
|
|
|
ret = &mode_steering;
|
|
|
|
break;
|
|
|
|
case Mode::Number::HOLD:
|
|
|
|
ret = &mode_hold;
|
|
|
|
break;
|
|
|
|
case Mode::Number::LOITER:
|
|
|
|
ret = &mode_loiter;
|
|
|
|
break;
|
|
|
|
case Mode::Number::FOLLOW:
|
|
|
|
ret = &mode_follow;
|
|
|
|
break;
|
|
|
|
case Mode::Number::SIMPLE:
|
|
|
|
ret = &mode_simple;
|
|
|
|
break;
|
|
|
|
case Mode::Number::AUTO:
|
|
|
|
ret = &mode_auto;
|
|
|
|
break;
|
|
|
|
case Mode::Number::RTL:
|
|
|
|
ret = &mode_rtl;
|
|
|
|
break;
|
|
|
|
case Mode::Number::SMART_RTL:
|
|
|
|
ret = &mode_smartrtl;
|
|
|
|
break;
|
|
|
|
case Mode::Number::GUIDED:
|
|
|
|
ret = &mode_guided;
|
|
|
|
break;
|
|
|
|
case Mode::Number::INITIALISING:
|
|
|
|
ret = &mode_initializing;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|