@@ -1054,9 +1054,6 @@ void AP_TECS::_update_pitch(void)
1054
1054
_pitch_dem_unc += (_TAS_dem_adj - _pitch_ff_v0) * _pitch_ff_k;
1055
1055
}
1056
1056
1057
- // Constrain pitch demand
1058
- _pitch_dem = constrain_float (_pitch_dem_unc, _PITCHminf, _PITCHmaxf);
1059
-
1060
1057
// Rate limit the pitch demand to comply with specified vertical
1061
1058
// acceleration limit
1062
1059
float ptchRateIncr = _DT * _vertAccLim / _TAS_state;
@@ -1067,6 +1064,9 @@ void AP_TECS::_update_pitch(void)
1067
1064
_pitch_dem = _last_pitch_dem - ptchRateIncr;
1068
1065
}
1069
1066
1067
+ // Constrain pitch demand
1068
+ _pitch_dem = constrain_float (_pitch_dem_unc, _PITCHminf, _PITCHmaxf);
1069
+
1070
1070
_last_pitch_dem = _pitch_dem;
1071
1071
1072
1072
#if HAL_LOGGING_ENABLED
@@ -1110,11 +1110,13 @@ void AP_TECS::_update_pitch(void)
1110
1110
#endif
1111
1111
}
1112
1112
1113
- void AP_TECS::_initialise_states (int32_t ptchMinCO_cd, float hgt_afe)
1113
+ void AP_TECS::_initialise_states (float hgt_afe)
1114
1114
{
1115
+ // Initialise states and variables if DT > 0.2 second or TECS is getting overriden or in climbout.
1115
1116
_flags.reset = false ;
1116
1117
1117
- // Initialise states and variables if DT > 0.2 second or in climbout
1118
+ _need_reset = _need_reset || (_flag_pitch_forced && _flag_throttle_forced);
1119
+
1118
1120
if (_DT > 0 .2f || _need_reset) {
1119
1121
_SKE_weighting = 1 .0f ;
1120
1122
_integTHR_state = 0 .0f ;
@@ -1156,7 +1158,16 @@ void AP_TECS::_initialise_states(int32_t ptchMinCO_cd, float hgt_afe)
1156
1158
_pitch_measured_lpf.reset (_ahrs.get_pitch ());
1157
1159
1158
1160
} else if (_flight_stage == AP_FixedWing::FlightStage::TAKEOFF || _flight_stage == AP_FixedWing::FlightStage::ABORT_LANDING) {
1159
- _PITCHminf = CentiDegreesToRadians (ptchMinCO_cd);
1161
+
1162
+ if (!_flag_throttle_forced) {
1163
+ // Calculate the takeoff target height offset before _hgt_dem_in_raw gets reset below.
1164
+ // Prevent the offset from becoming negative.
1165
+ _post_TO_hgt_offset = MAX (MIN (_climb_rate_limit * _hgt_dem_tconst, _hgt_dem_in_raw - hgt_afe), 0 );
1166
+ } else {
1167
+ // If throttle is externally forced, this mechanism of adding energy is unnecessary.
1168
+ _post_TO_hgt_offset = 0 ;
1169
+ }
1170
+
1160
1171
_hgt_afe = hgt_afe;
1161
1172
_hgt_dem_lpf = hgt_afe;
1162
1173
_hgt_dem_rate_ltd = hgt_afe;
@@ -1166,12 +1177,12 @@ void AP_TECS::_initialise_states(int32_t ptchMinCO_cd, float hgt_afe)
1166
1177
_hgt_dem_in_raw = hgt_afe;
1167
1178
_flags.underspeed = false ;
1168
1179
_flags.badDescent = false ;
1169
- _post_TO_hgt_offset = _climb_rate_limit * _hgt_dem_tconst; // Replacement prevents oscillating hgt_rate_dem.
1170
1180
_TAS_dem_adj = _TAS_dem;
1171
1181
_max_climb_scaler = 1 .0f ;
1172
1182
_max_sink_scaler = 1 .0f ;
1173
1183
_pitch_demand_lpf.reset (_ahrs.get_pitch ());
1174
1184
_pitch_measured_lpf.reset (_ahrs.get_pitch ());
1185
+
1175
1186
1176
1187
if (!_flag_have_reset_after_takeoff) {
1177
1188
_flags.reset = true ;
@@ -1250,77 +1261,7 @@ void AP_TECS::update_pitch_throttle(int32_t hgt_dem_cm,
1250
1261
// Update the throttle limits.
1251
1262
_update_throttle_limits ();
1252
1263
1253
- // work out the maximum and minimum pitch
1254
- // if TECS_PITCH_{MAX,MIN} isn't set then use
1255
- // LIM_PITCH_{MAX,MIN}. Don't allow TECS_PITCH_{MAX,MIN} to be
1256
- // larger than LIM_PITCH_{MAX,MIN}
1257
- if (_pitch_max == 0 ) {
1258
- _PITCHmaxf = aparm.pitch_limit_max ;
1259
- } else {
1260
- _PITCHmaxf = MIN (_pitch_max, aparm.pitch_limit_max );
1261
- }
1262
-
1263
- if (_pitch_min >= 0 ) {
1264
- _PITCHminf = aparm.pitch_limit_min ;
1265
- } else {
1266
- _PITCHminf = MAX (_pitch_min, aparm.pitch_limit_min );
1267
- }
1268
-
1269
- // apply temporary pitch limit and clear
1270
- if (_pitch_max_limit < 90 ) {
1271
- _PITCHmaxf = constrain_float (_PITCHmaxf, -90 , _pitch_max_limit);
1272
- _PITCHminf = constrain_float (_PITCHminf, -_pitch_max_limit, _PITCHmaxf);
1273
- _pitch_max_limit = 90 ;
1274
- }
1275
-
1276
- if (!_landing.is_on_approach ()) {
1277
- // reset land pitch min when not landing
1278
- _land_pitch_min = _PITCHminf;
1279
- }
1280
-
1281
- // calculate the expected pitch angle from the demanded climb rate and airspeed for use during approach and flare
1282
- if (_landing.is_flaring ()) {
1283
- // smoothly move the min pitch to the required minimum at touchdown
1284
- float p; // 0 at start of flare, 1 at finish
1285
- if (!_flare_initialised) {
1286
- p = 0 .0f ;
1287
- } else if (_hgt_at_start_of_flare > _flare_holdoff_hgt) {
1288
- p = constrain_float ((_hgt_at_start_of_flare - _hgt_afe) / _hgt_at_start_of_flare, 0 .0f , 1 .0f );
1289
- } else {
1290
- p = 1 .0f ;
1291
- }
1292
- const float pitch_limit_deg = (1 .0f - p) * _pitch_min_at_flare_entry + p * 0 .01f * _landing.get_pitch_cd ();
1293
-
1294
- // in flare use min pitch from LAND_PITCH_DEG
1295
- _PITCHminf = MAX (_PITCHminf, pitch_limit_deg);
1296
-
1297
- // and use max pitch from TECS_LAND_PMAX
1298
- if (_land_pitch_max != 0 ) {
1299
- // note that this allows a flare pitch outside the normal TECS auto limits
1300
- _PITCHmaxf = _land_pitch_max;
1301
- }
1302
- } else if (_landing.is_on_approach ()) {
1303
- _PITCHminf = MAX (_PITCHminf, aparm.pitch_limit_min );
1304
- _pitch_min_at_flare_entry = _PITCHminf;
1305
- _flare_initialised = false ;
1306
- } else {
1307
- _flare_initialised = false ;
1308
- }
1309
-
1310
- if (_landing.is_on_approach ()) {
1311
- // don't allow the lower bound of pitch to decrease, nor allow
1312
- // it to increase rapidly. This prevents oscillation of pitch
1313
- // demand while in landing approach based on rapidly changing
1314
- // time to flare estimate
1315
- if (_land_pitch_min <= -90 ) {
1316
- _land_pitch_min = _PITCHminf;
1317
- }
1318
- const float flare_pitch_range = 20 ;
1319
- const float delta_per_loop = (flare_pitch_range/_landTimeConst) * _DT;
1320
- _PITCHminf = MIN (_PITCHminf, _land_pitch_min+delta_per_loop);
1321
- _land_pitch_min = MAX (_land_pitch_min, _PITCHminf);
1322
- _PITCHminf = MAX (_land_pitch_min, _PITCHminf);
1323
- }
1264
+ _update_pitch_limits (ptchMinCO_cd);
1324
1265
1325
1266
if (flight_stage == AP_FixedWing::FlightStage::TAKEOFF || flight_stage == AP_FixedWing::FlightStage::ABORT_LANDING) {
1326
1267
if (!_flags.reached_speed_takeoff && _TAS_state >= _TASmin && _TASmin > 0 ) {
@@ -1330,15 +1271,8 @@ void AP_TECS::update_pitch_throttle(int32_t hgt_dem_cm,
1330
1271
}
1331
1272
}
1332
1273
1333
- // convert to radians
1334
- _PITCHmaxf = radians (_PITCHmaxf);
1335
- _PITCHminf = radians (_PITCHminf);
1336
-
1337
- // don't allow max pitch to go below min pitch
1338
- _PITCHmaxf = MAX (_PITCHmaxf, _PITCHminf);
1339
-
1340
1274
// initialise selected states and variables if DT > 1 second or in climbout
1341
- _initialise_states (ptchMinCO_cd, hgt_afe);
1275
+ _initialise_states (hgt_afe);
1342
1276
1343
1277
// Calculate Specific Total Energy Rate Limits
1344
1278
_update_STE_rate_lim ();
@@ -1460,10 +1394,9 @@ void AP_TECS::_update_throttle_limits() {
1460
1394
1461
1395
// Configure min throttle.
1462
1396
1463
- // If less min throttle is allowed during takeoff, use it .
1397
+ // Use takeoff min throttle, if applicable .
1464
1398
bool use_takeoff_throttle = _flight_stage == AP_FixedWing::FlightStage::TAKEOFF || _flight_stage == AP_FixedWing::FlightStage::ABORT_LANDING;
1465
- const bool use_throttle_range = (aparm.takeoff_options & (uint32_t )AP_FixedWing::TakeoffOption::THROTTLE_RANGE);
1466
- use_takeoff_throttle = use_takeoff_throttle && (use_throttle_range == 1 ) && (aparm.takeoff_throttle_min != 0 );
1399
+ use_takeoff_throttle = use_takeoff_throttle && (aparm.takeoff_throttle_min != 0 );
1467
1400
if ( use_takeoff_throttle ) {
1468
1401
_THRminf = MIN (_THRminf, aparm.takeoff_throttle_min * 0 .01f );
1469
1402
}
@@ -1491,13 +1424,128 @@ void AP_TECS::_update_throttle_limits() {
1491
1424
_THRminf = MAX (_THRminf, _THRminf_ext);
1492
1425
1493
1426
// Allow a minimum of 1% throttle range, primarily to prevent TECS numerical errors.
1494
- if (_THRmaxf < 1 ) {
1495
- _THRmaxf = MAX (_THRmaxf, _THRminf + 0 .01f );
1427
+ const float thr_eps = 0.01 ;
1428
+ if (fabsf (_THRminf-_THRmaxf) < thr_eps) {
1429
+ _flag_throttle_forced = true ;
1430
+ if (_THRmaxf < 1 ) {
1431
+ _THRmaxf = MAX (_THRmaxf, _THRminf + 0 .01f );
1432
+ } else {
1433
+ _THRminf = MIN (_THRminf, _THRmaxf - 0 .01f );
1434
+ }
1496
1435
} else {
1497
- _THRminf = MIN (_THRminf, _THRmaxf - 0 . 01f ) ;
1436
+ _flag_throttle_forced = false ;
1498
1437
}
1499
1438
1500
1439
// Reset the external throttle limits.
1501
1440
_THRminf_ext = -1 .0f ;
1502
1441
_THRmaxf_ext = 1 .0f ;
1442
+ }
1443
+
1444
+ void AP_TECS::set_pitch_min (const float pitch_min) {
1445
+ // Don't change the limit if it is already covered.
1446
+ if (pitch_min > _PITCHminf_ext) {
1447
+ _PITCHminf_ext = pitch_min;
1448
+ }
1449
+ }
1450
+
1451
+ void AP_TECS::set_pitch_max (const float pitch_max) {
1452
+ // Don't change the limit if it is already covered.
1453
+ if (pitch_max < _PITCHmaxf_ext) {
1454
+ _PITCHmaxf_ext = pitch_max;
1455
+ }
1456
+ }
1457
+
1458
+ void AP_TECS::_update_pitch_limits (const int32_t ptchMinCO_cd) {
1459
+ // If TECS_PITCH_{MAX,MIN} isn't set then use LIM_PITCH_{MAX,MIN}.
1460
+ // Don't allow TECS_PITCH_{MAX,MIN} to be larger than LIM_PITCH_{MAX,MIN}.
1461
+ if (_pitch_max == 0 ) {
1462
+ _PITCHmaxf = aparm.pitch_limit_max ;
1463
+ } else {
1464
+ _PITCHmaxf = _pitch_max;
1465
+ }
1466
+
1467
+ if (_pitch_min == 0 ) {
1468
+ _PITCHminf = aparm.pitch_limit_min ;
1469
+ } else {
1470
+ _PITCHminf = _pitch_min;
1471
+ }
1472
+
1473
+ if (!_landing.is_on_approach ()) {
1474
+ // reset land pitch min when not landing
1475
+ _land_pitch_min = _PITCHminf;
1476
+ }
1477
+
1478
+ // calculate the expected pitch angle from the demanded climb rate and airspeed for use during approach and flare
1479
+ if (_landing.is_flaring ()) {
1480
+ // smoothly move the min pitch to the required minimum at touchdown
1481
+ float p; // 0 at start of flare, 1 at finish
1482
+ if (!_flare_initialised) {
1483
+ p = 0 .0f ;
1484
+ } else if (_hgt_at_start_of_flare > _flare_holdoff_hgt) {
1485
+ p = constrain_float ((_hgt_at_start_of_flare - _hgt_afe) / _hgt_at_start_of_flare, 0 .0f , 1 .0f );
1486
+ } else {
1487
+ p = 1 .0f ;
1488
+ }
1489
+ const float pitch_limit_deg = (1 .0f - p) * _pitch_min_at_flare_entry + p * 0 .01f * _landing.get_pitch_cd ();
1490
+
1491
+ // in flare use min pitch from LAND_PITCH_DEG
1492
+ _PITCHminf = MAX (_PITCHminf, pitch_limit_deg);
1493
+
1494
+ // and use max pitch from TECS_LAND_PMAX
1495
+ if (_land_pitch_max != 0 ) {
1496
+ // note that this allows a flare pitch outside the normal TECS auto limits
1497
+ _PITCHmaxf = _land_pitch_max;
1498
+ }
1499
+ } else if (_landing.is_on_approach ()) {
1500
+ _PITCHminf = MAX (_PITCHminf, aparm.pitch_limit_min );
1501
+ _pitch_min_at_flare_entry = _PITCHminf;
1502
+ _flare_initialised = false ;
1503
+ } else {
1504
+ _flare_initialised = false ;
1505
+ }
1506
+
1507
+ if (_landing.is_on_approach ()) {
1508
+ // don't allow the lower bound of pitch to decrease, nor allow
1509
+ // it to increase rapidly. This prevents oscillation of pitch
1510
+ // demand while in landing approach based on rapidly changing
1511
+ // time to flare estimate
1512
+ if (_land_pitch_min <= -90 ) {
1513
+ _land_pitch_min = _PITCHminf;
1514
+ }
1515
+ const float flare_pitch_range = 20 ;
1516
+ const float delta_per_loop = (flare_pitch_range/_landTimeConst) * _DT;
1517
+ _PITCHminf = MIN (_PITCHminf, _land_pitch_min+delta_per_loop);
1518
+ _land_pitch_min = MAX (_land_pitch_min, _PITCHminf);
1519
+ _PITCHminf = MAX (_land_pitch_min, _PITCHminf);
1520
+ }
1521
+
1522
+ // Apply TAKEOFF minimum pitch
1523
+ if (_flight_stage == AP_FixedWing::FlightStage::TAKEOFF
1524
+ || _flight_stage == AP_FixedWing::FlightStage::ABORT_LANDING)
1525
+ {
1526
+ _PITCHminf = CentiDegreesToRadians (ptchMinCO_cd);
1527
+ }
1528
+
1529
+ // Apply external limits.
1530
+ _PITCHmaxf = MIN (_PITCHmaxf, _PITCHmaxf_ext);
1531
+ _PITCHminf = MAX (_PITCHminf, _PITCHminf_ext);
1532
+
1533
+ // Reset the external pitch limits.
1534
+ _PITCHminf_ext = -90 .0f ;
1535
+ _PITCHmaxf_ext = 90 .0f ;
1536
+
1537
+ // convert to radians
1538
+ _PITCHmaxf = radians (_PITCHmaxf);
1539
+ _PITCHminf = radians (_PITCHminf);
1540
+
1541
+ // don't allow max pitch to go below min pitch
1542
+ _PITCHmaxf = MAX (_PITCHmaxf, _PITCHminf);
1543
+
1544
+ // Test if someone is forcing pitch to a specific value.
1545
+ const float pitch_eps = DEG_TO_RAD*1 ;
1546
+ if (fabsf (_PITCHmaxf-_PITCHminf)<pitch_eps) {
1547
+ _flag_pitch_forced = true ;
1548
+ } else {
1549
+ _flag_pitch_forced = false ;
1550
+ }
1503
1551
}
0 commit comments