Spinning Box

Here is a basic example of building a simulation using newton.

import matplotlib.pyplot as plt
import newton
import numpy as np
import warp as wp
from lwmr.utils import RECORDING_BASE_PATH, create_viewer_viser
from tqdm.auto import trange

# Tell warp to be quiet before initializing a model
wp.config.quiet = True
/Users/ajcd2020/Documents/Repositories/anthonyjclark/simer-tutorial/2026-icra/.venv/lib/python3.14/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm
# Visualization timing
FRAME_STEP = 1.0 / 60.0

# Simulation timing relative to visualization
SIM_SUBSTEPS = 4
TIME_STEP = FRAME_STEP / SIM_SUBSTEPS
# Build a model
builder = newton.ModelBuilder()
body = builder.add_body()
builder.add_shape_box(body)
builder.add_ground_plane()
model = builder.finalize()
# Allocate state and create a solver
state_0 = model.state()
state_1 = model.state()
control = model.control()
contacts = model.contacts()
solver = newton.solvers.SolverMuJoCo(model)
# Step the simulation
for step in trange(1000):
    state_0.clear_forces()
    model.collide(state_0, contacts)
    solver.step(state_0, state_1, control, contacts, TIME_STEP)
    state_0, state_1 = state_1, state_0

  0%|          | 0/1000 [00:00<?, ?it/s]
  0%|          | 1/1000 [00:00<05:51,  2.85it/s]
  6%|▌         | 62/1000 [00:00<00:05, 178.17it/s]
 12%|█▎        | 125/1000 [00:00<00:02, 308.86it/s]
 19%|█▉        | 188/1000 [00:00<00:02, 401.10it/s]
 25%|██▌       | 251/1000 [00:00<00:01, 466.55it/s]
 31%|███▏      | 313/1000 [00:00<00:01, 510.50it/s]
 38%|███▊      | 376/1000 [00:00<00:01, 543.78it/s]
 44%|████▍     | 438/1000 [00:01<00:00, 564.34it/s]
 50%|████▉     | 499/1000 [00:01<00:00, 573.31it/s]
 56%|█████▌    | 559/1000 [00:01<00:00, 579.49it/s]
 62%|██████▏   | 620/1000 [00:01<00:00, 585.85it/s]
 68%|██████▊   | 682/1000 [00:01<00:00, 594.42it/s]
 74%|███████▍  | 744/1000 [00:01<00:00, 600.22it/s]
 81%|████████  | 807/1000 [00:01<00:00, 606.67it/s]
 87%|████████▋ | 870/1000 [00:01<00:00, 610.83it/s]
 93%|█████████▎| 933/1000 [00:01<00:00, 613.84it/s]
100%|█████████▉| 995/1000 [00:01<00:00, 615.05it/s]
100%|██████████| 1000/1000 [00:01<00:00, 504.78it/s]

Visualization

We’ll use the built-in support for the viser visualizer in newton.

sim_time = 0.0

rec_path = str(RECORDING_BASE_PATH / "cube.viser")
viewer = newton.viewer.ViewerViser(verbose=False, record_to_viser=rec_path)
viewer.set_model(model)

while sim_time < 4.0:
    viewer.begin_frame(sim_time)
    viewer.log_state(state_0)
    viewer.end_frame()
    sim_time += FRAME_STEP

# NOTE: this does not work in VSCode
viewer.show_notebook()
╭────── viser (listening *:8080) ───────╮
│             ╷                         │
│   HTTP      │ http://localhost:8080   │
│   Websocket │ ws://localhost:8080     │
│             ╵                         │
╰───────────────────────────────────────╯

Visualize a Spinning Cube

add_body() calls add_link(), add_joint_free(), and add_articulation()

builder = newton.ModelBuilder()
builder.add_ground_plane()

# Revolute body
xform = wp.transform(p=wp.vec3(0.0, -1.0, 1.0))
body = builder.add_link()
joint = builder.add_joint_revolute(parent=-1, child=body, parent_xform=xform, axis=wp.vec3(0.0, 0.0, 1.0))
builder.add_articulation([joint])
builder.add_shape_box(body)

# Just a single DOF for a revolute joint
joint_index = builder.joint_q_start[joint]
joint_dim = builder.joint_dof_dim[joint_index]
assert joint_dim[0] == 0 and joint_dim[1] == 1
builder.joint_q[joint_index] = 0.5

joint_index = builder.joint_qd_start[joint]
joint_dim = builder.joint_dof_dim[joint_index]
assert joint_dim[0] == 0 and joint_dim[1] == 1
builder.joint_qd[joint_index] = 10.0

# Free body
xform = wp.transform(p=wp.vec3(0.0, 1.0, 1.0))
body = builder.add_body(xform=xform)
builder.add_shape_box(body)

joint_index = builder.joint_q_start[joint] + 1
joint_dim = builder.joint_dof_dim[joint_index]
assert joint_dim[0] == 3 and joint_dim[1] == 3
# builder.joint_q[joint_index + 2] = 0.5

joint_index = builder.joint_qd_start[joint] + 1
joint_dim = builder.joint_dof_dim[joint_index]
assert joint_dim[0] == 3 and joint_dim[1] == 3
builder.joint_qd[joint_index + 5] = 10.0

model = builder.finalize()

state_0 = model.state()
state_1 = model.state()
control = model.control()
contacts = model.contacts()

solver = newton.solvers.SolverMuJoCo(model)

sim_time = 0.0

viewer = create_viewer_viser("spinning_cube", model, quiet=False, overwrite=False)

for step in trange(200):
    for substep in range(SIM_SUBSTEPS):
        state_0.clear_forces()
        model.collide(state_0, contacts)
        solver.step(state_0, state_1, control, contacts, TIME_STEP)
        state_0, state_1 = state_1, state_0

    viewer.begin_frame(sim_time)
    viewer.log_state(state_0)
    viewer.end_frame()

    sim_time += FRAME_STEP

viewer.show_notebook()
Recording to docs/_static/spinning_cube.viser...
╭────── viser (listening *:8081) ───────╮
│             ╷                         │
│   HTTP      │ http://localhost:8081   │
│   Websocket │ ws://localhost:8081     │
│             ╵                         │
╰───────────────────────────────────────╯

  0%|          | 0/200 [00:00<?, ?it/s]
  0%|          | 1/200 [00:00<00:23,  8.48it/s]
  6%|▌         | 11/200 [00:00<00:03, 58.60it/s]
 11%|█         | 22/200 [00:00<00:02, 78.04it/s]
 16%|█▋        | 33/200 [00:00<00:01, 87.59it/s]
 22%|██▏       | 44/200 [00:00<00:01, 92.95it/s]
 28%|██▊       | 55/200 [00:00<00:01, 95.64it/s]
 33%|███▎      | 66/200 [00:00<00:01, 97.78it/s]
 38%|███▊      | 77/200 [00:00<00:01, 99.34it/s]
 44%|████▍     | 88/200 [00:00<00:01, 100.34it/s]
 50%|████▉     | 99/200 [00:01<00:01, 100.31it/s]
 55%|█████▌    | 110/200 [00:01<00:00, 99.94it/s]
 60%|██████    | 121/200 [00:01<00:00, 100.42it/s]
 66%|██████▌   | 132/200 [00:01<00:00, 100.85it/s]
 72%|███████▏  | 143/200 [00:01<00:00, 101.15it/s]
 77%|███████▋  | 154/200 [00:01<00:00, 101.23it/s]
 82%|████████▎ | 165/200 [00:01<00:00, 101.27it/s]
 88%|████████▊ | 176/200 [00:01<00:00, 100.77it/s]
 94%|█████████▎| 187/200 [00:01<00:00, 100.97it/s]
 99%|█████████▉| 198/200 [00:02<00:00, 100.90it/s]
100%|██████████| 200/200 [00:02<00:00, 95.98it/s] 
builder = newton.ModelBuilder()
builder.add_ground_plane()

# Revolute body
xform = wp.transform(p=wp.vec3(0.0, -1.0, 1.0))
body = builder.add_link()
joint = builder.add_joint_revolute(parent=-1, child=body, parent_xform=xform, axis=wp.vec3(0.0, 0.0, 1.0))
builder.add_articulation([joint])
builder.add_shape_box(body)

# Free body
xform = wp.transform(p=wp.vec3(0.0, 1.0, 1.0))
body = builder.add_body(xform=xform, mass=1.0)
builder.add_shape_box(body)

model = builder.finalize()

state_0 = model.state()
state_1 = model.state()
control = model.control()
contacts = model.contacts()

solver = newton.solvers.SolverMuJoCo(model)

joint_forces = np.zeros(control.joint_f.shape, dtype=np.float32)  # type: ignore
joint_index = builder.joint_qd_start[joint]
joint_forces[joint_index] = 100

joint_index = builder.joint_qd_start[joint] + 1
# joint_forces[joint_index + 0] = -10000.0
joint_forces[joint_index + 5] = -5000.0

control.joint_f.assign(joint_forces)  # type: ignore

sim_time = 0.0

# viewer = create_viewer("spinning_cube", model)
viewer = create_viewer_viser("spinning_cube", model, quiet=False, overwrite=False)


for step in trange(200):
    for substep in range(SIM_SUBSTEPS):
        state_0.clear_forces()
        model.collide(state_0, contacts)
        solver.step(state_0, state_1, control, contacts, TIME_STEP)
        state_0, state_1 = state_1, state_0

    viewer.begin_frame(sim_time)
    viewer.log_state(state_0)
    viewer.end_frame()

    sim_time += FRAME_STEP

viewer.show_notebook()
Recording to docs/_static/spinning_cube_01.viser...
╭────── viser (listening *:8083) ───────╮
│             ╷                         │
│   HTTP      │ http://localhost:8083   │
│   Websocket │ ws://localhost:8083     │
│             ╵                         │
╰───────────────────────────────────────╯

  0%|          | 0/200 [00:00<?, ?it/s]
  2%|▏         | 3/200 [00:00<00:06, 29.05it/s]
  7%|▋         | 14/200 [00:00<00:02, 72.79it/s]
 12%|█▎        | 25/200 [00:00<00:02, 85.61it/s]
 18%|█▊        | 36/200 [00:00<00:01, 91.71it/s]
 24%|██▎       | 47/200 [00:00<00:01, 94.90it/s]
 29%|██▉       | 58/200 [00:00<00:01, 97.17it/s]
 34%|███▍      | 69/200 [00:00<00:01, 99.00it/s]
 40%|████      | 80/200 [00:00<00:01, 100.11it/s]
 46%|████▌     | 91/200 [00:00<00:01, 100.99it/s]
 51%|█████     | 102/200 [00:01<00:00, 100.60it/s]
 56%|█████▋    | 113/200 [00:01<00:00, 100.43it/s]
 62%|██████▏   | 124/200 [00:01<00:00, 100.91it/s]
 68%|██████▊   | 135/200 [00:01<00:00, 101.38it/s]
 73%|███████▎  | 146/200 [00:01<00:00, 101.66it/s]
 78%|███████▊  | 157/200 [00:01<00:00, 101.88it/s]
 84%|████████▍ | 168/200 [00:01<00:00, 101.87it/s]
 90%|████████▉ | 179/200 [00:01<00:00, 101.33it/s]
 95%|█████████▌| 190/200 [00:01<00:00, 101.70it/s]
100%|██████████| 200/200 [00:02<00:00, 97.73it/s] 
builder.plot_articulation()

builder = newton.ModelBuilder()
builder.add_ground_plane()

# Revolute body
xform = wp.transform(p=wp.vec3(0.0, -1.0, 1.0))
body = builder.add_link()
joint = builder.add_joint_revolute(
    parent=-1,
    child=body,
    parent_xform=xform,
    axis=wp.vec3(0.0, 0.0, 1.0),
    actuator_mode=newton.JointTargetMode.VELOCITY,
    target_kd=100,
)
builder.add_articulation([joint])
builder.add_shape_box(body)

model = builder.finalize()

state_0 = model.state()
state_1 = model.state()
control = model.control()
contacts = model.contacts()

solver = newton.solvers.SolverMuJoCo(model)

joint_target_vels = np.zeros(control.joint_target_vel.shape, dtype=np.float32)  # type: ignore
joint_index = builder.joint_qd_start[joint]
joint_target_vels[joint_index] = 8.0
control.joint_target_vel.assign(joint_target_vels)  # type: ignore

sim_time = 0.0

# viewer = create_viewer("spinning_cube", model)
viewer = create_viewer_viser("spinning_cube", model, quiet=False, overwrite=False)


vels = []

for step in trange(400):
    for substep in range(SIM_SUBSTEPS):
        state_0.clear_forces()
        model.collide(state_0, contacts)
        solver.step(state_0, state_1, control, contacts, TIME_STEP)
        state_0, state_1 = state_1, state_0

    viewer.begin_frame(sim_time)
    viewer.log_state(state_0)
    viewer.end_frame()

    vels.append(state_0.joint_qd.numpy().copy())  # type: ignore

    sim_time += FRAME_STEP

viewer.show_notebook()
Recording to docs/_static/spinning_cube_02.viser...
╭────── viser (listening *:8084) ───────╮
│             ╷                         │
│   HTTP      │ http://localhost:8084   │
│   Websocket │ ws://localhost:8084     │
│             ╵                         │
╰───────────────────────────────────────╯

  0%|          | 0/400 [00:00<?, ?it/s]
  0%|          | 1/400 [00:00<00:39,  9.98it/s]
  4%|▍         | 15/400 [00:00<00:04, 84.82it/s]
  7%|▋         | 29/400 [00:00<00:03, 108.60it/s]
 11%|█         | 43/400 [00:00<00:02, 119.82it/s]
 14%|█▍        | 57/400 [00:00<00:02, 125.95it/s]
 18%|█▊        | 71/400 [00:00<00:02, 129.73it/s]
 21%|██▏       | 85/400 [00:00<00:02, 131.87it/s]
 25%|██▍       | 99/400 [00:00<00:02, 132.81it/s]
 28%|██▊       | 113/400 [00:00<00:02, 133.80it/s]
 32%|███▏      | 127/400 [00:01<00:02, 134.11it/s]
 35%|███▌      | 141/400 [00:01<00:01, 134.68it/s]
 39%|███▉      | 155/400 [00:01<00:01, 135.54it/s]
 42%|████▏     | 169/400 [00:01<00:01, 135.91it/s]
 46%|████▌     | 183/400 [00:01<00:01, 136.22it/s]
 49%|████▉     | 197/400 [00:01<00:01, 136.56it/s]
 53%|█████▎    | 211/400 [00:01<00:01, 137.08it/s]
 56%|█████▋    | 225/400 [00:01<00:01, 136.84it/s]
 60%|█████▉    | 239/400 [00:01<00:01, 136.97it/s]
 63%|██████▎   | 253/400 [00:01<00:01, 136.27it/s]
 67%|██████▋   | 267/400 [00:02<00:00, 135.90it/s]
 70%|███████   | 281/400 [00:02<00:00, 135.62it/s]
 74%|███████▍  | 295/400 [00:02<00:00, 136.04it/s]
 77%|███████▋  | 309/400 [00:02<00:00, 136.63it/s]
 81%|████████  | 323/400 [00:02<00:00, 136.80it/s]
 84%|████████▍ | 337/400 [00:02<00:00, 136.95it/s]
 88%|████████▊ | 351/400 [00:02<00:00, 137.20it/s]
 91%|█████████▏| 365/400 [00:02<00:00, 136.73it/s]
 95%|█████████▍| 379/400 [00:02<00:00, 136.95it/s]
 98%|█████████▊| 393/400 [00:02<00:00, 136.47it/s]
100%|██████████| 400/400 [00:03<00:00, 132.30it/s]
plt.plot(vels);