Decouple onboard estimator updates onto a background thread#118
Open
Yuming-Lee24 wants to merge 2 commits into
Open
Decouple onboard estimator updates onto a background thread#118Yuming-Lee24 wants to merge 2 commits into
Yuming-Lee24 wants to merge 2 commits into
Conversation
ratheron
requested changes
Jul 14, 2026
Collaborator
|
The new version is also tested in real? Then we can merge it |
Collaborator
Author
Not yet, I will do it as soon as possible. |
Collaborator
Author
I have tested in real, all good, ready to merge |
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
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Decouple the onboard estimator (external mocap pose) updates from the control loop by
streaming them from a dedicated background thread at a fixed frequency (default 30 Hz).
Previously the external pose was sent inside the environment
step()and rate-limited there(
POS_UPDATE_FREQ). Because the send was coupled to the control loop, any stall or jitter inthe loop (heavy
compute_control, GC pause, blocking I/O) directly delayed the estimatorrefresh. When the loop stalled, the onboard Kalman filter stopped receiving fresh poses and had
to dead-reckon on IMU alone, causing drift and a visible correction once poses resumed.
What changed
Crazyflienow owns a persistent event loop running on a background thread and an_update_estimatorstask that sends the external pose every1 / update_freqseconds,independent of the control loop.
connect()starts the updater at the end of the connection sequence, so the estimatoralready receives poses before
reset()runs and the drone is armed.close()sends anemergency stop first, then signals the updater to stop and joins the thread cleanly (with a
bounded timeout).
_runsubmits operations withrun_coroutine_threadsafeinstead ofrun_until_complete, so control commands and poseupdates share the same loop safely.
send_external_pose()calls inRealRaceCoreEnv.step()and in
return_to_start(); the background thread keeps streaming poses in both cases.update_freqis exposed as a constructor /from_radioargument (default 30 Hz).How it was verified
The payoff of decoupling only shows up when the control loop stalls — under a healthy loop both
the old and new code send at a steady 30 Hz. To make the difference measurable, the send
interval was logged on every external-pose send and an artificial stall was injected into the
controller:
Both the injected stall and the logging are debug-only and are not part of this PR.
Results
Real deployment on
cf52, same injected stall (controller repeatedly exceeds the loop periodby ~0.28 s):
step())Sample logs (after, decoupled — note the gap stays ~33 ms despite the loop overrun):
Sample logs (before, coupled — the gap tracks the stall):
Re-verification after moving the updater start to the end of
connect()The first version started the updater in
reset(arm=True). After moving the start to the endof
connect()and reorderingclose()to emergency-stop first, the full deployment wasre-run on
cf52with the same injected stall and instrumentation:decoupling is unaffected by the new start location.
connect()returns, i.e. beforereset()andarming.
return_to_start()and the shutdown sequence;close()emergency-stops first and then joins the updater cleanly (no send or shutdownerrors logged, including on Ctrl-C).
Testing
ruff check .— passedruff format --check --diff .— passedpixi run -e tests tests -v— 83 passed, 20 skipped (GPU-only)cf52as above, re-verified after moving the updaterstart to the end of
connect()