/// calc_desired_velocity - updates desired velocity (i.e. feed forward) with pilot requested acceleration and fake wind resistance /// updated velocity sent directly to position controller void AC_Loiter::calc_desired_velocity(float nav_dt, float ekfGndSpdLimit) { // calculate a loiter speed limit which is the minimum of the value set by the LOITER_SPEED // parameter and the value set by the EKF to observe optical flow limits float gnd_speed_limit_cms = MIN(_speed_cms, ekfGndSpdLimit*100.0f); gnd_speed_limit_cms = MAX(gnd_speed_limit_cms, LOITER_SPEED_MIN); float pilot_acceleration_max = GRAVITY_MSS*100.0f * tanf(radians(get_angle_max_cd()*0.01f)); // range check nav_dt if (nav_dt < 0) { return; } _pos_control.set_speed_xy(gnd_speed_limit_cms); _pos_control.set_accel_xy(_accel_cmss); _pos_control.set_leash_length_xy(LOITER_POS_CORRECTION_MAX); // get loiters desired velocity from the position controller where it is being stored. const Vector3f &desired_vel_3d = _pos_control.get_desired_velocity(); Vector2f desired_vel(desired_vel_3d.x,desired_vel_3d.y); // update the desired velocity using our predicted acceleration desired_vel.x += _predicted_accel.x * nav_dt; desired_vel.y += _predicted_accel.y * nav_dt; Vector2f loiter_accel_brake; float desired_speed = desired_vel.length(); if (!is_zero(desired_speed)) { Vector2f desired_vel_norm = desired_vel/desired_speed; // TODO: consider using a velocity squared relationship like // pilot_acceleration_max*(desired_speed/gnd_speed_limit_cms)^2; // the drag characteristic of a multirotor should be examined to generate a curve // we could add a expo function here to fine tune it // calculate a drag acceleration based on the desired speed. float drag_decel = pilot_acceleration_max*desired_speed/gnd_speed_limit_cms; // calculate a braking acceleration if sticks are at zero float loiter_brake_accel = 0.0f; if (_desired_accel.is_zero()) { if ((AP_HAL::millis()-_brake_timer) > _brake_delay * 1000.0f) { float brake_gain = _pos_control.get_vel_xy_pid().kP() * 0.5f; loiter_brake_accel = constrain_float(AC_AttitudeControl::sqrt_controller(desired_speed, brake_gain, _brake_jerk_max_cmsss, nav_dt), 0.0f, _brake_accel_cmss); } } else { loiter_brake_accel = 0.0f; _brake_timer = AP_HAL::millis(); } _brake_accel += constrain_float(loiter_brake_accel-_brake_accel, -_brake_jerk_max_cmsss*nav_dt, _brake_jerk_max_cmsss*nav_dt); loiter_accel_brake = desired_vel_norm*_brake_accel; // update the desired velocity using the drag and braking accelerations desired_speed = MAX(desired_speed-(drag_decel+_brake_accel)*nav_dt,0.0f); desired_vel = desired_vel_norm*desired_speed; } // add braking to the desired acceleration _desired_accel -= loiter_accel_brake; // Apply EKF limit to desired velocity - this limit is calculated by the EKF and adjusted as required to ensure certain sensor limits are respected (eg optical flow sensing) float horizSpdDem = desired_vel.length(); if (horizSpdDem > gnd_speed_limit_cms) { desired_vel.x = desired_vel.x * gnd_speed_limit_cms / horizSpdDem; desired_vel.y = desired_vel.y * gnd_speed_limit_cms / horizSpdDem; } // Limit the velocity to prevent fence violations // TODO: We need to also limit the _desired_accel if (_avoid != nullptr) { _avoid->adjust_velocity(_pos_control.get_pos_xy_p().kP(), _accel_cmss, desired_vel, nav_dt); } // send adjusted feed forward acceleration and velocity back to the Position Controller _pos_control.set_desired_accel_xy(_desired_accel.x, _desired_accel.y); _pos_control.set_desired_velocity_xy(desired_vel.x, desired_vel.y); }
/// calc_loiter_desired_velocity - updates desired velocity (i.e. feed forward) with pilot requested acceleration and fake wind resistance /// updated velocity sent directly to position controller void AC_WPNav::calc_loiter_desired_velocity(float nav_dt, float ekfGndSpdLimit) { // calculate a loiter speed limit which is the minimum of the value set by the WPNAV_LOITER_SPEED // parameter and the value set by the EKF to observe optical flow limits float gnd_speed_limit_cms = MIN(_loiter_speed_cms,ekfGndSpdLimit*100.0f); gnd_speed_limit_cms = MAX(gnd_speed_limit_cms, 10.0f); // range check nav_dt if( nav_dt < 0 ) { return; } // check loiter speed and avoid divide by zero if(gnd_speed_limit_cms < WPNAV_LOITER_SPEED_MIN) { gnd_speed_limit_cms = WPNAV_LOITER_SPEED_MIN; } _pos_control.set_speed_xy(gnd_speed_limit_cms); _pos_control.set_accel_xy(_loiter_accel_cmss); _pos_control.set_jerk_xy(_loiter_jerk_max_cmsss); // rotate pilot input to lat/lon frame Vector2f desired_accel; desired_accel.x = (_pilot_accel_fwd_cms*_ahrs.cos_yaw() - _pilot_accel_rgt_cms*_ahrs.sin_yaw()); desired_accel.y = (_pilot_accel_fwd_cms*_ahrs.sin_yaw() + _pilot_accel_rgt_cms*_ahrs.cos_yaw()); // calculate the difference Vector2f des_accel_diff = (desired_accel - _loiter_desired_accel); // constrain and scale the desired acceleration float des_accel_change_total = pythagorous2(des_accel_diff.x, des_accel_diff.y); float accel_change_max = _loiter_jerk_max_cmsss * nav_dt; if (_loiter_jerk_max_cmsss > 0.0f && des_accel_change_total > accel_change_max && des_accel_change_total > 0.0f) { des_accel_diff.x = accel_change_max * des_accel_diff.x/des_accel_change_total; des_accel_diff.y = accel_change_max * des_accel_diff.y/des_accel_change_total; } // adjust the desired acceleration _loiter_desired_accel += des_accel_diff; // get pos_control's feed forward velocity const Vector3f &desired_vel_3d = _pos_control.get_desired_velocity(); Vector2f desired_vel(desired_vel_3d.x,desired_vel_3d.y); // add pilot commanded acceleration desired_vel.x += _loiter_desired_accel.x * nav_dt; desired_vel.y += _loiter_desired_accel.y * nav_dt; float desired_speed = desired_vel.length(); if (!is_zero(desired_speed)) { Vector2f desired_vel_norm = desired_vel/desired_speed; float drag_speed_delta = -_loiter_accel_cmss*nav_dt*desired_speed/gnd_speed_limit_cms; if (_pilot_accel_fwd_cms == 0 && _pilot_accel_rgt_cms == 0) { drag_speed_delta = MIN(drag_speed_delta,MAX(-_loiter_accel_min_cmss*nav_dt, -2.0f*desired_speed*nav_dt)); } desired_speed = MAX(desired_speed+drag_speed_delta,0.0f); desired_vel = desired_vel_norm*desired_speed; } // Apply EKF limit to desired velocity - this limit is calculated by the EKF and adjusted as required to ensure certain sensor limits are respected (eg optical flow sensing) float horizSpdDem = sqrtf(sq(desired_vel.x) + sq(desired_vel.y)); if (horizSpdDem > gnd_speed_limit_cms) { desired_vel.x = desired_vel.x * gnd_speed_limit_cms / horizSpdDem; desired_vel.y = desired_vel.y * gnd_speed_limit_cms / horizSpdDem; } // send adjusted feed forward velocity back to position controller _pos_control.set_desired_velocity_xy(desired_vel.x,desired_vel.y); }