Skip to content

Commit 4dce637

Browse files
Thomas StastnyJaeyoung-Lim
authored andcommitted
new bearing feasibility for handling zero airspeed
- simpler bearing feasibility function - get rid of wind ratio - replace wind ratio buffer with airspeed buffer
1 parent 5398428 commit 4dce637

File tree

5 files changed

+62
-112
lines changed

5 files changed

+62
-112
lines changed

src/lib/npfg/npfg.cpp

Lines changed: 32 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -56,45 +56,33 @@ void NPFG::evaluate(const Vector2f &ground_vel, const Vector2f &wind_vel, Vector
5656
Vector2f air_vel = ground_vel - wind_vel;
5757
const float airspeed = air_vel.norm();
5858

59-
if (airspeed < MIN_AIRSPEED) {
60-
// this case should only ever happen if we have not launched, the wind
61-
// estimator has failed, or the aircraft is legitimately in a very sad
62-
// situation
63-
airspeed_ref_ = airspeed_nom_;
64-
lateral_accel_ = 0.0f;
65-
feas_ = 0.0f;
66-
feas_on_track_ = 0.0f;
67-
return;
68-
}
69-
7059
const float wind_speed = wind_vel.norm();
71-
const float wind_ratio = wind_speed / airspeed;
7260

7361
float track_error = fabsf(signed_track_error);
7462

63+
// track error bound is dynamic depending on ground speed
64+
track_error_bound_ = trackErrorBound(ground_speed, time_const_);
65+
float normalized_track_error = normalizedTrackError(track_error, track_error_bound_);
66+
67+
// look ahead angle based purely on track proximity
68+
float look_ahead_ang = lookAheadAngle(normalized_track_error);
69+
70+
bearing_vec_ = bearingVec(unit_path_tangent, look_ahead_ang, signed_track_error);
71+
7572
// on-track wind triangle projections
7673
float wind_cross_upt = cross2D(wind_vel, unit_path_tangent);
7774
float wind_dot_upt = wind_vel.dot(unit_path_tangent);
7875

7976
// calculate the bearing feasibility on the track at the current closest point
80-
feas_on_track_ = bearingFeasibility(wind_cross_upt, wind_dot_upt, wind_speed, wind_ratio);
77+
feas_on_track_ = bearingFeasibility(wind_cross_upt, wind_dot_upt, airspeed, wind_speed);
8178

8279
// update control parameters considering upper and lower stability bounds (if enabled)
8380
// must be called before trackErrorBound() as it updates time_const_
84-
adapted_period_ = adaptPeriod(ground_speed, airspeed, wind_ratio, track_error,
81+
adapted_period_ = adaptPeriod(ground_speed, airspeed, wind_speed, track_error,
8582
path_curvature, wind_vel, unit_path_tangent, feas_on_track_);
8683
p_gain_ = pGain(adapted_period_, damping_);
8784
time_const_ = timeConst(adapted_period_, damping_);
8885

89-
// track error bound is dynamic depending on ground speed
90-
track_error_bound_ = trackErrorBound(ground_speed, time_const_);
91-
float normalized_track_error = normalizedTrackError(track_error, track_error_bound_);
92-
93-
// look ahead angle based purely on track proximity
94-
float look_ahead_ang = lookAheadAngle(normalized_track_error);
95-
96-
bearing_vec_ = bearingVec(unit_path_tangent, look_ahead_ang, signed_track_error);
97-
9886
// specific waypoint logic complications... handles case where we are following
9987
// waypoints and are in front of the first of the segment.
10088
// TODO: find a way to get this out of the main eval method!
@@ -118,9 +106,9 @@ void NPFG::evaluate(const Vector2f &ground_vel, const Vector2f &wind_vel, Vector
118106

119107
wind_cross_upt = cross2D(wind_vel, unit_path_tangent);
120108
wind_dot_upt = wind_vel.dot(unit_path_tangent);
121-
feas_on_track_ = bearingFeasibility(wind_cross_upt, wind_dot_upt, wind_speed, wind_ratio);
109+
feas_on_track_ = bearingFeasibility(wind_cross_upt, wind_dot_upt, airspeed, wind_speed);
122110

123-
adapted_period_ = adaptPeriod(ground_speed, airspeed, wind_ratio, track_error,
111+
adapted_period_ = adaptPeriod(ground_speed, airspeed, wind_speed, track_error,
124112
path_curvature, wind_vel, unit_path_tangent, feas_on_track_);
125113
p_gain_ = pGain(adapted_period_, damping_);
126114
time_const_ = timeConst(adapted_period_, damping_);
@@ -133,7 +121,7 @@ void NPFG::evaluate(const Vector2f &ground_vel, const Vector2f &wind_vel, Vector
133121
const float wind_dot_bearing = wind_vel.dot(bearing_vec_);
134122

135123
// continuous representation of the bearing feasibility
136-
feas_ = bearingFeasibility(wind_cross_bearing, wind_dot_bearing, wind_speed, wind_ratio);
124+
feas_ = bearingFeasibility(wind_cross_bearing, wind_dot_bearing, airspeed, wind_speed);
137125

138126
// we consider feasibility of both the current bearing as well as that on the track at the current closest point
139127
const float feas_combined = feas_ * feas_on_track_;
@@ -152,7 +140,7 @@ void NPFG::evaluate(const Vector2f &ground_vel, const Vector2f &wind_vel, Vector
152140

153141
// lateral acceleration needed to stay on curved track (assuming no heading error)
154142
lateral_accel_ff_ = lateralAccelFF(unit_path_tangent, ground_vel, wind_dot_upt,
155-
wind_cross_upt, airspeed, wind_speed, wind_ratio, signed_track_error, path_curvature);
143+
wind_cross_upt, airspeed, wind_speed, signed_track_error, path_curvature);
156144

157145
// total lateral acceleration to drive aircaft towards track as well as account
158146
// for path curvature. The full effect of the feed-forward acceleration is smoothly
@@ -161,13 +149,13 @@ void NPFG::evaluate(const Vector2f &ground_vel, const Vector2f &wind_vel, Vector
161149
lateral_accel_ = lateral_accel + feas_combined * track_proximity_ * lateral_accel_ff_;
162150
} // evaluate
163151

164-
float NPFG::adaptPeriod(const float ground_speed, const float airspeed, const float wind_ratio,
152+
float NPFG::adaptPeriod(const float ground_speed, const float airspeed, const float wind_speed,
165153
const float track_error, const float path_curvature, const Vector2f &wind_vel,
166154
const Vector2f &unit_path_tangent, const float feas_on_track) const
167155
{
168156
float period = period_;
169157
const float air_turn_rate = fabsf(path_curvature * airspeed);
170-
const float wind_factor = windFactor(wind_ratio);
158+
const float wind_factor = windFactor(airspeed, wind_speed);
171159

172160
if (en_period_lb_) {
173161
// lower bound the period for stability w.r.t. roll time constant and current flight condition
@@ -213,10 +201,15 @@ float NPFG::normalizedTrackError(const float track_error, const float track_erro
213201
return math::constrain(track_error / track_error_bound, 0.0f, 1.0f);
214202
}
215203

216-
float NPFG::windFactor(const float wind_ratio) const
204+
float NPFG::windFactor(const float airspeed, const float wind_speed) const
217205
{
218206
// See [TODO: include citation] for definition/elaboration of this approximation.
219-
return 2.0f * (1.0f - sqrtf(1.0f - math::min(1.0f, wind_ratio)));
207+
if (wind_speed > airspeed || airspeed < EPSILON) {
208+
return 2.0f;
209+
210+
} else {
211+
return 2.0f * (1.0f - sqrtf(1.0f - math::min(1.0f, wind_speed / airspeed)));
212+
}
220213
} // windFactor
221214

222215
float NPFG::periodUB(const float air_turn_rate, const float wind_factor, const float feas_on_track) const
@@ -419,58 +412,23 @@ Vector2f NPFG::infeasibleAirVelRef(const Vector2f &wind_vel, const Vector2f &bea
419412
return air_vel_ref.normalized() * airspeed;
420413
} // infeasibleAirVelRef
421414

422-
float NPFG::bearingFeasibility(const float wind_cross_bearing, const float wind_dot_bearing, const float wind_speed,
423-
const float wind_ratio) const
415+
float NPFG::bearingFeasibility(float wind_cross_bearing, const float wind_dot_bearing, const float airspeed,
416+
const float wind_speed) const
424417
{
425-
float sin_cross_wind_ang; // in [0, 1] (constant after 90 deg)
426-
427-
if (wind_dot_bearing <= 0.0f) {
428-
sin_cross_wind_ang = 1.0f;
418+
if (wind_dot_bearing < 0.0f) {
419+
wind_cross_bearing = wind_speed;
429420

430421
} else {
431-
sin_cross_wind_ang = fabsf(wind_cross_bearing / wind_speed);
432-
}
433-
434-
// upper and lower feasibility barriers
435-
float wind_ratio_ub, wind_ratio_lb;
436-
437-
if (sin_cross_wind_ang < CROSS_WIND_ANG_CO) { // small angle approx.
438-
// linear feasibility function (avoid singularity)
439-
440-
const float wind_ratio_ub_co = ONE_DIV_SIN_CROSS_WIND_ANG_CO;
441-
wind_ratio_ub = wind_ratio_ub_co + CO_SLOPE * (CROSS_WIND_ANG_CO - sin_cross_wind_ang);
442-
443-
const float wind_ratio_lb_co = (ONE_DIV_SIN_CROSS_WIND_ANG_CO - 2.0f) * wind_ratio_buffer_ + 1.0f;
444-
wind_ratio_lb = wind_ratio_lb_co + wind_ratio_buffer_ * CO_SLOPE * (CROSS_WIND_ANG_CO - sin_cross_wind_ang);
445-
446-
} else {
447-
const float one_div_sin_cross_wind_ang = 1.0f / sin_cross_wind_ang;
448-
wind_ratio_ub = one_div_sin_cross_wind_ang;
449-
wind_ratio_lb = (one_div_sin_cross_wind_ang - 2.0f) * wind_ratio_buffer_ + 1.0f;
450-
}
451-
452-
// calculate bearing feasibility
453-
float feas = 1.0f; // feasible
454-
455-
if (wind_ratio > wind_ratio_ub) {
456-
// infeasible
457-
feas = 0.0f;
458-
459-
} else if (wind_ratio > wind_ratio_lb) {
460-
// partially feasible
461-
// smoothly transition from fully feasible to fully infeasible
462-
feas = cosf(M_PI_F * 0.5f * math::constrain((wind_ratio - wind_ratio_lb) / (wind_ratio_ub - wind_ratio_lb), 0.0f,
463-
1.0f));
464-
feas *= feas;
422+
wind_cross_bearing = fabsf(wind_cross_bearing);
465423
}
466424

467-
return feas;
425+
float sin_arg = sinf(M_PI_F * 0.5f * math::constrain((airspeed - wind_cross_bearing) / airspeed_buffer_, 0.0f, 1.0f));
426+
return sin_arg * sin_arg;
468427
} // bearingFeasibility
469428

470429
float NPFG::lateralAccelFF(const Vector2f &unit_path_tangent, const Vector2f &ground_vel,
471430
const float wind_dot_upt, const float wind_cross_upt, const float airspeed,
472-
const float wind_speed, const float wind_ratio, const float signed_track_error,
473-
const float path_curvature) const
431+
const float wind_speed, const float signed_track_error, const float path_curvature) const
474432
{
475433
// NOTE: all calculations within this function take place at the closet point
476434
// on the path, as if the aircraft were already tracking the given path at

src/lib/npfg/npfg.hpp

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -118,22 +118,22 @@ class NPFG
118118
/*
119119
* Set the nominal airspeed reference [m/s].
120120
*/
121-
void setAirspeedNom(float airsp) { airspeed_nom_ = math::constrain(airsp, MIN_AIRSPEED, airspeed_max_); }
121+
void setAirspeedNom(float airsp) { airspeed_nom_ = math::max(airsp, 0.1f); }
122122

123123
/*
124124
* Set the maximum airspeed reference [m/s].
125125
*/
126-
void setAirspeedMax(float airsp) { airspeed_max_ = math::max(airsp, airspeed_nom_); }
126+
void setAirspeedMax(float airsp) { airspeed_max_ = math::max(airsp, 0.1f); }
127127

128128
/*
129129
* Set the autopilot roll response time constant [s].
130130
*/
131131
void setRollTimeConst(float tc) { roll_time_const_ = math::max(tc, 0.1f); }
132132

133133
/*
134-
* Set the wind ratio buffer size.
134+
* Set the airspeed buffer size.
135135
*/
136-
void setWindRatioBuffer(float buf) { wind_ratio_buffer_ = math::constrain(buf, 0.01f, 0.2f); }
136+
void setAirspeedBuffer(float buf) { airspeed_buffer_ = math::max(buf, 0.1f); }
137137

138138
/*
139139
* @return Controller proportional gain [rad/s]
@@ -335,22 +335,14 @@ class NPFG
335335
private:
336336

337337
static constexpr float EPSILON = 1.0e-4;
338-
static constexpr float MIN_AIRSPEED = 1.0f; // constrain airspeed to avoid singularities [m/s]
339338
static constexpr float MIN_RADIUS = 0.5f; // minimum effective radius (avoid singularities) [m]
340-
static constexpr float PERIOD_SAFETY_FACTOR = 4.0f; // multiplier for period lower bound
339+
static constexpr float PERIOD_SAFETY_FACTOR = 4.0f; // multiplier for period lower bound [s]
341340

342-
/* pre-computed constants for linear cut-off function for bearing feasibility calculation */
343-
static constexpr float CROSS_WIND_ANG_CO =
344-
0.02; // cross wind angle cut-off below which the feasibility barrier function is finite and linear [rad] (= approx. 1 deg)
345-
static constexpr float ONE_DIV_SIN_CROSS_WIND_ANG_CO = 50.003333488895450; // 1/sin(CROSS_WIND_ANG_CO)
346-
static constexpr float CO_SLOPE =
347-
2499.833309998360; // cross wind angle cut-off slope = cos(CROSS_WIND_ANG_CO)/sin(CROSS_WIND_ANG_CO)^2
348-
349-
float period_{30.0f}; // nominal (desired) period -- user defined [s]
350-
float damping_{0.25f}; // nominal (desired) damping ratio -- user defined
351-
float p_gain_{0.12566f}; // proportional gain (computed from period_ and damping_) [rad/s]
352-
float time_const_{9.0f}; // time constant (computed from period_ and damping_) [s]
353-
float adapted_period_{30.0f}; // auto-adapted period (if stability bounds enabled) [s]
341+
float period_{20.0f}; // nominal (desired) period -- user defined [s]
342+
float damping_{0.7071f}; // nominal (desired) damping ratio -- user defined
343+
float p_gain_{0.4442}; // proportional gain (computed from period_ and damping_) [rad/s]
344+
float time_const_{14.142f}; // time constant (computed from period_ and damping_) [s]
345+
float adapted_period_{20.0f}; // auto-adapted period (if stability bounds enabled) [s]
354346

355347
bool en_period_lb_{true}; // enables automatic lower bound constraints on controller period
356348
bool en_period_ub_{true}; // enables automatic upper bound constraints on controller period (remains disabled if lower bound is disabled)
@@ -368,7 +360,7 @@ class NPFG
368360
// ^determines at what fraction of the normalized track error the maximum track keeping forward ground speed demand is reached
369361
float feas_{1.0f}; // continous representation of bearing feasibility in [0,1] (0=infeasible, 1=feasible)
370362
float feas_on_track_{1.0f}; // continuous bearing feasibility "on track"
371-
float wind_ratio_buffer_{0.1f}; // a buffer region below unity wind ratio allowing continuous transition between feasible and infeasible conditions/commands
363+
float airspeed_buffer_{1.5f}; // size of the region above the feasibility boundary (into feasible space) where a continuous transition from feasible to infeasible is imposed [m/s]
372364

373365
float track_error_bound_{135.0f}; // the current ground speed dependent track error bound [m]
374366
float track_proximity_{0.0f}; // value in [0,1] indicating proximity to track, 0 = at track error boundary or beyond, 1 = on track
@@ -420,7 +412,7 @@ class NPFG
420412
*
421413
* @param[in] ground_speed Vehicle ground speed [m/s]
422414
* @param[in] airspeed Vehicle airspeed [m/s]
423-
* @param[in] wind_ratio Wind speed to airspeed ratio
415+
* @param[in] wind_speed Wind speed [m/s]
424416
* @param[in] track_error Track error (magnitude) [m]
425417
* @param[in] path_curvature Path curvature at closest point on track [m^-1]
426418
* @param[in] wind_vel Wind velocity vector in inertial frame [m/s]
@@ -429,7 +421,7 @@ class NPFG
429421
* @param[in] feas_on_track Bearing feasibility on track at the closest point
430422
* @return Adapted period [s]
431423
*/
432-
float adaptPeriod(const float ground_speed, const float airspeed, const float wind_ratio,
424+
float adaptPeriod(const float ground_speed, const float airspeed, const float wind_speed,
433425
const float track_error, const float path_curvature, const matrix::Vector2f &wind_vel,
434426
const matrix::Vector2f &unit_path_tangent, const float feas_on_track) const;
435427

@@ -445,10 +437,11 @@ class NPFG
445437
/*
446438
* Cacluates an approximation of the wind factor (see [TODO: include citation]).
447439
*
448-
* @param[in] wind_ratio Wind speed to airspeed ratio
440+
* @param[in] airspeed Vehicle airspeed [m/s]
441+
* @param[in] wind_speed Wind speed [m/s]
449442
* @return Non-dimensional wind factor approximation
450443
*/
451-
float windFactor(const float wind_ratio) const;
444+
float windFactor(const float airspeed, const float wind_speed) const;
452445

453446
/*
454447
* Calculates a theoretical upper bound on the user defined period to maintain
@@ -622,12 +615,12 @@ class NPFG
622615
*
623616
* @param[in] wind_cross_bearing 2D cross product of wind velocity and bearing vector [m/s]
624617
* @param[in] wind_dot_bearing 2D dot product of wind velocity and bearing vector [m/s]
618+
* @param[in] airspeed Vehicle airspeed [m/s]
625619
* @param[in] wind_speed Wind speed [m/s]
626-
* @param[in] wind_ratio Wind speed to airspeed ratio
627620
* @return bearing feasibility
628621
*/
629-
float bearingFeasibility(const float wind_cross_bearing, const float wind_dot_bearing, const float wind_speed,
630-
const float wind_ratio) const;
622+
float bearingFeasibility(float wind_cross_bearing, const float wind_dot_bearing, const float airspeed,
623+
const float wind_speed) const;
631624

632625
/*
633626
* Calculates an additional feed-forward lateral acceleration demand considering
@@ -639,16 +632,14 @@ class NPFG
639632
* @param[in] wind_vel Wind velocity vector [m/s]
640633
* @param[in] airspeed Vehicle airspeed [m/s]
641634
* @param[in] wind_speed Wind speed [m/s]
642-
* @param[in] wind_ratio Wind speed to airspeed ratio
643635
* @param[in] signed_track_error Signed error to track at closest point (sign
644636
* determined by path normal direction) [m]
645637
* @param[in] path_curvature Path curvature at closest point on track [m^-1]
646638
* @return Feed-forward lateral acceleration command [m/s^2]
647639
*/
648640
float lateralAccelFF(const matrix::Vector2f &unit_path_tangent, const matrix::Vector2f &ground_vel,
649641
const float wind_dot_upt, const float wind_cross_upt, const float airspeed,
650-
const float wind_speed, const float wind_ratio, const float signed_track_error,
651-
const float path_curvature) const;
642+
const float wind_speed, const float signed_track_error, const float path_curvature) const;
652643

653644
/*
654645
* Calculates a lateral acceleration demand from the heading error.

src/modules/fw_pos_control_l1/FixedwingPositionControl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ FixedwingPositionControl::parameters_update()
118118
_npfg.setMaxTrackKeepingMinGroundSpeed(_param_npfg_track_keeping_gsp_max.get());
119119
_npfg.setNormalizedTrackErrorFraction(_param_npfg_nte_fraction.get());
120120
_npfg.setRollTimeConst(_param_npfg_roll_time_const.get());
121-
_npfg.setWindRatioBuffer(_param_npfg_wind_ratio_buf.get());
121+
_npfg.setAirspeedBuffer(_param_npfg_airspeed_buffer.get());
122122
_npfg.setRollLimit(radians(_param_fw_r_lim.get()));
123123
_npfg.setRollSlewRate(radians(_param_fw_l1_r_slew_max.get()));
124124

src/modules/fw_pos_control_l1/FixedwingPositionControl.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ class FixedwingPositionControl final : public ModuleBase<FixedwingPositionContro
387387
(ParamFloat<px4::params::NPFG_GSP_MAX_TK>) _param_npfg_track_keeping_gsp_max,
388388
(ParamFloat<px4::params::NPFG_NTE_FRAC>) _param_npfg_nte_fraction,
389389
(ParamFloat<px4::params::NPFG_ROLL_TC>) _param_npfg_roll_time_const,
390-
(ParamFloat<px4::params::NPFG_WR_BUF>) _param_npfg_wind_ratio_buf,
390+
(ParamFloat<px4::params::NPFG_ASPD_BUF>) _param_npfg_airspeed_buffer,
391391

392392
(ParamFloat<px4::params::FW_LND_AIRSPD_SC>) _param_fw_lnd_airspd_sc,
393393
(ParamFloat<px4::params::FW_LND_ANG>) _param_fw_lnd_ang,

src/modules/fw_pos_control_l1/fw_pos_control_l1_params.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,19 +222,20 @@ PARAM_DEFINE_FLOAT(NPFG_NTE_FRAC, 0.5f);
222222
PARAM_DEFINE_FLOAT(NPFG_ROLL_TC, 0.5f);
223223

224224
/**
225-
* NPFG wind ratio buffer
225+
* NPFG airspeed buffer
226226
*
227227
* The size of the feasibility transition region at cross wind angle >= 90 deg.
228228
* This must be non-zero to avoid jumpy airspeed incrementation while using wind
229-
* excess handling logic.
229+
* excess handling logic. Similarly used as buffer region around zero airspeed.
230230
*
231-
* @min 0.01
232-
* @max 0.30
233-
* @decimal 2
234-
* @increment 0.01
231+
* @unit m/s
232+
* @min 0.1
233+
* @max 5.0
234+
* @decimal 1
235+
* @increment 0.1
235236
* @group FW NPFG Control
236237
*/
237-
PARAM_DEFINE_FLOAT(NPFG_WR_BUF, 0.1f);
238+
PARAM_DEFINE_FLOAT(NPFG_ASPD_BUF, 1.5f);
238239

239240
/**
240241
* Cruise throttle

0 commit comments

Comments
 (0)