Skip to content

anvil.sim

anvil.sim

The sim: MuJoCo standing in for the drives, a robot that collides with itself and with the scene it stands in.

A World is the scene and the clock, served by a process of its own on this machine or another; an anvil.hw.SimHw drives the robot in it as an anvil.hw.DamiaoHw drives real drives, and an anvil.camera.SimCamera watches it. anvil-robotics sim serves a world on a port, for a loop on another machine to drive. Importing this module loads no MuJoCo: the physics runs in the server.

The server draws its cameras and its viewer the way Anvil Forge draws a robot, each part shaded by its finish under a studio's lights, or with MuJoCo's own renderer: a Renderer says which, and how to pick one.

Classes:

Name Description
Contact

Two geoms of the world touching.

Contacts

What a world is touching, and what it has touched since it was set up, from World.contacts.

FreeCamera

Where a world's free camera looks from, orbiting a point as MuJoCo's own window does.

Picked

What World.pick found: the nearest surface under a point of the free camera's picture.

Renderer

What the sim's server draws its cameras and its window with.

World

A sim's world: a robot standing in a scene, and the clock it runs on.

Functions:

Name Description
scenes

Returns the scenes a server started from this package can stand a robot in, by the name a World takes.

Contact dataclass

Contact(first: str, second: str, depth_m: float)

Two geoms of the world touching.

Attributes:

Name Type Description
first str

One of them, by the name its scene or the robot's URDF gives it, e.g. "table_top".

second str

The other.

depth_m float

How far apart they are, in meters: negative is how deep they are pushed into each other.

first instance-attribute

first: str

One of them, by the name its scene or the robot's URDF gives it, e.g. "table_top".

second instance-attribute

second: str

The other.

depth_m instance-attribute

depth_m: float

How far apart they are, in meters: negative is how deep they are pushed into each other.

Contacts dataclass

Contacts(
    now: list[Contact],
    touched: set[tuple[str, str]],
    worst_depth_m: float,
)

What a world is touching, and what it has touched since it was set up, from World.contacts.

Attributes:

Name Type Description
now list[Contact]

Every pair touching at the instant the question was asked.

touched set[tuple[str, str]]

Every pair of geoms that has touched, whether or not it still is, as (first, second).

worst_depth_m float

The deepest any two have been pushed into each other, in meters: negative, or 0 where nothing touched.

now instance-attribute

now: list[Contact]

Every pair touching at the instant the question was asked.

touched instance-attribute

touched: set[tuple[str, str]]

Every pair of geoms that has touched, whether or not it still is, as (first, second).

worst_depth_m instance-attribute

worst_depth_m: float

The deepest any two have been pushed into each other, in meters: negative, or 0 where nothing touched.

FreeCamera dataclass

FreeCamera(
    lookat_m: tuple[float, float, float],
    distance_m: float,
    azimuth_deg: float,
    elevation_deg: float,
)

Where a world's free camera looks from, orbiting a point as MuJoCo's own window does.

A world has one, which World.set_free_camera moves and every anvil.camera.SimFreeCamera watching the world shows. It starts looking at the robot from in front of it and a little above.

Attributes:

Name Type Description
lookat_m tuple[float, float, float]

The point it orbits and looks at, x, y and z in the world frame, in meters.

distance_m float

How far from that point it stands, in meters.

azimuth_deg float

Which way it faces about world +z, in degrees from +x: 0 looks along +x.

elevation_deg float

How far it looks up, in degrees: negative looks down at the point from above it.

lookat_m instance-attribute

lookat_m: tuple[float, float, float]

The point it orbits and looks at, x, y and z in the world frame, in meters.

distance_m instance-attribute

distance_m: float

How far from that point it stands, in meters.

azimuth_deg instance-attribute

azimuth_deg: float

Which way it faces about world +z, in degrees from +x: 0 looks along +x.

elevation_deg instance-attribute

elevation_deg: float

How far it looks up, in degrees: negative looks down at the point from above it.

Picked dataclass

Picked(point_m: tuple[float, float, float], body: str)

What World.pick found: the nearest surface under a point of the free camera's picture.

Attributes:

Name Type Description
point_m tuple[float, float, float]

Where on it, x, y and z in the world frame, in meters.

body str

The body it belongs to, by name, e.g. "table" or a link of the robot.

point_m instance-attribute

point_m: tuple[float, float, float]

Where on it, x, y and z in the world frame, in meters.

body instance-attribute

body: str

The body it belongs to, by name, e.g. "table" or a link of the robot.

Renderer

Bases: StrEnum

What the sim's server draws its cameras and its window with.

A World that starts its server takes one, as does anvil-robotics sim --renderer; a server started with none named reads ANVIL_SIM_RENDERER from its environment, which a World passes on from this process's, and with that unset draws with ANVIL.

Attributes:

Name Type Description
ANVIL

Each part shaded by its finish under a studio's lights, as Anvil Forge draws a robot.

MUJOCO

MuJoCo's own renderer, untouched.

ANVIL class-attribute instance-attribute

ANVIL

Each part shaded by its finish under a studio's lights, as Anvil Forge draws a robot.

The room's reflections on it, and a line on every crease.

Where OpenGL renders on the CPU its key light casts no shadow, which would cost each camera most of its frame rate there.

MUJOCO class-attribute instance-attribute

MUJOCO

MuJoCo's own renderer, untouched.

For frames that have to be MuJoCo's, or a machine the shading costs too much on.

World

World(
    robot: RobotConfig,
    scene: str | None = None,
    lockstep_hz: int | None = None,
    viewer: bool = False,
    address: str | None = None,
    renderer: Renderer | None = None,
)

Bases: Handle

A sim's world: a robot standing in a scene, and the clock it runs on.

Without an address it starts a server on this machine, anvil-robotics sim in a process of its own. With one it sets up the world of a server already running, as often as not on another machine: the sim stands in for metal, so the box that would drive the metal need not also simulate it, and the window is nicer on the machine you are sitting at.

The robot stands in the world from the start with its drives disabled, as an arm stands on the bench switched off, so an anvil.camera.SimCamera watches it before anything drives it; an anvil.hw.SimHw then takes it over and drives it. This answers what is touching and places loose objects, beside whatever drives it. Another robot is another world. Closing it, or letting it be collected, ends this connection; a server it started stops once every anvil.hw.SimHw driving it is gone too. Every method but close raises anvil.OperationError once it is closed.

Sets the world up with robot standing in it, starting a server for it unless address names one.

Whatever world the server held goes, and its cameras are told so.

Parameters:

Name Type Description Default
robot RobotConfig

The robot standing in the world, e.g. anvil.workcell.WorkcellConfig.OPENYAM_V1_SINGLE.robot: its grippers grafted on, and every motor it declares, each saturating at its model's maxima. Its meshes are the ones the package ships, so only a robot Anvil ships stands in a world.

required
scene str | None

The world the robot is bolted into: one of scenes(), or a path to an MJCF file the server can read, wherever the server is, with a <frame name="robot_mount"> where the robot stands. None is the scene of the robot's anvil.workcell.RobotConfig.stand, named as it is, or for a robot on no stand the server's own, "empty" for one started here.

None
lockstep_hz int | None

The rate to step at, 250 or 500, giving up the sim's own clock for repeatability: the world then steps once per anvil.hw.SimHw.write and not otherwise, and an anvil.control.Runtime driving it runs at that rate. None leaves the world keeping its own clock.

None
viewer bool

Whether to open a window on the world. Only for a server started here.

False
address str | None

A server already listening, "host:port", or None to start one here.

None
renderer Renderer | None

What its cameras and window draw with. None leaves it to ANVIL_SIM_RENDERER in this process's environment, then Renderer.ANVIL. Only for a server started here.

None

Raises:

Type Description
TypeError

robot is not an anvil.workcell.RobotConfig.

InvalidArgumentError

lockstep_hz is not one of the two rates, renderer is not a Renderer, viewer or renderer is given with an address, address does not read as "host:port", or robot's URDF cannot be composed.

UnreachableError

Nothing answered at address.

OperationError

The server would not start, or it refused the world: a scene it does not have, a robot it cannot build, or an anvil.hw.SimHw driving it; or it is from another release of anvil, which the message names, since a sim and its clients run one release.

Methods:

Name Description
close

Ends this connection; a server it started stops once every anvil.hw.SimHw driving it is gone too.

contacts

Returns what the world is touching, and what it has touched since it was set up.

place

Puts a loose body of the scene, a block on the table, where a run wants it, and stops it dead there.

hold

Holds each named joint of the robot at its angle, at rest, whatever drives it, until told again.

free_camera

Returns where the world's free camera looks from: what every anvil.camera.SimFreeCamera shows.

set_free_camera

Moves the world's free camera, which every anvil.camera.SimFreeCamera shows from its next frame.

pick

Returns the nearest surface the free camera sees at a point of its picture, or None where only sky is.

show_window

Opens MuJoCo's window on the world, unless one is open already.

Attributes:

Name Type Description
address str

Where the server is, "host:port": what an anvil.hw.SimHw and an anvil.camera.SimCamera reach.

robot RobotConfig

The robot standing in the world.

robot_pose Pose

Where the robot's own frame sits in the world, on its stand and raised so its base plate rests there.

free_camera_fovy_deg float

How wide the free camera sees, vertically, in degrees.

address instance-attribute

address: str

Where the server is, "host:port": what an anvil.hw.SimHw and an anvil.camera.SimCamera reach.

robot instance-attribute

robot: RobotConfig

The robot standing in the world.

robot_pose property

robot_pose: Pose

Where the robot's own frame sits in the world, on its stand and raised so its base plate rests there.

It is the frame the robot's URDF and every target are stated in. The world frame is the one contacts, place, pick and the free camera are stated in, so this is what turns a point picked in the world into a target for the robot.

free_camera_fovy_deg property

free_camera_fovy_deg: float

How wide the free camera sees, vertically, in degrees.

The world's own, which a picture drawn over an anvil.camera.SimFreeCamera projects with.

close

close() -> None

Ends this connection; a server it started stops once every anvil.hw.SimHw driving it is gone too.

Closing twice does nothing.

contacts

contacts() -> Contacts

Returns what the world is touching, and what it has touched since it was set up.

The world looks every physics step, so a touch between two of these questions is still reported, which is how "nothing collided during the whole sweep" is asked. A round trip to the server, never a tick.

Raises:

Type Description
OperationError

The server went away.

place

place(
    body: str,
    position_m: Sequence[float],
    orientation_xyzw: Sequence[float] | None = None,
) -> None

Puts a loose body of the scene, a block on the table, where a run wants it, and stops it dead there.

Parameters:

Name Type Description Default
body str

The body's name in the scene.

required
position_m Sequence[float]

Where to put it, x, y and z in the world frame, in meters.

required
orientation_xyzw Sequence[float] | None

How to turn it, a unit quaternion with w last, or None to leave it turned as it is.

None

Raises:

Type Description
InvalidArgumentError

position_m does not have 3 entries or orientation_xyzw does not have 4.

OperationError

body is not one of the scene's loose objects, which the message lists, or the server went away.

hold

hold(q_rad: Mapping[str, float]) -> None

Holds each named joint of the robot at its angle, at rest, whatever drives it, until told again.

A hand holding the arm still, and moving it as a hand would when called again with the next angles: how a run moves an arm nothing drives, such as a leader the operator would move by hand, which an anvil.control.LeaderFollowerController leaves limp. The joints read back exactly these angles, still. Each call replaces the last, so a joint it does not name goes back to its drive.

Parameters:

Name Type Description Default
q_rad Mapping[str, float]

Angles in radians, or meters for a prismatic joint, by URDF joint name as anvil.workcell.RobotConfig.urdf_joints names them, e.g. {"leader_joint_1": 0.3}; {} lets every joint go.

required

Raises:

Type Description
InvalidArgumentError

q_rad holds something other than a number.

OperationError

q_rad names a joint the robot has no hinge or slide of, which the message names, or the server went away.

free_camera

free_camera() -> FreeCamera

Returns where the world's free camera looks from: what every anvil.camera.SimFreeCamera shows.

Raises:

Type Description
OperationError

The server went away.

set_free_camera

set_free_camera(free_camera: FreeCamera) -> None

Moves the world's free camera, which every anvil.camera.SimFreeCamera shows from its next frame.

A round trip to the server, never a tick, so a page orbiting the camera as a mouse drags costs the loop nothing.

Parameters:

Name Type Description Default
free_camera FreeCamera

Where it looks from.

required

Raises:

Type Description
OperationError

A number is not finite or the distance is not positive, which the message says, or the server went away.

pick

pick(
    x_frac: float, y_frac: float, aspect_ratio: float
) -> Picked | None

Returns the nearest surface the free camera sees at a point of its picture, or None where only sky is.

The picture is an anvil.camera.SimFreeCamera's frame, whose shape sets how wide the camera sees: a click on a stream of it, turned into a place in the world.

Parameters:

Name Type Description Default
x_frac float

How far across the picture from its left edge, 0 to 1.

required
y_frac float

How far down it from its top edge, 0 to 1.

required
aspect_ratio float

The picture's width over its height.

required

Raises:

Type Description
OperationError

A fraction is outside 0 to 1 or the aspect ratio is not positive, which the message says, or the server went away.

show_window

show_window() -> None

Opens MuJoCo's window on the world, unless one is open already.

The window opens on the machine the server runs on, which for a world started here is this one: orbit, pan and zoom with the mouse, and pick a body with a double-click to push it with Ctrl and a drag. Closing it leaves the world running. Unlike the viewer argument, this works on a world already set up, and on a server something else started.

Raises:

Type Description
OperationError

The server's machine has no display to open a window on, which the message says, or the server went away.

scenes

scenes() -> list[str]

Returns the scenes a server started from this package can stand a robot in, by the name a World takes.

"empty" is the ground alone, and "lift" and "lift_bracket" the robot's stands, what a World given no scene sets up for a robot on one. "workbench" and "fruit_bowl" put a workbench before the robot, the first with Anvil's Devbox 2.0, a leader handle and two camera modules on it, the second a bowl of fruit and a cutting board; "table" and "two_tables" are blocks on tables, as the gate tests use them. Every one but "empty" and "table" stands the robot on the lift.