Mass and Inertia#

Every dynamic rigid body in Newton needs positive mass and a physically meaningful inertia tensor for stable simulation. Newton provides three ways to assign these properties:

  1. Direct specification on the body via add_link().

  2. Density-based inference from collision shapes added with ShapeConfig.

  3. File import (USD, MJCF, URDF), where mass properties are parsed from the source format and mapped to Newton’s internal representation.

For the distinction between static, kinematic, and dynamic bodies see Articulations.

Best practices#

Tip

Dynamic bodies should have positive mass. If a body has no shapes with density, set mass and inertia explicitly on add_link().

Tip

Use is_kinematic=True for prescribed motion — do not rely on zero mass to make a body immovable. See Articulations for details.

Tip

Prefer density-based inference when possible. Letting Newton compute mass and inertia from shape geometry keeps mass properties consistent with collision geometry and avoids manual bookkeeping.

Tip

Use lock_inertia=True to protect hand-specified mass properties from subsequent shape additions. This is the mechanism the MJCF importer uses when an <inertial> element is present.

Tip

Check finalize warnings. Set validate_inertia_detailed=True on ModelBuilder during development to get per-body warnings for any mass or inertia values that were corrected.

Specifying mass and inertia#

Direct specification on the body#

add_link() accepts the following inertial parameters:

  • mass — body mass [kg]. Defaults to 0.0.

  • inertia — 3x3 inertia tensor [kg m2] relative to the center of mass. Defaults to the zero matrix.

  • com — center-of-mass offset [m] from the body origin. Defaults to the origin.

  • armature — artificial scalar inertia [kg m2] added to the diagonal of the inertia tensor. Useful for regularization.

These values serve as the initial mass properties of the body. If shapes with positive density are subsequently added, their contributions are accumulated on top of these initial values (see below).

Automatic inference from shape density#

When a shape is added via one of the add_shape_*() methods with ShapeConfig.density > 0, Newton automatically computes mass, center of mass, and inertia tensor from the shape geometry and accumulates the result onto the parent body.

The accumulation follows three steps:

  1. Mass: the shape’s mass is added to the body’s total mass.

  2. Center of mass: the body COM is recomputed as the mass-weighted average of the existing body COM and the shape’s COM (transformed to body frame).

  3. Inertia: both the existing body inertia and the shape inertia are shifted to the new COM using the parallel-axis theorem (Steiner’s theorem), then summed.

This means multiple shapes on the same body compose additively.

import numpy as np
import warp as wp
import newton

builder = newton.ModelBuilder()

# Body with initial mass 2.0 kg
body = builder.add_link(mass=2.0)
# Shape adds mass from density: a 1m-radius sphere at 1000 kg/m^3
builder.add_shape_sphere(body, radius=1.0, cfg=builder.ShapeConfig(density=1000.0))

sphere_mass = 4.0 / 3.0 * np.pi * 1000.0  # ~4189 kg
assert abs(builder.body_mass[body] - (2.0 + sphere_mass)) < 1.0

Special cases:

  • Planes and heightfields never contribute mass, regardless of density.

  • Sites enforce density=0 and never contribute mass.

  • lock_inertia=True on add_link() prevents subsequent shape additions from modifying the body’s mass, COM, or inertia.

  • Hollow shapes (ShapeConfig.is_solid=False) compute shell inertia by subtracting the inner volume’s contribution from the outer, using ShapeConfig.margin as shell thickness.

  • Cylinder mass properties include the circular side profile selected by barrel_radius.

Rod and cable mass#

add_rod() builds each segment as a capsule, and neighboring capsules overlap at their tips. Rods therefore take segment mass from the cylindrical part only, density * area * rest_length, so refining the same centerline preserves total mass. area is pi * radius**2, or for hollow rods (is_solid=False) the tube wall of thickness margin. total_mass [kg] instead sets the mass of the whole rod, distributed by rest length, and overrides ShapeConfig.density.

Segment inertia is the solid-cylinder or open-tube tensor at the assigned mass and rest length. For outer radius r, inner radius a (zero for a solid rod), and rest length L, the local axial moment is m * (r**2 + a**2) / 2 and both transverse moments are m * ((r**2 + a**2) / 4 + L**2 / 12). Collision geometry stays capsule-based. USD cable import uses the same solid cylinder mass and inertia model; default_shape_cfg.is_solid does not apply to imported cables. Capsules joined manually with rod joints keep ordinary capsule mass and inertia.

This replaces the previous capsule-based inertia for native rods and USD cables, changing their angular response. Scenes tuned to that approximation may need to retune their solver settings. Hollow mass and inertia use the tube wall; material-derived rod stiffness retains its solid-circle convention. Use explicit section rigidities when modeling a hollow material section.

Mass resolution during file import#

When importing from USD, MJCF, or URDF, each format has its own rules for how mass properties are authored, inferred, or overridden. Regardless of format, shape-derived contributions all flow through the same accumulation logic described above.

The common pattern across formats is:

  • Explicit inertial data takes precedence when present (UsdPhysics.MassAPI for USD, <inertial> for MJCF/URDF).

  • Shape-based inference is the fallback when explicit data is missing — mass and inertia are computed from collision geometry and density.

  • Both MJCF and URDF importers accept ignore_inertial_definitions=True to skip explicit inertial data and always infer from shapes.

For format-specific details, see:

  • USD: USD Parsing and Schema Resolver System — covers the MassAPI precedence cascade, ComputeMassProperties callback, and collider density priority.

  • MJCF: add_mjcf() — follows MuJoCo semantics where <inertial> fully overrides geom-derived mass (via lock_inertia), and geoms contribute via density when <inertial> is absent. Mesh geoms use the inertia mode of their <mesh> asset: MuJoCo’s default legacy sums absolute tetrahedron volumes, exact uses the signed volume, convex the convex hull (limited by maxhullvert as in MuJoCo), and shell the surface area. A mesh without faces uses MuJoCo’s convex hull of all its vertices, not the collision hull, and a primitive geom fitted to a mesh is sized and posed by the equivalent inertia box of the mesh’s mode, as in MuJoCo. A mesh with too little surface area or volume for its mode, which MuJoCo rejects, adds no mass and logs a warning; use inertia="shell" to give flat or open surface meshes mass. An exact mesh whose faces have inconsistent orientation, which MuJoCo also rejects, logs a warning and keeps its signed volume.

  • URDF: add_urdf() — uses <inertial> directly when present; falls back to collision geometry with default density otherwise. By default visual shapes do not contribute mass; however, parse_visuals_as_colliders=True promotes visual geometry into the collider set, making it mass-contributing at default_shape_density.

Validation and correction at finalize#

During finalize(), Newton validates and optionally corrects mass and inertia properties for all bodies.

Compiler settings#

The following attributes on ModelBuilder control validation behavior:

Setting

Default

Description

balance_inertia

True

Fix triangle-inequality violations on principal moments by shifting eigenvalues uniformly.

bound_mass

None

Minimum mass value. If set, clamps small masses up to this value.

bound_inertia

None

Minimum inertia eigenvalue. If set, ensures all principal moments are at least this value.

validate_inertia_detailed

False

When True, uses CPU validation with per-body warnings. When False, uses a fast GPU kernel that returns only the count of corrected bodies.

inertia_relative_tolerance

1e-6

Non-negative relative eigenvalue threshold (dimensionless).

inertia_absolute_tolerance

1e-10

Positive absolute floor for eigenvalue and triangle-inequality tolerances [kg·m²].

inertia_correction

1e-6

Non-negative extra diagonal shift applied when repairing inertia [kg·m²]. Zero disables the extra shift, not validation.

Small-inertia models#

The eigenvalue threshold is max(inertia_relative_tolerance * largest_moment, inertia_absolute_tolerance). When the smallest moment is below this threshold, all three moments are shifted by threshold - smallest_moment + inertia_correction. Thus the correction is not a minimum imposed on every tensor.

For example, a 0.1 mm diameter capsule with 0.5 mm cylindrical length and density 1,000 kg/m³ has an axial moment of approximately 5.4e-18 kg·m². To preserve this valid tensor while retaining validation, lower the absolute tolerance before finalization:

builder.inertia_absolute_tolerance = 1e-20
model = builder.finalize()

This example value is specific to that scale, not a universal recommended tolerance. Lowering inertia_correction alone does not prevent a tensor below the threshold from being adjusted. The numerical settings apply equally to the detailed CPU and fast Warp paths. They must be finite and within the float32 range; the absolute tolerance must remain positive in float32. Changing inertia_correction is a separate repair-policy choice. Setting it to zero removes the extra padding for tensors that fail validation; for example, a positive-mass body with zero inertia is repaired to approximately inertia_absolute_tolerance * identity. Retaining the default correction avoids that change and does not affect tensors that pass validation. A repair still includes a representable positive shift if rounding would leave the tensor singular. Inverse inertia is computed with float64 intermediates and stored in float32; both the tensor and its inverse must remain representable in float32.

To skip the entire mass/inertia validation and correction stage:

model = builder.finalize(skip_validation_inertia=True)

This preserves the builder’s mass and inertia, including invalid values. Finalization always recomputes derived inverses from the final properties, including after manual edits or imports. Non-positive masses receive zero inverse mass and zero inverse inertia; singular tensors receive zero inverse inertia. Validation skipping omits finiteness, symmetry, positive-definiteness, mass and inertia bounds, and triangle-inequality checks. The caller is responsible for supplying consistent properties supported by the chosen solver. finalize(skip_all_validations=True) also skips this stage. Unlike earlier releases, skip_all_validations=True now also disables mass and inertia bounds and balancing. To retain those corrections while skipping other checks, leave skip_all_validations=False and skip_validation_inertia=False and enable the individual skip flags for the checks you want to omit.

Neither option undoes earlier importer processing, shape-derived mass computation, or fixed-joint merging. These settings are model-compilation policy, applied after import; they do not introduce new USD-authored fields.

Checks performed#

The detailed (validate_inertia_detailed=True) and fast (default) validation paths apply the same conceptual checks, but the detailed path emits per-body warnings. Work is underway to unify the two implementations.

The following checks are applied in order:

  1. Negative mass — set to zero.

  2. Small positive mass below bound_mass — clamped (if bound_mass is set).

  3. Zero mass with non-zero inertia — inertia zeroed.

  4. Inertia symmetry — enforced via \((I + I^T) / 2\).

  5. Positive definiteness — eigenvalues below the configured threshold adjusted.

  6. Eigenvalue bounds — all eigenvalues clamped to at least bound_inertia (if set).

  7. Triangle inequality — principal moments must satisfy \(I_1 + I_2 \geq I_3\). If violated and balance_inertia is True, eigenvalues are shifted uniformly to satisfy the inequality.

Note

collapse_fixed_joints() merges mass and inertia across collapsed bodies before validation runs.

Shape inertia reference#

The table below summarizes the mass formula for each shape type when density is positive. For the full inertia tensor expressions, see compute_inertia_shape().

Shape

Mass

Notes

Sphere

\(\tfrac{4}{3} \pi r^3 \rho\)

Hollow: shell inertia (outer minus inner).

Box

\(8\, h_x h_y h_z\, \rho\)

Half-extents.

Capsule

hemisphere caps + cylinder

\((\tfrac{4}{3}\pi r^3 + 2\pi r^2 h)\,\rho\)

Cylinder

\(\pi r^2 h\, \rho\)

Cone

\(\tfrac{1}{3} \pi r^2 h\, \rho\)

Center of mass offset from base.

Ellipsoid

\(\tfrac{4}{3} \pi a\,b\,c\, \rho\)

Semi-axes.

Mesh

Integrated from triangles

Cached on Mesh when available; recomputed from vertices otherwise. Supports solid and hollow.

Plane

Always 0

Regardless of density.

Heightfield

Always 0

Regardless of density.

Hollow shapes (ShapeConfig.is_solid=False) compute shell inertia by subtracting the inner volume’s contribution, using ShapeConfig.margin as shell thickness.