Skip to content

Panda models

Peter Corke edited this page Sep 15, 2026 · 2 revisions

RTB ships four Franka-Emika Panda-related models — DH-based DH.Panda, and the URDF-based Panda, Frankie, FrankieOmni — and they don't all load the same underlying data. This page explains why, and what tradeoffs that implies.

The short version

Model Source Inertial data Collision geometry
DH.Panda() hand-coded DH parameters; m/r/I ported from MATLAB RTB's mdl_panda.m (PR #294) real (see caveat below) none — DH models carry no collision primitives at all
Panda() (default) bundled qut_frankie_description xacro none — every link is massless hand-built capsule approximation
Panda(use_robot_descriptions=True) robot_descriptions real (0.63–4.97 kg per arm link) plain meshes
Frankie() robot_descriptions real plain meshes
FrankieOmni() bundled qut_frankie_description xacro (arm) + bundled clearpath_ridgeback_description xacro (base) none on the arm hand-built capsule approximation

If you need correct dynamics (rne(), inertia(), coriolis(), gravload()) from a Panda model, use Frankie(), or Panda(use_robot_descriptions=True). If you need fast real-time collision checking against the robot's own geometry (reactive avoidance, link_collision_damper(), examples/neo.py-style control loops), the default Panda() is faster.

Why this split exists

RTB bundles its own copy of a Panda arm xacro (qut_frankie_description, rtb-data/rtbdata/xacro/qut_frankie_description/) — originally vendored to support FrankieOmni, RTB's model of a real QUT lab robot (a Panda arm mounted on a Robotnik-style omnidirectional mobile base), which needs to splice a Panda arm onto a mobile base xacro that no public package provides pre-combined. Panda() reused that same bundled arm file opportunistically, since it was already sitting there — not because it needed anything specific from it.

That bundled xacro has real, hand-built collision geometry: each link is approximated as a capsule (a cylinder with sphere caps at each end), which is fast and numerically well-behaved to collision-check against. It also has a safety_distance macro parameter that could pad every collision shape's radius with a margin — but the currently-shipped file instantiates it at 0.00, so it's dormant, not actually doing anything today. What it does not have is any <inertial> tag anywhere in the package — confirmed by grepping the whole tree. It was built for geometry (visual + collision), not dynamics.

robot_descriptions (a third-party package for fetching robot description files on demand) has its own Panda entry, sourced from example-robot-data. It carries real inertial parameters — masses matching DH.Panda()'s own dynamics data almost exactly, suggesting a common manufacturer-data lineage — and real collision geometry too, but as plain meshes (one per link, plus box primitives for the fingers) rather than hand-tuned capsules.

Frankie() already loads the robot_descriptions Panda directly (super().__init__("panda", ...)), and has done so in production for a while — it's not a hypothetical, it's a working precedent that this source is solid.

The DH model's own history

This isn't the first time a "Panda has no inertial data" report has come up — issue #278 raised exactly this for DH.Panda() back in 2022. It was fixed shortly after: PR #294, by Antun Škurić (askuric), ported the parameters straight out of the MATLAB Robotics Toolbox's own mdl_panda.m, which itself traces back to the same manufacturer data robot_descriptions' URDF carries.

That fix doesn't transfer to the URDF models, and porting it directly would be a real mistake, not just redundant work. DH inertial parameters (r, the CoM offset, and I, the inertia tensor) are expressed relative to the DH link frame, a convention with its own specific axis placement rules — not the same frame a URDF joint uses. Copying DH-frame numbers into a URDF link without also transforming them through the frame difference would produce plausible-looking numbers that are dynamically wrong, silently — exactly the class of bug this whole investigation started by chasing down (see #636/#684). If you need real inertial data on a URDF Panda, get it from a source that's already self-consistent in the URDF's own frame convention — robot_descriptions, not DH.Panda.py.

That same doubt applies reflexively to DH.Panda() itself, though: PR #294's own description calls it "just a quick transfer," with no stated check that MATLAB RTB's DH link frame for each Panda joint lines up with Python RTB's for the same a/d/alpha/mdh values. Issue #688 tracks actually verifying this — cross-checked against Frankie()'s independently-sourced data, gravity torque matches at every configuration tried and the full inertia matrix M(q) matches to within ~1%, which is reassuring but not exhaustive (no Coriolis-term-specific check, no third independent reference yet). See that issue for the numbers.

The collision-performance tradeoff, measured

Why not just always use robot_descriptions and be done with it? Measured directly, same obstacle, same robot pose, same machine:

  • A single closest_point query: capsule primitive (sphere/cylinder) ≈0.003ms once warmed up, vs. mesh ≈0.19ms — roughly 60x more expensive per shape pair. A mesh-vs-shape query runs full GJK/EPA against a triangulated surface; a capsule has an analytic closed-form distance formula.
  • link_collision_damper() end to end (the function real-time reactive-avoidance code, including examples/neo.py, calls every control-loop iteration): 1.3ms with the bundled capsule geometry vs. 15.4ms with robot_descriptions' meshes — about 12x slower, despite the mesh model having fewer total collision shapes per link (1 vs. 3–6). The per-shape mesh cost dominates enough to overcome that.

For a control loop budgeting a few milliseconds per step, that difference is the kind of thing you'd actually notice, not a rounding error. That's why Panda(use_robot_descriptions=True) is an opt-in rather than a default-flip — neither source is strictly better, it depends on what you're doing with the model.

What's still open

  • FrankieOmni() doesn't have an equivalent option. It builds its link list by hand (URDF_read() twice, then manual parent-splicing) rather than going through URDFRobot's own loader, so adding a toggle there means threading the choice through that manual assembly rather than a single constructor kwarg.
  • The ideal fix — real inertial data and the fast capsule collision geometry, for every model — needs a hybrid: load robot_descriptions for kinematics/mass/inertia, then graft the bundled xacro's capsule shapes onto the same links by name afterward. Not yet implemented.
  • dual_panda_example.urdf.xacro, also in the bundled qut_frankie_description package, is unreferenced by anything in src/ — dead content, noted but not cleaned up.
  • DH.Panda()'s own inertial data hasn't been exhaustively verified — see #688.

Tracked in issue #686 (this page) and #688 (DH.Panda() verification).

Clone this wiki locally