The gap
rtb.models.URDF.Panda() loads RTB's own bundled qut_frankie_description/robots/panda_arm_hand.urdf.xacro (rtb-data/rtbdata/xacro/qut_frankie_description/). Checked at the raw file level: zero <inertial> tags anywhere in the whole package -- every link has <visual>/<collision> only. robot.rne()/robot.inertia()/robot.coriolis()/robot.gravload() are all silently zero for every configuration, not because of an algorithm bug (see #636/#684) but because the underlying URDF genuinely carries no mass data. Confirmed on current main.
This is the URDF-model twin of #278 (closed 2022, same complaint about DH.Panda() at the time -- fixed later that year by porting values from the MATLAB toolbox's mdl_panda.m, see DH/Panda.py's git blame, commit 217d69c9, PR #294 by @askuric). Nobody has filed the equivalent report for the URDF model.
Don't just port DH/Panda.py's values across -- DH r/I are expressed in the DH link frame convention, which doesn't in general coincide with the URDF joint frame. Copying the numbers without also transforming them would produce plausible-looking but wrong dynamics, silently.
A real alternative already exists, and is already proven in this codebase
robot_descriptions's Panda (example-robot-data's panda_description) has real inertial data -- 13 <inertial> blocks, masses 0.629769 to 4.970684 kg for the arm links (matching DH/Panda.py's values almost exactly, suggesting a common manufacturer-data lineage) -- plus full collision geometry (17 shapes: meshes for the 8 arm links, box primitives for the 2 fingers) and the complete hand/gripper.
Frankie() (URDF/Frankie.py) already loads exactly this (super().__init__("panda", manufacturer="Franka Emika", gripper_link_index=9)) in production today. Verified directly: real per-link mass, 1 collision shape per link, sane nonzero rne() output, correct gripper resolution. This is a working, shipping precedent, not a hypothetical.
The tradeoff that stopped a straight swap
The vendored qut_frankie_description Panda's collision geometry isn't generic -- it's a hand-built capsule approximation (cylinder + sphere caps per link segment), 3-6 shapes per link, vs. robot_descriptions's 1 mesh (or box) per link.
Measured directly (same obstacle, same pose, both geometries):
- Single-shape
closest_point query: capsule primitive (sphere/cylinder) ~0.003ms once warmed up vs. mesh ~0.19ms -- ~60x per shape pair (analytic distance formula vs. mesh GJK/EPA).
link_collision_damper() (the function examples/neo.py-style reactive avoidance calls) end to end: 1.3ms vs 15.4ms, ~12x slower with robot_descriptions's geometry, despite it having fewer total shapes per link (1 vs 3-6) -- the per-shape mesh cost dominates.
The vendored xacro's collision macro does have a safety_distance parameter (looked like it might matter too), but it's shipped instantiated at 0.00 in the actual file -- a dormant, unused capability, not a real factor. The actual avoidance margin comes from link_collision_damper's own ds/di runtime parameters regardless of which geometry is used.
Recommended path, not yet implemented
Hybrid: load robot_descriptions's "panda" for kinematics/mass/inertia (as Frankie() already does), then graft the vendored xacro's capsule collision shapes onto the same links by name afterward. Gets real inertial data with no collision-performance regression for reactive-avoidance examples. More code than a straight swap, but keeps both correctness properties.
Also noted in passing: dual_panda_example.urdf.xacro in the vendored package is unreferenced anywhere in src/ -- dead content, worth a separate look whenever this is picked up.
Not scoped/prioritized for this session -- filed so the investigation (and the concrete numbers above) aren't lost.
The gap
rtb.models.URDF.Panda()loads RTB's own bundledqut_frankie_description/robots/panda_arm_hand.urdf.xacro(rtb-data/rtbdata/xacro/qut_frankie_description/). Checked at the raw file level: zero<inertial>tags anywhere in the whole package -- every link has<visual>/<collision>only.robot.rne()/robot.inertia()/robot.coriolis()/robot.gravload()are all silently zero for every configuration, not because of an algorithm bug (see #636/#684) but because the underlying URDF genuinely carries no mass data. Confirmed on currentmain.This is the URDF-model twin of #278 (closed 2022, same complaint about
DH.Panda()at the time -- fixed later that year by porting values from the MATLAB toolbox'smdl_panda.m, seeDH/Panda.py's git blame, commit217d69c9, PR #294 by @askuric). Nobody has filed the equivalent report for the URDF model.Don't just port
DH/Panda.py's values across -- DHr/Iare expressed in the DH link frame convention, which doesn't in general coincide with the URDF joint frame. Copying the numbers without also transforming them would produce plausible-looking but wrong dynamics, silently.A real alternative already exists, and is already proven in this codebase
robot_descriptions's Panda (example-robot-data'spanda_description) has real inertial data -- 13<inertial>blocks, masses 0.629769 to 4.970684 kg for the arm links (matchingDH/Panda.py's values almost exactly, suggesting a common manufacturer-data lineage) -- plus full collision geometry (17 shapes: meshes for the 8 arm links, box primitives for the 2 fingers) and the complete hand/gripper.Frankie()(URDF/Frankie.py) already loads exactly this (super().__init__("panda", manufacturer="Franka Emika", gripper_link_index=9)) in production today. Verified directly: real per-link mass, 1 collision shape per link, sane nonzerorne()output, correct gripper resolution. This is a working, shipping precedent, not a hypothetical.The tradeoff that stopped a straight swap
The vendored
qut_frankie_descriptionPanda's collision geometry isn't generic -- it's a hand-built capsule approximation (cylinder + sphere caps per link segment), 3-6 shapes per link, vs.robot_descriptions's 1 mesh (or box) per link.Measured directly (same obstacle, same pose, both geometries):
closest_pointquery: capsule primitive (sphere/cylinder) ~0.003ms once warmed up vs. mesh ~0.19ms -- ~60x per shape pair (analytic distance formula vs. mesh GJK/EPA).link_collision_damper()(the functionexamples/neo.py-style reactive avoidance calls) end to end: 1.3ms vs 15.4ms, ~12x slower withrobot_descriptions's geometry, despite it having fewer total shapes per link (1 vs 3-6) -- the per-shape mesh cost dominates.The vendored xacro's collision macro does have a
safety_distanceparameter (looked like it might matter too), but it's shipped instantiated at0.00in the actual file -- a dormant, unused capability, not a real factor. The actual avoidance margin comes fromlink_collision_damper's ownds/diruntime parameters regardless of which geometry is used.Recommended path, not yet implemented
Hybrid: load
robot_descriptions's"panda"for kinematics/mass/inertia (asFrankie()already does), then graft the vendored xacro's capsule collision shapes onto the same links by name afterward. Gets real inertial data with no collision-performance regression for reactive-avoidance examples. More code than a straight swap, but keeps both correctness properties.Also noted in passing:
dual_panda_example.urdf.xacroin the vendored package is unreferenced anywhere insrc/-- dead content, worth a separate look whenever this is picked up.Not scoped/prioritized for this session -- filed so the investigation (and the concrete numbers above) aren't lost.