-
Notifications
You must be signed in to change notification settings - Fork 0
Stability Indicator Upgrade and v3.3.0 Bump #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 3.2.0 | ||
| 3.3.0 |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,160 @@ | ||||||||||||||||||||
| #pragma once | ||||||||||||||||||||
|
|
||||||||||||||||||||
| #include <cmath> | ||||||||||||||||||||
| #include <cstdint> | ||||||||||||||||||||
| #include <iostream> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // ===================================================== | ||||||||||||||||||||
| // HLV-RAPS Stability Indicator Upgrade (v3.3.0) | ||||||||||||||||||||
| // ===================================================== | ||||||||||||||||||||
|
|
||||||||||||||||||||
| namespace StabilityConfig { | ||||||||||||||||||||
|
|
||||||||||||||||||||
| enum class ManeuverClass { | ||||||||||||||||||||
| CRUISE, | ||||||||||||||||||||
| MILD_MANEUVER, | ||||||||||||||||||||
| AGGRESSIVE_MANEUVER | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| struct Thresholds { | ||||||||||||||||||||
| double S_u_min; | ||||||||||||||||||||
| double S_u_warn; | ||||||||||||||||||||
| double S_u_rate_limit; | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| constexpr Thresholds CRUISE_THRESHOLDS = {0.82, 0.86, 0.015}; | ||||||||||||||||||||
| constexpr Thresholds MILD_THRESHOLDS = {0.78, 0.83, 0.025}; | ||||||||||||||||||||
| constexpr Thresholds AGGRESSIVE_THRESHOLDS = {0.72, 0.78, 0.040}; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| inline constexpr Thresholds get_thresholds(ManeuverClass mclass) { | ||||||||||||||||||||
| switch (mclass) { | ||||||||||||||||||||
| case ManeuverClass::MILD_MANEUVER: | ||||||||||||||||||||
| return MILD_THRESHOLDS; | ||||||||||||||||||||
| case ManeuverClass::AGGRESSIVE_MANEUVER: | ||||||||||||||||||||
| return AGGRESSIVE_THRESHOLDS; | ||||||||||||||||||||
| case ManeuverClass::CRUISE: | ||||||||||||||||||||
| default: | ||||||||||||||||||||
| return CRUISE_THRESHOLDS; | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| } // namespace StabilityConfig | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // ===================================================== | ||||||||||||||||||||
| // DSM Integration | ||||||||||||||||||||
| // ===================================================== | ||||||||||||||||||||
|
|
||||||||||||||||||||
| enum class DSMFlagType { | ||||||||||||||||||||
| NONE = 0, | ||||||||||||||||||||
| SU_LOW = 1, | ||||||||||||||||||||
| SU_RATE_VIOLATION = 2, | ||||||||||||||||||||
| SU_HYSTERESIS_TRANSITION = 3 | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| struct DSMEvent { | ||||||||||||||||||||
| uint32_t timestamp; | ||||||||||||||||||||
| StabilityConfig::ManeuverClass maneuver_class; | ||||||||||||||||||||
| double S_u; | ||||||||||||||||||||
| double dS_u_dt; | ||||||||||||||||||||
| DSMFlagType flag_type; | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // ===================================================== | ||||||||||||||||||||
| // Stability Indicator Core | ||||||||||||||||||||
| // ===================================================== | ||||||||||||||||||||
|
|
||||||||||||||||||||
| class StabilityIndicator { | ||||||||||||||||||||
| public: | ||||||||||||||||||||
| StabilityIndicator() | ||||||||||||||||||||
| : last_S_u_(1.0), | ||||||||||||||||||||
| last_timestamp_(0), | ||||||||||||||||||||
| is_safe_mode_(true), | ||||||||||||||||||||
| initialized_(false) {} | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Pure mathematical computation of S_u | ||||||||||||||||||||
| // S_u = 1 / (1 + phi^2 + chi^3) | ||||||||||||||||||||
| static double compute_Su(double phi, double chi) { | ||||||||||||||||||||
| double phi_sq = phi * phi; | ||||||||||||||||||||
| double chi_cu = chi * chi * chi; | ||||||||||||||||||||
|
Comment on lines
+75
to
+78
|
||||||||||||||||||||
| // S_u = 1 / (1 + phi^2 + chi^3) | |
| static double compute_Su(double phi, double chi) { | |
| double phi_sq = phi * phi; | |
| double chi_cu = chi * chi * chi; | |
| // S_u = 1 / (1 + phi^2 + |chi|^3) | |
| static double compute_Su(double phi, double chi) { | |
| double phi_sq = phi * phi; | |
| double chi_abs = std::abs(chi); | |
| double chi_cu = chi_abs * chi_abs * chi_abs; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -129,3 +129,37 @@ add_test( | |
| NAME raps_sil_rollback_test | ||
| COMMAND raps_sil_rollback_tests | ||
| ) | ||
|
|
||
| # ------------------------------------------------------------ | ||
| # Version Tests | ||
| # ------------------------------------------------------------ | ||
| add_executable(raps_sil_version_tests | ||
| test_version.cpp | ||
| ) | ||
|
|
||
| target_include_directories(raps_sil_version_tests PRIVATE | ||
| ${PROJECT_SOURCE_DIR}/../../include | ||
| ${PROJECT_SOURCE_DIR}/../../src | ||
| ) | ||
|
|
||
| add_test( | ||
| NAME raps_sil_version_test | ||
| COMMAND raps_sil_version_tests | ||
| ) | ||
|
|
||
| # ------------------------------------------------------------ | ||
| # Stability Indicator Tests | ||
| # ------------------------------------------------------------ | ||
| add_executable(raps_sil_stability_tests | ||
| test_stability_indicator.cpp | ||
| ) | ||
|
|
||
| target_include_directories(raps_sil_stability_tests PRIVATE | ||
| ${PROJECT_SOURCE_DIR}/../../include | ||
| ${PROJECT_SOURCE_DIR}/../../src | ||
| ) | ||
|
|
||
| add_test( | ||
| NAME raps_sil_stability_test | ||
| COMMAND raps_sil_stability_tests | ||
| ) | ||
|
Comment on lines
+136
to
+165
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| #include "raps/safety/stability_indicator.hpp" | ||
| #include <cassert> | ||
| #include <iostream> | ||
| #include <cmath> | ||
|
|
||
| void test_compute_Su() { | ||
| // 1. Boundary behavior: phi=0, chi=0 -> S_u = 1.0 | ||
| double su_max = StabilityIndicator::compute_Su(0.0, 0.0); | ||
| assert(std::abs(su_max - 1.0) < 1e-9); | ||
|
|
||
| // 2. Numerical output | ||
| // S_u = 1 / (1 + 0.5^2 + 0.5^3) = 1 / (1 + 0.25 + 0.125) = 1 / 1.375 = 0.727272... | ||
| double su_val = StabilityIndicator::compute_Su(0.5, 0.5); | ||
| assert(std::abs(su_val - 1.0/1.375) < 1e-9); | ||
|
|
||
| // 3. Monotonicity: as phi or chi increase, S_u decreases | ||
| double su_larger_phi = StabilityIndicator::compute_Su(0.6, 0.5); | ||
| assert(su_larger_phi < su_val); | ||
|
|
||
| double su_larger_chi = StabilityIndicator::compute_Su(0.5, 0.6); | ||
| assert(su_larger_chi < su_val); | ||
| } | ||
|
Comment on lines
+6
to
+22
|
||
|
|
||
| void test_maneuver_aware_thresholds() { | ||
| auto cruise = StabilityConfig::get_thresholds(StabilityConfig::ManeuverClass::CRUISE); | ||
| assert(std::abs(cruise.S_u_min - 0.82) < 1e-9); | ||
| assert(std::abs(cruise.S_u_warn - 0.86) < 1e-9); | ||
|
|
||
| auto mild = StabilityConfig::get_thresholds(StabilityConfig::ManeuverClass::MILD_MANEUVER); | ||
| assert(std::abs(mild.S_u_min - 0.78) < 1e-9); | ||
| assert(std::abs(mild.S_u_warn - 0.83) < 1e-9); | ||
|
|
||
| auto aggressive = StabilityConfig::get_thresholds(StabilityConfig::ManeuverClass::AGGRESSIVE_MANEUVER); | ||
| assert(std::abs(aggressive.S_u_min - 0.72) < 1e-9); | ||
| assert(std::abs(aggressive.S_u_warn - 0.78) < 1e-9); | ||
| } | ||
|
|
||
| void test_rate_of_change() { | ||
| StabilityIndicator ind; | ||
| // initial state: S_u is ~1.0 | ||
| // We send a valid reading at t=1000 | ||
| ind.update_stability_state(1000, StabilityConfig::ManeuverClass::CRUISE, 0.0, 0.0); // S_u = 1.0 | ||
|
|
||
| // Small change at t=1001, rate should be compliant | ||
| // phi=0.1, chi=0 => S_u = 1/(1+0.01) = 0.990099 | ||
| // dS_u/dt = (0.990099 - 1.0) / 1 = -0.0099 | ||
| // cruise rate limit is 0.015, so |dS_u/dt| is compliant. | ||
| auto ev = ind.update_stability_state(1001, StabilityConfig::ManeuverClass::CRUISE, 0.1, 0.0); | ||
| assert(ev.flag_type == DSMFlagType::NONE); | ||
|
|
||
| // Large change at t=1002, rate violation | ||
| // phi=0.5, chi=0 => S_u = 1/(1+0.25) = 0.8 | ||
| // dS_u/dt = (0.8 - 0.990099) / 1 = -0.19 | ||
| // |dS_u/dt| > 0.015 -> violation | ||
| auto ev_viol = ind.update_stability_state(1002, StabilityConfig::ManeuverClass::CRUISE, 0.5, 0.0); | ||
| assert(ev_viol.flag_type == DSMFlagType::SU_RATE_VIOLATION); | ||
| } | ||
|
|
||
| void test_hysteresis() { | ||
| StabilityIndicator ind; | ||
| ind.update_stability_state(1000, StabilityConfig::ManeuverClass::CRUISE, 0.0, 0.0); // S_u = 1.0 | ||
| assert(ind.is_safe() == true); | ||
|
|
||
| // Drop to just above S_u_min (0.82) -> e.g. 0.83 | ||
| // S_u = 1 / (1 + phi^2) => phi = sqrt(1/S_u - 1) | ||
| // For S_u = 0.83 => phi = sqrt(1/0.83 - 1) ~ 0.4525 | ||
| auto ev1 = ind.update_stability_state(2000, StabilityConfig::ManeuverClass::CRUISE, 0.4525, 0.0); | ||
| assert(ind.is_safe() == true); // still safe | ||
| assert(ev1.flag_type == DSMFlagType::NONE); | ||
|
|
||
| // Drop below S_u_min (0.82) -> e.g. 0.80 | ||
| // S_u = 0.80 => phi = sqrt(1/0.80 - 1) = sqrt(0.25) = 0.5 | ||
| auto ev2 = ind.update_stability_state(3000, StabilityConfig::ManeuverClass::CRUISE, 0.5, 0.0); | ||
| assert(ind.is_safe() == false); // exited safe mode | ||
| assert(ev2.flag_type == DSMFlagType::SU_HYSTERESIS_TRANSITION); | ||
|
|
||
| // Hover near boundary: S_u goes to 0.84 (below warn 0.86, above min 0.82) | ||
| // S_u = 0.84 => phi = sqrt(1/0.84 - 1) ~ 0.4364 | ||
| auto ev3 = ind.update_stability_state(4000, StabilityConfig::ManeuverClass::CRUISE, 0.4364, 0.0); | ||
| assert(ind.is_safe() == false); // no oscillation, still not safe | ||
| // Since it's still unsafe but above S_u_min, and no transition, flag is NONE. | ||
| assert(ev3.flag_type == DSMFlagType::NONE); | ||
|
|
||
| // Enter safe mode: S_u goes above S_u_warn (0.86) -> e.g. 0.90 | ||
| // S_u = 0.90 => phi = sqrt(1/0.90 - 1) ~ 0.3333 | ||
| auto ev4 = ind.update_stability_state(5000, StabilityConfig::ManeuverClass::CRUISE, 0.3333, 0.0); | ||
| assert(ind.is_safe() == true); // entered safe mode | ||
| assert(ev4.flag_type == DSMFlagType::SU_HYSTERESIS_TRANSITION); | ||
| } | ||
|
|
||
| void test_dsm_integration() { | ||
| StabilityIndicator ind; | ||
| // S_u = 1.0 -> no flag | ||
| auto ev = ind.update_stability_state(1000, StabilityConfig::ManeuverClass::AGGRESSIVE_MANEUVER, 0.0, 0.0); | ||
| assert(ev.timestamp == 1000); | ||
| assert(ev.maneuver_class == StabilityConfig::ManeuverClass::AGGRESSIVE_MANEUVER); | ||
| assert(std::abs(ev.S_u - 1.0) < 1e-9); | ||
| assert(ev.dS_u_dt == 0.0); | ||
| assert(ev.flag_type == DSMFlagType::NONE); | ||
|
|
||
| // S_u drops to 0.6 (aggressive min is 0.72) | ||
| // S_u = 0.6 => phi = sqrt(1/0.6 - 1) = sqrt(0.666) ~ 0.8165 | ||
| // large drop -> rate violation triggers first | ||
| auto ev2 = ind.update_stability_state(1001, StabilityConfig::ManeuverClass::AGGRESSIVE_MANEUVER, 0.8165, 0.0); | ||
| assert(ev2.timestamp == 1001); | ||
| assert(ev2.flag_type == DSMFlagType::SU_RATE_VIOLATION); // Rate limit takes precedence as coded | ||
|
|
||
| // Keep it at 0.6 for long time -> no rate violation, but it's low | ||
| auto ev3 = ind.update_stability_state(2001, StabilityConfig::ManeuverClass::AGGRESSIVE_MANEUVER, 0.8165, 0.0); | ||
| assert(ev3.timestamp == 2001); | ||
| // Since is_safe_mode was false from previous transition/drop (actually previous trigger was rate violation, | ||
| // but the hysteresis state was updated to unsafe), and it is still below S_u_min, it emits SU_LOW. | ||
| assert(ev3.flag_type == DSMFlagType::SU_LOW); | ||
| } | ||
|
|
||
| int main() { | ||
| test_compute_Su(); | ||
| test_maneuver_aware_thresholds(); | ||
| test_rate_of_change(); | ||
| test_hysteresis(); | ||
| test_dsm_integration(); | ||
| std::cout << "Stability Indicator tests passed." << std::endl; | ||
| return 0; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changelog edit makes the
[3.2.1]section claim it added aVERSIONfile of3.3.0, which is internally inconsistent (a 3.2.1 release shouldn’t be documenting a 3.3.0 version). Instead of rewriting the historical 3.2.1 entry, add a new[3.3.0]section (or update the bullet to reflect what actually happened in 3.2.1).