2021-04-19 08:53:38 -03:00
/*
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "AR_PosControl.h"
# include <AP_HAL/AP_HAL.h>
# include <AP_Math/AP_Math.h>
# include <AP_AHRS/AP_AHRS.h>
# include <AP_Logger/AP_Logger.h>
# include <GCS_MAVLink/GCS.h>
# include <AC_Avoidance/AC_Avoid.h>
# define AR_POSCON_TIMEOUT_MS 100 // timeout after 0.1 sec
# define AR_POSCON_POS_P 0.2f // default position P gain
# define AR_POSCON_VEL_P 1.0f // default velocity P gain
2022-02-08 23:07:14 -04:00
# define AR_POSCON_VEL_I 0.0f // default velocity I gain
2021-04-19 08:53:38 -03:00
# define AR_POSCON_VEL_D 0.0f // default velocity D gain
# define AR_POSCON_VEL_FF 0.0f // default velocity FF gain
# define AR_POSCON_VEL_IMAX 1.0f // default velocity IMAX
# define AR_POSCON_VEL_FILT 5.0f // default velocity filter
# define AR_POSCON_VEL_FILT_D 5.0f // default velocity D term filter
# define AR_POSCON_DT 0.02f // default dt for PID controllers
2023-06-02 02:48:02 -03:00
extern const AP_HAL : : HAL & hal ;
AR_PosControl * AR_PosControl : : _singleton ;
2021-04-19 08:53:38 -03:00
const AP_Param : : GroupInfo AR_PosControl : : var_info [ ] = {
// @Param: _POS_P
// @DisplayName: Position controller P gain
// @Description: Position controller P gain. Converts the distance to the target location into a desired speed which is then passed to the loiter latitude rate controller
// @Range: 0.500 2.000
// @User: Standard
AP_SUBGROUPINFO ( _p_pos , " _POS_ " , 1 , AR_PosControl , AC_P_2D ) ,
// @Param: _VEL_P
// @DisplayName: Velocity (horizontal) P gain
// @Description: Velocity (horizontal) P gain. Converts the difference between desired and actual velocity to a target acceleration
// @Range: 0.1 6.0
// @Increment: 0.1
// @User: Advanced
// @Param: _VEL_I
// @DisplayName: Velocity (horizontal) I gain
// @Description: Velocity (horizontal) I gain. Corrects long-term difference between desired and actual velocity to a target acceleration
2023-11-24 20:03:04 -04:00
// @Range: 0.00 1.00
2021-04-19 08:53:38 -03:00
// @Increment: 0.01
// @User: Advanced
// @Param: _VEL_D
// @DisplayName: Velocity (horizontal) D gain
// @Description: Velocity (horizontal) D gain. Corrects short-term changes in velocity
// @Range: 0.00 1.00
// @Increment: 0.001
// @User: Advanced
// @Param: _VEL_IMAX
// @DisplayName: Velocity (horizontal) integrator maximum
// @Description: Velocity (horizontal) integrator maximum. Constrains the target acceleration that the I gain will output
// @Range: 0 4500
// @Increment: 10
// @Units: cm/s/s
// @User: Advanced
// @Param: _VEL_FLTE
// @DisplayName: Velocity (horizontal) input filter
// @Description: Velocity (horizontal) input filter. This filter (in Hz) is applied to the input for P and I terms
// @Range: 0 100
// @Units: Hz
// @User: Advanced
// @Param: _VEL_FLTD
// @DisplayName: Velocity (horizontal) input filter
// @Description: Velocity (horizontal) input filter. This filter (in Hz) is applied to the input for D term
// @Range: 0 100
// @Units: Hz
// @User: Advanced
// @Param: _VEL_FF
// @DisplayName: Velocity (horizontal) feed forward gain
// @Description: Velocity (horizontal) feed forward gain. Converts the difference between desired velocity to a target acceleration
// @Range: 0 6
// @Increment: 0.01
// @User: Advanced
AP_SUBGROUPINFO ( _pid_vel , " _VEL_ " , 2 , AR_PosControl , AC_PID_2D ) ,
AP_GROUPEND
} ;
AR_PosControl : : AR_PosControl ( AR_AttitudeControl & atc ) :
_atc ( atc ) ,
2022-11-30 01:24:18 -04:00
_p_pos ( AR_POSCON_POS_P ) ,
_pid_vel ( AR_POSCON_VEL_P , AR_POSCON_VEL_I , AR_POSCON_VEL_D , AR_POSCON_VEL_FF , AR_POSCON_VEL_IMAX , AR_POSCON_VEL_FILT , AR_POSCON_VEL_FILT_D )
2021-04-19 08:53:38 -03:00
{
2023-06-02 02:48:02 -03:00
_singleton = this ;
2021-04-19 08:53:38 -03:00
AP_Param : : setup_object_defaults ( this , var_info ) ;
}
// update navigation
void AR_PosControl : : update ( float dt )
{
// exit immediately if no current location, destination or disarmed
Vector2f curr_pos_NE ;
Vector3f curr_vel_NED ;
if ( ! hal . util - > get_soft_armed ( ) | | ! AP : : ahrs ( ) . get_relative_position_NE_origin ( curr_pos_NE ) | |
! AP : : ahrs ( ) . get_velocity_NED ( curr_vel_NED ) ) {
_desired_speed = _atc . get_desired_speed_accel_limited ( 0.0f , dt ) ;
_desired_lat_accel = 0.0f ;
_desired_turn_rate_rads = 0.0f ;
return ;
}
// check for ekf xy position reset
handle_ekf_xy_reset ( ) ;
// if no recent calls reset velocity controller
if ( ! is_active ( ) ) {
_pid_vel . reset_I ( ) ;
_pid_vel . reset_filter ( ) ;
}
_last_update_ms = AP_HAL : : millis ( ) ;
// calculate position error and convert to desired velocity
2022-02-22 01:36:10 -04:00
_vel_target . zero ( ) ;
2021-04-19 08:53:38 -03:00
if ( _pos_target_valid ) {
Vector2p pos_target = _pos_target ;
2022-02-22 01:36:10 -04:00
_vel_target = _p_pos . update_all ( pos_target . x , pos_target . y , curr_pos_NE ) ;
2021-04-19 08:53:38 -03:00
}
// calculation velocity error
2023-11-15 08:40:42 -04:00
bool stopping = false ;
2022-02-22 01:36:10 -04:00
if ( _vel_desired_valid ) {
2021-04-19 08:53:38 -03:00
// add target velocity to desired velocity from position error
2022-02-22 01:36:10 -04:00
_vel_target + = _vel_desired ;
2023-11-15 08:40:42 -04:00
stopping = _vel_desired . is_zero ( ) ;
2021-04-19 08:53:38 -03:00
}
// limit velocity to maximum speed
2022-02-22 01:36:10 -04:00
_vel_target . limit_length ( get_speed_max ( ) ) ;
2021-04-19 08:53:38 -03:00
// Limit the velocity to prevent fence violations
bool backing_up = false ;
AC_Avoid * avoid = AP : : ac_avoid ( ) ;
if ( avoid ! = nullptr ) {
2022-02-22 01:36:10 -04:00
Vector3f vel_3d_cms { _vel_target . x * 100.0f , _vel_target . y * 100.0f , 0.0f } ;
2021-04-19 08:53:38 -03:00
const float accel_max_cmss = MIN ( _accel_max , _lat_accel_max ) * 100.0 ;
avoid - > adjust_velocity ( vel_3d_cms , backing_up , _p_pos . kP ( ) , accel_max_cmss , _p_pos . kP ( ) , accel_max_cmss , dt ) ;
2022-02-22 01:36:10 -04:00
_vel_target . x = vel_3d_cms . x * 0.01 ;
_vel_target . y = vel_3d_cms . y * 0.01 ;
2021-04-19 08:53:38 -03:00
}
2023-06-27 01:11:07 -03:00
// calculate limit vector based on steering limits
Vector2f steering_limit_vec ;
if ( _atc . steering_limit_left ( ) ) {
steering_limit_vec = AP : : ahrs ( ) . body_to_earth2D ( Vector2f { 0 , _reversed ? 1.0f : - 1.0f } ) ;
} else if ( _atc . steering_limit_right ( ) ) {
steering_limit_vec = AP : : ahrs ( ) . body_to_earth2D ( Vector2f { 0 , _reversed ? - 1.0f : 1.0f } ) ;
}
2021-04-19 08:53:38 -03:00
// calculate desired acceleration
2023-06-27 01:11:07 -03:00
_accel_target = _pid_vel . update_all ( _vel_target , curr_vel_NED . xy ( ) , dt , steering_limit_vec ) ;
2022-02-22 01:36:10 -04:00
if ( _accel_desired_valid ) {
_accel_target + = _accel_desired ;
2021-04-19 08:53:38 -03:00
}
2023-06-27 01:11:07 -03:00
// velocity controller I-term zeroed in forward-back direction
const Vector2f lat_vec_ef = AP : : ahrs ( ) . body_to_earth2D ( Vector2f { 0 , 1 } ) ;
const Vector2f vel_i = _pid_vel . get_i ( ) . projected ( lat_vec_ef ) ;
_pid_vel . set_integrator ( vel_i ) ;
2021-04-19 08:53:38 -03:00
// convert desired acceleration to desired forward-back speed, desired lateral speed and desired turn rate
// rotate acceleration into body frame using current heading
2022-02-22 01:36:10 -04:00
const Vector2f accel_target_FR = AP : : ahrs ( ) . earth_to_body2D ( _accel_target ) ;
2021-04-19 08:53:38 -03:00
// calculate minimum turn speed which is the max speed the vehicle could turn through the corner
// given the vehicle's turn radius and half its max lateral acceleration
// todo: remove MAX of zero when safe_sqrt fixed
float turn_speed_min = MAX ( safe_sqrt ( _atc . get_turn_lat_accel_max ( ) * 0.5 * _turn_radius ) , 0 ) ;
// rotate target velocity from earth-frame to body frame
2022-02-22 01:36:10 -04:00
const Vector2f vel_target_FR = AP : : ahrs ( ) . earth_to_body2D ( _vel_target ) ;
2021-04-19 08:53:38 -03:00
// desired speed is normally the forward component (only) of the target velocity
2023-11-15 08:40:42 -04:00
float des_speed = vel_target_FR . x ;
if ( ! stopping ) {
// do not let target speed fall below the minimum turn speed unless the vehicle is slowing down
const float abs_des_speed_min = MIN ( _vel_target . length ( ) , turn_speed_min ) ;
if ( _reversed ! = backing_up ) {
// if reversed or backing up desired speed will be negative
des_speed = MIN ( - abs_des_speed_min , vel_target_FR . x ) ;
} else {
des_speed = MAX ( abs_des_speed_min , vel_target_FR . x ) ;
}
2021-04-19 08:53:38 -03:00
}
_desired_speed = _atc . get_desired_speed_accel_limited ( des_speed , dt ) ;
// calculate turn rate from desired lateral acceleration
2023-11-15 08:40:42 -04:00
_desired_lat_accel = stopping ? 0 : accel_target_FR . y ;
2021-04-19 08:53:38 -03:00
_desired_turn_rate_rads = _atc . get_turn_rate_from_lat_accel ( _desired_lat_accel , _desired_speed ) ;
}
// true if update has been called recently
bool AR_PosControl : : is_active ( ) const
{
return ( ( AP_HAL : : millis ( ) - _last_update_ms ) < AR_POSCON_TIMEOUT_MS ) ;
}
// set limits
void AR_PosControl : : set_limits ( float speed_max , float accel_max , float lat_accel_max , float jerk_max )
{
_speed_max = MAX ( speed_max , 0 ) ;
_accel_max = MAX ( accel_max , 0 ) ;
_lat_accel_max = MAX ( lat_accel_max , 0 ) ;
_jerk_max = MAX ( jerk_max , 0 ) ;
// set position P controller limits
_p_pos . set_limits ( _speed_max , MIN ( _accel_max , _lat_accel_max ) , _jerk_max ) ;
}
// setter to allow vehicle code to provide turn related param values to this library (should be updated regularly)
void AR_PosControl : : set_turn_params ( float turn_radius , bool pivot_possible )
{
if ( pivot_possible ) {
_turn_radius = 0 ;
} else {
_turn_radius = turn_radius ;
}
}
// initialise the position controller to the current position, velocity, acceleration and attitude
// this should be called before the input shaping methods are used
bool AR_PosControl : : init ( )
{
// get current position and velocity from AHRS
Vector2f pos_NE ;
Vector3f vel_NED ;
if ( ! AP : : ahrs ( ) . get_relative_position_NE_origin ( pos_NE ) | | ! AP : : ahrs ( ) . get_velocity_NED ( vel_NED ) ) {
return false ;
}
// set target position to current position
_pos_target . x = pos_NE . x ;
_pos_target . y = pos_NE . y ;
// set target velocity and acceleration
2022-02-22 01:36:10 -04:00
_vel_desired = vel_NED . xy ( ) ;
_vel_target . zero ( ) ;
2022-07-26 21:21:30 -03:00
_accel_desired = AP : : ahrs ( ) . get_accel_ef ( ) . xy ( ) ;
2022-02-22 01:36:10 -04:00
_accel_target . zero ( ) ;
2021-04-19 08:53:38 -03:00
// clear reversed setting
_reversed = false ;
// initialise ekf xy reset handler
init_ekf_xy_reset ( ) ;
return true ;
}
2023-04-21 01:11:09 -03:00
// adjust position, velocity and acceleration targets smoothly using input shaping
// pos is the target position as an offset from the EKF origin (in meters)
// vel is the target velocity in m/s. accel is the target acceleration in m/s/s
2021-04-19 08:53:38 -03:00
// dt should be the update rate in seconds
2023-04-21 01:11:09 -03:00
// init should be called once before starting to use these methods
2021-04-19 08:53:38 -03:00
void AR_PosControl : : input_pos_target ( const Vector2p & pos , float dt )
2023-04-21 01:11:09 -03:00
{
Vector2f vel ;
Vector2f accel ;
input_pos_vel_accel_target ( pos , vel , accel , dt ) ;
}
// adjust position, velocity and acceleration targets smoothly using input shaping
// pos is the target position as an offset from the EKF origin (in meters)
// vel is the target velocity in m/s. accel is the target acceleration in m/s/s
// dt should be the update rate in seconds
// init should be called once before starting to use these methods
void AR_PosControl : : input_pos_vel_target ( const Vector2p & pos , const Vector2f & vel , float dt )
{
Vector2f accel ;
input_pos_vel_accel_target ( pos , vel , accel , dt ) ;
}
// adjust position, velocity and acceleration targets smoothly using input shaping
// pos is the target position as an offset from the EKF origin (in meters)
// vel is the target velocity in m/s. accel is the target acceleration in m/s/s
// dt should be the update rate in seconds
// init should be called once before starting to use these methods
void AR_PosControl : : input_pos_vel_accel_target ( const Vector2p & pos , const Vector2f & vel , const Vector2f & accel , float dt )
2021-04-19 08:53:38 -03:00
{
// adjust target position, velocity and acceleration forward by dt
2022-02-22 01:36:10 -04:00
update_pos_vel_accel_xy ( _pos_target , _vel_desired , _accel_desired , dt , Vector2f ( ) , Vector2f ( ) , Vector2f ( ) ) ;
2021-04-19 08:53:38 -03:00
// call shape_pos_vel_accel_xy to pull target towards final destination
const float accel_max = MIN ( _accel_max , _lat_accel_max ) ;
2022-02-22 01:36:10 -04:00
shape_pos_vel_accel_xy ( pos , vel , accel , _pos_target , _vel_desired , _accel_desired ,
2021-04-19 08:53:38 -03:00
_speed_max , accel_max , _jerk_max , dt , false ) ;
2022-02-22 01:36:10 -04:00
// set flags so update will consume target position, desired velocity and desired acceleration
2021-04-19 08:53:38 -03:00
_pos_target_valid = true ;
2022-02-22 01:36:10 -04:00
_vel_desired_valid = true ;
_accel_desired_valid = true ;
2021-04-19 08:53:38 -03:00
}
2022-02-22 01:36:10 -04:00
// set target position, desired velocity and acceleration. These should be from an externally created path and are not "input shaped"
2021-04-19 08:53:38 -03:00
void AR_PosControl : : set_pos_vel_accel_target ( const Vector2p & pos , const Vector2f & vel , const Vector2f & accel )
{
_pos_target = pos ;
2022-02-22 01:36:10 -04:00
_vel_desired = vel ;
_accel_desired = accel ;
2021-04-19 08:53:38 -03:00
_pos_target_valid = true ;
2022-02-22 01:36:10 -04:00
_vel_desired_valid = true ;
_accel_desired_valid = true ;
2021-04-19 08:53:38 -03:00
}
// returns desired velocity vector (i.e. feed forward) in cm/s in lat and lon direction
Vector2f AR_PosControl : : get_desired_velocity ( ) const
{
2022-02-22 01:36:10 -04:00
if ( _vel_desired_valid ) {
return _vel_desired ;
2021-04-19 08:53:38 -03:00
}
return Vector2f ( ) ;
}
// return desired acceleration vector in m/s in lat and lon direction
Vector2f AR_PosControl : : get_desired_accel ( ) const
{
2022-02-22 01:36:10 -04:00
if ( _accel_desired_valid ) {
return _accel_desired ;
2021-04-19 08:53:38 -03:00
}
return Vector2f ( ) ;
}
/// get position error as a vector from the current position to the target position
Vector2p AR_PosControl : : get_pos_error ( ) const
{
// return zero error is not active or no position estimate
Vector2f curr_pos_NE ;
if ( ! is_active ( ) | | ! AP : : ahrs ( ) . get_relative_position_NE_origin ( curr_pos_NE ) ) {
return Vector2p { } ;
}
// get current position
return ( _pos_target - curr_pos_NE . topostype ( ) ) ;
}
2023-06-02 02:48:02 -03:00
// get the slew rate value for velocity. used for oscillation detection in lua scripts
void AR_PosControl : : get_srate ( float & velocity_srate )
{
// slew rate is the same for x and y axis
velocity_srate = _pid_vel . get_pid_info_x ( ) . slew_rate ;
}
2023-07-13 21:58:06 -03:00
# if HAL_LOGGING_ENABLED
2021-04-19 08:53:38 -03:00
// write PSC logs
void AR_PosControl : : write_log ( )
{
// exit immediately if not active
if ( ! is_active ( ) ) {
return ;
}
// exit immediately if no position or velocity estimate
Vector3f curr_pos_NED ;
Vector3f curr_vel_NED ;
if ( ! AP : : ahrs ( ) . get_relative_position_NED_origin ( curr_pos_NED ) | | ! AP : : ahrs ( ) . get_velocity_NED ( curr_vel_NED ) ) {
return ;
}
// get acceleration
2022-07-26 21:21:30 -03:00
const Vector3f curr_accel_NED = AP : : ahrs ( ) . get_accel_ef ( ) * 100.0 ;
2021-04-19 08:53:38 -03:00
2022-02-22 01:36:10 -04:00
// convert position to required format
2021-04-19 08:53:38 -03:00
Vector2f pos_target_2d_cm = get_pos_target ( ) . tofloat ( ) * 100.0 ;
AP : : logger ( ) . Write_PSCN ( pos_target_2d_cm . x , // position target
curr_pos_NED . x * 100.0 , // position
2022-02-22 01:36:10 -04:00
_vel_desired . x * 100.0 , // desired velocity
_vel_target . x * 100.0 , // target velocity
2021-04-19 08:53:38 -03:00
curr_vel_NED . x * 100.0 , // velocity
2022-02-22 01:36:10 -04:00
_accel_desired . x * 100.0 , // desired accel
_accel_target . x * 100.0 , // target accel
2021-04-19 08:53:38 -03:00
curr_accel_NED . x ) ; // accel
AP : : logger ( ) . Write_PSCE ( pos_target_2d_cm . y , // position target
curr_pos_NED . y * 100.0 , // position
2022-02-22 01:36:10 -04:00
_vel_desired . y * 100.0 , // desired velocity
_vel_target . y * 100.0 , // target velocity
2021-04-19 08:53:38 -03:00
curr_vel_NED . y * 100.0 , // velocity
2022-02-22 01:36:10 -04:00
_accel_desired . y * 100.0 , // desired accel
_accel_target . y * 100.0 , // target accel
2021-04-19 08:53:38 -03:00
curr_accel_NED . y ) ; // accel
}
2023-07-13 21:58:06 -03:00
# endif
2021-04-19 08:53:38 -03:00
/// initialise ekf xy position reset check
void AR_PosControl : : init_ekf_xy_reset ( )
{
Vector2f pos_shift ;
_ekf_xy_reset_ms = AP : : ahrs ( ) . getLastPosNorthEastReset ( pos_shift ) ;
}
/// handle_ekf_xy_reset - check for ekf position reset and adjust loiter or brake target position
void AR_PosControl : : handle_ekf_xy_reset ( )
{
// check for position shift
Vector2f pos_shift ;
uint32_t reset_ms = AP : : ahrs ( ) . getLastPosNorthEastReset ( pos_shift ) ;
if ( reset_ms ! = _ekf_xy_reset_ms ) {
Vector2f pos_NE ;
if ( ! AP : : ahrs ( ) . get_relative_position_NE_origin ( pos_NE ) ) {
return ;
}
_pos_target = ( pos_NE + _p_pos . get_error ( ) ) . topostype ( ) ;
Vector3f vel_NED ;
if ( ! AP : : ahrs ( ) . get_velocity_NED ( vel_NED ) ) {
return ;
}
2022-02-22 01:36:10 -04:00
_vel_desired = vel_NED . xy ( ) + _pid_vel . get_error ( ) ;
2021-04-19 08:53:38 -03:00
_ekf_xy_reset_ms = reset_ms ;
}
}