Model-based 3D wind estimator for fixed-wing
Solved Problem
Fixed-wing vehicles are sensitive to wind, but the current wind estimation only considers 2D wind. This is because the vertical component of the wind is unobservable with the pitot tube.
Solution
This PR implements a 3D wind estimator, which estimates the airflow angles relative to the vehicle. This implementation uses a first principle models with some simplifications.
- Implementation of a model-based air vector estimator
- Wind estimation based on projected estimated airflow vectors
- Added airlfow.msg to log air relative vectors
- Added groundtruth comparison with simulation for evaluation
While the implemented model is a bit simplistic, I believe this should be a good place to start and add more sophisticated algorithms, with the simulated evaluation environment.
Changelog Entry
For release notes:
Feature: Model-based 3D wind estimation for fixed-wing vehicles
New parameter:
FW_W_MASS: Vehicle Mass
FW_W_AREA: Wing Area
FW_W_C_B1: Vehicle Aerodynamic coefficient (side slip)
FW_W_C_A0: Vehicle Aerodynamic coefficient (zero angle of attack)
FW_W_C_A1: Vehicle Aerodynamic coefficient (angle of attack)
Documentation: Need to document once implemenation is finished
Alternatives
- I have defined a new
airflow.msgthat includes both air-relative velocities and projected wind estimates. We can maybe track this as angle of attack, but given the small values thought using velocities may be better
Test coverage
Tested with advanced_plane in gz sim
Context
- This implementation is adapted from @MarvinHarms's masters thesis. (Paper link will follow)
- @msberk FYI
🔎 FLASH Analysis
px4_fmu-v5x [Total VM Diff: 160 byte (0.01 %)]
FILE SIZE VM SIZE
-------------- --------------
+0.0% +160 +0.0% +160 .text
+0.2% +44 +0.2% +44 uORB::compressed_fields
+1.1% +36 +1.1% +36 px4::logger::LoggedTopics::add_default_topics()
+0.0% +28 +0.0% +28 [section .text]
[NEW] +16 [NEW] +16 __orb_airflow
[NEW] +16 [NEW] +16 __orb_airflow_groundtruth
+0.0% +16 +0.0% +16 g_cromfs_image
+12% +16 +12% +16 px4::wq_configurations::lp_default
+0.7% +8 +0.7% +8 uorb_topics_list
+0.8% +4 +0.8% +4 ModuleBase<>::unlock_module()
+0.4% +4 +0.4% +4 two_over_pi
-30.8% -4 -30.8% -4 g_nullstring
-3.1% -24 -3.1% -24 px4::logger::Logger::write_formats()
+0.0% +863 [ = ] 0 .debug_abbrev
+0.0% +32 [ = ] 0 .debug_aranges
+0.0% +48 [ = ] 0 .debug_frame
+0.1% +16.5Ki [ = ] 0 .debug_info
+0.0% +1.41Ki [ = ] 0 .debug_line
-16.7% -1 [ = ] 0 [Unmapped]
+0.0% +1.41Ki [ = ] 0 [section .debug_line]
+0.0% +20 [ = ] 0 .debug_loclists
+0.0% +25 [ = ] 0 .debug_rnglists
[NEW] +3 [ = ] 0 [Unmapped]
+0.0% +22 [ = ] 0 [section .debug_rnglists]
+0.0% +187 [ = ] 0 .debug_str
+0.0% +40 [ = ] 0 .strtab
+48% +16 [ = ] 0 __nxsched_add_prioritized_veneer
-41.0% -16 [ = ] 0 __nxsem_trywait_veneer
[NEW] +14 [ = ] 0 __orb_airflow
[NEW] +26 [ = ] 0 __orb_airflow_groundtruth
+0.0% +80 [ = ] 0 .symtab
+67% +32 [ = ] 0 __nxsched_add_prioritized_veneer
-40.0% -32 [ = ] 0 __nxsem_trywait_veneer
[NEW] +48 [ = ] 0 __orb_airflow
[NEW] +32 [ = ] 0 __orb_airflow_groundtruth
-33.3% -16 [ = ] 0 __stm32_dopoll_veneer
+50% +16 [ = ] 0 __stm32_recvfifo_veneer
-1.6% -160 [ = ] 0 [Unmapped]
+0.0% +19.2Ki +0.0% +160 TOTAL
px4_fmu-v6x [Total VM Diff: 168 byte (0.01 %)]
FILE SIZE VM SIZE
-------------- --------------
+0.0% +168 +0.0% +168 .text
+0.0% +56 +0.0% +56 [section .text]
+0.2% +44 +0.2% +44 uORB::compressed_fields
+1.1% +36 +1.1% +36 px4::logger::LoggedTopics::add_default_topics()
[NEW] +16 [NEW] +16 __orb_airflow
[NEW] +16 [NEW] +16 __orb_airflow_groundtruth
+0.0% +12 +0.0% +12 g_cromfs_image
+0.7% +8 +0.7% +8 uorb_topics_list
+0.8% +4 +0.8% +4 ModuleBase<>::unlock_module()
+0.4% +4 +0.4% +4 two_over_pi
-30.8% -4 -30.8% -4 g_nullstring
-3.1% -24 -3.1% -24 px4::logger::Logger::write_formats()
+0.0% +863 [ = ] 0 .debug_abbrev
+0.0% +32 [ = ] 0 .debug_aranges
+0.0% +48 [ = ] 0 .debug_frame
+0.1% +16.3Ki [ = ] 0 .debug_info
+0.0% +1.41Ki [ = ] 0 .debug_line
-60.0% -3 [ = ] 0 [Unmapped]
+0.0% +1.41Ki [ = ] 0 [section .debug_line]
+0.0% +46 [ = ] 0 .debug_loclists
+0.0% +23 [ = ] 0 .debug_rnglists
+100% +1 [ = ] 0 [Unmapped]
+0.0% +22 [ = ] 0 [section .debug_rnglists]
+0.0% +187 [ = ] 0 .debug_str
+0.0% +40 [ = ] 0 .strtab
[NEW] +14 [ = ] 0 __orb_airflow
[NEW] +26 [ = ] 0 __orb_airflow_groundtruth
+0.0% +80 [ = ] 0 .symtab
[NEW] +48 [ = ] 0 __orb_airflow
[NEW] +32 [ = ] 0 __orb_airflow_groundtruth
-2.8% -168 [ = ] 0 [Unmapped]
+0.0% +19.0Ki +0.0% +168 TOTAL
Updated: 2025-12-11T22:24:55
The cool one!
@bresch Thanks for the review and comments! I believe I have addressed all of them the best I can
Thanks for the FYI, this is interesting! @jwhgit01 how does this compare to some of the MB wind estimation work you've done with PX4?
@msberk This is more of laying the ground work so that we can integrate @jwhgit01 's work into PX4 :smile: As this implementation does not include a proper estimator, it will be more fragile to noisy pitot measurements and model errors
@Jaeyoung-Lim Very cool! FMI does this replace the old estimator or live in parallel?
Anyway, what I'm wondering about is docs? What changes for users in order to use this? Are there any risks we need to highlight?
@hamishwillee This should live in parallel. This requires careful identification of the parameters, and would then be used for model-based controllers where none of the default px4 controllers are.
@hamishwillee This should live in parallel. This requires careful identification of the parameters, and would then be used for model-based controllers where none of the default px4 controllers are.
Great. Then it needs separate docs, and should include tagging to indicate how it is enabled, the version it appears in,, and a release note.
@hamishwillee Are the message docs manually generated?
The only blurry point is "how are we going to tune that on a new plane"?
@bresch So, I think this is something that should be "identified". I know it is common practice that a lot of Drone people don't have a good aeromodel for their vehicle. However, for model-based methods I don't think there is a simple work around. Given that said, having a reliable procedure for this seems to be an open question, and highly depends on the vehicle as well.
At least for the simplistic model used here are based on a first principle model, so we should be able to identify the longitudinal dynamics parameters through the performance model and some maneuvers. However, identifying sideslip would require breaking coordinated turns which might be a bit more tricky.
Once this is in, I would like to work in the direction of having an easy way of model identification. Maybe we can do something like injecting maneuvers as done in autotuning and get a reasonable model on the other end. This would also allow us to utilize more complex aero models for wind estimation (and control) with better confidence.
@hamishwillee Are the message docs manually generated?
No. You don't need to update them, but you might get flaws reported.
The reason is that they are supposed to be updated in CI but I am still waiting on @mrpollo to automate running them in CI. What usually happens is that I manually update them once a week. Yes, it is a colossal waste of my time, but not as much as trying to get CI working.
Maybe we can do something like injecting maneuvers as done in autotuning and get a reasonable model on the other end. This would also allow us to utilize more complex aero models for wind estimation (and control) with better confidence.
FYI I believe this is what ardupilot does (on all frames) to refine the model https://ardupilot.org/copter/docs/common-systemid-mode-operation.html
This pull request has been mentioned on Discussion Forum for PX4, Pixhawk, QGroundControl, MAVSDK, MAVLink. There might be relevant details there:
https://discuss.px4.io/t/px4-dev-call-dec-3-2025-team-sync-and-community-q-a/48052/3
@sfuhrer Would appreciate if you could take a look as well.
This pull request has been mentioned on Discussion Forum for PX4, Pixhawk, QGroundControl, MAVSDK, MAVLink. There might be relevant details there:
https://discuss.px4.io/t/px4-dev-call-dec-3-2025-team-sync-and-community-q-a/48052/1
@bresch Anything else that needs to be addressed?
@bresch @sfuhrer Please let me know if there are any other concerns regarding this PR!