Product Hunt Live on Product Hunt!
physics robot-training joints

How to set up mass, friction, and joint properties for robot training

The three pillars of robot physics setup — mass, friction, joints — determine whether your trained policy transfers to real hardware. Here's the calibration target for each, the schemas, and the common mistakes that quietly break training.

Rigyd Team
·

The three settings that determine whether a trained robotics policy transfers to real hardware are mass, friction, and joint properties. They sound basic. They are also the source of nearly every “works in sim, fails on robot” report I’ve seen from production teams.

This guide covers what each setting actually controls, the calibration target for each, the schemas you need to populate (USDPhysics, MJCF, URDF), and the common mistakes that quietly break training.

Pillar 1: Mass

Mass determines how much force is needed to move an object — and how the object responds to gravity, collisions, and contact.

What you need to specify

  • Total mass (kg). Critical: zero or unset triggers the simulator to infer from density × volume, which usually produces wrong values.
  • Center of mass (m, local space). Default zero is wrong for asymmetric objects (tools, bottles, electronics).
  • Inertia tensor (kg·m²). The 3×3 matrix that determines rotational dynamics. Default identity is wrong for almost everything except a unit sphere.

USDPhysics schema

def Cube "Mug" (
    prepend apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"]
)
{
    float physics:mass = 0.350
    vector3f physics:centerOfMass = (0, 0, 0.04)
    vector3f physics:diagonalInertia = (0.00045, 0.00045, 0.00031)
}

Calibration target

PropertySufficient accuracyNotes
Total mass±15-20% of measuredDR variance dominates beyond this
Center of massWithin 1-2 cm for normal-sized objectsTighter for precision parts
Inertia tensorComputed from geometry + estimated mass distributionNever use default identity

How to estimate

  • Volume × density. Compute volume from mesh. Identify dominant material (steel, plastic, ceramic, etc.). Look up density. Multiply. Accuracy: ±25-40% on typical objects, better with hollowness corrections.
  • Material identification + per-region density. Vision model classifies materials per-region (head vs handle of a hammer). Compute mass region-by-region. Accuracy: ±15-20%.
  • Catalog lookup. If you have catalog mass for the SKU, use it directly.
  • Lab measurement. Weigh the real object. Tightest accuracy, but doesn’t scale.

Common mistakes

  • Setting physics:mass = 0 and assuming the simulator will compute from density × volume. Some simulators do, most don’t, all produce inconsistent results. Always set mass explicitly.
  • Using the geometric centroid as center of mass. Asymmetric objects (any tool with a heavier head, anything with batteries on one side) have off-center COMs that affect manipulation realism.
  • Default identity inertia. A 2-meter-tall robot with default identity inertia tips implausibly under any rotation. Compute inertia from geometry.

Pillar 2: Friction

Friction determines how surfaces resist sliding when in contact — and is the most common cause of sim-to-real manipulation failures.

What you need to specify

  • Static friction coefficient (μ_s). Resistance to starting motion. Typical range: 0.1 (Teflon-on-Teflon) to 1.5 (rubber-on-rubber).
  • Dynamic friction coefficient (μ_d). Resistance once moving. Always ≤ static. Typical: 0.05-1.2.
  • Restitution (e). Bounciness on impact. 0 (clay) to 1 (perfect rubber). Most real objects: 0.1-0.4.

USDPhysics schema

def Material "RubberLike" (
    prepend apiSchemas = ["PhysicsMaterialAPI"]
)
{
    float physics:staticFriction = 0.8
    float physics:dynamicFriction = 0.7
    float physics:restitution = 0.4
    float physics:density = 1100
}

Apply via physics:material relationship on the collider:

def Cube "Object" (
    prepend apiSchemas = ["PhysicsCollisionAPI"]
)
{
    rel physics:material = </Materials/RubberLike>
    bool physics:collisionEnabled = true
    token physics:approximation = "convexDecomposition"
}

Calibration target

Material categoryStatic frictionDynamic frictionRestitution
Glass-on-glass0.940.400.5
Steel-on-steel (clean)0.740.570.6
Aluminum-on-steel0.610.470.5
Plastic-on-plastic0.300.200.3
Rubber-on-concrete (dry)0.900.850.4
Wood-on-wood0.500.300.4
Ceramic-on-ceramic0.750.450.3
Cardboard-on-cardboard0.450.300.1
Cloth-on-cloth0.300.200.0

These are reference values from material physics handbooks (MIT Material Constants, MatWeb). Real values vary ±0.1-0.2 with surface finish, contamination, and humidity.

Practical target: within ±0.1 coefficient. Tighter requires lab measurement of the specific surface; rarely worth it.

Common mistakes

  • Setting both friction values to 0.5 as a “default that works.” It doesn’t. 0.5 is mid-range but wrong for most actual materials. Set values that match the identified material.
  • Static < dynamic. Physically impossible. Some simulators auto-correct; most just produce wrong contact behavior.
  • Restitution = 1.0 (“perfectly elastic”). Almost no real material has restitution above 0.7. Most are 0.2-0.4. Default 1.0 produces unrealistic bouncing.
  • Forgetting to bind the collider to a material. Without binding, simulators fall back to default friction (often 0.5/0.5) regardless of what’s set on the body. Always check rel physics:material is set.

Pillar 3: Joints

For multi-link mechanisms — robot arms, manipulators, articulated objects — joints define how links move relative to each other.

Joint types

  • Revolute (rotational): rotation about one axis. Most common — every robot arm joint.
  • Prismatic (linear): translation along one axis. Linear actuators, sliders.
  • Spherical (ball): rotation about all three axes. Hip joints, ball-and-socket.
  • Fixed: rigid attachment, no movement. For welding parts together while keeping the prim hierarchy clean.
  • D6: fully configurable 6-DOF, used for everything custom.

USDPhysics schema (revolute joint)

def PhysicsRevoluteJoint "Joint_Shoulder"
{
    rel physics:body0 = </Robot/Base>
    rel physics:body1 = </Robot/Shoulder>
    point3f physics:localPos0 = (0, 0, 0.5)
    point3f physics:localPos1 = (0, 0, 0)
    float physics:axis = 0    # Rotates about local X
    float physics:lowerLimit = -180
    float physics:upperLimit = 180

    # Drive
    float drive:angular:physics:targetPosition = 0
    float drive:angular:physics:stiffness = 1000
    float drive:angular:physics:damping = 100
    float drive:angular:physics:maxForce = 1000
}

Articulation Body vs individual rigid bodies

For any robot with more than 3-4 joints, use Articulation Body (PhysicsArticulationRootAPI on the robot’s root prim). This solves the chain in reduced coordinates — fast, stable, and the production-grade choice.

Individual rigid bodies with joints use full 6-DOF dynamics per body. Slower, prone to constraint drift, and only worth the cost when you need the per-body flexibility (e.g., a chain where individual links sometimes detach).

Joint drives

A joint without a drive is passive — it moves freely under external forces. A joint with a drive is actively controlled.

Drive parameters that matter:

  • stiffness — proportional gain (P term). Higher = faster response, risk of vibration.
  • damping — derivative gain (D term). Higher = smoother but slower response.
  • maxForce — torque/force limit. Set to match your real actuator.
  • targetPosition / targetVelocity — what you’re commanding.

Tuning is task-specific. Start with stiffness ~1000, damping ~100 for a typical 7-DOF arm, then adjust based on tracking accuracy and stability.

Joint limits

Always set lowerLimit and upperLimit. Without limits:

  • Joints can rotate past physical stops, producing impossible poses
  • Self-collision detection still works but the simulator allows the violation, hurting training

Common mistakes

  • Forgetting PhysicsArticulationRootAPI on the robot’s root. Robot loads but joints behave as independent rigid bodies, producing slow and unstable physics.
  • Default-zero joint limits — the joint can’t move at all. Most subtle bug to find: robot loads, no errors, but every commanded motion fails.
  • Drive stiffness too high. Causes oscillation. Halve until stable, then raise damping.
  • Drive stiffness too low. Robot can’t hold pose against gravity. Raise stiffness or add gravity compensation in your controller.
  • Mixing position and velocity targets. Some simulators allow both; the solver picks one and silently ignores the other. Pick one mode per joint.
  • Wrong joint axis. USDPhysics uses local-frame axes; misalignment between body0 and body1 frames produces unintended motion. Always verify with a small test motion before training.

Domain randomization integration

For sim-to-real transfer, you want to randomize physics parameters within physically plausible bands rather than using fixed values:

PropertyRandomization bandNotes
Mass±15-20% of estimateWider for unknown SKUs, tighter for catalog items
Static/dynamic friction±0.1 coefficientWider for outdoor materials
Restitution±0.05Most contact is plastic; restitution rarely dominates
Joint friction±20% of nominalOften forgotten, matters for arm controllers
Joint damping±20% of tuned valueStable bands tighten this for some controllers
External wrenches±5% of expected weightSimulates measurement noise

DR works best when centered on calibrated estimates, not uniform-random over a wide range. Wide-uniform DR over implausible values produces worse policies, not more robust ones.

Validation: how to know your physics setup is right

Before any asset or robot enters training, validate:

  • Drop test on the asset. Settles correctly under gravity, doesn’t tip implausibly.
  • Hold test on the robot. With joints commanded to home pose and gravity on, the robot maintains pose without oscillation. Joint torques in the dataset stay within maxForce.
  • Sweep test. Command each joint through its full range. No interpenetration at extremes, no constraint violations.
  • Contact test. Push the robot with a known external force. Reaction matches expectation (within ±20%).
  • Validation suite in CI. Automate the above. Every asset and robot revision passes the suite before entering training.

Practical recommendations

  1. Get mass right per-asset, friction right per-material, joints right per-robot. These three categories cover 90% of physics-related sim-to-real bugs.
  2. Use AI-automated estimation (Rigyd or similar) for asset mass and friction at scale. Manual tuning is fine for a handful of hero objects but doesn’t scale to the asset diversity production training needs.
  3. Hand-tune joint drives for your specific robot. Per-robot, not per-asset.
  4. Always populate inertia tensors. Default identity is the silent killer of physics realism.
  5. Validate everything in CI with the test suite above. The cost of a bad physics setup compounds across millions of training steps and weeks of failed real-world tests.

The teams shipping policies that actually transfer to hardware all have one thing in common: they treat physics calibration as a first-class engineering concern, not as default values someone set once and forgot. Mass, friction, and joints get more engineering attention than the policy network architecture in many production setups — and that’s the right priority.

Frequently asked questions

How accurate do mass and friction values need to be?

Mass within 15-20% of measured values and friction within ±0.1 coefficient — both inside typical domain randomization variance bands. Tighter accuracy rarely improves real-world performance because policies trained with proper DR generalize across that variance anyway. The exception is precision tasks (peg-in-hole, dexterous manipulation) where ground-truth measurement on hero assets is worth the effort.

What's the difference between Articulation Body and individual rigid bodies for a robot?

Articulation Body in NVIDIA PhysX (and similar in MuJoCo) treats the robot as a single articulated chain solved with reduced coordinates — fast, stable, and the right choice for production training. Individual rigid bodies linked by joints use full 6-DOF dynamics per body, which is more flexible but slower and prone to constraint drift. Use Articulation Body for any robot with more than 3-4 joints. The Isaac Sim convention is to mark the robot's root prim with PhysicsArticulationRootAPI.

Why does my robot drift or vibrate under no commanded forces?

Almost always one of: (1) joint drives have non-zero target without intent — set targetVelocity and targetPosition explicitly, (2) joint stiffness too low and gravity is pulling links down, (3) inertia tensors are unset or unrealistic, causing the solver to compute incorrect torques, (4) collision interpenetration at rest creates phantom forces. Check inertia first — most simulator-time vibration is bad inertia.

Skip the manual physics work

Upload any 3D model and get a SimReady OpenUSD asset in minutes. Mass, friction, collision meshes — all calibrated automatically.