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.
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
| Property | Sufficient accuracy | Notes |
|---|---|---|
| Total mass | ±15-20% of measured | DR variance dominates beyond this |
| Center of mass | Within 1-2 cm for normal-sized objects | Tighter for precision parts |
| Inertia tensor | Computed from geometry + estimated mass distribution | Never 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 = 0and 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 category | Static friction | Dynamic friction | Restitution |
|---|---|---|---|
| Glass-on-glass | 0.94 | 0.40 | 0.5 |
| Steel-on-steel (clean) | 0.74 | 0.57 | 0.6 |
| Aluminum-on-steel | 0.61 | 0.47 | 0.5 |
| Plastic-on-plastic | 0.30 | 0.20 | 0.3 |
| Rubber-on-concrete (dry) | 0.90 | 0.85 | 0.4 |
| Wood-on-wood | 0.50 | 0.30 | 0.4 |
| Ceramic-on-ceramic | 0.75 | 0.45 | 0.3 |
| Cardboard-on-cardboard | 0.45 | 0.30 | 0.1 |
| Cloth-on-cloth | 0.30 | 0.20 | 0.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:materialis 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
PhysicsArticulationRootAPIon 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:
| Property | Randomization band | Notes |
|---|---|---|
| Mass | ±15-20% of estimate | Wider for unknown SKUs, tighter for catalog items |
| Static/dynamic friction | ±0.1 coefficient | Wider for outdoor materials |
| Restitution | ±0.05 | Most contact is plastic; restitution rarely dominates |
| Joint friction | ±20% of nominal | Often forgotten, matters for arm controllers |
| Joint damping | ±20% of tuned value | Stable bands tighten this for some controllers |
| External wrenches | ±5% of expected weight | Simulates 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
- 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.
- 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.
- Hand-tune joint drives for your specific robot. Per-robot, not per-asset.
- Always populate inertia tensors. Default identity is the silent killer of physics realism.
- 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.