From Tom's review on #4096: #4096 (comment)
ConnectedTwistBase.read_state() reads both read_velocities() and read_odometry() and packs them into the same three virtual joints, so chassis/vx.position is world x while chassis/vx.velocity is body vx. Those are not a position and its derivative: with a non-zero yaw, body vx is not d(world x)/dt, so one joint carries two quantities in two frames. Every consumer has to know that convention to use it, and the joint names describe the command DOFs rather than the state they carry.
The visible cost in #4096 is joint_state_aliases, a coordinator-name to model-name map that exists only so a twist base's odometry joints can feed the model's planar base joints. That is a stopgap for this, not a feature.
Two ways out. The mechanical one splits the two: three command joints (vx, vy, wz, velocity mode) and three read-only state joints (base_x, base_y, base_yaw, position), with read_state filling pose into the state joints and twist into the command joints. Each follower then reads pose from the state names, and the alias disappears because state joints can be named exactly as the model names them. The better one stops expressing base pose as joints at all and publishes odometry on a pose/TF topic that followers subscribe to.
Consumers that read pose out of joint positions today: holonomic_pose_follower_task.py:198, path_follower_task.py:217 and :238 (the RPP follower inherits both), planar_base_trajectory_task.py:237, plus the manipulation module's _on_joint_state alias path. Worth doing alongside the control coordinator refactor rather than before it.
From Tom's review on #4096: #4096 (comment)
ConnectedTwistBase.read_state()reads bothread_velocities()andread_odometry()and packs them into the same three virtual joints, sochassis/vx.positionis world x whilechassis/vx.velocityis body vx. Those are not a position and its derivative: with a non-zero yaw, body vx is not d(world x)/dt, so one joint carries two quantities in two frames. Every consumer has to know that convention to use it, and the joint names describe the command DOFs rather than the state they carry.The visible cost in #4096 is
joint_state_aliases, a coordinator-name to model-name map that exists only so a twist base's odometry joints can feed the model's planar base joints. That is a stopgap for this, not a feature.Two ways out. The mechanical one splits the two: three command joints (vx, vy, wz, velocity mode) and three read-only state joints (base_x, base_y, base_yaw, position), with
read_statefilling pose into the state joints and twist into the command joints. Each follower then reads pose from the state names, and the alias disappears because state joints can be named exactly as the model names them. The better one stops expressing base pose as joints at all and publishes odometry on a pose/TF topic that followers subscribe to.Consumers that read pose out of joint positions today:
holonomic_pose_follower_task.py:198,path_follower_task.py:217and:238(the RPP follower inherits both),planar_base_trajectory_task.py:237, plus the manipulation module's_on_joint_statealias path. Worth doing alongside the control coordinator refactor rather than before it.