2012-05-24 10:53:14 +02:00
/**
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
2012-05-24 11:13:21 +02:00
* @ file fixedwingpathfollower . c
2012-05-24 10:53:14 +02:00
* @ author The OpenPilot Team , http : //www.openpilot.org Copyright (C) 2010.
2013-05-18 14:17:26 +02:00
* @ brief This module compared @ ref PositionActuatl to @ ref ActiveWaypoint
2012-05-24 10:53:14 +02:00
* and sets @ ref AttitudeDesired . It only does this when the FlightMode field
* of @ ref ManualControlCommand is Auto .
*
* @ see The GNU Public License ( GPL ) Version 3
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* 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 , write to the Free Software Foundation , Inc . ,
* 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/**
* Input object : ActiveWaypoint
2013-05-18 19:36:45 +02:00
* Input object : PositionState
2012-05-24 10:53:14 +02:00
* Input object : ManualControlCommand
* Output object : AttitudeDesired
*
* This module will periodically update the value of the AttitudeDesired object .
*
* The module executes in its own thread in this example .
*
* Modules have no API , all communication to other modules is done through UAVObjects .
* However modules may use the API exposed by shared libraries .
* See the OpenPilot wiki for more details .
* http : //www.openpilot.org/OpenPilot_Application_Architecture
*
*/
2013-05-02 23:31:14 +02:00
# include <openpilot.h>
2012-05-24 10:53:14 +02:00
2012-05-24 18:19:52 +02:00
# include "hwsettings.h"
2013-05-18 19:36:45 +02:00
# include "attitudestate.h"
2013-05-18 14:17:26 +02:00
# include "pathdesired.h" // object that will be updated by the module
2013-05-18 19:36:45 +02:00
# include "positionstate.h"
2012-05-24 10:53:14 +02:00
# include "manualcontrol.h"
# include "flightstatus.h"
2012-05-24 18:19:52 +02:00
# include "pathstatus.h"
2013-05-18 19:36:45 +02:00
# include "airspeedstate.h"
2012-05-24 11:13:21 +02:00
# include "fixedwingpathfollowersettings.h"
# include "fixedwingpathfollowerstatus.h"
2012-05-24 10:53:14 +02:00
# include "homelocation.h"
# include "stabilizationdesired.h"
# include "stabilizationsettings.h"
# include "systemsettings.h"
# include "velocitydesired.h"
2013-05-18 19:36:45 +02:00
# include "velocitystate.h"
2013-05-02 23:31:14 +02:00
# include "taskinfo.h"
2013-09-01 12:10:55 +02:00
# include <pios_struct_helper.h>
2013-05-02 23:31:14 +02:00
# include "paths.h"
2012-05-24 10:53:14 +02:00
# include "CoordinateConversions.h"
2013-05-02 23:31:14 +02:00
2012-05-24 10:53:14 +02:00
// Private constants
2013-05-18 14:17:26 +02:00
# define MAX_QUEUE_SIZE 4
2012-05-24 10:53:14 +02:00
# define STACK_SIZE_BYTES 1548
2013-05-18 14:17:26 +02:00
# define TASK_PRIORITY (tskIDLE_PRIORITY + 2)
2012-05-24 10:53:14 +02:00
// Private variables
2012-05-24 18:19:52 +02:00
static bool followerEnabled = false ;
static xTaskHandle pathfollowerTaskHandle ;
2012-05-24 23:28:13 +02:00
static PathDesiredData pathDesired ;
2012-05-28 01:51:17 +02:00
static PathStatusData pathStatus ;
2012-05-24 18:19:52 +02:00
static FixedWingPathFollowerSettingsData fixedwingpathfollowerSettings ;
2012-05-24 10:53:14 +02:00
// Private functions
2012-05-24 18:19:52 +02:00
static void pathfollowerTask ( void * parameters ) ;
2013-05-18 14:17:26 +02:00
static void SettingsUpdatedCb ( UAVObjEvent * ev ) ;
2012-05-28 01:51:17 +02:00
static void updatePathVelocity ( ) ;
2012-05-27 02:38:56 +02:00
static uint8_t updateFixedDesiredAttitude ( ) ;
2012-05-24 23:28:13 +02:00
static void updateFixedAttitude ( ) ;
2013-05-18 19:36:45 +02:00
static void airspeedStateUpdatedCb ( UAVObjEvent * ev ) ;
2012-05-24 18:19:52 +02:00
static float bound ( float val , float min , float max ) ;
2012-05-24 10:53:14 +02:00
/**
* Initialise the module , called on startup
* \ returns 0 on success or - 1 if initialisation failed
*/
2012-05-24 11:13:21 +02:00
int32_t FixedWingPathFollowerStart ( )
2012-05-24 10:53:14 +02:00
{
2013-05-18 14:17:26 +02:00
if ( followerEnabled ) {
// Start main task
xTaskCreate ( pathfollowerTask , ( signed char * ) " PathFollower " , STACK_SIZE_BYTES / 4 , NULL , TASK_PRIORITY , & pathfollowerTaskHandle ) ;
PIOS_TASK_MONITOR_RegisterTask ( TASKINFO_RUNNING_PATHFOLLOWER , pathfollowerTaskHandle ) ;
}
2012-05-24 10:53:14 +02:00
2013-05-18 14:17:26 +02:00
return 0 ;
2012-05-24 10:53:14 +02:00
}
/**
* Initialise the module , called on startup
* \ returns 0 on success or - 1 if initialisation failed
*/
2012-05-24 11:13:21 +02:00
int32_t FixedWingPathFollowerInitialize ( )
2012-05-24 10:53:14 +02:00
{
2013-05-18 14:17:26 +02:00
HwSettingsInitialize ( ) ;
2013-09-01 13:23:20 +02:00
HwSettingsOptionalModulesData optionalModules ;
HwSettingsOptionalModulesGet ( & optionalModules ) ;
if ( optionalModules . FixedWingPathFollower = = HWSETTINGS_OPTIONALMODULES_ENABLED ) {
2013-05-18 14:17:26 +02:00
followerEnabled = true ;
FixedWingPathFollowerSettingsInitialize ( ) ;
FixedWingPathFollowerStatusInitialize ( ) ;
PathDesiredInitialize ( ) ;
PathStatusInitialize ( ) ;
VelocityDesiredInitialize ( ) ;
2013-05-18 19:36:45 +02:00
AirspeedStateInitialize ( ) ;
2013-05-18 14:17:26 +02:00
} else {
followerEnabled = false ;
}
return 0 ;
2012-05-24 10:53:14 +02:00
}
2013-06-04 05:37:40 +02:00
MODULE_INITCALL ( FixedWingPathFollowerInitialize , FixedWingPathFollowerStart ) ;
2012-05-24 10:53:14 +02:00
static float northVelIntegral = 0 ;
2013-05-18 14:17:26 +02:00
static float eastVelIntegral = 0 ;
static float downVelIntegral = 0 ;
2012-05-24 10:53:14 +02:00
2013-07-11 16:22:24 +02:00
static float courseIntegral = 0 ;
2013-05-18 14:17:26 +02:00
static float speedIntegral = 0 ;
static float powerIntegral = 0 ;
static float airspeedErrorInt = 0 ;
2012-05-24 10:53:14 +02:00
// correct speed by measured airspeed
2013-05-18 19:36:45 +02:00
static float indicatedAirspeedStateBias = 0 ;
2012-05-24 10:53:14 +02:00
/**
* Module thread , should not return .
*/
2013-05-05 09:02:24 +02:00
static void pathfollowerTask ( __attribute__ ( ( unused ) ) void * parameters )
2012-05-24 10:53:14 +02:00
{
2013-05-18 14:17:26 +02:00
SystemSettingsData systemSettings ;
FlightStatusData flightStatus ;
portTickType lastUpdateTime ;
2013-05-18 19:36:45 +02:00
AirspeedStateConnectCallback ( airspeedStateUpdatedCb ) ;
2013-05-18 14:17:26 +02:00
FixedWingPathFollowerSettingsConnectCallback ( SettingsUpdatedCb ) ;
PathDesiredConnectCallback ( SettingsUpdatedCb ) ;
FixedWingPathFollowerSettingsGet ( & fixedwingpathfollowerSettings ) ;
PathDesiredGet ( & pathDesired ) ;
// Main task loop
lastUpdateTime = xTaskGetTickCount ( ) ;
while ( 1 ) {
// Conditions when this runs:
// 1. Must have FixedWing type airframe
// 2. Flight mode is PositionHold and PathDesired.Mode is Endpoint OR
// FlightMode is PathPlanner and PathDesired.Mode is Endpoint or Path
SystemSettingsGet ( & systemSettings ) ;
if ( ( systemSettings . AirframeType ! = SYSTEMSETTINGS_AIRFRAMETYPE_FIXEDWING ) & &
( systemSettings . AirframeType ! = SYSTEMSETTINGS_AIRFRAMETYPE_FIXEDWINGELEVON ) & &
( systemSettings . AirframeType ! = SYSTEMSETTINGS_AIRFRAMETYPE_FIXEDWINGVTAIL ) ) {
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_WARNING ) ;
vTaskDelay ( 1000 ) ;
continue ;
}
// Continue collecting data if not enough time
vTaskDelayUntil ( & lastUpdateTime , fixedwingpathfollowerSettings . UpdatePeriod / portTICK_RATE_MS ) ;
FlightStatusGet ( & flightStatus ) ;
PathStatusGet ( & pathStatus ) ;
uint8_t result ;
// Check the combinations of flightmode and pathdesired mode
switch ( flightStatus . FlightMode ) {
case FLIGHTSTATUS_FLIGHTMODE_POSITIONHOLD :
case FLIGHTSTATUS_FLIGHTMODE_RETURNTOBASE :
if ( pathDesired . Mode = = PATHDESIRED_MODE_FLYENDPOINT ) {
updatePathVelocity ( ) ;
result = updateFixedDesiredAttitude ( ) ;
if ( result ) {
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_OK ) ;
} else {
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_WARNING ) ;
}
} else {
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_ERROR ) ;
}
break ;
case FLIGHTSTATUS_FLIGHTMODE_PATHPLANNER :
pathStatus . UID = pathDesired . UID ;
pathStatus . Status = PATHSTATUS_STATUS_INPROGRESS ;
switch ( pathDesired . Mode ) {
case PATHDESIRED_MODE_FLYENDPOINT :
case PATHDESIRED_MODE_FLYVECTOR :
case PATHDESIRED_MODE_FLYCIRCLERIGHT :
case PATHDESIRED_MODE_FLYCIRCLELEFT :
updatePathVelocity ( ) ;
result = updateFixedDesiredAttitude ( ) ;
if ( result ) {
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_OK ) ;
} else {
pathStatus . Status = PATHSTATUS_STATUS_CRITICAL ;
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_WARNING ) ;
}
break ;
case PATHDESIRED_MODE_FIXEDATTITUDE :
updateFixedAttitude ( pathDesired . ModeParameters ) ;
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_OK ) ;
break ;
case PATHDESIRED_MODE_DISARMALARM :
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_CRITICAL ) ;
break ;
default :
pathStatus . Status = PATHSTATUS_STATUS_CRITICAL ;
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_ERROR ) ;
break ;
}
break ;
default :
// Be cleaner and get rid of global variables
northVelIntegral = 0 ;
eastVelIntegral = 0 ;
downVelIntegral = 0 ;
2013-07-11 16:22:24 +02:00
courseIntegral = 0 ;
2013-05-18 14:17:26 +02:00
speedIntegral = 0 ;
powerIntegral = 0 ;
break ;
}
PathStatusSet ( & pathStatus ) ;
}
2012-05-24 10:53:14 +02:00
}
/**
* Compute desired velocity from the current position and path
*
2013-05-18 19:36:45 +02:00
* Takes in @ ref PositionState and compares it to @ ref PathDesired
2012-05-24 10:53:14 +02:00
* and computes @ ref VelocityDesired
*/
2012-05-28 01:51:17 +02:00
static void updatePathVelocity ( )
2012-05-24 10:53:14 +02:00
{
2013-05-18 19:36:45 +02:00
PositionStateData positionState ;
2013-05-18 14:17:26 +02:00
2013-05-18 19:36:45 +02:00
PositionStateGet ( & positionState ) ;
VelocityStateData velocityState ;
VelocityStateGet ( & velocityState ) ;
2013-05-18 14:17:26 +02:00
2013-07-10 15:55:12 +02:00
// look ahead fixedwingpathfollowerSettings.CourseFeedForward seconds
float cur [ 3 ] = { positionState . North + ( velocityState . North * fixedwingpathfollowerSettings . CourseFeedForward ) ,
positionState . East + ( velocityState . East * fixedwingpathfollowerSettings . CourseFeedForward ) ,
positionState . Down + ( velocityState . Down * fixedwingpathfollowerSettings . CourseFeedForward ) } ;
2013-05-18 14:17:26 +02:00
struct path_status progress ;
2013-09-01 12:10:55 +02:00
path_progress ( cast_struct_to_array ( pathDesired . Start , pathDesired . Start . North ) ,
cast_struct_to_array ( pathDesired . End , pathDesired . End . North ) ,
cur , & progress , pathDesired . Mode ) ;
2013-05-18 14:17:26 +02:00
float groundspeed ;
float altitudeSetpoint ;
switch ( pathDesired . Mode ) {
case PATHDESIRED_MODE_FLYCIRCLERIGHT :
case PATHDESIRED_MODE_DRIVECIRCLERIGHT :
case PATHDESIRED_MODE_FLYCIRCLELEFT :
case PATHDESIRED_MODE_DRIVECIRCLELEFT :
groundspeed = pathDesired . EndingVelocity ;
2013-09-01 12:10:55 +02:00
altitudeSetpoint = pathDesired . End . Down ;
2013-05-18 14:17:26 +02:00
break ;
case PATHDESIRED_MODE_FLYENDPOINT :
case PATHDESIRED_MODE_DRIVEENDPOINT :
case PATHDESIRED_MODE_FLYVECTOR :
case PATHDESIRED_MODE_DRIVEVECTOR :
default :
groundspeed = pathDesired . StartingVelocity + ( pathDesired . EndingVelocity - pathDesired . StartingVelocity ) *
bound ( progress . fractional_progress , 0 , 1 ) ;
2013-09-01 12:10:55 +02:00
altitudeSetpoint = pathDesired . Start . Down + ( pathDesired . End . Down - pathDesired . Start . Down ) *
2013-05-18 14:17:26 +02:00
bound ( progress . fractional_progress , 0 , 1 ) ;
break ;
}
// make sure groundspeed is not zero
if ( groundspeed < 1e-2 f ) {
groundspeed = 1e-2 f ;
}
// calculate velocity - can be zero if waypoints are too close
VelocityDesiredData velocityDesired ;
velocityDesired . North = progress . path_direction [ 0 ] ;
velocityDesired . East = progress . path_direction [ 1 ] ;
float error_speed = progress . error * fixedwingpathfollowerSettings . HorizontalPosP ;
// if a plane is crossing its desired flightpath facing the wrong way (away from flight direction)
// it would turn towards the flightpath to get on its desired course. This however would reverse the correction vector
// once it crosses the flightpath again, which would make it again turn towards the flightpath (but away from its desired heading)
// leading to an S-shape snake course the wrong way
// this only happens especially if HorizontalPosP is too high, as otherwise the angle between velocity desired and path_direction won't
// turn steep unless there is enough space complete the turn before crossing the flightpath
// in this case the plane effectively needs to be turned around
// indicators:
2013-05-18 19:36:45 +02:00
// difference between correction_direction and velocitystate >90 degrees and
// difference between path_direction and velocitystate >90 degrees ( 4th sector, facing away from eerything )
2013-05-18 14:17:26 +02:00
// fix: ignore correction, steer in path direction until the situation has become better (condition doesn't apply anymore)
2013-05-18 19:36:45 +02:00
float angle1 = RAD2DEG ( atan2f ( progress . path_direction [ 1 ] , progress . path_direction [ 0 ] ) - atan2f ( velocityState . East , velocityState . North ) ) ;
float angle2 = RAD2DEG ( atan2f ( progress . correction_direction [ 1 ] , progress . correction_direction [ 0 ] ) - atan2f ( velocityState . East , velocityState . North ) ) ;
2013-05-18 14:17:26 +02:00
if ( angle1 < - 180.0f ) {
angle1 + = 360.0f ;
}
if ( angle1 > 180.0f ) {
angle1 - = 360.0f ;
}
if ( angle2 < - 180.0f ) {
angle2 + = 360.0f ;
}
if ( angle2 > 180.0f ) {
angle2 - = 360.0f ;
}
if ( fabsf ( angle1 ) > = 90.0f & & fabsf ( angle2 ) > = 90.0f ) {
error_speed = 0 ;
}
// calculate correction - can also be zero if correction vector is 0 or no error present
velocityDesired . North + = progress . correction_direction [ 0 ] * error_speed ;
velocityDesired . East + = progress . correction_direction [ 1 ] * error_speed ;
// scale to correct length
float l = sqrtf ( velocityDesired . North * velocityDesired . North + velocityDesired . East * velocityDesired . East ) ;
velocityDesired . North * = groundspeed / l ;
velocityDesired . East * = groundspeed / l ;
2013-05-18 19:36:45 +02:00
float downError = altitudeSetpoint - positionState . Down ;
2013-05-18 14:17:26 +02:00
velocityDesired . Down = downError * fixedwingpathfollowerSettings . VerticalPosP ;
// update pathstatus
pathStatus . error = progress . error ;
pathStatus . fractional_progress = progress . fractional_progress ;
VelocityDesiredSet ( & velocityDesired ) ;
2012-05-24 10:53:14 +02:00
}
2012-05-24 18:19:52 +02:00
/**
* Compute desired attitude from a fixed preset
*
*/
2013-05-18 14:17:26 +02:00
static void updateFixedAttitude ( float * attitude )
2012-05-24 18:19:52 +02:00
{
2013-05-18 14:17:26 +02:00
StabilizationDesiredData stabDesired ;
StabilizationDesiredGet ( & stabDesired ) ;
stabDesired . Roll = attitude [ 0 ] ;
stabDesired . Pitch = attitude [ 1 ] ;
stabDesired . Yaw = attitude [ 2 ] ;
stabDesired . Throttle = attitude [ 3 ] ;
2013-09-01 12:10:55 +02:00
stabDesired . StabilizationMode . Roll = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE ;
stabDesired . StabilizationMode . Pitch = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE ;
stabDesired . StabilizationMode . Yaw = STABILIZATIONDESIRED_STABILIZATIONMODE_RATE ;
2013-05-18 14:17:26 +02:00
StabilizationDesiredSet ( & stabDesired ) ;
2012-05-24 18:19:52 +02:00
}
2012-05-24 10:53:14 +02:00
/**
* Compute desired attitude from the desired velocity
*
2013-05-18 19:36:45 +02:00
* Takes in @ ref NedState which has the acceleration in the
2013-05-18 14:17:26 +02:00
* NED frame as the feedback term and then compares the
2013-05-18 19:36:45 +02:00
* @ ref VelocityState against the @ ref VelocityDesired
2012-05-24 10:53:14 +02:00
*/
2012-05-27 02:38:56 +02:00
static uint8_t updateFixedDesiredAttitude ( )
2012-05-24 10:53:14 +02:00
{
2013-05-18 14:17:26 +02:00
uint8_t result = 1 ;
float dT = fixedwingpathfollowerSettings . UpdatePeriod / 1000.0f ; // Convert from [ms] to [s]
VelocityDesiredData velocityDesired ;
2013-05-18 19:36:45 +02:00
VelocityStateData velocityState ;
2013-05-18 14:17:26 +02:00
StabilizationDesiredData stabDesired ;
2013-05-18 19:36:45 +02:00
AttitudeStateData attitudeState ;
2013-05-18 14:17:26 +02:00
StabilizationSettingsData stabSettings ;
FixedWingPathFollowerStatusData fixedwingpathfollowerStatus ;
2013-05-18 19:36:45 +02:00
AirspeedStateData airspeedState ;
2013-07-10 15:55:12 +02:00
SystemSettingsData systemSettings ;
2013-05-18 14:17:26 +02:00
2013-05-18 19:36:45 +02:00
float groundspeedState ;
2013-05-18 14:17:26 +02:00
float groundspeedDesired ;
2013-05-18 19:36:45 +02:00
float indicatedAirspeedState ;
2013-05-18 14:17:26 +02:00
float indicatedAirspeedDesired ;
float airspeedError ;
float pitchCommand ;
float descentspeedDesired ;
float descentspeedError ;
float powerCommand ;
2013-07-11 16:22:24 +02:00
float bearing ;
float heading ;
float headingError ;
float course ;
float courseError ;
float courseCommand ;
2013-05-18 14:17:26 +02:00
FixedWingPathFollowerStatusGet ( & fixedwingpathfollowerStatus ) ;
2013-05-18 19:36:45 +02:00
VelocityStateGet ( & velocityState ) ;
2013-05-18 14:17:26 +02:00
StabilizationDesiredGet ( & stabDesired ) ;
VelocityDesiredGet ( & velocityDesired ) ;
2013-05-18 19:36:45 +02:00
AttitudeStateGet ( & attitudeState ) ;
2013-05-18 14:17:26 +02:00
StabilizationSettingsGet ( & stabSettings ) ;
2013-05-18 19:36:45 +02:00
AirspeedStateGet ( & airspeedState ) ;
2013-07-10 15:55:12 +02:00
SystemSettingsGet ( & systemSettings ) ;
2013-05-18 14:17:26 +02:00
/**
* Compute speed error ( required for throttle and pitch )
*/
// Current ground speed
2013-05-18 19:36:45 +02:00
groundspeedState = sqrtf ( velocityState . East * velocityState . East + velocityState . North * velocityState . North ) ;
// note that airspeedStateBias is ( calibratedAirspeed - groundSpeed ) at the time of measurement,
2013-05-18 14:17:26 +02:00
// but thanks to accelerometers, groundspeed reacts faster to changes in direction
// than airspeed and gps sensors alone
2013-05-18 19:36:45 +02:00
indicatedAirspeedState = groundspeedState + indicatedAirspeedStateBias ;
2013-05-18 14:17:26 +02:00
// Desired ground speed
2013-05-18 19:36:45 +02:00
groundspeedDesired = sqrtf ( velocityDesired . North * velocityDesired . North + velocityDesired . East * velocityDesired . East ) ;
indicatedAirspeedDesired = bound ( groundspeedDesired + indicatedAirspeedStateBias ,
2013-07-10 15:55:12 +02:00
fixedwingpathfollowerSettings . HorizontalVelMin ,
fixedwingpathfollowerSettings . HorizontalVelMax ) ;
2013-05-18 14:17:26 +02:00
// Airspeed error
2013-05-18 19:36:45 +02:00
airspeedError = indicatedAirspeedDesired - indicatedAirspeedState ;
2013-05-18 14:17:26 +02:00
// Vertical speed error
descentspeedDesired = bound (
velocityDesired . Down ,
- fixedwingpathfollowerSettings . VerticalVelMax ,
fixedwingpathfollowerSettings . VerticalVelMax ) ;
2013-05-18 19:36:45 +02:00
descentspeedError = descentspeedDesired - velocityState . Down ;
2013-05-18 14:17:26 +02:00
// Error condition: plane too slow or too fast
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Errors . Highspeed = 0 ;
fixedwingpathfollowerStatus . Errors . Lowspeed = 0 ;
if ( indicatedAirspeedState > systemSettings . AirSpeedMax * fixedwingpathfollowerSettings . Safetymargins . Overspeed ) {
fixedwingpathfollowerStatus . Errors . Overspeed = 1 ;
2013-05-18 14:17:26 +02:00
result = 0 ;
}
2013-09-01 12:10:55 +02:00
if ( indicatedAirspeedState > fixedwingpathfollowerSettings . HorizontalVelMax * fixedwingpathfollowerSettings . Safetymargins . Highspeed ) {
fixedwingpathfollowerStatus . Errors . Highspeed = 1 ;
2013-05-18 14:17:26 +02:00
result = 0 ;
}
2013-09-01 12:10:55 +02:00
if ( indicatedAirspeedState < fixedwingpathfollowerSettings . HorizontalVelMin * fixedwingpathfollowerSettings . Safetymargins . Lowspeed ) {
fixedwingpathfollowerStatus . Errors . Lowspeed = 1 ;
2013-05-18 14:17:26 +02:00
result = 0 ;
}
2013-09-01 12:10:55 +02:00
if ( indicatedAirspeedState < systemSettings . AirSpeedMin * fixedwingpathfollowerSettings . Safetymargins . Stallspeed ) {
fixedwingpathfollowerStatus . Errors . Stallspeed = 1 ;
2013-05-18 14:17:26 +02:00
result = 0 ;
}
2013-05-18 19:36:45 +02:00
if ( indicatedAirspeedState < 1e-6 f ) {
2013-05-18 14:17:26 +02:00
// prevent division by zero, abort without controlling anything. This guidance mode is not suited for takeoff or touchdown, or handling stationary planes
// also we cannot handle planes flying backwards, lets just wait until the nose drops
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Errors . Lowspeed = 1 ;
2013-05-18 14:17:26 +02:00
return 0 ;
}
/**
* Compute desired throttle command
*/
// compute saturated integral error throttle response. Make integral leaky for better performance. Approximately 30s time constant.
2013-09-01 12:10:55 +02:00
if ( fixedwingpathfollowerSettings . PowerPI . Ki > 0 ) {
2013-05-18 14:17:26 +02:00
powerIntegral = bound ( powerIntegral + - descentspeedError * dT ,
2013-09-01 12:10:55 +02:00
- fixedwingpathfollowerSettings . PowerPI . ILimit / fixedwingpathfollowerSettings . PowerPI . Ki ,
fixedwingpathfollowerSettings . PowerPI . ILimit / fixedwingpathfollowerSettings . PowerPI . Ki
2013-05-18 14:17:26 +02:00
) * ( 1.0f - 1.0f / ( 1.0f + 30.0f / dT ) ) ;
} else { powerIntegral = 0 ; }
// Compute the cross feed from vertical speed to pitch, with saturation
float speedErrorToPowerCommandComponent = bound (
2013-09-01 12:10:55 +02:00
( airspeedError / fixedwingpathfollowerSettings . HorizontalVelMin ) * fixedwingpathfollowerSettings . AirspeedToPowerCrossFeed . Kp ,
- fixedwingpathfollowerSettings . AirspeedToPowerCrossFeed . Max ,
fixedwingpathfollowerSettings . AirspeedToPowerCrossFeed . Max
2013-05-18 14:17:26 +02:00
) ;
// Compute final throttle response
2013-09-01 12:10:55 +02:00
powerCommand = - descentspeedError * fixedwingpathfollowerSettings . PowerPI . Kp +
powerIntegral * fixedwingpathfollowerSettings . PowerPI . Ki +
2013-05-18 14:17:26 +02:00
speedErrorToPowerCommandComponent ;
// Output internal state to telemetry
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Error . Power = descentspeedError ;
fixedwingpathfollowerStatus . ErrorInt . Power = powerIntegral ;
fixedwingpathfollowerStatus . Command . Power = powerCommand ;
2013-05-18 14:17:26 +02:00
// set throttle
2013-09-01 12:10:55 +02:00
stabDesired . Throttle = bound ( fixedwingpathfollowerSettings . ThrottleLimit . Neutral + powerCommand ,
fixedwingpathfollowerSettings . ThrottleLimit . Min ,
fixedwingpathfollowerSettings . ThrottleLimit . Max ) ;
2013-05-18 14:17:26 +02:00
// Error condition: plane cannot hold altitude at current speed.
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Errors . Lowpower = 0 ;
if ( powerCommand > = fixedwingpathfollowerSettings . ThrottleLimit . Max & & // throttle at maximum
2013-05-18 19:36:45 +02:00
velocityState . Down > 0 & & // we ARE going down
2013-05-18 14:17:26 +02:00
descentspeedDesired < 0 & & // we WANT to go up
2013-07-17 14:50:23 +02:00
airspeedError > 0 & & // we are too slow already
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerSettings . Safetymargins . Lowpower > 0.5f ) { // alarm switched on
fixedwingpathfollowerStatus . Errors . Lowpower = 1 ;
2013-05-18 14:17:26 +02:00
result = 0 ;
}
// Error condition: plane keeps climbing despite minimum throttle (opposite of above)
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Errors . Highpower = 0 ;
if ( powerCommand > = fixedwingpathfollowerSettings . ThrottleLimit . Min & & // throttle at minimum
2013-05-18 19:36:45 +02:00
velocityState . Down < 0 & & // we ARE going up
2013-05-18 14:17:26 +02:00
descentspeedDesired > 0 & & // we WANT to go down
2013-07-17 14:50:23 +02:00
airspeedError < 0 & & // we are too fast already
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerSettings . Safetymargins . Highpower > 0.5f ) { // alarm switched on
fixedwingpathfollowerStatus . Errors . Highpower = 1 ;
2013-05-18 14:17:26 +02:00
result = 0 ;
}
/**
* Compute desired pitch command
*/
2013-09-01 12:10:55 +02:00
if ( fixedwingpathfollowerSettings . SpeedPI . Ki > 0 ) {
2013-05-18 14:17:26 +02:00
// Integrate with saturation
airspeedErrorInt = bound ( airspeedErrorInt + airspeedError * dT ,
2013-09-01 12:10:55 +02:00
- fixedwingpathfollowerSettings . SpeedPI . ILimit / fixedwingpathfollowerSettings . SpeedPI . Ki ,
fixedwingpathfollowerSettings . SpeedPI . ILimit / fixedwingpathfollowerSettings . SpeedPI . Ki ) ;
2013-05-18 14:17:26 +02:00
}
// Compute the cross feed from vertical speed to pitch, with saturation
2013-09-01 12:10:55 +02:00
float verticalSpeedToPitchCommandComponent = bound ( - descentspeedError * fixedwingpathfollowerSettings . VerticalToPitchCrossFeed . Kp ,
- fixedwingpathfollowerSettings . VerticalToPitchCrossFeed . Max ,
fixedwingpathfollowerSettings . VerticalToPitchCrossFeed . Max
2013-05-18 14:17:26 +02:00
) ;
// Compute the pitch command as err*Kp + errInt*Ki + X_feed.
2013-09-01 12:10:55 +02:00
pitchCommand = - ( airspeedError * fixedwingpathfollowerSettings . SpeedPI . Kp
+ airspeedErrorInt * fixedwingpathfollowerSettings . SpeedPI . Ki
2013-05-18 14:17:26 +02:00
) + verticalSpeedToPitchCommandComponent ;
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Error . Speed = airspeedError ;
fixedwingpathfollowerStatus . ErrorInt . Speed = airspeedErrorInt ;
fixedwingpathfollowerStatus . Command . Speed = pitchCommand ;
2013-05-18 14:17:26 +02:00
2013-09-01 12:10:55 +02:00
stabDesired . Pitch = bound ( fixedwingpathfollowerSettings . PitchLimit . Neutral + pitchCommand ,
fixedwingpathfollowerSettings . PitchLimit . Min ,
fixedwingpathfollowerSettings . PitchLimit . Max ) ;
2013-05-18 14:17:26 +02:00
// Error condition: high speed dive
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Errors . Pitchcontrol = 0 ;
if ( pitchCommand > = fixedwingpathfollowerSettings . PitchLimit . Max & & // pitch demand is full up
2013-05-18 19:36:45 +02:00
velocityState . Down > 0 & & // we ARE going down
2013-05-18 14:17:26 +02:00
descentspeedDesired < 0 & & // we WANT to go up
2013-07-17 14:50:23 +02:00
airspeedError < 0 & & // we are too fast already
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerSettings . Safetymargins . Pitchcontrol > 0.5f ) { // alarm switched on
fixedwingpathfollowerStatus . Errors . Pitchcontrol = 1 ;
2013-05-18 14:17:26 +02:00
result = 0 ;
}
2013-07-11 16:22:24 +02:00
/**
* Calculate where we are heading and why ( wind issues )
*/
bearing = attitudeState . Yaw ;
heading = RAD2DEG ( atan2f ( velocityState . East , velocityState . North ) ) ;
headingError = heading - bearing ;
if ( headingError < - 180.0f ) {
headingError + = 360.0f ;
}
if ( headingError > 180.0f ) {
headingError - = 360.0f ;
}
// Error condition: wind speed is higher than airspeed. We are forced backwards!
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Errors . Wind = 0 ;
if ( ( headingError > fixedwingpathfollowerSettings . Safetymargins . Wind | |
headingError < - fixedwingpathfollowerSettings . Safetymargins . Wind ) & &
fixedwingpathfollowerSettings . Safetymargins . Highpower > 0.5f ) { // alarm switched on
2013-07-11 16:22:24 +02:00
// we are flying backwards
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Errors . Wind = 1 ;
2013-07-11 16:22:24 +02:00
result = 0 ;
}
2013-05-18 14:17:26 +02:00
/**
* Compute desired roll command
*/
if ( groundspeedDesired > 1e-6 f ) {
2013-07-11 16:22:24 +02:00
course = RAD2DEG ( atan2f ( velocityDesired . East , velocityDesired . North ) ) ;
courseError = course - heading ;
2013-05-18 14:17:26 +02:00
} else {
// if we are not supposed to move, run in a circle
2013-07-11 16:22:24 +02:00
courseError = - 90.0f ;
result = 0 ;
2013-05-18 14:17:26 +02:00
}
2013-07-11 16:22:24 +02:00
if ( courseError < - 180.0f ) {
courseError + = 360.0f ;
2013-05-18 14:17:26 +02:00
}
2013-07-11 16:22:24 +02:00
if ( courseError > 180.0f ) {
courseError - = 360.0f ;
2013-05-18 14:17:26 +02:00
}
2013-09-01 12:10:55 +02:00
courseIntegral = bound ( courseIntegral + courseError * dT * fixedwingpathfollowerSettings . CoursePI . Ki ,
- fixedwingpathfollowerSettings . CoursePI . ILimit ,
fixedwingpathfollowerSettings . CoursePI . ILimit ) ;
courseCommand = ( courseError * fixedwingpathfollowerSettings . CoursePI . Kp +
2013-07-11 16:22:24 +02:00
courseIntegral ) ;
2013-05-18 14:17:26 +02:00
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerStatus . Error . Course = courseError ;
fixedwingpathfollowerStatus . ErrorInt . Course = courseIntegral ;
fixedwingpathfollowerStatus . Command . Course = courseCommand ;
2013-05-18 14:17:26 +02:00
2013-09-01 12:10:55 +02:00
stabDesired . Roll = bound ( fixedwingpathfollowerSettings . RollLimit . Neutral +
2013-07-11 16:22:24 +02:00
courseCommand ,
2013-09-01 12:10:55 +02:00
fixedwingpathfollowerSettings . RollLimit . Min ,
fixedwingpathfollowerSettings . RollLimit . Max ) ;
2013-05-18 14:17:26 +02:00
// TODO: find a check to determine loss of directional control. Likely needs some check of derivative
/**
* Compute desired yaw command
*/
// TODO implement raw control mode for yaw and base on Accels.Y
stabDesired . Yaw = 0 ;
2013-09-01 12:10:55 +02:00
stabDesired . StabilizationMode . Roll = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE ;
stabDesired . StabilizationMode . Pitch = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE ;
stabDesired . StabilizationMode . Yaw = STABILIZATIONDESIRED_STABILIZATIONMODE_NONE ;
2013-05-18 14:17:26 +02:00
StabilizationDesiredSet ( & stabDesired ) ;
FixedWingPathFollowerStatusSet ( & fixedwingpathfollowerStatus ) ;
return result ;
2012-05-24 10:53:14 +02:00
}
/**
* Bound input value between limits
*/
static float bound ( float val , float min , float max )
{
2013-05-18 14:17:26 +02:00
if ( val < min ) {
val = min ;
} else if ( val > max ) {
val = max ;
}
return val ;
2012-05-24 10:53:14 +02:00
}
2013-05-05 09:02:24 +02:00
static void SettingsUpdatedCb ( __attribute__ ( ( unused ) ) UAVObjEvent * ev )
2012-05-24 18:19:52 +02:00
{
2013-05-18 14:17:26 +02:00
FixedWingPathFollowerSettingsGet ( & fixedwingpathfollowerSettings ) ;
PathDesiredGet ( & pathDesired ) ;
2012-05-24 18:19:52 +02:00
}
2012-05-24 10:53:14 +02:00
2013-05-18 19:36:45 +02:00
static void airspeedStateUpdatedCb ( __attribute__ ( ( unused ) ) UAVObjEvent * ev )
2012-05-24 10:53:14 +02:00
{
2013-05-18 19:36:45 +02:00
AirspeedStateData airspeedState ;
VelocityStateData velocityState ;
2012-05-24 10:53:14 +02:00
2013-05-18 19:36:45 +02:00
AirspeedStateGet ( & airspeedState ) ;
VelocityStateGet ( & velocityState ) ;
float groundspeed = sqrtf ( velocityState . East * velocityState . East + velocityState . North * velocityState . North ) ;
2012-05-24 10:53:14 +02:00
2013-05-18 19:36:45 +02:00
indicatedAirspeedStateBias = airspeedState . CalibratedAirspeed - groundspeed ;
2013-05-18 14:17:26 +02:00
// note - we do fly by Indicated Airspeed (== calibrated airspeed)
// however since airspeed is updated less often than groundspeed, we use sudden changes to groundspeed to offset the airspeed by the same measurement.
2012-05-24 10:53:14 +02:00
}