| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
…ome altitude filter The low pass filter smoothing barometric altitude for the in-air home position correction was fed the raw timestamp difference. uORB timestamps are microseconds, but AlphaFilter::setParameters() documents both of its arguments as seconds and the filter is constructed with a 5 second time constant, so the sample interval arrived a million times too large: alpha = dt / (tau + dt) rate dt alpha (before) alpha (intended) 50 Hz 0.0200 s 0.999750 0.003984 100 Hz 0.0100 s 0.999500 0.001996 200 Hz 0.0050 s 0.999001 0.000999 At an alpha of 0.9997 the filter passes essentially every raw sample through, giving an effective time constant of 5 us instead of 5 s, so _lpf_baro.getState() has been effectively unfiltered barometric altitude. That state feeds the in-air home altitude correction: it is offset by _baro_gps_static_offset and then compared against the GNSS altitude, and home.alt is shifted when the two differ by more than kAltitudeDifferenceThreshold. A GNSS velocity integral gates that comparison for consistency. The same conversion is already done correctly for the GNSS integral a few lines below in this file, and for the geoid height filter in EKF2. Note that this does change behaviour: the filter now actually applies its 5 s time constant, so _lpf_baro.getState() lags during a climb by roughly the time constant times the climb rate. _baro_gps_static_offset is captured once when the correction window opens, so that lag does not cancel and it biases baro_alt_corrected while climbing. Reviewers who know this feature should say whether the 5 s constant and the 1 m threshold, both tuned while the filter was effectively a pass-through, still want the same values now that it filters. Assisted-by: Claude:claude-fable-5 Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu>
🔎 FLASH Analysispx4_fmu-v5x [Total VM Diff: 8 byte (0 %)] FILE SIZE VM SIZE
-------------- --------------
+0.0% +8 +0.0% +8 .text
+0.9% +8 +0.9% +8 HomePosition::update()
+0.0% +55 [ = ] 0 .debug_abbrev
-0.0% -2 [ = ] 0 .debug_info
-0.0% -5 [ = ] 0 .debug_line
-33.3% -2 [ = ] 0 [Unmapped]
-0.0% -3 [ = ] 0 [section .debug_line]
+0.0% +115 [ = ] 0 .debug_loclists
+0.0% +1 [ = ] 0 .debug_rnglists
-0.1% -8 [ = ] 0 [Unmapped]
+0.0% +164 +0.0% +8 TOTAL
FILE SIZE VM SIZE
-------------- --------------
+0.0% +8 +0.0% +8 .text
+0.9% +8 +0.9% +8 HomePosition::update()
+0.0% +55 [ = ] 0 .debug_abbrev
-0.0% -2 [ = ] 0 .debug_info
+0.0% +3 [ = ] 0 .debug_line
+600% +6 [ = ] 0 [Unmapped]
-0.0% -3 [ = ] 0 [section .debug_line]
+0.0% +115 [ = ] 0 .debug_loclists
+0.0% +1 [ = ] 0 .debug_rnglists
-0.2% -8 [ = ] 0 [Unmapped]
+0.0% +172 +0.0% +8 TOTAL
Updated: 2026-08-27T21:53:02 |
Sorry, something went wrong.
There was a problem hiding this comment.
Would you mind doing a sweep across all uses of AlphaFilter to ensure the time_constant is in the correct units? Even better would be to change time_constant to an uint64_t microseconds and convert it to float in the CTOR. Most of our interfaces that take time arguments use microseconds so it would be good to have a single convention to avoid these mistakes in the future
Sorry, something went wrong.
The AlphaFilter interfaces took float seconds while most PX4 time sources are integer microseconds, and the PX4#28415 bug came from exactly that mismatch: a microseconds sample interval passed into the seconds interface. Take the time parameters of the constructor, setParameters and update as uint64_t microseconds and convert once inside the filter, as suggested in the PX4#28415 review. The float and mixed-type overloads are deleted, so a caller passing float seconds now fails to compile instead of silently producing a wrong alpha. FilteredDerivative wraps the same interface and moves with it. Call sites that already hold a microseconds timestamp delta pass it directly and drop their 1e-6 conversion. Call sites that only have a float seconds value convert explicitly at the call, clamped to zero first where the value is a user settable parameter, since a negative float to unsigned conversion is undefined. Constants that also serve non-filter uses stay in seconds and convert at the call. Behaviour is equivalent at every site, to within one microsecond of truncation and one float ulp on reconstructed constants. Assisted-by: Claude:claude-fable-5 Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu>
* fix(mathlib): swap the misnamed AlphaFilter alpha tests AlphaOneTest configures an alpha of almost zero and asserts the state does not move, while AlphaZeroTest configures an alpha of one and asserts pass through. The assertions are correct but each carries the other's name. Swap the names. Assisted-by: Claude:claude-fable-5 Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu> * refactor(lib): take AlphaFilter time parameters in microseconds The AlphaFilter interfaces took float seconds while most PX4 time sources are integer microseconds, and the #28415 bug came from exactly that mismatch: a microseconds sample interval passed into the seconds interface. Take the time parameters of the constructor, setParameters and update as uint64_t microseconds and convert once inside the filter, as suggested in the #28415 review. The float and mixed-type overloads are deleted, so a caller passing float seconds now fails to compile instead of silently producing a wrong alpha. FilteredDerivative wraps the same interface and moves with it. Call sites that already hold a microseconds timestamp delta pass it directly and drop their 1e-6 conversion. Call sites that only have a float seconds value convert explicitly at the call, clamped to zero first where the value is a user settable parameter, since a negative float to unsigned conversion is undefined. Constants that also serve non-filter uses stay in seconds and convert at the call. Behaviour is equivalent at every site, to within one microsecond of truncation and one float ulp on reconstructed constants. Assisted-by: Claude:claude-fable-5 Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu> * test(ekf2): update change indication baselines for the microsecond filter interface The microsecond conversion truncates each sample interval to a whole microsecond before reconstructing the float alpha, which shifts the EKF outputs by float rounding amounts. 23 values change in each baseline, all at the least significant digits. Assisted-by: Claude:claude-fable-5 Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu> * refactor(lib): use hrt_abstime for microsecond filter times at call sites The AlphaFilter interface stays uint64_t so mathlib does not depend on drv_hrt.h, but modules and drivers that already hold hrt_abstime timestamps declared their filter intervals and time constants as raw uint64_t next to them. * style(lib): use time literals for AlphaFilter constants Files that already include drv_hrt.h spelled microsecond constants as raw integers with a comment giving the unit. * refactor(vision_target_estimator): store the bias LPF time constant in microseconds Every other AlphaFilter constant was converted to microseconds; this one stayed float seconds and was cast at both use sites. * style(lib): drop redundant hrt_abstime casts on time literals The _s and _ms literals already return hrt_abstime. * style(ekf2): move the time_literals using-directive below the includes * fix(microstrain): pass the geoid height update timestamp as hrt_abstime The float parameter received a microsecond timestamp and loses the microsecond resolution after about 17 seconds of uptime. --------- Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu> Co-authored-by: Jacob Dahl <dahl.jakejacob@gmail.com>
…ome altitude filter (#28415) (#28535) The low pass filter smoothing barometric altitude for the in-air home position correction was fed the raw timestamp difference. uORB timestamps are microseconds, but AlphaFilter::setParameters() documents both of its arguments as seconds and the filter is constructed with a 5 second time constant, so the sample interval arrived a million times too large: alpha = dt / (tau + dt) rate dt alpha (before) alpha (intended) 50 Hz 0.0200 s 0.999750 0.003984 100 Hz 0.0100 s 0.999500 0.001996 200 Hz 0.0050 s 0.999001 0.000999 At an alpha of 0.9997 the filter passes essentially every raw sample through, giving an effective time constant of 5 us instead of 5 s, so _lpf_baro.getState() has been effectively unfiltered barometric altitude. That state feeds the in-air home altitude correction: it is offset by _baro_gps_static_offset and then compared against the GNSS altitude, and home.alt is shifted when the two differ by more than kAltitudeDifferenceThreshold. A GNSS velocity integral gates that comparison for consistency. The same conversion is already done correctly for the GNSS integral a few lines below in this file, and for the geoid height filter in EKF2. Note that this does change behaviour: the filter now actually applies its 5 s time constant, so _lpf_baro.getState() lags during a climb by roughly the time constant times the climb rate. _baro_gps_static_offset is captured once when the correction window opens, so that lag does not cancel and it biases baro_alt_corrected while climbing. Reviewers who know this feature should say whether the 5 s constant and the 1 m threshold, both tuned while the filter was effectively a pass-through, still want the same values now that it filters. Assisted-by: Claude:claude-fable-5 (cherry picked from commit 0bdf8c2) Signed-off-by: Saibernard Yogendran <bernie97@seas.upenn.edu>
| Back | FazBrowse Home | New Git URL |
Summary
The low pass filter that smooths barometric altitude for the in-air home position correction is fed the raw uORB timestamp difference, which is in microseconds, while AlphaFilter::setParameters() documents both of its arguments as seconds and the filter is constructed with a 5 second time constant.
src/modules/commander/HomePosition.cpp:
Effect
Since alpha = dt / (tau + dt):
At an alpha of 0.9997 the filter passes essentially every raw sample through. The effective time constant is 5 us instead of 5 s, so _lpf_baro.getState() has been effectively unfiltered barometric altitude.
That state feeds the in-air home altitude correction: it is offset by _baro_gps_static_offset and compared against GNSS altitude, and home.alt is shifted when the two differ by more than kAltitudeDifferenceThreshold. A GNSS velocity integral gates that comparison for consistency.
Why this looks like an oversight
The same conversion is already done correctly a few lines below in this file for the GNSS velocity integral:
and in EKF2 for the geoid height filter, which uses the identical setParameters(dt, tau) call.
Note on behaviour change
This does change behaviour: the filter now actually applies its 5 second time constant, so _lpf_baro.getState() lags during a climb by roughly the time constant times the climb rate. _baro_gps_static_offset is captured once when the correction window opens, so that lag does not cancel and it biases baro_alt_corrected while climbing.
I would appreciate a look from whoever knows this feature: the 5 second constant and the 1 m threshold were both tuned while the filter was effectively a pass-through, so it is worth confirming they are still the values you want now that it filters.
Testing
make px4_sitl builds and make check_format passes. The unit analysis above is the substantive evidence and can be checked against the AlphaFilter docstring without running anything. I do not have flight logs for this; happy to add SITL traces if that would help the review.