Deformable terrain
World::addDeformableTerrain creates a soil bed that deforms under rigid bodies and articulated systems. Distributed normal pressure supports their weight, while shear resistance provides traction. The model describes pressure-dependent sinkage, elastic recovery, permanent ruts, and traction buildup with slip.

Car visuals are based on Sci - Fi Buggy by TiyaMakes, licensed under CC BY 4.0. The meshes and textures are modified; the buggy asset credits describe the changes and redistribution requirements, and Asset Credits lists every third-party model on this site.


Creating a bed
raisim::DeformableTerrain::Material soil;
soil.bekkerKc = 0.0;
soil.bekkerKphi = 4.0e4;
soil.sinkageExponent = 1.1;
soil.elasticStiffness = 2.0e6;
soil.damping = 2000.0;
soil.cohesion = 100.0;
soil.frictionCoefficient = 0.6;
soil.shearDisplacement = 0.01;
const size_t nx = 201, ny = 101;
auto* bed = world.addDeformableTerrain(
nx, ny, 8.0, 4.0, 0.0, 0.0, std::vector<double>(nx * ny, 0.0), soil);
bed->setName("soil");Heights use height[y * nx + x]. The bed covers the same local coordinate range as Height Map. You may place the bed with setPosition and setOrientation between steps. Deformation and normal pressure act along the bed's local Z axis. The bed is static; dynamic and kinematic body types are rejected.
The default values are illustrative parameters, not a calibrated material. Choose the mesh spacing to resolve the footprint and the desired rut width; 2--5 cm is a useful starting point for robot feet and wheels of comparable size. Finer spacing represents curved footprints and rut boundaries in more detail.
Constitutive model
For total downward displacement
The effective width
The elastic branch
Penetration adds loading-only viscosity:
Here
Shear strength follows Mohr--Coulomb and Janosi--Hanamoto:
Here shearDisplacement = 0 selects fully mobilized friction immediately. The normal pressure
Material parameters
DeformableTerrain::Material defines the soil response for the entire bed. These parameters use SI units: metres, seconds, and pascals (N/m2). They describe pressure and shear stress per unit area. Total contact forces are the corresponding stresses integrated over the loaded footprint. The parameters describe the material and do not need rescaling with mesh spacing or timestep.
Defaults and valid values
| C++ member | Units | Default | Valid values |
|---|---|---|---|
bekkerKc | 0.0 | Finite, nonnegative | |
bekkerKphi | 2.0e5 | Finite, nonnegative | |
sinkageExponent | Dimensionless | 1.1 | Finite, strictly positive |
elasticStiffness | Pa/m | 2.0e6 | Finite, strictly positive |
damping | Pa s/m | 1000.0 | Finite, nonnegative; zero disables viscosity |
cohesion | Pa | 0.0 | Finite, nonnegative |
frictionCoefficient | Dimensionless | 0.6 | Finite, nonnegative |
shearDisplacement | m | 0.01 | Finite, nonnegative; zero gives full mobilization |
At least one of bekkerKc and bekkerKphi must be positive. Invalid materials throw std::invalid_argument at construction. The API imposes no upper bound on finite coefficients; acceptance does not establish that a value represents a particular soil. The defaults and the softer example bed above are illustrative rather than measured material presets.
Normal loading: bekkerKc, bekkerKphi and sinkageExponent
The Bekker loading envelope is bekkerKc supplies the width dependence: its contribution increases as bekkerKphi supplies the width-independent contribution. Setting bekkerKc = 0 removes this explicit width dependence, although the footprint's area still affects force and its shape affects the pressure distribution. Despite its name, bekkerKc is a normal pressure coefficient; it is distinct from the tangential cohesion parameter.
For a flat plate with uniform pressure
For example, a 20 kg, 0.4 by 0.6 m plate has
sinkageExponent controls the curvature of the loading envelope. Its incremental stiffness is
Elastic recovery: elasticStiffness
elasticStiffness is the pressure slope during elastic unloading and reloading:
For example, 1000 Pa with
Loading rate: damping
damping adds pressure
The total normal pressure
Shear strength: cohesion and frictionCoefficient
The fully mobilized shear stress is cohesion sets its pressure-independent intercept. For example, frictionCoefficient sets the pressure-dependent slope;
Increasing either parameter raises available traction at the same normal pressure and shear history. With frictionCoefficient = 0, nonzero cohesion can still resist slip while contact is loaded. Setting both to zero removes tangential resistance. Cohesion does not bond a body to the surface or create a tensile normal force when it lifts away.
These soil parameters govern the distributed terrain contact law. The rigid material-pair friction and restitution settings in Material System do not tune this constitutive response. Use the soil's Material for its traction and loading response, and material pairs for ordinary rigid contacts.
Traction buildup: shearDisplacement
shearDisplacement is the Janosi--Hanamoto displacement scale
For positive
The history is accumulated slip displacement at a loaded soil patch, not elapsed time, commanded wheel travel, or body displacement in free flight. It grows while a patch is loaded and resets when that patch unloads. Permanent sinkage survives unloading separately. A new footprint therefore does not inherit the shear history of an unloaded rut. Fit

Constitutive curves for the dynamics model. The loading curves share 1000 Pa at 20 mm; the recovery example unloads with
Calibrating a material
- Measure slow plate-loading curves at several widths and loads. Fit
and the two Bekker coefficients together. At a fixed , plotting against gives slope and intercept . A single width identifies only their sum ; it cannot identify both coefficients separately. Use the same projected-width convention as the colliders being simulated. - Measure unloading/reloading slopes to fit
, including recovered depth and the remaining rut. Fit the loading envelope and elastic branch consistently; the normal pressure is their minimum. - Repeat indentation at different speeds to fit
from the extra loading pressure, after accounting for quasistatic pressure. Recheck impact compaction rather than fitting only the final resting depth. - Measure shear stress versus displacement at several normal pressures. Fit
and to the fully mobilized stresses, then fit to the buildup curves. Include low-speed slip if creep matters. - Validate with the intended feet or wheels, loads, speeds, mesh spacing and timestep, including unloading and repeated passes. Compare sinkage, recovered depth, force and traction histories as well as final poses. Agreement with an analytical plate case alone does not establish accuracy for a particular soil or for phenomena outside the supported model.
Terrain state and saving
getSoilState() returns three arrays with one value per vertex: sinkage, plasticSinkage, and shear. They represent total downward displacement, permanent downward displacement, and accumulated loaded slip, respectively. setSoilState restores these arrays. resetSoil returns to the undeformed mesh. Call state setters between complete steps. HeightMap height-edit APIs are rejected for this subclass because they bypass soil history. getUndeformedHeights returns the original surface.
getSoilStepStats() describes the last step. contacts counts the quadrature contacts near bodies, including those a body approaches but has not yet loaded. contactArea and normalForce are the area and total normal force of the loaded contacts. A soil contact's getDepth() is its separation from the floor of the current rut: positive while the body approaches, negative while it compresses the soil.
World checkpoints capture the soil history and support deterministic replay. XML export saves original heights and the complete state. To author a soil bed in a world XML file, use explicit height samples and a soil child:
<heightmap name="soil" x_size="2" y_size="2" x_sample="3" y_sample="3"
height="0 0 0 0 0 0 0 0 0">
<soil bekker_kc="0" bekker_kphi="40000" sinkage_exponent="1.1"
elastic_stiffness="2000000" damping="2000" cohesion="100"
friction_coefficient="0.6" shear_displacement="0.01"/>
</heightmap>Soil XML currently requires height; generate or load a height array in C++ when starting from PNG, text, or procedural terrain. Exported soil elements also contain sinkage, plastic_sinkage, and shear arrays.
For terrain-only contacts on articulated systems, the solver packs the fixed impulse-response matrices once per physics step and uses vectorized updates of the generalized velocities. When a second sweep is needed, it packs padded Jacobian columns once so all three contact-velocity components can use vector instructions, while retaining each component's original summation order. The responses include the suspension's eliminated pin and equality constraints. Contact order, quadrature samples, material equations, and convergence tolerances remain the same.
When an articulated terrain solve needs more than four sweeps, sufficiently large contact groups can use six-component velocity and force/torque coordinates for each contacting link. Setup is attempted only when the residual after four sweeps still exceeds 1,000 times the existing stopping threshold, so solves near convergence retain the cheaper full-coordinate updates. Each quadrature point still receives its own normal and shear solve, with its original apparent inertia, material history, and residual tolerances. While consecutive points on one link are visited, changes to other links accumulate as force and torque. Their coupled velocity is updated before the next contact on another link is read. The Gauss-Seidel contact order and stopping criteria are retained; contacts are never merged.
The six-direction basis is rebuilt from well-separated points in the current step. Every original sparse Jacobian and every full generalized impulse response must pass reconstruction checks at double-precision roundoff before this path is enabled. The responses include eliminated pin and equality constraints. Small groups and poorly conditioned geometry continue through the full articulated update. At the end of the solve, the original point responses update all generalized velocities, including passive joints absent from the contact Jacobians. This reduction can change floating-point summation order; it changes neither the mesh resolution, timestep, constitutive equations, nor solver accuracy settings.
The solver also seeds mesh-soil contacts with the last converged traction on the same bed and body link. The cache sorts contact positions by X to limit nearest-point searches to the contact matching radius. It keeps them apart from the same body's rigid contacts, so the search cost stays linear when a body rests on soil and on a rigid support at once. Quadrature points are matched one to one; the cached traction is scaled by the current contact area and timestep and projected into the current soil strength bound. Impacts, unmatched points, and stale or non-converged cache entries receive a cold start. Current soil history and material forces are still solved each step at the existing convergence tolerances. Normal and shear updates continue iterating until their coupled force converges, including an isolated contact.
Warm starting changes the numerical iteration path, so a cold-start trajectory need not be bit-for-bit identical. Set world.getContactSolver().getConfig().terrainWarmStart = false to disable cached traction guesses while keeping the same mesh, timestep, material equations, and tolerances. World checkpoints include this configuration and cache for deterministic replay.
Within each coupled solve, shear updates also reuse the previous scalar root as an initial guess inside the current analytic bracket, in both compliance and shift coordinates. The current slip, normal load, and shear strength are evaluated again before a root is accepted, using the same constitutive residual tolerance. A valid seed avoids evaluating unused bracket endpoints; the solver verifies a feasible endpoint if its safeguarded iterations exhaust their limit. It retains the impulse already evaluated at the accepted root. These guesses reset at the start of each physics step.
The scalar shear roots use Newton for small corrections and safeguarded Halley updates for larger corrections, including the curvature of the displacement-dependent shear strength. This reduces repeated constitutive evaluations without changing the force equations. An update is accepted only after re-evaluating the current residual on the feasible side of the original tolerance. Poorly scaled curvature uses Newton, and proposals outside the current bracket use bisection. The fixed tangential matrix from that residual evaluation is reused in the derivative calculations.
Normal warm seeds also reuse the reciprocals of the fixed elastic loading and unloading slopes. These are separate because loading includes the material's viscous term. Yield branches retain their nonlinear slope calculation. Each nonzero warm residual still receives a correction before acceptance, and the updated pressure and depth must pass the same normal residual tolerance. For nonlinear warm seeds, a conservative tangent below the convex Bekker pressure envelope can certify an interval where the elastic branch wins. The interval is constructed only when an elastic warm seed needs it, and a new ordinary pressure anchor invalidates it. Inside that interval, one exact elastic correction replaces the general Newton loop when loading or unloading does not change. The updated pressure is still checked against the original normal residual tolerance. An envelope crossing, a viscous loading transition, an unsupported exponent, or a poorly scaled interval retains the safeguarded solve.
When the sinkage exponent is one, both pressure branches are affine. The solver computes the four elastic/yield and loading/unloading depth candidates from coefficients fixed for the current physics step. Taking the minimum of each branch's loading/unloading candidates and then the maximum of the two pressure candidates solves the same piecewise pressure equation directly. It evaluates the current pressure and residual before acceptance; poorly scaled coefficients and candidates that fail the existing tolerance use the iterative solve.
Warm shear visits can also use a fixed compliance cap from the tangential mobility matrix. A conservative bound using the current normal load, shear history, and tangential velocity components proves the cap feasible before it replaces the more expensive analytic bracket calculation. If that proof fails, the original analytic bracket is calculated. Both paths evaluate the current slip-dependent force at the same tolerance.
All of these scalar caches reset each physics step.
For bodies with many terrain quadrature points, dense pin elimination projects six spatial impulse directions once per body. Each point's response and pin coupling are recovered from those directions and its lever arm. This uses the linearity of the current mass-weighted projection; it retains every contact point and recomputes the mass response and constraint factorization each step. Smaller constraint or contact groups, tracked bodies, and the structured pin factorization use the existing per-contact projection.
The deformable_terrain benchmark accepts --body=articulated to measure a 60-DOF plate with the same analytical load and sinkage checks as its default rigid plate. --body=pinned adds eight independent joint relations to exercise the shared suspension projection with the same analytical load. --body=multilink uses a 64-DOF body supported by four separate articulated pads with horizontal sliders, retaining the same total 20 kg load and 0.24 square metre footprint. This exercises coupling between contacting links and checks sinkage against the same analytical pressure law. Compare --start=warm (default) and --start=cold to measure warm starting at the same accuracy settings; each case reports solver sweeps and analytical sinkage error. Use one CPU thread for timing comparisons.
Add --contact-law to time the normal and shear updates independently of collision detection and articulated-body calculations. This workload visits 32 anisotropic point contacts with changing loads and slip speeds, covering fresh, partially mobilized, and fully mobilized shear histories. It verifies normal pressure, shear feasibility, tangential alignment, and dissipation after each timed run. For example:
./benchmarks --bench deformable_terrain --raisim -- --contact-law --start=warm --steps=10000 --repeats=3Use --start=cold for the same inputs with fresh scalar solve state at every visit. Add --material=linear to isolate the piecewise affine pressure solve; the default is --material=nonlinear with exponent 1.1. Both material options retain displacement-dependent shear and the same force checks. This microbenchmark complements the full mesh and vehicle workloads; its timing does not include mesh updates, collision queries, or suspension constraints.
Model scope
This is a surface terramechanics model for a supported bed. The surface deforms only along the bed's local Z axis. It represents distributed support, permanent compaction, elastic recovery, loading viscosity, and slip-dependent traction for rigid bodies and articulated systems.
The model does not include soil inertia, lateral soil transport, displaced-volume berms, excavation, overhangs, or failure planes. It is not a volumetric continuum model.
The soil has no contact model for granular media or deformable objects (cloth and volumes), so a world cannot hold a deformable terrain together with either. Adding the second of them is a fatal error (RSFATAL), whichever is added first and also while a world XML file loads, so the combination fails before any simulation step.
Vehicle example
The rayrai_deformable_terrain example drives an 896 kg off-road buggy over a 16 m by 6 m soil bed. It uses the same vehicle model as Rayrai Example: Off-Road Buggy: double-wishbone suspension, coil springs and dampers, rack-and-pinion steering, and tyre compliance. The cylindrical wheel colliders have radius 0.3611 m and contact widths of 0.126 m in front and 0.1562 m at the rear.
While meshes load, the example shows the same loading bar as the forest example. Its gradient and deep-purple label use the colors from the RaiSim logo. Physics stays paused until the assets are ready, then driving starts without a settling phase.

The bed uses bekkerKphi = 2.0e6, elasticStiffness = 2.0e7, damping = 4.0e4, sinkageExponent = 1.1, and cohesion = 100. Other material fields retain their defaults. These parameters demonstrate wheel sinkage, permanent ruts, and traction; they are not measurements of a particular soil. Rear-wheel drive requests about 0.8 m/s, with the actual speed determined by sinkage, slip, and soil resistance.