# How DART Handles Friction and Force-Dependent Slip in Contact Constraints

> Discover how DART handles friction and force-dependent slip in contact constraints by using CFM damping to relate sliding velocity to tangential friction force. Learn more.

- Repository: [DART: Dynamic Animation and Robotics Toolkit/dart](https://github.com/dartsim/dart)
- Tags: deep-dive
- Published: 2026-02-28

---

**DART implements force-dependent slip by injecting a constraint-force-mixing (CFM) damping term into the `ContactConstraint` velocity-change step, establishing a linear relationship between steady-state sliding velocity and applied tangential friction force.**

DART (Dynamic Animation and Robotics Toolkit) models contact between rigid bodies through the `ContactConstraint` class, which extends standard Coulomb friction with **force-dependent slip** (slip compliance). This allows surfaces to drift at velocities proportional to applied tangential forces, critical for simulating tires, robotic grasps, or compliant sliding contacts.

## Slip Compliance Parameters in ShapeNode Dynamics

DART stores slip-compliance properties within the `DynamicsAspect` of each `ShapeNode`. These parameters define how much a surface will slide under a given friction load.

### Primary and Secondary Slip Compliance

Each `ShapeNode` exposes two independent compliance values through the C++ API:

```cpp
// Access the DynamicsAspect and set slip compliance
shapeNode->getDynamicsAspect()->setPrimarySlipCompliance(0.02);   // 2% slip
shapeNode->getDynamicsAspect()->setSecondarySlipCompliance(0.03); // 3% slip

```

- **`PrimarySlipCompliance`**: Governs sliding along the first friction direction.
- **`SecondarySlipCompliance`**: Governs sliding along the orthogonal friction direction.

### Default Values and Validation

The default value for both parameters is `DART_DEFAULT_SLIP_COMPLIANCE` (defined as `0.0`). Negative values, including the historic sentinel `-1.0`, are interpreted as "use the default" during validation. In [`dart/constraint/contact_surface.cpp`](https://github.com/dartsim/dart/blob/main/dart/constraint/contact_surface.cpp) (lines 97–130), the `DefaultContactSurfaceHandler::computePrimarySlipCompliance` method reads these aspect properties, validates them, and falls back to the default when necessary.

## Parameter Aggregation Pipeline

Before simulation, DART aggregates per-shape slip compliances into per-contact constraint parameters through a three-stage pipeline.

### Extraction and Combination

When a contact is detected, `DefaultContactSurfaceHandler::createParams` (in [`dart/constraint/contact_surface.cpp`](https://github.com/dartsim/dart/blob/main/dart/constraint/contact_surface.cpp), lines 131–138) combines the individual compliances of the two colliding bodies by simple addition:

```

combinedSlipCompliance = slipComplianceA + slipComplianceB

```

This additive combination preserves the physical interpretation that both surfaces contribute to the overall contact compliance.

### Scaling by Contact Count

To ensure the effect remains independent of collision-detection tessellation, `DefaultContactSurfaceHandler::createConstraint` (lines 90–97) scales the combined compliance by the number of contact points generated for that collision:

```cpp
// Pseudo-code from the scaling step
mPrimarySlipCompliance *= numContactsOnCollisionObject;

```

This scaling guarantees that splitting a single contact into multiple points does not artificially increase the total slip.

## CFM-Based Implementation in ContactConstraint

The actual physics integration occurs within `ContactConstraint`, where slip compliance modifies the constraint velocity through a CFM damping term.

### Velocity Change Damping

During the constraint resolution step, `ContactConstraint::getVelocityChange` (in [`dart/constraint/contact_constraint.cpp`](https://github.com/dartsim/dart/blob/main/dart/constraint/contact_constraint.cpp), lines 96–106) adds a damping offset proportional to the slip compliance when the `withCfm` flag is enabled:

```cpp
// For primary tangential direction (index 1)
velocityChange[1] += mPrimarySlipCompliance / mTimeStep;

// For secondary tangential direction (index 2)  
velocityChange[2] += mSecondarySlipCompliance / mTimeStep;

```

This damping term relaxes the strict zero-relative-velocity constraint of Coulomb friction, allowing persistent sliding.

### Physical Interpretation

The implementation produces the force-dependent slip relationship:

```

v_slip = slip_compliance × F_friction

```

As verified in [`tests/integration/constraint/test_force_dependent_slip.cpp`](https://github.com/dartsim/dart/blob/main/tests/integration/constraint/test_force_dependent_slip.cpp), a body subjected to constant external force `F_ext` reaches a steady-state velocity of `F_ext × slip_compliance`, demonstrating that **slip compliance acts as a mobility coefficient** rather than modifying the friction impulse directly (the impulse calculation in `applyImpulse` remains unchanged; only the velocity-level constraint is relaxed).

## Practical Usage Examples

### C++ Configuration

The following example configures a box with 2% primary slip compliance:

```cpp
#include <dart/dart.hpp>

int main()
{
  auto skel = dart::dynamics::Skeleton::create("box");
  auto* body = skel->createJointAndBodyNodePair<dart::dynamics::FreeJoint>().second;
  
  auto shape = std::make_shared<dart::dynamics::BoxShape>(
      Eigen::Vector3d(0.3, 0.3, 0.3));
  
  auto* shapeNode = body->createShapeNodeWith<
      dart::dynamics::VisualAspect,
      dart::dynamics::CollisionAspect,
      dart::dynamics::DynamicsAspect>(shape);

  // Enable 2% slip in the primary friction direction (+X)
  shapeNode->getDynamicsAspect()->setPrimarySlipCompliance(0.02);
  shapeNode->getDynamicsAspect()->setFirstFrictionDirection(
      Eigen::Vector3d::UnitX());

  // ... add to world, apply force, step simulation ...
  // Steady-state velocity will be F_ext * 0.02
}

```

### Python Configuration

Using `dartpy`, the same configuration is accomplished as follows:

```python
import dartpy as dart
import numpy as np

world = dart.simulation.World()
world.setGravity([0, 0, -9.81])

# Create floor

floor = dart.dynamics.Skeleton()
floor_body = floor.create_joint_and_body_node_pair(dart.dynamics.WeldJoint)[1]
floor_shape = dart.dynamics.BoxShape([10, 10, 0.01])
floor_body.create_shape_node(floor_shape)
world.add_skeleton(floor)

# Create sliding box

box = dart.dynamics.Skeleton()
body = box.create_joint_and_body_node_pair(dart.dynamics.FreeJoint)[1]
shape = dart.dynamics.BoxShape([0.3, 0.3, 0.3])
node = body.create_shape_node(shape)

# Configure slip compliance

node.set_primary_slip_compliance(0.02)
node.set_first_friction_direction(np.array([1, 0, 0]))

world.add_skeleton(box)

# Apply constant 10N force in +X direction

force = np.array([10.0, 0.0, 0.0])
for _ in range(2000):
    body.add_ext_force(force)
    world.step()

# Verify: velocity should approach 0.2 m/s (10N * 0.02 s/kg)

print(f"Final velocity: {body.linear_velocity()[0]:.4f} m/s")

```

## Summary

- **Storage**: Slip compliance is stored per-shape in `DynamicsAspect` via `setPrimarySlipCompliance()` and `setSecondarySlipCompliance()`.
- **Aggregation**: `DefaultContactSurfaceHandler` combines compliances by addition and scales by contact count to maintain physical consistency.
- **Integration**: `ContactConstraint::getVelocityChange()` applies the compliance as a CFM damping term `slipCompliance / timeStep` to the tangential velocity constraints.
- **Behavior**: The system exhibits steady-state sliding velocity proportional to applied force (`v = F × compliance`), validated by [`test_force_dependent_slip.cpp`](https://github.com/dartsim/dart/blob/main/test_force_dependent_slip.cpp).

## Frequently Asked Questions

### What is the default slip compliance in DART?

The default slip compliance is `0.0`, defined as `DART_DEFAULT_SLIP_COMPLIANCE` in the source headers. When set to zero, the contact enforces standard Coulomb friction without force-dependent slip. Negative values passed to the API are automatically interpreted as requests to use this default.

### How does DART combine slip compliance values for two contacting bodies?

DART combines the individual `PrimarySlipCompliance` values of both collision objects through simple addition in `DefaultContactSurfaceHandler::createParams`. The combined value is then scaled by the number of contact points in `createConstraint` to ensure the total slip behavior remains invariant to contact sampling density.

### Does slip compliance affect the friction impulse directly?

No. According to the implementation in [`dart/constraint/contact_constraint.cpp`](https://github.com/dartsim/dart/blob/main/dart/constraint/contact_constraint.cpp), slip compliance does not modify the impulse calculation in `applyImpulse()`. Instead, it affects the **velocity-level constraint** by adding a damping term during `getVelocityChange()`, effectively softening the friction constraint without altering the maximum friction force limit.

### How can I verify force-dependent slip behavior in my simulation?

You can validate the implementation by creating a rigid body with non-zero slip compliance, applying a constant external force, and measuring the terminal sliding velocity. As demonstrated in [`tests/integration/constraint/test_force_dependent_slip.cpp`](https://github.com/dartsim/dart/blob/main/tests/integration/constraint/test_force_dependent_slip.cpp), the steady-state velocity should equal the applied force multiplied by the slip compliance coefficient.