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.
* @ brief This module compared @ ref PositionActuatl to @ ref ActiveWaypoint
* 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
* Input object : PositionActual
* 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
*
*/
# include "openpilot.h"
# include "paths.h"
# include "accels.h"
2012-05-24 18:19:52 +02:00
# include "hwsettings.h"
2012-05-24 10:53:14 +02:00
# include "attitudeactual.h"
# include "pathdesired.h" // object that will be updated by the module
# include "positionactual.h"
# include "manualcontrol.h"
# include "flightstatus.h"
2012-05-24 18:19:52 +02:00
# include "pathstatus.h"
2012-10-28 17:45:40 +01:00
# include "airspeedactual.h"
2012-05-24 10:53:14 +02:00
# include "gpsvelocity.h"
# include "gpsposition.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"
# include "velocityactual.h"
# include "CoordinateConversions.h"
// Private constants
# define MAX_QUEUE_SIZE 4
# define STACK_SIZE_BYTES 1548
# define TASK_PRIORITY (tskIDLE_PRIORITY+2)
# define F_PI 3.14159265358979323846f
# define RAD2DEG (180.0f / F_PI)
2012-05-27 20:49:07 +02:00
# define DEG2RAD (F_PI / 180.0f)
2012-06-06 08:39:45 +02:00
# define GEE 9.81f
2012-05-24 10:53:14 +02:00
// Private types
// 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 ) ;
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 ( ) ;
2012-10-28 17:45:40 +01:00
static void airspeedActualUpdatedCb ( 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
{
2012-05-24 18:19:52 +02:00
if ( followerEnabled ) {
// Start main task
xTaskCreate ( pathfollowerTask , ( signed char * ) " PathFollower " , STACK_SIZE_BYTES / 4 , NULL , TASK_PRIORITY , & pathfollowerTaskHandle ) ;
TaskMonitorAdd ( TASKINFO_RUNNING_PATHFOLLOWER , pathfollowerTaskHandle ) ;
}
2012-05-24 10:53:14 +02:00
return 0 ;
}
/**
* 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
{
2012-05-24 18:19:52 +02:00
HwSettingsInitialize ( ) ;
uint8_t optionalModules [ HWSETTINGS_OPTIONALMODULES_NUMELEM ] ;
HwSettingsOptionalModulesGet ( optionalModules ) ;
2012-05-24 23:28:13 +02:00
if ( optionalModules [ HWSETTINGS_OPTIONALMODULES_FIXEDWINGPATHFOLLOWER ] = = HWSETTINGS_OPTIONALMODULES_ENABLED ) {
2012-05-24 18:19:52 +02:00
followerEnabled = true ;
FixedWingPathFollowerSettingsInitialize ( ) ;
FixedWingPathFollowerStatusInitialize ( ) ;
PathDesiredInitialize ( ) ;
PathStatusInitialize ( ) ;
VelocityDesiredInitialize ( ) ;
2012-10-28 17:45:40 +01:00
AirspeedActualInitialize ( ) ;
2012-05-24 18:19:52 +02:00
} else {
followerEnabled = false ;
}
2012-05-24 10:53:14 +02:00
return 0 ;
}
2012-05-24 11:13:21 +02:00
MODULE_INITCALL ( FixedWingPathFollowerInitialize , FixedWingPathFollowerStart )
2012-05-24 10:53:14 +02:00
static float northVelIntegral = 0 ;
static float eastVelIntegral = 0 ;
static float downVelIntegral = 0 ;
2012-06-15 17:02:24 +02:00
static float bearingIntegral = 0 ;
2012-05-24 10:53:14 +02:00
static float speedIntegral = 0 ;
static float powerIntegral = 0 ;
2012-06-15 17:02:24 +02:00
static float airspeedErrorInt = 0 ;
2012-05-24 10:53:14 +02:00
// correct speed by measured airspeed
2012-10-28 17:45:40 +01:00
static float indicatedAirspeedActualBias = 0 ;
2012-05-24 10:53:14 +02:00
/**
* Module thread , should not return .
*/
2012-05-24 18:19:52 +02:00
static void pathfollowerTask ( void * parameters )
2012-05-24 10:53:14 +02:00
{
SystemSettingsData systemSettings ;
FlightStatusData flightStatus ;
2012-05-24 18:19:52 +02:00
2012-05-24 10:53:14 +02:00
portTickType lastUpdateTime ;
2012-05-24 18:19:52 +02:00
2012-10-28 17:45:40 +01:00
AirspeedActualConnectCallback ( airspeedActualUpdatedCb ) ;
2012-05-24 18:19:52 +02:00
FixedWingPathFollowerSettingsConnectCallback ( SettingsUpdatedCb ) ;
PathDesiredConnectCallback ( SettingsUpdatedCb ) ;
FixedWingPathFollowerSettingsGet ( & fixedwingpathfollowerSettings ) ;
PathDesiredGet ( & pathDesired ) ;
2012-05-24 10:53:14 +02:00
// Main task loop
lastUpdateTime = xTaskGetTickCount ( ) ;
while ( 1 ) {
2012-05-24 18:19:52 +02:00
// 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 ) )
2012-05-24 10:53:14 +02:00
{
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_WARNING ) ;
2012-05-24 18:19:52 +02:00
vTaskDelay ( 1000 ) ;
continue ;
2012-05-24 10:53:14 +02:00
}
// Continue collecting data if not enough time
2012-05-24 23:28:13 +02:00
vTaskDelayUntil ( & lastUpdateTime , fixedwingpathfollowerSettings . UpdatePeriod / portTICK_RATE_MS ) ;
2012-05-24 10:53:14 +02:00
FlightStatusGet ( & flightStatus ) ;
2012-05-24 18:19:52 +02:00
PathStatusGet ( & pathStatus ) ;
2012-05-27 02:38:56 +02:00
uint8_t result ;
2012-05-24 18:19:52 +02:00
// 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 ) {
2012-05-28 01:51:17 +02:00
updatePathVelocity ( ) ;
2012-05-27 02:38:56 +02:00
result = updateFixedDesiredAttitude ( ) ;
if ( result ) {
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_OK ) ;
} else {
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_WARNING ) ;
}
2012-05-24 10:53:14 +02:00
} else {
2012-05-24 18:19:52 +02:00
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 :
2012-05-26 15:55:52 +02:00
case PATHDESIRED_MODE_FLYCIRCLERIGHT :
case PATHDESIRED_MODE_FLYCIRCLELEFT :
2012-05-28 01:51:17 +02:00
updatePathVelocity ( ) ;
2012-05-27 02:38:56 +02:00
result = updateFixedDesiredAttitude ( ) ;
if ( result ) {
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_OK ) ;
} else {
pathStatus . Status = PATHSTATUS_STATUS_CRITICAL ;
AlarmsSet ( SYSTEMALARMS_ALARM_GUIDANCE , SYSTEMALARMS_ALARM_WARNING ) ;
}
2012-05-24 18:19:52 +02:00
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 ;
2012-05-24 10:53:14 +02:00
}
2012-05-24 18:19:52 +02:00
break ;
default :
// Be cleaner and get rid of global variables
northVelIntegral = 0 ;
eastVelIntegral = 0 ;
downVelIntegral = 0 ;
2012-06-15 17:02:24 +02:00
bearingIntegral = 0 ;
2012-05-24 18:19:52 +02:00
speedIntegral = 0 ;
powerIntegral = 0 ;
break ;
2012-05-24 10:53:14 +02:00
}
2012-05-27 02:38:56 +02:00
PathStatusSet ( & pathStatus ) ;
2012-05-24 10:53:14 +02:00
}
}
/**
* Compute desired velocity from the current position and path
*
* Takes in @ ref PositionActual and compares it to @ ref PathDesired
* and computes @ ref VelocityDesired
*/
2012-05-28 01:51:17 +02:00
static void updatePathVelocity ( )
2012-05-24 10:53:14 +02:00
{
PositionActualData positionActual ;
PositionActualGet ( & positionActual ) ;
2012-05-27 20:49:07 +02:00
VelocityActualData velocityActual ;
VelocityActualGet ( & velocityActual ) ;
2012-06-15 17:02:24 +02:00
// look ahead fixedwingpathfollowerSettings.HeadingFeedForward seconds
float cur [ 3 ] = { positionActual . North + ( velocityActual . North * fixedwingpathfollowerSettings . HeadingFeedForward ) ,
positionActual . East + ( velocityActual . East * fixedwingpathfollowerSettings . HeadingFeedForward ) ,
positionActual . Down + ( velocityActual . Down * fixedwingpathfollowerSettings . HeadingFeedForward )
2012-05-27 20:49:07 +02:00
} ;
2012-05-24 10:53:14 +02:00
struct path_status progress ;
2012-05-26 15:55:52 +02:00
2012-05-28 01:51:17 +02:00
path_progress ( pathDesired . Start , pathDesired . End , cur , & progress , pathDesired . Mode ) ;
2012-05-24 10:53:14 +02:00
2012-05-26 15:55:52 +02:00
float groundspeed ;
2012-05-28 01:51:17 +02:00
float altitudeSetpoint ;
switch ( pathDesired . Mode ) {
2012-06-15 17:02:24 +02:00
case PATHDESIRED_MODE_FLYCIRCLERIGHT :
case PATHDESIRED_MODE_DRIVECIRCLERIGHT :
case PATHDESIRED_MODE_FLYCIRCLELEFT :
case PATHDESIRED_MODE_DRIVECIRCLELEFT :
groundspeed = pathDesired . EndingVelocity ;
altitudeSetpoint = pathDesired . End [ 2 ] ;
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 ) *
2012-10-28 21:49:22 +01:00
bound ( progress . fractional_progress , 0 , 1 ) ;
2012-06-15 17:02:24 +02:00
altitudeSetpoint = pathDesired . Start [ 2 ] + ( pathDesired . End [ 2 ] - pathDesired . Start [ 2 ] ) *
2012-10-28 21:49:22 +01:00
bound ( progress . fractional_progress , 0 , 1 ) ;
2012-06-15 17:02:24 +02:00
break ;
2012-05-26 15:55:52 +02:00
}
2012-11-11 16:35:19 +01:00
// make sure groundspeed is not zero
2012-11-23 11:49:17 +01:00
if ( groundspeed < 1e-2 ) groundspeed = 1e-2 ;
2012-05-24 10:53:14 +02:00
2012-05-28 01:51:17 +02:00
// calculate velocity - can be zero if waypoints are too close
2012-05-24 10:53:14 +02:00
VelocityDesiredData velocityDesired ;
2012-11-11 16:35:19 +01:00
velocityDesired . North = progress . path_direction [ 0 ] ;
velocityDesired . East = progress . path_direction [ 1 ] ;
2012-05-24 10:53:14 +02:00
2012-05-24 23:28:13 +02:00
float error_speed = progress . error * fixedwingpathfollowerSettings . HorizontalPosP ;
2012-05-27 02:38:56 +02:00
2012-12-02 19:33:15 +01:00
// 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:
// difference between correction_direction and velocityactual >90 degrees and
// difference between path_direction and velocityactual >90 degrees ( 4th sector, facing away from eerything )
// fix: ignore correction, steer in path direction until the situation has become better (condition doesn't apply anymore)
float angle1 = RAD2DEG * ( atan2f ( progress . path_direction [ 1 ] , progress . path_direction [ 0 ] ) - atan2f ( velocityActual . East , velocityActual . North ) ) ;
float angle2 = RAD2DEG * ( atan2f ( progress . correction_direction [ 1 ] , progress . correction_direction [ 0 ] ) - atan2f ( velocityActual . East , velocityActual . North ) ) ;
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 ( fabs ( angle1 ) > = 90.0f & & fabs ( angle2 ) > = 90.0f ) {
error_speed = 0 ;
}
2012-05-28 01:51:17 +02:00
// calculate correction - can also be zero if correction vector is 0 or no error present
2012-05-26 15:55:52 +02:00
velocityDesired . North + = progress . correction_direction [ 0 ] * error_speed ;
velocityDesired . East + = progress . correction_direction [ 1 ] * error_speed ;
2012-11-11 16:35:19 +01:00
//scale to correct length
float l = sqrtf ( velocityDesired . North * velocityDesired . North + velocityDesired . East * velocityDesired . East ) ;
velocityDesired . North * = groundspeed / l ;
velocityDesired . East * = groundspeed / l ;
2012-05-26 15:55:52 +02:00
2012-05-24 10:53:14 +02:00
float downError = altitudeSetpoint - positionActual . Down ;
2012-05-27 02:38:56 +02:00
velocityDesired . Down = downError * fixedwingpathfollowerSettings . VerticalPosP ;
2012-05-28 01:51:17 +02:00
// update pathstatus
pathStatus . error = progress . error ;
pathStatus . fractional_progress = progress . fractional_progress ;
2012-05-24 10:53:14 +02:00
VelocityDesiredSet ( & velocityDesired ) ;
}
2012-05-24 18:19:52 +02:00
/**
* Compute desired attitude from a fixed preset
*
*/
static void updateFixedAttitude ( float * attitude )
{
StabilizationDesiredData stabDesired ;
StabilizationDesiredGet ( & stabDesired ) ;
stabDesired . Roll = attitude [ 0 ] ;
stabDesired . Pitch = attitude [ 1 ] ;
stabDesired . Yaw = attitude [ 2 ] ;
stabDesired . Throttle = attitude [ 3 ] ;
stabDesired . StabilizationMode [ STABILIZATIONDESIRED_STABILIZATIONMODE_ROLL ] = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE ;
stabDesired . StabilizationMode [ STABILIZATIONDESIRED_STABILIZATIONMODE_PITCH ] = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE ;
stabDesired . StabilizationMode [ STABILIZATIONDESIRED_STABILIZATIONMODE_YAW ] = STABILIZATIONDESIRED_STABILIZATIONMODE_RATE ;
StabilizationDesiredSet ( & stabDesired ) ;
}
2012-05-24 10:53:14 +02:00
/**
* Compute desired attitude from the desired velocity
*
* Takes in @ ref NedActual which has the acceleration in the
* NED frame as the feedback term and then compares the
* @ ref VelocityActual against the @ ref VelocityDesired
*/
2012-05-27 02:38:56 +02:00
static uint8_t updateFixedDesiredAttitude ( )
2012-05-24 10:53:14 +02:00
{
2012-05-27 02:38:56 +02:00
uint8_t result = 1 ;
2012-06-15 17:02:24 +02:00
float dT = fixedwingpathfollowerSettings . UpdatePeriod / 1000.0f ; //Convert from [ms] to [s]
2012-05-24 10:53:14 +02:00
VelocityDesiredData velocityDesired ;
VelocityActualData velocityActual ;
StabilizationDesiredData stabDesired ;
AttitudeActualData attitudeActual ;
AccelsData accels ;
2012-05-24 11:13:21 +02:00
FixedWingPathFollowerSettingsData fixedwingpathfollowerSettings ;
2012-05-24 10:53:14 +02:00
StabilizationSettingsData stabSettings ;
2012-05-24 11:13:21 +02:00
FixedWingPathFollowerStatusData fixedwingpathfollowerStatus ;
2012-10-28 17:45:40 +01:00
AirspeedActualData airspeedActual ;
2012-06-15 17:02:24 +02:00
2012-10-28 17:45:40 +01:00
float groundspeedActual ;
2012-05-27 02:38:56 +02:00
float groundspeedDesired ;
2012-10-28 17:45:40 +01:00
float indicatedAirspeedActual ;
float indicatedAirspeedDesired ;
2012-06-15 17:02:24 +02:00
float airspeedError ;
2012-05-27 02:38:56 +02:00
float pitchCommand ;
2012-05-24 10:53:14 +02:00
2012-06-15 17:02:24 +02:00
float descentspeedDesired ;
float descentspeedError ;
2012-05-24 10:53:14 +02:00
float powerCommand ;
2012-06-15 17:02:24 +02:00
float bearingError ;
2012-07-19 17:13:39 +02:00
float bearingCommand ;
2012-05-27 02:38:56 +02:00
2012-05-24 11:13:21 +02:00
FixedWingPathFollowerSettingsGet ( & fixedwingpathfollowerSettings ) ;
2012-05-24 10:53:14 +02:00
2012-05-24 11:13:21 +02:00
FixedWingPathFollowerStatusGet ( & fixedwingpathfollowerStatus ) ;
2012-05-24 10:53:14 +02:00
VelocityActualGet ( & velocityActual ) ;
StabilizationDesiredGet ( & stabDesired ) ;
VelocityDesiredGet ( & velocityDesired ) ;
AttitudeActualGet ( & attitudeActual ) ;
AccelsGet ( & accels ) ;
StabilizationSettingsGet ( & stabSettings ) ;
2012-10-28 17:45:40 +01:00
AirspeedActualGet ( & airspeedActual ) ;
2012-05-24 10:53:14 +02:00
2012-05-27 02:38:56 +02:00
/**
* Compute speed error ( required for throttle and pitch )
*/
2012-05-24 10:53:14 +02:00
2012-05-27 02:38:56 +02:00
// Current ground speed
2012-10-28 17:45:40 +01:00
groundspeedActual = sqrtf ( velocityActual . East * velocityActual . East + velocityActual . North * velocityActual . North ) ;
// note that airspeedActualBias is ( calibratedAirspeed - groundSpeed ) at the time of measurement,
// but thanks to accelerometers, groundspeed reacts faster to changes in direction
// than airspeed and gps sensors alone
indicatedAirspeedActual = groundspeedActual + indicatedAirspeedActualBias ;
2012-05-24 10:53:14 +02:00
2012-05-27 02:38:56 +02:00
// Desired ground speed
groundspeedDesired = sqrtf ( velocityDesired . North * velocityDesired . North + velocityDesired . East * velocityDesired . East ) ;
2012-10-28 17:45:40 +01:00
indicatedAirspeedDesired = bound ( groundspeedDesired + indicatedAirspeedActualBias ,
2012-06-15 17:02:24 +02:00
fixedwingpathfollowerSettings . BestClimbRateSpeed ,
fixedwingpathfollowerSettings . CruiseSpeed ) ;
2012-05-24 10:53:14 +02:00
2012-05-27 02:38:56 +02:00
// Airspeed error
2012-10-28 17:45:40 +01:00
airspeedError = indicatedAirspeedDesired - indicatedAirspeedActual ;
2012-06-15 17:02:24 +02:00
// Vertical speed error
descentspeedDesired = bound (
2012-05-27 02:38:56 +02:00
velocityDesired . Down ,
- fixedwingpathfollowerSettings . VerticalVelMax ,
fixedwingpathfollowerSettings . VerticalVelMax ) ;
2012-06-15 17:02:24 +02:00
descentspeedError = descentspeedDesired - velocityActual . Down ;
2012-05-27 02:38:56 +02:00
// Error condition: wind speed is higher than maximum allowed speed. We are forced backwards!
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_WIND ] = 0 ;
2012-10-28 17:45:40 +01:00
if ( groundspeedDesired - indicatedAirspeedActualBias < = 0 ) {
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_WIND ] = 1 ;
2012-05-27 02:38:56 +02:00
result = 0 ;
}
// Error condition: plane too slow or too fast
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_HIGHSPEED ] = 0 ;
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_LOWSPEED ] = 0 ;
2012-10-28 17:45:40 +01:00
if ( indicatedAirspeedActual > fixedwingpathfollowerSettings . AirSpeedMax ) {
2012-06-15 17:02:24 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_OVERSPEED ] = 1 ;
result = 0 ;
}
2012-10-28 17:45:40 +01:00
if ( indicatedAirspeedActual > fixedwingpathfollowerSettings . CruiseSpeed * 1.2f ) {
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_HIGHSPEED ] = 1 ;
result = 0 ;
}
2012-10-28 17:45:40 +01:00
if ( indicatedAirspeedActual < fixedwingpathfollowerSettings . BestClimbRateSpeed * 0.8f & & 1 ) { //The next three && 1 are placeholders for UAVOs representing LANDING and TAKEOFF
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_LOWSPEED ] = 1 ;
2012-05-27 02:38:56 +02:00
result = 0 ;
}
2012-10-28 17:45:40 +01:00
if ( indicatedAirspeedActual < fixedwingpathfollowerSettings . StallSpeedClean & & 1 & & 1 ) { //Where the && 1 represents the UAVO that will control whether the airplane is prepped for landing or not
2012-06-15 17:02:24 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_STALLSPEED ] = 1 ;
result = 0 ;
}
2012-10-28 17:45:40 +01:00
if ( indicatedAirspeedActual < fixedwingpathfollowerSettings . StallSpeedDirty & & 1 ) {
2012-06-15 17:02:24 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_STALLSPEED ] = 1 ;
result = 0 ;
}
2012-10-28 17:45:40 +01:00
if ( indicatedAirspeedActual < 1e-6 ) {
2012-05-27 20:49:07 +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
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_LOWSPEED ] = 1 ;
2012-05-27 20:49:07 +02:00
return 0 ;
}
2012-05-24 10:53:14 +02:00
2012-05-27 02:38:56 +02:00
/**
* Compute desired throttle command
*/
2012-06-15 17:02:24 +02:00
// compute saturated integral error throttle response. Make integral leaky for better performance. Approximately 30s time constant.
if ( fixedwingpathfollowerSettings . PowerPI [ FIXEDWINGPATHFOLLOWERSETTINGS_POWERPI_KI ] > 0 ) {
powerIntegral = bound ( powerIntegral + - descentspeedError * dT ,
- fixedwingpathfollowerSettings . PowerPI [ FIXEDWINGPATHFOLLOWERSETTINGS_POWERPI_ILIMIT ] / fixedwingpathfollowerSettings . PowerPI [ FIXEDWINGPATHFOLLOWERSETTINGS_POWERPI_KI ] ,
fixedwingpathfollowerSettings . PowerPI [ FIXEDWINGPATHFOLLOWERSETTINGS_POWERPI_ILIMIT ] / fixedwingpathfollowerSettings . PowerPI [ FIXEDWINGPATHFOLLOWERSETTINGS_POWERPI_KI ]
) * ( 1.0f - 1.0f / ( 1.0f + 30.0f / dT ) ) ;
2012-10-28 17:45:40 +01:00
} else powerIntegral = 0 ;
2012-06-15 17:02:24 +02:00
2012-11-11 16:35:19 +01:00
//Compute the cross feed from vertical speed to pitch, with saturation
float speedErrorToPowerCommandComponent = bound (
( airspeedError / fixedwingpathfollowerSettings . BestClimbRateSpeed ) * fixedwingpathfollowerSettings . AirspeedToPowerCrossFeed [ FIXEDWINGPATHFOLLOWERSETTINGS_AIRSPEEDTOPOWERCROSSFEED_KP ] ,
- fixedwingpathfollowerSettings . AirspeedToPowerCrossFeed [ FIXEDWINGPATHFOLLOWERSETTINGS_AIRSPEEDTOPOWERCROSSFEED_MAX ] ,
fixedwingpathfollowerSettings . AirspeedToPowerCrossFeed [ FIXEDWINGPATHFOLLOWERSETTINGS_AIRSPEEDTOPOWERCROSSFEED_MAX ]
) ;
2012-06-15 17:02:24 +02:00
// Compute final throttle response
2012-11-11 16:35:19 +01:00
powerCommand = - descentspeedError * fixedwingpathfollowerSettings . PowerPI [ FIXEDWINGPATHFOLLOWERSETTINGS_POWERPI_KP ] +
powerIntegral * fixedwingpathfollowerSettings . PowerPI [ FIXEDWINGPATHFOLLOWERSETTINGS_POWERPI_KI ] +
speedErrorToPowerCommandComponent ;
2012-05-24 10:53:14 +02:00
2012-06-15 17:02:24 +02:00
//Output internal state to telemetry
2012-11-11 16:35:19 +01:00
fixedwingpathfollowerStatus . Error [ FIXEDWINGPATHFOLLOWERSTATUS_ERROR_POWER ] = descentspeedError ;
2012-06-15 17:02:24 +02:00
fixedwingpathfollowerStatus . ErrorInt [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORINT_POWER ] = powerIntegral ;
fixedwingpathfollowerStatus . Command [ FIXEDWINGPATHFOLLOWERSTATUS_COMMAND_POWER ] = powerCommand ;
2012-05-24 10:53:14 +02:00
// set throttle
2012-11-11 16:35:19 +01:00
stabDesired . Throttle = bound ( fixedwingpathfollowerSettings . ThrottleLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_THROTTLELIMIT_NEUTRAL ] + powerCommand ,
fixedwingpathfollowerSettings . ThrottleLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_THROTTLELIMIT_MIN ] ,
fixedwingpathfollowerSettings . ThrottleLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_THROTTLELIMIT_MAX ] ) ;
2012-05-24 10:53:14 +02:00
2012-05-27 02:38:56 +02:00
// Error condition: plane cannot hold altitude at current speed.
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_LOWPOWER ] = 0 ;
2012-05-27 02:38:56 +02:00
if (
powerCommand = = fixedwingpathfollowerSettings . ThrottleLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_THROTTLELIMIT_MAX ] // throttle at maximum
& & velocityActual . Down > 0 // we ARE going down
2012-06-15 17:02:24 +02:00
& & descentspeedDesired < 0 // we WANT to go up
& & airspeedError > 0 // we are too slow already
)
{
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_LOWPOWER ] = 1 ;
2012-05-27 02:38:56 +02:00
result = 0 ;
}
// Error condition: plane keeps climbing despite minimum throttle (opposite of above)
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_HIGHPOWER ] = 0 ;
2012-05-27 02:38:56 +02:00
if (
powerCommand = = fixedwingpathfollowerSettings . ThrottleLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_THROTTLELIMIT_MIN ] // throttle at minimum
& & velocityActual . Down < 0 // we ARE going up
2012-06-15 17:02:24 +02:00
& & descentspeedDesired > 0 // we WANT to go down
& & airspeedError < 0 // we are too fast already
)
{
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_HIGHPOWER ] = 1 ;
2012-05-27 02:38:56 +02:00
result = 0 ;
}
/**
* Compute desired pitch command
*/
2012-06-15 17:02:24 +02:00
2012-10-28 21:49:22 +01:00
if ( fixedwingpathfollowerSettings . SpeedPI [ FIXEDWINGPATHFOLLOWERSETTINGS_SPEEDPI_KI ] > 0 ) {
//Integrate with saturation
airspeedErrorInt = bound ( airspeedErrorInt + airspeedError * dT ,
- fixedwingpathfollowerSettings . SpeedPI [ FIXEDWINGPATHFOLLOWERSETTINGS_SPEEDPI_ILIMIT ] / fixedwingpathfollowerSettings . SpeedPI [ FIXEDWINGPATHFOLLOWERSETTINGS_SPEEDPI_KI ] ,
fixedwingpathfollowerSettings . SpeedPI [ FIXEDWINGPATHFOLLOWERSETTINGS_SPEEDPI_ILIMIT ] / fixedwingpathfollowerSettings . SpeedPI [ FIXEDWINGPATHFOLLOWERSETTINGS_SPEEDPI_KI ] ) ;
}
2012-05-27 02:38:56 +02:00
2012-10-28 21:49:22 +01:00
//Compute the cross feed from vertical speed to pitch, with saturation
float verticalSpeedToPitchCommandComponent = bound ( - descentspeedError * fixedwingpathfollowerSettings . VerticalToPitchCrossFeed [ FIXEDWINGPATHFOLLOWERSETTINGS_VERTICALTOPITCHCROSSFEED_KP ] ,
- fixedwingpathfollowerSettings . VerticalToPitchCrossFeed [ FIXEDWINGPATHFOLLOWERSETTINGS_VERTICALTOPITCHCROSSFEED_MAX ] ,
fixedwingpathfollowerSettings . VerticalToPitchCrossFeed [ FIXEDWINGPATHFOLLOWERSETTINGS_VERTICALTOPITCHCROSSFEED_MAX ]
) ;
//Compute the pitch command as err*Kp + errInt*Ki + X_feed.
pitchCommand = - ( airspeedError * fixedwingpathfollowerSettings . SpeedPI [ FIXEDWINGPATHFOLLOWERSETTINGS_SPEEDPI_KP ]
+ airspeedErrorInt * fixedwingpathfollowerSettings . SpeedPI [ FIXEDWINGPATHFOLLOWERSETTINGS_SPEEDPI_KI ]
) + verticalSpeedToPitchCommandComponent ;
fixedwingpathfollowerStatus . Error [ FIXEDWINGPATHFOLLOWERSTATUS_ERROR_SPEED ] = airspeedError ;
fixedwingpathfollowerStatus . ErrorInt [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORINT_SPEED ] = airspeedErrorInt ;
fixedwingpathfollowerStatus . Command [ FIXEDWINGPATHFOLLOWERSTATUS_COMMAND_SPEED ] = pitchCommand ;
2012-06-15 17:02:24 +02:00
2012-05-27 02:38:56 +02:00
stabDesired . Pitch = bound ( fixedwingpathfollowerSettings . PitchLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_PITCHLIMIT_NEUTRAL ] +
pitchCommand ,
fixedwingpathfollowerSettings . PitchLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_PITCHLIMIT_MIN ] ,
fixedwingpathfollowerSettings . PitchLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_PITCHLIMIT_MAX ] ) ;
// Error condition: high speed dive
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_PITCHCONTROL ] = 0 ;
2012-05-27 02:38:56 +02:00
if (
pitchCommand = = fixedwingpathfollowerSettings . PitchLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_PITCHLIMIT_MAX ] // pitch demand is full up
& & velocityActual . Down > 0 // we ARE going down
2012-06-15 17:02:24 +02:00
& & descentspeedDesired < 0 // we WANT to go up
& & airspeedError < 0 // we are too fast already
2012-05-27 02:38:56 +02:00
) {
2012-05-28 14:46:03 +02:00
fixedwingpathfollowerStatus . Errors [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORS_PITCHCONTROL ] = 1 ;
2012-05-27 02:38:56 +02:00
result = 0 ;
}
/**
* Compute desired roll command
*/
if ( groundspeedDesired > 1e-6 ) {
2012-06-15 17:02:24 +02:00
bearingError = RAD2DEG * ( atan2f ( velocityDesired . East , velocityDesired . North ) - atan2f ( velocityActual . East , velocityActual . North ) ) ;
2012-05-27 02:38:56 +02:00
} else {
2012-12-02 19:33:15 +01:00
// if we are not supposed to move, run in a circle
bearingError = - 90.0f ;
2012-05-27 02:38:56 +02:00
}
2012-06-15 17:02:24 +02:00
if ( bearingError < - 180.0f ) bearingError + = 360.0f ;
if ( bearingError > 180.0f ) bearingError - = 360.0f ;
bearingIntegral = bound ( bearingIntegral + bearingError * dT * fixedwingpathfollowerSettings . BearingPI [ FIXEDWINGPATHFOLLOWERSETTINGS_BEARINGPI_KI ] ,
- fixedwingpathfollowerSettings . BearingPI [ FIXEDWINGPATHFOLLOWERSETTINGS_BEARINGPI_ILIMIT ] ,
fixedwingpathfollowerSettings . BearingPI [ FIXEDWINGPATHFOLLOWERSETTINGS_BEARINGPI_ILIMIT ] ) ;
2012-07-19 17:13:39 +02:00
bearingCommand = ( bearingError * fixedwingpathfollowerSettings . BearingPI [ FIXEDWINGPATHFOLLOWERSETTINGS_BEARINGPI_KP ] +
2012-06-15 17:02:24 +02:00
bearingIntegral ) ;
fixedwingpathfollowerStatus . Error [ FIXEDWINGPATHFOLLOWERSTATUS_ERROR_BEARING ] = bearingError ;
fixedwingpathfollowerStatus . ErrorInt [ FIXEDWINGPATHFOLLOWERSTATUS_ERRORINT_BEARING ] = bearingIntegral ;
2012-07-19 17:13:39 +02:00
fixedwingpathfollowerStatus . Command [ FIXEDWINGPATHFOLLOWERSTATUS_COMMAND_BEARING ] = bearingCommand ;
2012-05-27 02:38:56 +02:00
stabDesired . Roll = bound ( fixedwingpathfollowerSettings . RollLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_ROLLLIMIT_NEUTRAL ] +
2012-07-19 17:13:39 +02:00
bearingCommand ,
2012-05-27 02:38:56 +02:00
fixedwingpathfollowerSettings . RollLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_ROLLLIMIT_MIN ] ,
fixedwingpathfollowerSettings . RollLimit [ FIXEDWINGPATHFOLLOWERSETTINGS_ROLLLIMIT_MAX ] ) ;
// TODO: find a check to determine loss of directional control. Likely needs some check of derivative
/**
* Compute desired yaw command
*/
2012-10-28 17:45:40 +01:00
// TODO implement raw control mode for yaw and base on Accels.Y
2012-05-27 02:38:56 +02:00
stabDesired . Yaw = 0 ;
2012-05-24 10:53:14 +02:00
stabDesired . StabilizationMode [ STABILIZATIONDESIRED_STABILIZATIONMODE_ROLL ] = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE ;
stabDesired . StabilizationMode [ STABILIZATIONDESIRED_STABILIZATIONMODE_PITCH ] = STABILIZATIONDESIRED_STABILIZATIONMODE_ATTITUDE ;
2012-05-24 23:28:13 +02:00
stabDesired . StabilizationMode [ STABILIZATIONDESIRED_STABILIZATIONMODE_YAW ] = STABILIZATIONDESIRED_STABILIZATIONMODE_NONE ;
2012-05-24 10:53:14 +02:00
StabilizationDesiredSet ( & stabDesired ) ;
2012-05-24 11:13:21 +02:00
FixedWingPathFollowerStatusSet ( & fixedwingpathfollowerStatus ) ;
2012-05-27 02:38:56 +02:00
return result ;
2012-05-24 10:53:14 +02:00
}
/**
* Bound input value between limits
*/
static float bound ( float val , float min , float max )
{
if ( val < min ) {
val = min ;
} else if ( val > max ) {
val = max ;
}
return val ;
}
2012-05-24 18:19:52 +02:00
static void SettingsUpdatedCb ( UAVObjEvent * ev )
{
2012-05-24 23:28:13 +02:00
FixedWingPathFollowerSettingsGet ( & fixedwingpathfollowerSettings ) ;
2012-05-24 18:19:52 +02:00
PathDesiredGet ( & pathDesired ) ;
}
2012-05-24 10:53:14 +02:00
2012-10-28 17:45:40 +01:00
static void airspeedActualUpdatedCb ( UAVObjEvent * ev )
2012-05-24 10:53:14 +02:00
{
2012-10-28 17:45:40 +01:00
AirspeedActualData airspeedActual ;
2012-05-24 10:53:14 +02:00
VelocityActualData velocityActual ;
2012-10-28 17:45:40 +01:00
AirspeedActualGet ( & airspeedActual ) ;
VelocityActualGet ( & velocityActual ) ;
float groundspeed = sqrtf ( velocityActual . East * velocityActual . East + velocityActual . North * velocityActual . North ) ;
2012-05-24 10:53:14 +02:00
2012-10-28 17:45:40 +01:00
indicatedAirspeedActualBias = airspeedActual . CalibratedAirspeed - groundspeed ;
// 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
}