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.

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.

World

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

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.

World

World(
    robot: RobotConfig,
    scene: str | None = None,
    lockstep_hz: int | None = None,
    viewer: bool = False,
    address: str | 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: "empty", "table", "two_tables", "lift" or "lift_bracket" as shipped, 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

Raises:

Type Description
TypeError

robot is not an anvil.workcell.RobotConfig.

InvalidArgumentError

lockstep_hz is not one of the two rates, viewer 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.

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.

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.

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.

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.