Pinocchio’s limb-length example changes four entries in model.jointPlacements: the local y translations of the left and right knee and wheel joints. It retains the joints and their motion coordinates. The edit changes the relative geometry used by the model, rather than choosing a new joint angle or locking part of the robot.
The official Upkie example loads a robot description, checks the expected offsets for 24-centimeter limbs, and changes the femur and tibia lengths to 30 centimeters. Its useful lesson is how a physical length maps to a model’s joint origins: the value stored in a joint placement is not necessarily the full length of the part.
Why the stored translation is shorter than the limb
The example defines two known offsets from the robot description: 0.072 meters for the knee and 0.065 meters for the wheel. It treats both offsets as lying along the relevant local y axis. For each side of the robot, it writes the requested length minus the corresponding offset into translation[1].
Applying those formulas gives:
| Joint entry on each side | At a limb length of 0.24 m | At a limb length of 0.30 m |
|---|---|---|
| Knee: length − 0.072 m | 0.168 m | 0.228 m |
| Wheel: length − 0.065 m | 0.175 m | 0.235 m |
Each stored translation grows by 0.06 meters. The knee and wheel entries still differ by 0.007 meters because their known offsets differ. Assigning 0.30 directly to both entries would describe another geometry; it would discard the offsets encoded by this particular robot description.
The numbers come from the example’s mapping, not a general rule that every knee origin sits 72 millimeters from a link end. When adapting the pattern to another robot, identify the named joints, the origin of each parent frame and the axis along which the length is represented before substituting new values.
A local y change can move a joint along world x
The Pinocchio model definition defines jointPlacements relative to each joint’s parent joint frame. The array entry is therefore not a world-coordinate position. The parent frame’s orientation determines how its translation appears in the world.
For a simple geometric illustration, let a child’s placement origin be p = r + R d, where r is the parent origin in world coordinates, R rotates parent-frame vectors into world coordinates, and d is the child’s relative translation. Holding the parent pose fixed, a local change Δd produces Δp = R Δd.
Take Δd = (0, 0.06, 0) meters:
| Parent-frame orientation in this hypothetical example | World displacement of the child origin |
|---|---|
| Aligned with the world axes | (0, 0.06, 0) m |
| Rotated +90° about world z | (−0.06, 0, 0) m |
Both edits use the same local y increment. Their world directions differ. These are illustrative frame orientations, not measured Upkie poses.
In a chain, an upstream translation also moves its descendants. If two segments each grow by 0.06 meters, a particular endpoint need not move 0.12 meters along world y: the two local directions depend on the chain’s pose. To inspect the effect, compare the same named frame at the same joint configuration before and after the model edit. Changing the joint configuration at the same time would mix two causes of movement.
Keep the model edit separate from the pose
A joint configuration selects the position of the robot’s movable joints within a model. The placement edit changes the model on which those configurations act. This gives two useful comparisons: one pose evaluated in two geometries, or several poses evaluated in the new geometry.
For example, a longer segment may shift a tool when the arm is straight and shift it in another direction when the arm is bent. The edited length remains the same in both cases. The coordinate transformations distribute its effect differently.
This also differs from constructing a reduced model. Reduction fixes selected joints at a reference configuration and removes their independent motion. In the limb-length example, the selected knee and wheel joints remain present; their placement translations change. Use a geometry edit when the segment dimensions changed, and a reduction when a joint should stay locked in a chosen pose.
Check the four edits, then inspect the model layers you use
The example’s check_limb_lengths function compares the four scalar placement entries with the expected length-minus-offset values using np.allclose. That directly checks whether those entries have the intended values. Its first check prints a message on success; a failed check is not implemented as an exception that stops the later update.
For your own model, make a mismatch actionable before modifying it. Confirm that the intended joints exist and that their current placements agree with your robot-description assumptions. Preserve a copy of the original model or its source parameters so the before-and-after comparison is explicit.
The shown function writes only the translation entries. It does not rewrite the URDF, rescale a mesh or recompute an inertia tensor. The geometry-placement code composes an attached geometry object’s placement with its parent joint’s transform. Recomputing those placements can move a shape with the joint while leaving the shape’s own dimensions unchanged. This is why a longer kinematic segment and a longer rendered part require separate inspection.
Choose that inspection from the model’s actual use. A reach calculation needs the intended joint and tool-frame transforms. Collision planning also needs the relevant collision shapes and their attachments. A dynamics calculation additionally needs mass, center-of-mass and inertia values for the changed parts. The URDF and SDFormat guide explains where these separate model layers originate.
Keep the change record concrete: joint names, old and new relative translations, the reference configuration used for comparison, and the model layers updated with the dimensions. That makes a later discrepancy traceable to a specific length, frame or asset rather than to a vague “rescaled robot.”
This explanation follows the official moving devel source checked on October 16, 2026. The example was read, not executed. The offset table is arithmetic from its constants; the rotated-frame table is the hypothetical geometry specified above.