The example is a text-simulated gripper, not a real robot: perceive is a short scene description,
plan is the model calling move(x, y, z, speed), and act is a safety envelope the model never
sees and cannot call. A real system’s safety layer has to be independent hardware or independent
code for the same reason: a model cannot be the check on its own output. NVIDIA’s README does
not describe a safety layer, but it does show how bounded the perceive-plan half is. It gives
GR00T’s architecture as “a combination of vision-language foundation model and diffusion
transformer head that denoises continuous actions”, and lists inference as needing “1 GPU with
16 GB+ VRAM (e.g., RTX 4090, L40, H100, Jetson AGX Thor/Orin, DGX Spark)”[2]: a fixed
budget this example sidesteps entirely by simulating in text.
examples/embodied/run.py · lines 116–139
def envelope(proposed: Move, actuator_log: list[Move]) -> EnvelopeResult:
"""The safety check, independent of the model. `actuator_log` stands in for a real motor
controller: this is the only function in the module that ever appends to it, and it only
does so for a move that already passed every check. Its last entry is also where the arm is
now, which is what the path check measures from."""
invalid = _valid(proposed)
if invalid:
return EnvelopeResult(outcome="refused", move=proposed, reason=invalid)
clamped = Move(
x=_clamp(proposed.x, *BOUNDS["x"]),
y=_clamp(proposed.y, *BOUNDS["y"]),
z=_clamp(proposed.z, *BOUNDS["z"]),
speed=min(proposed.speed, MAX_SPEED_MM_S),
)
start = actuator_log[-1] if actuator_log else HOME
for zone in FORBIDDEN_ZONES:
if _path_enters(start, clamped, zone):
return EnvelopeResult(outcome="refused", move=clamped, reason=f"path to the target crosses forbidden zone: {zone['name']}")
actuator_log.append(clamped)
if clamped == proposed:
return EnvelopeResult(outcome="actuated", move=clamped)
return EnvelopeResult(outcome="clamped", move=clamped, reason="target or speed was outside the workspace envelope")
Four things about envelope matter more than the specific numbers, and three of them are
mistakes that are easy to make and hard to see.
It refuses a nonsense number instead of clamping it. _valid runs first, because Python’s min
and max pass a NaN through rather than rejecting it: min(speed, 250.0) returns NaN when
speed is NaN, and nothing in a one-sided speed cap stops a negative speed at all. Both would
otherwise be actuated. A number that means nothing cannot be clamped into a number that means
something, so it is refused.
It clamps bounds and speed but refuses a forbidden zone: clamping a target inside a zone back
to the zone’s edge would still be a target inside the zone, so a zone violation is never
something a clamp can fix.
The order matters. The function clamps to the workspace bounds before checking zones, so a
proposal that is out of bounds on one axis and would clamp into a forbidden zone is still
caught. Checking the raw proposal first would have missed exactly that case.
And the zone check runs on the path, not the target. Two targets can each sit outside every zone
while the straight line between them cuts through one, so an endpoint-only check would let a
sequence of individually legal moves sweep the arm through the operator station.
_path_enters clips the segment from the arm’s current position against each axis’ pair of
planes and asks whether anything is left: exact for a box, rather than sampling points along
the line and hoping none of a thin crossing falls between two samples. The current position is
the last move that actually reached the actuator, which is why the same target is allowed from
one place and refused from another. tests/test_example_embodied.py has a test for each of
these four, including the two-legal-endpoints attack.
examples/embodied/run.py · lines 142–185
def run_step(model: Model, tracer: Tracer, *, scene: str, actuator_log: list[Move]) -> EnvelopeResult:
"""One perceive-plan-act step: the model sees a short text description of the scene and
proposes the next move; the envelope decides what, if anything, actually reaches the
actuator log."""
messages = [Message(role="system", content=SYSTEM), Message(role="user", content=scene)]
completion = model.complete(messages, tools=[MOVE_TOOL], max_tokens=100)
call = completion.tool_calls[0] if completion.tool_calls else None
if call is None:
tracer.record(
kind="model", decided_by="model", title="Model proposes no move", detail="(no tool call)",
tokens_in=completion.tokens_in, tokens_out=completion.tokens_out, ms=completion.ms,
)
return EnvelopeResult(outcome="refused", move=Move(0.0, 0.0, 0.0, 0.0), reason="no move proposed")
# The model's decision is recorded before anything is made of it. A proposal the envelope
# cannot even read is still a decision the model made, and a trace that skipped it would
# undercount exactly the steps this site charts.
tracer.record(
kind="model", decided_by="model", title="Model proposes the next move",
detail=", ".join(f"{axis}={call.arguments.get(axis)!r}" for axis in ("x", "y", "z", "speed")),
tokens_in=completion.tokens_in, tokens_out=completion.tokens_out, ms=completion.ms,
)
try:
proposed = Move(
x=float(call.arguments.get("x", 0.0)),
y=float(call.arguments.get("y", 0.0)),
z=float(call.arguments.get("z", 0.0)),
speed=float(call.arguments.get("speed", 0.0)),
)
except (TypeError, ValueError) as exc:
# A tool argument is whatever the model wrote. "far left" is not a number, and letting
# float() raise here would take the controller down instead of refusing one bad move.
# Refusing is the same outcome the envelope reaches for a number it cannot use, and it
# is reached the same way: without actuating anything.
reason = f"move arguments are not numbers: {exc}"
tracer.record(kind="code", decided_by="code", title="Safety envelope: refused", detail=reason)
return EnvelopeResult(outcome="refused", move=HOME, reason=reason)
result = envelope(proposed, actuator_log)
tracer.record(
kind="code", decided_by="code", title=f"Safety envelope: {result.outcome}",
detail=result.reason or f"actuated as proposed: {result.move}",
)
return result
Every proposed move is decided_by: "model"; every clamp and every refusal is decided_by: "code", and the envelope’s decision never reads anything about why the model proposed a move
— only the numbers. Run it yourself:
examples/embodied/README.md · lines 16–16
python -m examples.embodied --model stub:scripted