Repository navigation
AP_WheelEncoder: add MAVLink backend and support up to four encoders - #34815
Open
Ali-barari wants to merge 8 commits into
Open
Ali-barari wants to merge 8 commits into
Ali-barari wants to merge 8 commits into
Conversation
A vehicle with one encoder per wheel, rather than one per side, could not be represented with only two instances. Default to four on boards with more than 1MB of flash and keep two elsewhere, and allow the limit to be overridden at build time. Vehicles configuring only the first two instances behave as before.
added 7 commits
October 19, 2026 12:21
WENC had fields for the first two wheel encoders only. Add Dist2/Qual2 and Dist3/Qual3, keeping the existing fields so that tools reading Dist0 and Dist1 are unaffected.
Adds WENCx_TYPE=2, fed by the WHEEL_DISTANCE message, for vehicles whose encoders are read by an external board rather than the autopilot's own pins. Each instance takes the distance at its own index in the message. Only messages from components on this vehicle are accepted, as other vehicles on the same network may stream their own WHEEL_DISTANCE. WHEEL_DISTANCE reports metres while the library stores counts, so the distance is converted using the instance's CPR and radius. These cancel when converting back, so only the radius affects the reported rate. Backends can now report their health, and whether their readings are streamed. A streamed sensor reports at its own rate, so consumers must not treat an unchanged reading as the wheel having stopped. healthy() also now returns false for an instance with no driver. The backend is enabled by default on boards with more than 1MB of flash.
A streamed wheel encoder reports at its sender's rate, which may be slower than the 50Hz loop that feeds EKF3. An unchanged reading then means no new data, but was sent to EKF3 as a zero rotation, and the next real reading was divided by the short time since that send, giving alternating zero and inflated speeds. For streamed sensors, wait for a new reading and use the time between readings, discarding any that span a gap in the stream. Also skip instances that are not healthy, which includes an unconfigured instance below the highest configured one; these were previously fed to EKF3 as a stationary wheel. Locally decoded encoders, where no pulse does mean no movement, are unchanged.
Streams WHEEL_DISTANCE for four wheels at a few Hz from a component on the vehicle, and checks that every sample sent to EKF3 carries the true wheel speed, that no readings are lost, that a gap in the stream is not fused as one long reading, that all four distances are logged, and that WHEEL_DISTANCE from another system is ignored.
Ali-barari
pushed a commit
to Ali-barari/ardupilot_wiki
that referenced
this pull request
Oct 19, 2026
Covers WENCx_TYPE = 2, fed by the WHEEL_DISTANCE message, the third and fourth encoder instances, and the per-instance WENC log message, added in ArduPilot/ardupilot#34815.
Ali-barari
pushed a commit
to Ali-barari/ardupilot_wiki
that referenced
this pull request
Oct 19, 2026
Covers WENCx_TYPE = 2, fed by the WHEEL_DISTANCE message, the third and fourth encoder instances, and the per-instance WENC log message, added in ArduPilot/ardupilot#34815.
Ali-barari
force-pushed
the
feature/wheelencoder-mavlink
branch
from
October 19, 2026 19:52
3cfc40c to
2a442ea
Compare
Author
|
Hardware logs referenced in the description. |
Global Size Summary (Total Flash delta in bytes)Built from 2a442ea rebased onto dc78bc5
|
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
Adds a wheel encoder backend fed by the
WHEEL_DISTANCEmessage (WENCx_TYPE = 2), for vehicles whose encoders are read by an external board rather than by the autopilot's own pins. Also raises the instance limit to four, so that a vehicle can have one encoder per wheel rather than one per side.Classification & Testing (check all that apply and add your own)
Checked by a human programmer
Non-functional change
No-binary change
Infrastructure change (e.g. unit tests, helper scripts)
Automated test(s) verify changes (e.g. unit test, autotest)
Tested manually, description below (e.g. SITL)
Tested on hardware
Logs attached
Logs available on request
New autotest
Rover.MAVLinkWheelEncoders. It streamsWHEEL_DISTANCEfor four wheels at about 3–5Hz from a component on the vehicle, with all wheels at 0.5m/s and a 1.5s pause in the stream, and checks:WHEEL_DISTANCEfrom another system ID is ignored;WENClogs all four distances;RWOH), every sample sent to EKF3 is close to the true speed, no readings are lost, no sample spans the pause, and readings resume after it.The test fails without the fix. With the existing
update_wheel_encoder()it fails on a fabricated sample (speed 0.000m/s (DA=0 DT=0.040)). With a variant that discards readings more than 100ms apart, no samples reach EKF3. Without the gap check it fails on a sample spanning the pause (DT=1.94).Existing tests pass.
Rover.WheelEncodersstill passes.Builds. Built for SITL, ARKV6X (4 instances) and MatekF405 (2 instances). Size change from
size_compare_branches.pyagainst the merge base, in bytes:The MAVLink backend and the extra instances are excluded on MatekF405 (1MB). The Copter change is the longer
WENCformat string.Real hardware: this branch on an ARKV6X in a four-wheel skid-steer rover. An external board reads the four encoders and streams
WHEEL_DISTANCEat 25Hz over serial, using the autopilot's system ID. Over three drives:RWOH, each wheel is sent to EKF3 every 80ms (longest 122ms) with no spikes. About 1% of samples are zero, and only when the wheel is barely moving (under 0.25m/s either side), which is the encoder's resolution;VELXY = 7, alongside external nav),XKFDvelocity innovations are median 0.016m/s, 95th percentile 0.078m/s.An earlier version of this change was also run on ArduRover 4.6.3. Logs: wheelencoder_mavlink_hw_logs.zip, containing
fc_log_140900.bin(drive with outputs and wheel feed) andfc_log_142610.bin(drive with wheel odometry fused).Description
Rover already sends
WHEEL_DISTANCE; it now also accepts it.WHEELENCODER_MAX_INSTANCEScan be overridden at build time. Vehicles that configure only the first two instances behave as before.AP_WheelRateControlstill uses instances 0 and 1 (left and right).WENChad fields for two encoders only. It gainsDist2/Qual2andDist3/Qual3, which are zero for encoders that are not present. The existing fields are unchanged, so graphs and tools that readDist0/Dist1keep working.distance[0], and so on).WHEEL_DISTANCE, and these would otherwise be read as this vehicle's wheels.WENCx_CPRandWENCx_RADIUS. These cancel on the way back, so only the radius affectsget_rate().AP_WHEELENCODER_MAV_ENABLED, which is listed inbuild_options.py.WHEEL_DISTANCEto the library.update_wheel_encoder()assumed that an unchanged reading means the wheel has stopped. That holds for a locally decoded encoder but not for a streamed one. When the sender is slower than the 50Hz loop, EKF3 received a fabricated zero rotation, and the next real reading was divided by the short time since that send. For streamed sensors it now waits for a new reading, uses the time between readings, and discards readings that span a gap of more than 500ms. It also skips unhealthy instances, which includes an unconfigured instance below the highest configured one. Previously such an instance was fed to EKF3 as a stationary wheel. Locally decoded encoders are unchanged.Notes for reviewers
AP_LoggerandAP_WheelEncoderlogging commits are split by subsystem and only build together.get_distance()andget_signal_quality()do not bounds-check the instance, soLog_Write()only calls them for instances that exist.