Repository navigation
fix(autoware_carla_interface): publish the ego pose at base_link instead of the vehicle center - #13372
Conversation
…ead of the vehicle center The sensor kit loader spawns sensors wheelbase/2 ahead of their base_link calibration, so base_link is the rear axle and the CARLA actor origin is the vehicle center. The GNSS pose, the ground-truth localization (kinematic_state and map->base_link) and the RViz initial pose still used the actor origin as base_link, which put the ego wheelbase/2 (1.4 m with the default wheelbase) ahead of the sensors and of the other actors. Apply the same wheelbase offset in those three places. Signed-off-by: Tomoki Cho <tomoki225255@gmail.com>
|
Thank you for contributing to the Autoware project! 🚧 If your pull request is in progress, switch it to draft mode. Please ensure:
|
…README and the pose conversions Note in the README that the sensor mapping's wheelbase also places base_link wheelbase/2 behind the actor origin for the published poses, and that spawn_point places the actor origin. Comment the reverse conversion of the RViz initial pose and shorten the helper docstring to the line length. No behavior change. Signed-off-by: Tomoki Cho <tomoki225255@gmail.com>
There was a problem hiding this comment.
The base_link diagnosis looks right to me, and the mechanics hold up — the offset goes through Transform.transform() rather than a flat 2D shift, and the rotation is carried through unchanged so the body-frame conversion is untouched.
This is about the one thing the Notes leave open — "the twist is still the actor origin's velocity". After this change that is no longer neutral: Odometry.twist is expressed in child_frame_id, which stays "base_link" while only the pose moves, so one message now refers to two points ~1.8 m apart. Before this PR both were the actor origin: offset from the sensors, but self-consistent.
Measured on Town10HD, vehicle.toyota.prius, autopilot, 60 Hz synchronous, max_substep_delta_time 2 ms; 197 turning samples of 600. The twist is rebuilt exactly as _publish_ground_truth_odometry does, and the true velocity of the base_link point comes independently from central-differencing that point's position.
twist.linear.y at base_link, turning samples, rms [m/s]:
| as published | 0.5084 |
true at base_link |
0.1914 |
| error as published | 0.6993 |
residual after adding omega x r |
0.1577 |
The noise floor — the same point differenced two ways — is 0.0909 m/s, so this is 7.7x the floor. Regressing (published − true) on the lever-arm term gives R² = 0.9999, so the error is that term and nothing else. In all 169 samples with |omega_z| > 0.05 the two have opposite signs; worst is omega_z −0.678 rad/s at 8.0 m/s publishing +0.904 m/s against a true −0.340. The standing/straight comparison in the description is the one condition under which this term vanishes, which is why it passed.
The part I did not expect. The slope came out 1.287x the term computed with wheelbase / 2, so I solved for the reference point separately: regressing (reported velocity − central-differenced velocity of the actor origin) on omega_z gives 0.4116 * omega_z, R² = 0.9964. get_velocity() is not referenced to the actor origin — it reports a point ~0.412 m ahead of it along body x. Declared center_of_mass.x is 0.500 for this vehicle, 18% off the measured value, so that number does not look safe either.
So the lever arm to base_link is 1.425 + 0.412 = 1.837 m, and the independent error regression required 1.834 m — agreeing to 3 mm. Adding omega x (wheelbase / 2) alone would recover about 78% of it.
Worth noting autoware_pose_instability_detector dead-reckons this twist and ERRORs when it disagrees with the pose, though I have not checked whether it runs with publish_ground_truth_localization.
Not asking you to widen this PR — you flagged the twist yourself and keeping the pose change separate is defensible. Would you rather take it here or as a follow-up? Happy to open the follow-up with this measurement.
Measured with Claude Code; I can post the probe scripts if you want to reproduce.
The ground truth odometry places the pose at base_link, but the twist was still CARLA's velocity, so while turning twist.linear.y described a point about 1.8 m ahead of the pose. Move the linear velocity to base_link as v + omega x r, with the same wheelbase/2 offset as the pose, before the CARLA-to-ROS conversion. CARLA reports the velocity of the physics body's center of mass, whose position the Python API does not expose, so the offset between it and the actor origin (about 0.4 m on the Prius) remains as before. Signed-off-by: Tomoki Cho <tomoki225255@gmail.com>
… with the pose CARLA reports the velocity of the center of mass, so shifting it by wheelbase/2 leaves the twist about 0.4 m ahead of base_link rather than at it. Describe the shift instead of claiming it reaches base_link. No behavior change. Signed-off-by: Tomoki Cho <tomoki225255@gmail.com>
|
The fix covers what I raised, thanks for taking it on — and your read on the remaining ~0.4 m matches what I measured, so here are the numbers in case they help whoever picks up the follow-up. Regressing (reported velocity − central-differenced velocity of the actor origin) on So
One vehicle and one map, so the 0.412 is presumably vehicle-dependent; the structure — that the reference point is not the actor origin — should not be. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #13372 +/- ##
=======================================
Coverage 37.44% 37.44%
=======================================
Files 2030 2030
Lines 142134 142134
=======================================
Hits 53217 53217
Misses 88917 88917
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ead of the vehicle center (autowarefoundation/autoware_universe#13372) * fix(autoware_carla_interface): publish the ego pose at base_link instead of the vehicle center The sensor kit loader spawns sensors wheelbase/2 ahead of their base_link calibration, so base_link is the rear axle and the CARLA actor origin is the vehicle center. The GNSS pose, the ground-truth localization (kinematic_state and map->base_link) and the RViz initial pose still used the actor origin as base_link, which put the ego wheelbase/2 (1.4 m with the default wheelbase) ahead of the sensors and of the other actors. Apply the same wheelbase offset in those three places. Signed-off-by: Tomoki Cho <tomoki225255@gmail.com> * docs(autoware_carla_interface): describe the base_link offset in the README and the pose conversions Note in the README that the sensor mapping's wheelbase also places base_link wheelbase/2 behind the actor origin for the published poses, and that spawn_point places the actor origin. Comment the reverse conversion of the RViz initial pose and shorten the helper docstring to the line length. No behavior change. Signed-off-by: Tomoki Cho <tomoki225255@gmail.com> * fix(autoware_carla_interface): move the ground truth twist to base_link The ground truth odometry places the pose at base_link, but the twist was still CARLA's velocity, so while turning twist.linear.y described a point about 1.8 m ahead of the pose. Move the linear velocity to base_link as v + omega x r, with the same wheelbase/2 offset as the pose, before the CARLA-to-ROS conversion. CARLA reports the velocity of the physics body's center of mass, whose position the Python API does not expose, so the offset between it and the actor origin (about 0.4 m on the Prius) remains as before. Signed-off-by: Tomoki Cho <tomoki225255@gmail.com> * docs(autoware_carla_interface): say the ground truth twist is shifted with the pose CARLA reports the velocity of the center of mass, so shifting it by wheelbase/2 leaves the twist about 0.4 m ahead of base_link rather than at it. Describe the shift instead of claiming it reaches base_link. No behavior change. Signed-off-by: Tomoki Cho <tomoki225255@gmail.com> --------- Signed-off-by: Tomoki Cho <tomoki225255@gmail.com>
Description
SensorKitLoaderplaces every sensorwheelbase / 2behind itsbase_linkcalibration (it subtractswheelbase / 2from x before spawning), so in this bridgebase_linkis the rear axle and the CARLA actor origin is the vehicle center,wheelbase / 2ahead of it. The GNSS pose, the ground truth localization (publish_ground_truth_localization) and the RViz initial pose still used the actor origin asbase_link, which put the ego 1.425 m (with the default wheelbase) ahead of its sensors and of every other actor.This applies the same wheelbase offset in those three places, using the value the sensor loader already reads from the sensor mapping (
vehicle_config.wheelbase, default 2.85 m), so the published poses stay consistent with the sensor placement.Related links
Parent Issue:
Related:
carla_sensor_kitcalibration is authored the same way (x_base = x_car_center + 1.425 (WB=2.850 m)): https://github.com/autowarefoundation/autoware_launch/blob/main/sensor_kit/carla_sensor_kit_launch/carla_sensor_kit_description/config/sensor_kit_calibration.yamlHow was this PR tested?
colcon build --packages-select autoware_carla_interface --symlink-installand pre-commit pass.Run against a live CARLA 0.9.16 server on Town01 with
planning_simulator.launch.xmland this bridge, localization replaced by the package'scarla_state_publisher(GNSS pose to/localization/kinematic_state, no NDT), and the ground truth objects of #13321 as the reference. Compared with the rear axle that CARLA reports through the wheel physics,base_linkmoved from 1.40 m ahead of it to 0.03 m behind it (mostly the default wheelbase against the Prius's 2.82 m). A vehicle placed right beside the ego is drawn beside it in RViz after the change and 1.4 m behind it before; driving to a goal 200 m away behaved the same. A video is attached.Notes for reviewers
The offset reuses the sensor loader's wheelbase on purpose: the
carla_sensor_kitcalibration is authored against WB = 2.85 m, so the poses have to use the same number as the sensors;vehicle_info.wheel_base(2.79 m forsample_vehicle) and the CARLA physics wheelbase differ from it.The RViz initial pose is converted the other way, so a 2D Pose Estimate now puts
base_linkon the clicked point.spawn_pointstill places the actor origin.Interface changes
None.
Effects on system behavior
With
publish_ground_truth_localization(the E2E planning setup) the ego localization moves from the vehicle center to the rear axle, 1.425 m backward with the default wheelbase; a setup that compensated for the old offset elsewhere, for example in its goal or spawn positions, will see that compensation become an error.spawn_pointkeeps placing the actor origin, sobase_linknow startswheelbase / 2behind the given coordinates.With NDT localization the estimated pose does not change: the GNSS pose only seeds the initialization, and a 2D Pose Estimate now lands
base_linkon the clicked point instead of 1.4 m behind it. Any consumer that takes the GNSS pose asbase_linkgets the corrected pose, for example the package'scarla_state_publisher(not started by the shipped launch files) or external tools that read/sensing/gnss/pose_with_covariance.The twist of that odometry is shifted with the pose, so while turning its
twist.linear.ychanges byomega_z * wheelbase / 2from main, which the E2E planners read as the ego lateral velocity.Update after review: shift the twist with the pose
c32da50 also shifts the linear velocity of the ground truth odometry back by the same
wheelbase / 2as the pose, asv + omega x r. This cancels exactly the mismatch between pose and twist that this PR introduced. Thanks to @ujinf74 for the measurement that caught it.The other ~0.4 m found in the review is on main as well: with the default physics, CARLA reports the velocity of the physics body's center of mass, and the Python API exposes neither the velocity of another point nor where the center of mass is (
center_of_massin the physics control is only an offset added to the computed one). Removing that part would need thebase_linkvelocity computed by differencing its pose between ticks, which adds state and teleport handling, so I kept it out of this PR; I can follow up on it separately if needed.Measured in my own run with the review's settings on CARLA 0.9.16 (Town10HD_Opt,
vehicle.toyota.priuson autopilot, 60 Hz synchronous,max_substep_delta_time2 ms, 600 frames). A probe calls_publish_ground_truth_odometry()of main, cfb1381 and c32da50 on the same vehicle every frame and takes the true velocity from central differences of each version's own published poses (probe written and run with Claude Code). Over the 197 samples with |yaw rate| > 1 deg/s, cfb1381 reproduces the review's table to within 0.0001 m/s.twist.linear.yatbase_link, 197 turning samples, rms [m/s]:base_linkomega x rc32da50 gives exactly the residual predicted by adding
omega x (wheelbase / 2)to cfb1381, removing 77% of the error as the review estimated. The rest corresponds to a 0.416 m lever arm, the distance from the actor origin to the center of mass; main shows the same (0.1586 m/s, 0.418 m) against its own pose at the actor origin. A 60 s run and the default 0.05 s step (10 ms substeps) give the same result, about 0.61 → 0.14 m/s.At the default 0.05 s step, the yaw rate CARLA reports jitters around the actual turning rate even on straight roads, flipping sign every frame, by about 0.04 rad/s rms;
twist.angular.zon main already carries this, and it disappears at 60 Hz with 2 ms substeps. Because the shift multiplies the yaw rate bywheelbase / 2, the jitter now also reachestwist.linear.y, as about 0.05 m/s rms while driving straight (0.005 m/s before).AI assistance
I used Claude Opus 5.5 / Opus 5 / Fable 5 to check, modify, and review the code.
I have personally reviewed the entire content of this PR.
town01_pr_sample.mp4