Watch a robot live in Foxglove
See what a running workcell is doing as it happens, in Foxglove: the robot posed in 3D, each camera's images, and a plot of every joint, the same channels an episode records. This runs in the sim as it is.
"""Serves an arm in the sim, with its wrist camera, to Foxglove as it runs, and writes the layout that draws it."""
import json
import tempfile
import time
from pathlib import Path
import anvil.camera
import anvil.control
import anvil.hw
import anvil.recorder
import anvil.sim
import anvil.workcell
# How long this example serves; a script of your own serves for as long as it runs.
SERVE_S = 3.0
workcell = anvil.workcell.WorkcellConfig.OPENYAM_V1_SINGLE
workcell.cameras.append(
anvil.workcell.CameraConfig(
name="wrist",
device="",
mode=anvil.camera.Mode(width_px=640, height_px=480, fps=30),
link="wrist_cam",
)
)
robot = workcell.robot
with (
anvil.sim.World(robot=robot, scene="table") as world,
anvil.camera.Cameras(workcell=workcell, world=world) as cameras,
):
hw = anvil.hw.SimHw(world=world)
hw.enable()
trajectory_controller = anvil.control.JointTrajectoryController(robot=robot)
with anvil.control.Runtime(hw=hw, controllers=[trajectory_controller]) as runtime:
runtime.start()
handle = runtime.controller(kind=anvil.control.JointTrajectoryController)
runtime.activate(controller=handle)
# This machine alone; address="0.0.0.0:8765" serves every interface, for a laptop on the same network.
live = anvil.recorder.LiveView(workcell=workcell, runtime=runtime, cameras=cameras)
live.start()
layout_path = Path(tempfile.mkdtemp()) / "live.foxglove-layout.json"
layout_path.write_text(json.dumps(live.layout))
print(f"open ws://localhost:{live.port} in Foxglove, and import the layout {layout_path}")
handle.play(trajectory=robot.home_trajectory(from_q=runtime.latest().q), timeout_s=30.0)
time.sleep(SERVE_S)
live.stop()
- Start a live view with
LiveView, given the workcell, the runtime and the cameras to show. It reads them the way a recorder does, so the loop never waits on it and aRecordercan record beside it.startreturns once it listens;portsays where. - Choose who can connect. By default only this machine can. To watch from a laptop, pass
address="0.0.0.0:8765", which serves every interface; nothing it serves is encrypted or authenticated, so do that only on a network you trust. - Connect from Foxglove. In the desktop app, Open connection, Foxglove WebSocket, and give
ws://<machine>:<port>. - Import the layout once. Write
layoutto a file as JSON and import it with Layouts, Import from file. Foxglove keeps it, so the next connection opens drawn.
Each viewer is sent only what its open panels show, at the rates a rated recording keeps by default: the loop at 100 Hz, or its own rate if slower, and each camera at its fps. A viewer on a slow link loses messages until it keeps up, and never slows the loop, the other viewers or a recording. Cameras are most of the traffic, around a megabyte a second each at 640 by 480, so close the image panels a viewer does not need.