FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(commander): convert the baro timestamp delta to seconds for the home altitude filter by Saibernard · Pull Request #28415 · PX4/PX4-Autopilot · GitHub

fix(commander): convert the baro timestamp delta to seconds for the home altitude filter - #28415

Merged
dakejahl merged 1 commit into
PX4:mainfrom
Saibernard:pr-fix-home-baro-lpf-dt
Aug 28, 2026
Merged

dakejahl merged 1 commit into
PX4:mainfrom
Saibernard:pr-fix-home-baro-lpf-dt

Conversation

Copy link
Copy Markdown
Contributor

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:

const float dt = baro_data.timestamp - _last_baro_timestamp;   // microseconds
_lpf_baro.update(baro_alt, dt);

Effect

Since alpha = dt / (tau + dt):

baro 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. 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:

_gps_vel_integral += 1e-6f * (vehicle_gps_position.timestamp - _last_gps_timestamp) * (-vehicle_gps_position.vel_d_m_s);

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.

…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>
github-actions Bot added kind:bug Something is broken or behaving incorrectly. scope:commander Arming, modes, failsafe, health checks, or vehicle state. labels Aug 27, 2026

Copy link
Copy Markdown
Contributor

🔎 FLASH Analysis

px4_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

px4_fmu-v6x [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%      +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

dakejahl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

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

dakejahl requested a review from haumarco August 27, 2026 22:50

Copy link
Copy Markdown
Contributor Author

Thanks for the review @dakejahl, happy to do both. I'd suggest keeping this PR as the minimal fix since it's already approved, and doing the sweep as a follow-up PR, so this one can merge as soon as @haumarco has had a look.

dakejahl merged commit 0bdf8c2 into PX4:main Aug 28, 2026
68 checks passed
Saibernard added a commit to Saibernard/PX4-Autopilot that referenced this pull request Aug 28, 2026
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>
dakejahl added a commit that referenced this pull request Aug 28, 2026
* 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>
dakejahl pushed a commit that referenced this pull request Sep 4, 2026
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:bug Something is broken or behaving incorrectly. scope:commander Arming, modes, failsafe, health checks, or vehicle state.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL