2013-03-20 03:27:26 -03:00
/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
# include <AP_HAL.h>
# include <AC_WPNav.h>
extern const AP_HAL : : HAL & hal ;
const AP_Param : : GroupInfo AC_WPNav : : var_info [ ] PROGMEM = {
// index 0 was used for the old orientation matrix
// @Param: SPEED
2013-04-17 23:17:41 -03:00
// @DisplayName: Waypoint Horizontal Speed Target
// @Description: Defines the speed in cm/s which the aircraft will attempt to maintain horizontally during a WP mission
2013-06-15 23:39:54 -03:00
// @Units: cm/s
2013-05-07 05:11:24 -03:00
// @Range: 0 2000
2013-04-17 23:17:41 -03:00
// @Increment: 50
2013-04-14 01:24:14 -03:00
// @User: Standard
2013-05-07 05:11:24 -03:00
AP_GROUPINFO ( " SPEED " , 0 , AC_WPNav , _wp_speed_cms , WPNAV_WP_SPEED ) ,
2013-04-08 23:50:12 -03:00
// @Param: RADIUS
// @DisplayName: Waypoint Radius
// @Description: Defines the distance from a waypoint, that when crossed indicates the wp has been hit.
2013-06-15 23:39:54 -03:00
// @Units: cm
2013-04-08 23:50:12 -03:00
// @Range: 100 1000
// @Increment: 1
// @User: Standard
2013-05-07 05:11:24 -03:00
AP_GROUPINFO ( " RADIUS " , 1 , AC_WPNav , _wp_radius_cm , WPNAV_WP_RADIUS ) ,
2013-03-20 03:27:26 -03:00
2013-04-18 02:51:01 -03:00
// @Param: SPEED_UP
// @DisplayName: Waypoint Climb Speed Target
// @Description: Defines the speed in cm/s which the aircraft will attempt to maintain while climbing during a WP mission
2013-06-15 23:39:54 -03:00
// @Units: cm/s
2013-04-17 23:17:41 -03:00
// @Range: 0 1000
// @Increment: 50
// @User: Standard
2013-05-07 05:11:24 -03:00
AP_GROUPINFO ( " SPEED_UP " , 2 , AC_WPNav , _wp_speed_up_cms , WPNAV_WP_SPEED_UP ) ,
2013-04-17 23:17:41 -03:00
2013-04-18 02:51:01 -03:00
// @Param: SPEED_DN
// @DisplayName: Waypoint Descent Speed Target
// @Description: Defines the speed in cm/s which the aircraft will attempt to maintain while descending during a WP mission
2013-06-15 23:39:54 -03:00
// @Units: cm/s
2013-04-18 02:51:01 -03:00
// @Range: 0 1000
// @Increment: 50
// @User: Standard
2013-05-07 05:11:24 -03:00
AP_GROUPINFO ( " SPEED_DN " , 3 , AC_WPNav , _wp_speed_down_cms , WPNAV_WP_SPEED_DOWN ) ,
// @Param: LOIT_SPEED
// @DisplayName: Loiter Horizontal Maximum Speed
// @Description: Defines the maximum speed in cm/s which the aircraft will travel horizontally while in loiter mode
2013-06-15 23:39:54 -03:00
// @Units: cm/s
2013-05-07 05:11:24 -03:00
// @Range: 0 2000
// @Increment: 50
// @User: Standard
AP_GROUPINFO ( " LOIT_SPEED " , 4 , AC_WPNav , _loiter_speed_cms , WPNAV_LOITER_SPEED ) ,
2013-06-15 23:39:54 -03:00
// @Param: ACCEL
// @DisplayName: Waypoint Acceleration
// @Description: Defines the horizontal acceleration in cm/s/s used during missions
// @Units: cm/s/s
// @Range: 0 980
// @Increment: 10
// @User: Standard
AP_GROUPINFO ( " ACCEL " , 5 , AC_WPNav , _wp_accel_cms , WPNAV_ACCELERATION ) ,
2013-03-20 03:27:26 -03:00
AP_GROUPEND
} ;
// Default constructor.
// Note that the Vector/Matrix constructors already implicitly zero
// their values.
//
2013-07-24 11:52:39 -03:00
AC_WPNav : : AC_WPNav ( const AP_InertialNav * inav , const AP_AHRS * ahrs , APM_PI * pid_pos_lat , APM_PI * pid_pos_lon , AC_PID * pid_rate_lat , AC_PID * pid_rate_lon ) :
2013-03-20 03:27:26 -03:00
_inav ( inav ) ,
2013-05-09 06:32:02 -03:00
_ahrs ( ahrs ) ,
2013-03-20 03:27:26 -03:00
_pid_pos_lat ( pid_pos_lat ) ,
_pid_pos_lon ( pid_pos_lon ) ,
2013-03-21 06:28:10 -03:00
_pid_rate_lat ( pid_rate_lat ) ,
2013-04-01 03:45:23 -03:00
_pid_rate_lon ( pid_rate_lon ) ,
2013-05-03 04:58:00 -03:00
_loiter_last_update ( 0 ) ,
_wpnav_last_update ( 0 ) ,
_cos_yaw ( 1.0 ) ,
_sin_yaw ( 0.0 ) ,
_cos_pitch ( 1.0 ) ,
2013-05-31 09:03:27 -03:00
_althold_kP ( WPNAV_ALT_HOLD_P ) ,
2013-05-03 04:58:00 -03:00
_desired_roll ( 0 ) ,
_desired_pitch ( 0 ) ,
_target ( 0 , 0 , 0 ) ,
_pilot_vel_forward_cms ( 0 ) ,
_pilot_vel_right_cms ( 0 ) ,
_target_vel ( 0 , 0 , 0 ) ,
_vel_last ( 0 , 0 , 0 ) ,
2013-05-14 23:51:26 -03:00
_loiter_leash ( WPNAV_MIN_LEASH_LENGTH ) ,
2013-06-15 23:39:54 -03:00
_loiter_accel_cms ( WPNAV_LOITER_ACCEL_MAX ) ,
2013-08-11 00:51:08 -03:00
_lean_angle_max_cd ( MAX_LEAN_ANGLE ) ,
2013-05-14 23:51:26 -03:00
_wp_leash_xy ( WPNAV_MIN_LEASH_LENGTH ) ,
2013-05-31 09:03:27 -03:00
_wp_leash_z ( WPNAV_MIN_LEASH_LENGTH ) ,
_track_accel ( 0 ) ,
_track_speed ( 0 ) ,
_track_leash_length ( 0 ) ,
2013-05-03 04:58:00 -03:00
dist_error ( 0 , 0 ) ,
desired_vel ( 0 , 0 ) ,
desired_accel ( 0 , 0 )
2013-03-20 03:27:26 -03:00
{
AP_Param : : setup_object_defaults ( this , var_info ) ;
2013-05-07 05:11:24 -03:00
2013-12-12 03:23:37 -04:00
// initialise leash lengths
calculate_wp_leash_length ( true ) ;
2013-05-07 05:11:24 -03:00
calculate_loiter_leash_length ( ) ;
2013-03-20 03:27:26 -03:00
}
///
/// simple loiter controller
///
2013-05-11 04:05:42 -03:00
/// get_stopping_point - returns vector to stopping point based on a horizontal position and velocity
void AC_WPNav : : get_stopping_point ( const Vector3f & position , const Vector3f & velocity , Vector3f & target ) const
2013-03-27 02:10:02 -03:00
{
float linear_distance ; // half the distace we swap between linear and sqrt and the distace we offset sqrt.
float linear_velocity ; // the velocity we swap between linear and sqrt.
float vel_total ;
float target_dist ;
2013-05-14 23:51:26 -03:00
float kP = _pid_pos_lat - > kP ( ) ;
2013-03-27 02:10:02 -03:00
2013-05-14 23:51:26 -03:00
// calculate current velocity
vel_total = safe_sqrt ( velocity . x * velocity . x + velocity . y * velocity . y ) ;
2013-06-15 23:39:54 -03:00
// avoid divide by zero by using current position if the velocity is below 10cm/s, kP is very low or acceleration is zero
if ( vel_total < 10.0f | | kP < = 0.0f | | _wp_accel_cms < = 0.0f ) {
2013-04-21 07:54:31 -03:00
target = position ;
return ;
2013-03-27 02:10:02 -03:00
}
// calculate point at which velocity switches from linear to sqrt
2013-06-15 23:39:54 -03:00
linear_velocity = _wp_accel_cms / kP ;
2013-03-27 02:10:02 -03:00
// calculate distance within which we can stop
if ( vel_total < linear_velocity ) {
2013-05-14 23:51:26 -03:00
target_dist = vel_total / kP ;
2013-03-27 02:10:02 -03:00
} else {
2013-06-15 23:39:54 -03:00
linear_distance = _wp_accel_cms / ( 2.0f * kP * kP ) ;
target_dist = linear_distance + ( vel_total * vel_total ) / ( 2.0f * _wp_accel_cms ) ;
2013-03-27 02:10:02 -03:00
}
2013-12-13 08:59:23 -04:00
target_dist = constrain_float ( target_dist , 0 , _wp_leash_xy ) ;
2013-03-27 02:10:02 -03:00
2013-04-14 06:27:39 -03:00
target . x = position . x + ( target_dist * velocity . x / vel_total ) ;
target . y = position . y + ( target_dist * velocity . y / vel_total ) ;
target . z = position . z ;
}
2013-05-25 02:07:04 -03:00
/// set_loiter_target in cm from home
void AC_WPNav : : set_loiter_target ( const Vector3f & position )
{
_target = position ;
_target_vel . x = 0 ;
_target_vel . y = 0 ;
}
2013-06-15 06:14:36 -03:00
/// init_loiter_target - set initial loiter target based on current position and velocity
void AC_WPNav : : init_loiter_target ( const Vector3f & position , const Vector3f & velocity )
2013-04-14 06:27:39 -03:00
{
2013-05-24 11:45:03 -03:00
// set target position and velocity based on current pos and velocity
_target . x = position . x ;
_target . y = position . y ;
_target_vel . x = velocity . x ;
_target_vel . y = velocity . y ;
2013-05-09 06:32:02 -03:00
// initialise desired roll and pitch to current roll and pitch. This avoids a random twitch between now and when the loiter controller is first run
2013-08-11 00:51:08 -03:00
_desired_roll = constrain_int32 ( _ahrs - > roll_sensor , - _lean_angle_max_cd , _lean_angle_max_cd ) ;
_desired_pitch = constrain_int32 ( _ahrs - > pitch_sensor , - _lean_angle_max_cd , _lean_angle_max_cd ) ;
2013-06-15 06:14:36 -03:00
// initialise pilot input
_pilot_vel_forward_cms = 0 ;
_pilot_vel_right_cms = 0 ;
// set last velocity to current velocity
2013-06-15 23:39:54 -03:00
// To-Do: remove the line below by instead forcing reset_I to be called on the first loiter_update call
2013-06-15 06:14:36 -03:00
_vel_last = _inav - > get_velocity ( ) ;
2013-03-27 02:10:02 -03:00
}
2013-03-20 03:27:26 -03:00
/// move_loiter_target - move loiter target by velocity provided in front/right directions in cm/s
2013-04-02 11:22:09 -03:00
void AC_WPNav : : move_loiter_target ( float control_roll , float control_pitch , float dt )
2013-03-20 03:27:26 -03:00
{
2013-04-02 11:22:09 -03:00
// convert pilot input to desired velocity in cm/s
2013-07-10 07:44:24 -03:00
_pilot_vel_forward_cms = - control_pitch * _loiter_accel_cms / 4500.0f ;
_pilot_vel_right_cms = control_roll * _loiter_accel_cms / 4500.0f ;
2013-04-01 09:53:40 -03:00
}
/// translate_loiter_target_movements - consumes adjustments created by move_loiter_target
void AC_WPNav : : translate_loiter_target_movements ( float nav_dt )
{
2013-04-17 23:17:41 -03:00
Vector2f target_vel_adj ;
2013-04-01 09:53:40 -03:00
float vel_total ;
// range check nav_dt
if ( nav_dt < 0 ) {
return ;
}
2013-05-24 11:45:03 -03:00
// check loiter speed and avoid divide by zero
if ( _loiter_speed_cms < 100.0f ) {
_loiter_speed_cms = 100.0f ;
2013-04-01 09:53:40 -03:00
}
2013-03-20 03:27:26 -03:00
2013-05-24 11:45:03 -03:00
// rotate pilot input to lat/lon frame
target_vel_adj . x = ( _pilot_vel_forward_cms * _cos_yaw - _pilot_vel_right_cms * _sin_yaw ) ;
target_vel_adj . y = ( _pilot_vel_forward_cms * _sin_yaw + _pilot_vel_right_cms * _cos_yaw ) ;
// add desired change in velocity to current target velocit
_target_vel . x + = target_vel_adj . x * nav_dt ;
_target_vel . y + = target_vel_adj . y * nav_dt ;
if ( _target_vel . x > 0 ) {
2013-07-10 07:44:24 -03:00
_target_vel . x - = ( _loiter_accel_cms - WPNAV_LOITER_ACCEL_MIN ) * nav_dt * _target_vel . x / _loiter_speed_cms ;
2013-05-24 11:45:03 -03:00
_target_vel . x = max ( _target_vel . x - WPNAV_LOITER_ACCEL_MIN * nav_dt , 0 ) ;
} else if ( _target_vel . x < 0 ) {
2013-07-10 07:44:24 -03:00
_target_vel . x - = ( _loiter_accel_cms - WPNAV_LOITER_ACCEL_MIN ) * nav_dt * _target_vel . x / _loiter_speed_cms ;
2013-05-24 11:45:03 -03:00
_target_vel . x = min ( _target_vel . x + WPNAV_LOITER_ACCEL_MIN * nav_dt , 0 ) ;
}
if ( _target_vel . y > 0 ) {
2013-07-10 07:44:24 -03:00
_target_vel . y - = ( _loiter_accel_cms - WPNAV_LOITER_ACCEL_MIN ) * nav_dt * _target_vel . y / _loiter_speed_cms ;
2013-05-24 11:45:03 -03:00
_target_vel . y = max ( _target_vel . y - WPNAV_LOITER_ACCEL_MIN * nav_dt , 0 ) ;
} else if ( _target_vel . y < 0 ) {
2013-07-10 07:44:24 -03:00
_target_vel . y - = ( _loiter_accel_cms - WPNAV_LOITER_ACCEL_MIN ) * nav_dt * _target_vel . y / _loiter_speed_cms ;
2013-05-24 11:45:03 -03:00
_target_vel . y = min ( _target_vel . y + WPNAV_LOITER_ACCEL_MIN * nav_dt , 0 ) ;
}
2013-03-20 03:27:26 -03:00
// constrain the velocity vector and scale if necessary
2013-04-01 09:53:40 -03:00
vel_total = safe_sqrt ( _target_vel . x * _target_vel . x + _target_vel . y * _target_vel . y ) ;
2013-05-14 23:51:26 -03:00
if ( vel_total > _loiter_speed_cms & & vel_total > 0.0f ) {
2013-05-07 05:11:24 -03:00
_target_vel . x = _loiter_speed_cms * _target_vel . x / vel_total ;
_target_vel . y = _loiter_speed_cms * _target_vel . y / vel_total ;
2013-03-20 03:27:26 -03:00
}
// update target position
2013-04-01 09:53:40 -03:00
_target . x + = _target_vel . x * nav_dt ;
_target . y + = _target_vel . y * nav_dt ;
2013-04-02 11:22:09 -03:00
// constrain target position to within reasonable distance of current location
Vector3f curr_pos = _inav - > get_position ( ) ;
Vector3f distance_err = _target - curr_pos ;
float distance = safe_sqrt ( distance_err . x * distance_err . x + distance_err . y * distance_err . y ) ;
2013-05-14 23:51:26 -03:00
if ( distance > _loiter_leash & & distance > 0.0f ) {
2013-05-07 05:11:24 -03:00
_target . x = curr_pos . x + _loiter_leash * distance_err . x / distance ;
_target . y = curr_pos . y + _loiter_leash * distance_err . y / distance ;
2013-04-02 11:22:09 -03:00
}
2013-03-20 03:27:26 -03:00
}
2013-03-20 10:28:05 -03:00
/// get_distance_to_target - get horizontal distance to loiter target in cm
2013-04-21 07:54:31 -03:00
float AC_WPNav : : get_distance_to_target ( ) const
2013-03-20 10:28:05 -03:00
{
return _distance_to_target ;
}
/// get_bearing_to_target - get bearing to loiter target in centi-degrees
2013-04-21 07:54:31 -03:00
int32_t AC_WPNav : : get_bearing_to_target ( ) const
2013-03-20 10:28:05 -03:00
{
return get_bearing_cd ( _inav - > get_position ( ) , _target ) ;
}
/// update_loiter - run the loiter controller - should be called at 10hz
void AC_WPNav : : update_loiter ( )
{
2013-08-27 23:33:10 -03:00
// calculate dt
2013-03-20 10:28:05 -03:00
uint32_t now = hal . scheduler - > millis ( ) ;
2013-04-14 06:27:39 -03:00
float dt = ( now - _loiter_last_update ) / 1000.0f ;
2013-03-20 10:28:05 -03:00
// catch if we've just been started
2013-12-10 19:21:41 -04:00
if ( dt > = 1.0f ) {
dt = 0.0f ;
2013-03-20 10:28:05 -03:00
reset_I ( ) ;
2013-08-27 23:33:10 -03:00
_loiter_step = 0 ;
2013-03-20 10:28:05 -03:00
}
2013-08-27 23:33:10 -03:00
// reset step back to 0 if 0.1 seconds has passed and we completed the last full cycle
if ( dt > 0.095f & & _loiter_step > 3 ) {
_loiter_step = 0 ;
}
2013-04-06 10:25:58 -03:00
2013-08-27 23:33:10 -03:00
// run loiter steps
switch ( _loiter_step ) {
case 0 :
// capture time since last iteration
_loiter_dt = dt ;
_loiter_last_update = now ;
// translate any adjustments from pilot to loiter target
translate_loiter_target_movements ( _loiter_dt ) ;
_loiter_step + + ;
break ;
case 1 :
// run loiter's position to velocity step
get_loiter_position_to_velocity ( _loiter_dt , WPNAV_LOITER_SPEED_MAX_TO_CORRECT_ERROR ) ;
_loiter_step + + ;
break ;
case 2 :
// run loiter's velocity to acceleration step
get_loiter_velocity_to_acceleration ( desired_vel . x , desired_vel . y , _loiter_dt ) ;
_loiter_step + + ;
break ;
case 3 :
// run loiter's acceleration to lean angle step
get_loiter_acceleration_to_lean_angles ( desired_accel . x , desired_accel . y ) ;
_loiter_step + + ;
break ;
}
2013-05-07 05:11:24 -03:00
}
/// calculate_loiter_leash_length - calculates the maximum distance in cm that the target position may be from the current location
void AC_WPNav : : calculate_loiter_leash_length ( )
{
// get loiter position P
float kP = _pid_pos_lat - > kP ( ) ;
2013-07-10 05:33:53 -03:00
// check loiter speed
if ( _loiter_speed_cms < 100.0f ) {
_loiter_speed_cms = 100.0f ;
}
// set loiter acceleration to 1/2 loiter speed
_loiter_accel_cms = _loiter_speed_cms / 2.0f ;
2013-05-14 23:51:26 -03:00
// avoid divide by zero
2013-06-15 23:39:54 -03:00
if ( kP < = 0.0f | | _wp_accel_cms < = 0.0f ) {
2013-05-14 23:51:26 -03:00
_loiter_leash = WPNAV_MIN_LEASH_LENGTH ;
return ;
}
2013-07-10 05:33:53 -03:00
// calculate horizontal leash length
if ( WPNAV_LOITER_SPEED_MAX_TO_CORRECT_ERROR < = _wp_accel_cms / kP ) {
2013-05-07 05:11:24 -03:00
// linear leash length based on speed close in
2013-07-10 05:33:53 -03:00
_loiter_leash = WPNAV_LOITER_SPEED_MAX_TO_CORRECT_ERROR / kP ;
2013-05-07 05:11:24 -03:00
} else {
// leash length grows at sqrt of speed further out
2013-07-10 05:33:53 -03:00
_loiter_leash = ( _wp_accel_cms / ( 2.0f * kP * kP ) ) + ( WPNAV_LOITER_SPEED_MAX_TO_CORRECT_ERROR * WPNAV_LOITER_SPEED_MAX_TO_CORRECT_ERROR / ( 2.0f * _wp_accel_cms ) ) ;
2013-05-07 05:11:24 -03:00
}
// ensure leash is at least 1m long
2013-05-14 23:51:26 -03:00
if ( _loiter_leash < WPNAV_MIN_LEASH_LENGTH ) {
_loiter_leash = WPNAV_MIN_LEASH_LENGTH ;
2013-05-07 05:11:24 -03:00
}
2013-03-20 10:28:05 -03:00
}
2013-03-20 03:27:26 -03:00
///
/// waypoint navigation
///
/// set_destination - set destination using cm from home
void AC_WPNav : : set_destination ( const Vector3f & destination )
{
2013-04-14 06:27:39 -03:00
// if waypoint controlls is active and copter has reached the previous waypoint use it for the origin
2013-05-08 12:18:02 -03:00
if ( _flags . reached_destination & & ( ( hal . scheduler - > millis ( ) - _wpnav_last_update ) < 1000 ) ) {
2013-04-21 07:54:31 -03:00
_origin = _destination ;
2013-04-14 06:27:39 -03:00
} else {
// otherwise calculate origin from the current position and velocity
2013-05-11 04:05:42 -03:00
get_stopping_point ( _inav - > get_position ( ) , _inav - > get_velocity ( ) , _origin ) ;
2013-04-14 06:27:39 -03:00
}
// set origin and destination
2013-04-21 07:54:31 -03:00
set_origin_and_destination ( _origin , destination ) ;
2013-03-20 03:27:26 -03:00
}
/// set_origin_and_destination - set origin and destination using lat/lon coordinates
void AC_WPNav : : set_origin_and_destination ( const Vector3f & origin , const Vector3f & destination )
{
2013-05-03 04:58:00 -03:00
// store origin and destination locations
2013-03-20 03:27:26 -03:00
_origin = origin ;
_destination = destination ;
2013-04-06 10:25:58 -03:00
Vector3f pos_delta = _destination - _origin ;
2013-04-18 02:51:01 -03:00
2013-05-24 11:45:03 -03:00
// calculate leash lengths
2013-05-03 04:58:00 -03:00
bool climb = pos_delta . z > = 0 ; // climbing vs descending leads to different leash lengths because speed_up_cms and speed_down_cms can be different
2013-04-18 02:51:01 -03:00
2013-05-31 09:03:27 -03:00
_track_length = pos_delta . length ( ) ; // get track length
2013-05-14 23:51:26 -03:00
// calculate each axis' percentage of the total distance to the destination
if ( _track_length = = 0.0f ) {
// avoid possible divide by zero
_pos_delta_unit . x = 0 ;
_pos_delta_unit . y = 0 ;
_pos_delta_unit . z = 0 ;
2013-05-24 11:45:03 -03:00
} else {
2013-05-14 23:51:26 -03:00
_pos_delta_unit = pos_delta / _track_length ;
}
2013-05-31 09:03:27 -03:00
calculate_wp_leash_length ( climb ) ; // update leash lengths
2013-04-02 06:23:46 -03:00
2013-05-03 04:58:00 -03:00
// initialise intermediate point to the origin
2013-03-20 03:27:26 -03:00
_track_desired = 0 ;
2013-05-03 04:58:00 -03:00
_target = origin ;
2013-05-08 12:18:02 -03:00
_flags . reached_destination = false ;
2013-05-08 11:20:29 -03:00
2013-05-22 02:17:26 -03:00
// initialise the limited speed to current speed along the track
2013-08-12 10:58:16 -03:00
const Vector3f & curr_vel = _inav - > get_velocity ( ) ;
2013-05-29 23:52:04 -03:00
// get speed along track (note: we convert vertical speed into horizontal speed equivalent)
2013-05-31 09:03:27 -03:00
float speed_along_track = curr_vel . x * _pos_delta_unit . x + curr_vel . y * _pos_delta_unit . y + curr_vel . z * _pos_delta_unit . z ;
2013-05-22 02:17:26 -03:00
_limited_speed_xy_cms = constrain_float ( speed_along_track , 0 , _wp_speed_cms ) ;
2013-05-08 12:18:02 -03:00
// default waypoint back to slow
_flags . fast_waypoint = false ;
2013-05-09 06:32:02 -03:00
// initialise desired roll and pitch to current roll and pitch. This avoids a random twitch between now and when the wpnav controller is first run
2013-08-11 00:51:08 -03:00
_desired_roll = constrain_int32 ( _ahrs - > roll_sensor , - _lean_angle_max_cd , _lean_angle_max_cd ) ;
_desired_pitch = constrain_int32 ( _ahrs - > pitch_sensor , - _lean_angle_max_cd , _lean_angle_max_cd ) ;
2013-05-24 11:45:03 -03:00
// reset target velocity - only used by loiter controller's interpretation of pilot input
_target_vel . x = 0 ;
_target_vel . y = 0 ;
2013-03-20 03:27:26 -03:00
}
/// advance_target_along_track - move target location along track from origin to destination
2013-04-29 22:20:23 -03:00
void AC_WPNav : : advance_target_along_track ( float dt )
2013-03-20 03:27:26 -03:00
{
float track_covered ;
2013-05-31 09:03:27 -03:00
Vector3f track_error ;
2013-03-20 03:27:26 -03:00
float track_desired_max ;
2013-04-06 11:32:06 -03:00
float track_desired_temp = _track_desired ;
2013-04-06 10:25:58 -03:00
float track_extra_max ;
2013-03-20 03:27:26 -03:00
// get current location
2013-04-08 23:50:12 -03:00
Vector3f curr_pos = _inav - > get_position ( ) ;
Vector3f curr_delta = curr_pos - _origin ;
2013-03-20 03:27:26 -03:00
2013-05-31 09:03:27 -03:00
// calculate how far along the track we are
track_covered = curr_delta . x * _pos_delta_unit . x + curr_delta . y * _pos_delta_unit . y + curr_delta . z * _pos_delta_unit . z ;
Vector3f track_covered_pos = _pos_delta_unit * track_covered ;
track_error = curr_delta - track_covered_pos ;
// calculate the horizontal error
float track_error_xy = safe_sqrt ( track_error . x * track_error . x + track_error . y * track_error . y ) ;
// calculate the vertical error
float track_error_z = fabsf ( track_error . z ) ;
// calculate how far along the track we could move the intermediate target before reaching the end of the leash
track_extra_max = min ( _track_leash_length * ( _wp_leash_z - track_error_z ) / _wp_leash_z , _track_leash_length * ( _wp_leash_xy - track_error_xy ) / _wp_leash_xy ) ;
if ( track_extra_max < 0 ) {
track_desired_max = track_covered ;
} else {
track_desired_max = track_covered + track_extra_max ;
}
2013-05-22 02:17:26 -03:00
// get current velocity
2013-08-12 10:58:16 -03:00
const Vector3f & curr_vel = _inav - > get_velocity ( ) ;
2013-05-31 09:03:27 -03:00
// get speed along track
float speed_along_track = curr_vel . x * _pos_delta_unit . x + curr_vel . y * _pos_delta_unit . y + curr_vel . z * _pos_delta_unit . z ;
2013-05-22 02:17:26 -03:00
// calculate point at which velocity switches from linear to sqrt
float linear_velocity = _wp_speed_cms ;
float kP = _pid_pos_lat - > kP ( ) ;
if ( kP > = 0.0f ) { // avoid divide by zero
2013-05-31 09:03:27 -03:00
linear_velocity = _track_accel / kP ;
2013-04-29 22:20:23 -03:00
}
2013-05-22 02:17:26 -03:00
// let the limited_speed_xy_cms be some range above or below current velocity along track
if ( speed_along_track < - linear_velocity ) {
// we are travelling fast in the opposite direction of travel to the waypoint so do not move the intermediate point
_limited_speed_xy_cms = 0 ;
} else {
// increase intermediate target point's velocity if not yet at target speed (we will limit it below)
2013-05-31 09:03:27 -03:00
if ( dt > 0 ) {
if ( track_desired_max > _track_desired ) {
2013-12-10 19:21:41 -04:00
_limited_speed_xy_cms + = 2.0f * _track_accel * dt ;
2013-05-31 09:03:27 -03:00
} else {
// do nothing, velocity stays constant
_track_desired = track_desired_max ;
}
2013-05-22 02:17:26 -03:00
}
// do not go over top speed
2013-05-31 09:03:27 -03:00
if ( _limited_speed_xy_cms > _track_speed ) {
_limited_speed_xy_cms = _track_speed ;
2013-05-22 02:17:26 -03:00
}
// if our current velocity is within the linear velocity range limit the intermediate point's velocity to be no more than the linear_velocity above or below our current velocity
if ( fabsf ( speed_along_track ) < linear_velocity ) {
_limited_speed_xy_cms = constrain_float ( _limited_speed_xy_cms , speed_along_track - linear_velocity , speed_along_track + linear_velocity ) ;
}
2013-04-29 22:20:23 -03:00
}
2013-03-20 03:27:26 -03:00
// advance the current target
2013-05-31 09:03:27 -03:00
track_desired_temp + = _limited_speed_xy_cms * dt ;
2013-04-06 11:32:06 -03:00
2013-04-05 06:55:23 -03:00
// do not let desired point go past the end of the segment
2013-05-01 21:25:40 -03:00
track_desired_temp = constrain_float ( track_desired_temp , 0 , _track_length ) ;
2013-04-06 11:32:06 -03:00
_track_desired = max ( _track_desired , track_desired_temp ) ;
2013-03-20 03:27:26 -03:00
// recalculate the desired position
2013-05-31 09:03:27 -03:00
_target = _origin + _pos_delta_unit * _track_desired ;
2013-04-08 23:50:12 -03:00
// check if we've reached the waypoint
2013-05-08 12:18:02 -03:00
if ( ! _flags . reached_destination ) {
2013-04-08 23:50:12 -03:00
if ( _track_desired > = _track_length ) {
2013-05-08 12:18:02 -03:00
// "fast" waypoints are complete once the intermediate point reaches the destination
if ( _flags . fast_waypoint ) {
_flags . reached_destination = true ;
} else {
// regular waypoints also require the copter to be within the waypoint radius
Vector3f dist_to_dest = curr_pos - _destination ;
if ( dist_to_dest . length ( ) < = _wp_radius_cm ) {
_flags . reached_destination = true ;
}
2013-04-08 23:50:12 -03:00
}
}
}
2013-03-20 03:27:26 -03:00
}
2013-03-20 10:28:05 -03:00
/// get_distance_to_destination - get horizontal distance to destination in cm
float AC_WPNav : : get_distance_to_destination ( )
2013-03-20 03:27:26 -03:00
{
2013-03-20 10:28:05 -03:00
// get current location
Vector3f curr = _inav - > get_position ( ) ;
return pythagorous2 ( _destination . x - curr . x , _destination . y - curr . y ) ;
}
2013-03-20 03:27:26 -03:00
2013-03-20 10:28:05 -03:00
/// get_bearing_to_destination - get bearing to next waypoint in centi-degrees
int32_t AC_WPNav : : get_bearing_to_destination ( )
{
return get_bearing_cd ( _inav - > get_position ( ) , _destination ) ;
}
2013-03-20 03:27:26 -03:00
2013-03-20 10:28:05 -03:00
/// update_wpnav - run the wp controller - should be called at 10hz
void AC_WPNav : : update_wpnav ( )
{
2013-08-27 23:33:10 -03:00
// calculate dt
2013-03-20 10:28:05 -03:00
uint32_t now = hal . scheduler - > millis ( ) ;
2013-04-14 06:27:39 -03:00
float dt = ( now - _wpnav_last_update ) / 1000.0f ;
2013-03-20 10:28:05 -03:00
// catch if we've just been started
2013-12-10 19:21:41 -04:00
if ( dt > = 1.0f ) {
2013-03-20 10:28:05 -03:00
dt = 0.0 ;
reset_I ( ) ;
2013-08-27 23:33:10 -03:00
_wpnav_step = 0 ;
}
// reset step back to 0 if 0.1 seconds has passed and we completed the last full cycle
if ( dt > 0.095f & & _wpnav_step > 3 ) {
_wpnav_step = 0 ;
2013-03-20 03:27:26 -03:00
}
2013-08-27 23:33:10 -03:00
// run loiter steps
switch ( _wpnav_step ) {
case 0 :
// capture time since last iteration
_wpnav_dt = dt ;
_wpnav_last_update = now ;
// advance the target if necessary
if ( dt > 0.0f ) {
advance_target_along_track ( dt ) ;
}
_wpnav_step + + ;
break ;
case 1 :
// run loiter's position to velocity step
get_loiter_position_to_velocity ( _wpnav_dt , _wp_speed_cms ) ;
_wpnav_step + + ;
break ;
case 2 :
// run loiter's velocity to acceleration step
get_loiter_velocity_to_acceleration ( desired_vel . x , desired_vel . y , _wpnav_dt ) ;
_wpnav_step + + ;
break ;
case 3 :
// run loiter's acceleration to lean angle step
get_loiter_acceleration_to_lean_angles ( desired_accel . x , desired_accel . y ) ;
_wpnav_step + + ;
break ;
}
2013-03-20 03:27:26 -03:00
}
2013-03-20 10:28:05 -03:00
///
/// shared methods
///
2013-04-15 09:54:29 -03:00
/// get_loiter_position_to_velocity - loiter position controller
/// converts desired position held in _target vector to desired velocity
2013-05-07 05:11:24 -03:00
void AC_WPNav : : get_loiter_position_to_velocity ( float dt , float max_speed_cms )
2013-03-20 03:27:26 -03:00
{
2013-04-08 23:50:12 -03:00
Vector3f curr = _inav - > get_position ( ) ;
2013-04-02 09:53:34 -03:00
float dist_error_total ;
2013-03-20 03:27:26 -03:00
2013-04-02 09:53:34 -03:00
float vel_sqrt ;
float vel_total ;
2013-03-20 03:27:26 -03:00
2013-04-02 09:53:34 -03:00
float linear_distance ; // the distace we swap between linear and sqrt.
2013-05-14 23:51:26 -03:00
float kP = _pid_pos_lat - > kP ( ) ;
2013-03-20 03:27:26 -03:00
2013-05-14 23:51:26 -03:00
// avoid divide by zero
if ( kP < = 0.0f ) {
desired_vel . x = 0.0 ;
desired_vel . y = 0.0 ;
2013-03-20 03:27:26 -03:00
} else {
2013-05-14 23:51:26 -03:00
// calculate distance error
dist_error . x = _target . x - curr . x ;
dist_error . y = _target . y - curr . y ;
2013-06-15 23:39:54 -03:00
linear_distance = _wp_accel_cms / ( 2.0f * kP * kP ) ;
2013-05-14 23:51:26 -03:00
dist_error_total = safe_sqrt ( dist_error . x * dist_error . x + dist_error . y * dist_error . y ) ;
2013-07-10 06:20:05 -03:00
_distance_to_target = dist_error_total ; // for reporting purposes
2013-06-15 23:39:54 -03:00
if ( dist_error_total > 2.0f * linear_distance ) {
vel_sqrt = safe_sqrt ( 2.0f * _wp_accel_cms * ( dist_error_total - linear_distance ) ) ;
2013-05-14 23:51:26 -03:00
desired_vel . x = vel_sqrt * dist_error . x / dist_error_total ;
desired_vel . y = vel_sqrt * dist_error . y / dist_error_total ;
} else {
2013-07-10 04:23:51 -03:00
desired_vel . x = _pid_pos_lat - > kP ( ) * dist_error . x ;
desired_vel . y = _pid_pos_lon - > kP ( ) * dist_error . y ;
2013-05-14 23:51:26 -03:00
}
2013-03-20 03:27:26 -03:00
2013-05-14 23:51:26 -03:00
// ensure velocity stays within limits
vel_total = safe_sqrt ( desired_vel . x * desired_vel . x + desired_vel . y * desired_vel . y ) ;
if ( vel_total > max_speed_cms ) {
desired_vel . x = max_speed_cms * desired_vel . x / vel_total ;
desired_vel . y = max_speed_cms * desired_vel . y / vel_total ;
}
2013-05-24 11:45:03 -03:00
// feed forward velocity request
desired_vel . x + = _target_vel . x ;
desired_vel . y + = _target_vel . y ;
2013-03-20 03:27:26 -03:00
}
}
2013-04-15 09:54:29 -03:00
/// get_loiter_velocity_to_acceleration - loiter velocity controller
/// converts desired velocities in lat/lon directions to accelerations in lat/lon frame
void AC_WPNav : : get_loiter_velocity_to_acceleration ( float vel_lat , float vel_lon , float dt )
2013-03-20 03:27:26 -03:00
{
2013-08-12 10:58:16 -03:00
const Vector3f & vel_curr = _inav - > get_velocity ( ) ; // current velocity in cm/s
2013-04-02 06:23:46 -03:00
Vector3f vel_error ; // The velocity error in cm/s.
float accel_total ; // total acceleration in cm/s/s
// reset last velocity if this controller has just been engaged or dt is zero
2013-12-10 19:21:41 -04:00
if ( dt = = 0.0f ) {
2013-04-02 06:23:46 -03:00
desired_accel . x = 0 ;
desired_accel . y = 0 ;
} else {
// feed forward desired acceleration calculation
desired_accel . x = ( vel_lat - _vel_last . x ) / dt ;
desired_accel . y = ( vel_lon - _vel_last . y ) / dt ;
}
2013-03-20 03:27:26 -03:00
2013-04-02 06:23:46 -03:00
// store this iteration's velocities for the next iteration
_vel_last . x = vel_lat ;
_vel_last . y = vel_lon ;
2013-03-20 03:27:26 -03:00
2013-04-02 06:23:46 -03:00
// calculate velocity error
vel_error . x = vel_lat - vel_curr . x ;
vel_error . y = vel_lon - vel_curr . y ;
2013-03-20 03:27:26 -03:00
2013-04-02 06:23:46 -03:00
// combine feed foward accel with PID outpu from velocity error
desired_accel . x + = _pid_rate_lat - > get_pid ( vel_error . x , dt ) ;
desired_accel . y + = _pid_rate_lon - > get_pid ( vel_error . y , dt ) ;
2013-03-20 03:27:26 -03:00
2013-04-02 06:23:46 -03:00
// scale desired acceleration if it's beyond acceptable limit
accel_total = safe_sqrt ( desired_accel . x * desired_accel . x + desired_accel . y * desired_accel . y ) ;
2013-05-24 11:45:03 -03:00
if ( accel_total > WPNAV_ACCEL_MAX ) {
desired_accel . x = WPNAV_ACCEL_MAX * desired_accel . x / accel_total ;
desired_accel . y = WPNAV_ACCEL_MAX * desired_accel . y / accel_total ;
2013-03-20 03:27:26 -03:00
}
}
2013-04-15 09:54:29 -03:00
/// get_loiter_acceleration_to_lean_angles - loiter acceleration controller
/// converts desired accelerations provided in lat/lon frame to roll/pitch angles
void AC_WPNav : : get_loiter_acceleration_to_lean_angles ( float accel_lat , float accel_lon )
2013-03-20 03:27:26 -03:00
{
2013-04-13 01:57:27 -03:00
float z_accel_meas = - GRAVITY_MSS * 100 ; // gravity in cm/s/s
2013-03-20 03:27:26 -03:00
float accel_forward ;
float accel_right ;
2013-03-20 10:28:05 -03:00
// To-Do: add 1hz filter to accel_lat, accel_lon
2013-03-20 03:27:26 -03:00
// rotate accelerations into body forward-right frame
accel_forward = accel_lat * _cos_yaw + accel_lon * _sin_yaw ;
accel_right = - accel_lat * _sin_yaw + accel_lon * _cos_yaw ;
// update angle targets that will be passed to stabilize controller
2013-12-10 19:21:41 -04:00
_desired_roll = constrain_float ( fast_atan ( accel_right * _cos_pitch / ( - z_accel_meas ) ) * ( 18000 / M_PI_F ) , - _lean_angle_max_cd , _lean_angle_max_cd ) ;
_desired_pitch = constrain_float ( fast_atan ( - accel_forward / ( - z_accel_meas ) ) * ( 18000 / M_PI_F ) , - _lean_angle_max_cd , _lean_angle_max_cd ) ;
2013-03-20 03:27:26 -03:00
}
2013-03-20 10:28:05 -03:00
// get_bearing_cd - return bearing in centi-degrees between two positions
// To-Do: move this to math library
2013-04-21 07:54:31 -03:00
float AC_WPNav : : get_bearing_cd ( const Vector3f & origin , const Vector3f & destination ) const
2013-03-20 10:28:05 -03:00
{
float bearing = 9000 + atan2f ( - ( destination . x - origin . x ) , destination . y - origin . y ) * 5729.57795f ;
if ( bearing < 0 ) {
bearing + = 36000 ;
}
return bearing ;
}
/// reset_I - clears I terms from loiter PID controller
void AC_WPNav : : reset_I ( )
{
_pid_pos_lon - > reset_I ( ) ;
_pid_pos_lat - > reset_I ( ) ;
_pid_rate_lon - > reset_I ( ) ;
_pid_rate_lat - > reset_I ( ) ;
2013-04-02 06:23:46 -03:00
// set last velocity to current velocity
_vel_last = _inav - > get_velocity ( ) ;
2013-04-18 02:51:01 -03:00
}
2013-05-07 05:11:24 -03:00
/// calculate_wp_leash_length - calculates horizontal and vertical leash lengths for waypoint controller
void AC_WPNav : : calculate_wp_leash_length ( bool climb )
2013-04-18 02:51:01 -03:00
{
// get loiter position P
float kP = _pid_pos_lat - > kP ( ) ;
2013-06-15 23:39:54 -03:00
// sanity check acceleration and avoid divide by zero
if ( _wp_accel_cms < = 0.0f ) {
_wp_accel_cms = WPNAV_ACCELERATION_MIN ;
}
2013-05-14 23:51:26 -03:00
// avoid divide by zero
if ( kP < = 0.0f ) {
_wp_leash_xy = WPNAV_MIN_LEASH_LENGTH ;
return ;
}
2013-04-18 02:51:01 -03:00
// calculate horiztonal leash length
2013-06-15 23:39:54 -03:00
if ( _wp_speed_cms < = _wp_accel_cms / kP ) {
2013-04-18 02:51:01 -03:00
// linear leash length based on speed close in
2013-05-07 05:11:24 -03:00
_wp_leash_xy = _wp_speed_cms / kP ;
2013-04-18 02:51:01 -03:00
} else {
// leash length grows at sqrt of speed further out
2013-06-15 23:39:54 -03:00
_wp_leash_xy = ( _wp_accel_cms / ( 2.0f * kP * kP ) ) + ( _wp_speed_cms * _wp_speed_cms / ( 2.0f * _wp_accel_cms ) ) ;
2013-04-18 02:51:01 -03:00
}
// ensure leash is at least 1m long
2013-05-14 23:51:26 -03:00
if ( _wp_leash_xy < WPNAV_MIN_LEASH_LENGTH ) {
_wp_leash_xy = WPNAV_MIN_LEASH_LENGTH ;
2013-04-18 02:51:01 -03:00
}
// calculate vertical leash length
2013-05-31 09:03:27 -03:00
float speed_vert ;
2013-04-18 02:51:01 -03:00
if ( climb ) {
2013-05-07 05:11:24 -03:00
speed_vert = _wp_speed_up_cms ;
2013-04-18 02:51:01 -03:00
} else {
2013-05-07 05:11:24 -03:00
speed_vert = _wp_speed_down_cms ;
2013-04-18 02:51:01 -03:00
}
2013-05-31 09:03:27 -03:00
if ( speed_vert < = WPNAV_ALT_HOLD_ACCEL_MAX / _althold_kP ) {
2013-04-18 02:51:01 -03:00
// linear leash length based on speed close in
2013-05-31 09:03:27 -03:00
_wp_leash_z = speed_vert / _althold_kP ;
2013-04-18 02:51:01 -03:00
} else {
// leash length grows at sqrt of speed further out
2013-05-31 09:03:27 -03:00
_wp_leash_z = ( WPNAV_ALT_HOLD_ACCEL_MAX / ( 2.0 * _althold_kP * _althold_kP ) ) + ( speed_vert * speed_vert / ( 2 * WPNAV_ALT_HOLD_ACCEL_MAX ) ) ;
2013-04-18 02:51:01 -03:00
}
// ensure leash is at least 1m long
2013-05-31 09:03:27 -03:00
if ( _wp_leash_z < WPNAV_MIN_LEASH_LENGTH ) {
_wp_leash_z = WPNAV_MIN_LEASH_LENGTH ;
2013-04-18 02:51:01 -03:00
}
2013-05-31 09:03:27 -03:00
// length of the unit direction vector in the horizontal
float pos_delta_unit_xy = sqrt ( _pos_delta_unit . x * _pos_delta_unit . x + _pos_delta_unit . y * _pos_delta_unit . y ) ;
float pos_delta_unit_z = fabsf ( _pos_delta_unit . z ) ;
// calculate the maximum acceleration, maximum velocity, and leash length in the direction of travel
if ( pos_delta_unit_z = = 0 & & pos_delta_unit_xy = = 0 ) {
_track_accel = 0 ;
_track_speed = 0 ;
_track_leash_length = WPNAV_MIN_LEASH_LENGTH ;
} else if ( _pos_delta_unit . z = = 0 ) {
2013-06-15 23:39:54 -03:00
_track_accel = _wp_accel_cms / pos_delta_unit_xy ;
2013-05-31 09:03:27 -03:00
_track_speed = _wp_speed_cms / pos_delta_unit_xy ;
_track_leash_length = _wp_leash_xy / pos_delta_unit_xy ;
} else if ( pos_delta_unit_xy = = 0 ) {
_track_accel = WPNAV_ALT_HOLD_ACCEL_MAX / pos_delta_unit_z ;
_track_speed = speed_vert / pos_delta_unit_z ;
_track_leash_length = _wp_leash_z / pos_delta_unit_z ;
} else {
2013-06-15 23:39:54 -03:00
_track_accel = min ( WPNAV_ALT_HOLD_ACCEL_MAX / pos_delta_unit_z , _wp_accel_cms / pos_delta_unit_xy ) ;
2013-05-31 09:03:27 -03:00
_track_speed = min ( speed_vert / pos_delta_unit_z , _wp_speed_cms / pos_delta_unit_xy ) ;
_track_leash_length = min ( _wp_leash_z / pos_delta_unit_z , _wp_leash_xy / pos_delta_unit_xy ) ;
2013-05-29 23:52:04 -03:00
}
2013-04-21 07:54:31 -03:00
}