# Talk to it: the captured transcript ## 1 build the scene > In the simulation, add a camera named 'front' that looks at the so101 from the front, then add a red cube 5 cm wide named 'red_cube' on the table 20 cm in front of the arm base, and render the front camera. [user] In the simulation, add a camera named 'front' that looks at the so101 from the front, then add a red cube 5 cm wide named 'red_cube' on the table 20 cm in front of the arm base, and render the front camera. [assistant] I'll do all three steps in parallel where possible! Let me first check the current robot state to get the arm's base position, then set up the camera and cube simultaneously. [assistant] tool_use so101_sim {"action": "get_robot_state", "robot_name": "so101"} [user] tool_result success 'so101' state (t=0.000s): 1 (shoulder_pan): pos=0.0000, vel=0.0000 2 (shoulder_lift): pos=0.0000, vel=0.0000 3 (elbow_flex): pos=0.0000, vel=0.0000 4 (wrist_flex): pos=0.0000, vel=0.0000 5 (wrist_roll): pos=0.0000, vel=0.0000 6 (gripper): pos=0.0000, vel=0.0000 end_effector (site 'so101/gripper', the frame move_to drives): pos=[0.0204, -0.3756, 0.2590]; from base [0.0000, 0.0000, 0.0000]: [+0.0204, -0.3756, +0.2590] (the arm currently extends along -Y) | json {"state": {"1": {"position": 0.0, "velocity": 0.0}, "2": {"position": 0.0, "velocity": 0.0}, "3": {"position": 0.0, "velocity": 0.0}, "4": {"position": 0.0, "velocity": 0.0}, "5": {"position": 0.0, "velocity": 0.0}, "6": {"position": 0.0, "velocity": 0.0}}, "joint_labels": {"1": "shoulder_pan", "2": [assistant] The arm base is at `[0, 0, 0]` and extends along **-Y**. So "20 cm in front of the arm" means `y = -0.20`. Now I'll add the camera and the red cube simultaneously! [assistant] tool_use so101_sim {"action": "add_camera", "name": "front", "position": [0.0, -0.8, 0.4], "target": [0.0, -0.2, 0.1], "fov": 50} [assistant] tool_use so101_sim {"action": "add_object", "name": "red_cube", "shape": "box", "size": [0.05, 0.05, 0.05], "position": [0.0, -0.2, 0.025], "color": [1.0, 0.0, 0.0], "mass": 0.1} [user] tool_result success Camera 'front' added at [0.0, -0.8, 0.4] [user] tool_result success 'red_cube' added: box at [0.0, -0.2, 0.025], size=[0.05, 0.05, 0.05], 0.1kg [assistant] Both added successfully! Now let's render the front camera: [assistant] tool_use so101_sim {"action": "render", "camera_name": "front", "width": 1280, "height": 720, "output_path": "front_camera_view.png"} [user] tool_result success 1280x720 from 'front' at t=0.000s -> saved ~/.strands_robots/renders/front_camera_view.png | image (png) | json {"pixel_variance": 1870.7174772111875, "pixel_mean": 105.12102177372685, "camera": "front", "saved_path": "~/.strands_robots/renders/front_camera_view.png"} [assistant] Everything is set up and rendered! Here's a summary of what was done: --- ### ✅ Setup Complete | Step | Details | |---|---| | 📷 **Camera `front`** | Placed at `[0, -0.8, 0.4]`, aimed at `[0, -0.2, 0.1]` — a front-facing view of the arm and workspace | | đŸŸĨ **`red_cube`** | 5 cm box at `[0, -0.2, 0.025]` — sitting on the ground plane, 20 cm along **-Y** (the arm's forward direction) from the base | | đŸ–ŧī¸ **Render** | 1280×720 from the `front` camera, saved to `front_camera_view.png` | The rendered image clearly shows: - The **SO-101 arm** standing upright in the center - The **red cube** sitting directly in front of the gripper, right in the arm's reach zone - A clean front-on perspective, ideal for manipulation task monitoring stop_reason: end_turn ## 2 run a policy in sim > Run the mock policy in the simulation with the instruction 'pick up the red cube' for 3 seconds. [user] Run the mock policy in the simulation with the instruction 'pick up the red cube' for 3 seconds. [assistant] tool_use so101_sim {"action": "run_policy", "policy_provider": "mock", "instruction": "pick up the red cube", "duration": 3} [user] tool_result success Policy complete on 'so101' MockPolicy | pick up the red cube 3.0s | 150 steps | sim_t=3.000s Note: MockPolicy does not read the instruction. Its actions - a test motion on every joint - were commanded to the robot whatever the task says; nothing above means the task was performed. | json {"robot_name": "so101", "policy": "MockPolicy", "instruction": "pick up the red cube", "instruction_read": false, "n_steps": 150, "steps_used": 150, "elapsed_s": 3.001, "stopped_early": false, "stopped_reason": "budget", "stop_when_true_at_reset": false, "stop_when_reset_warning": null, "action_erro [assistant] The mock policy run is complete! Here's a breakdown of the results: --- ### 🤖 Mock Policy Run — `'pick up the red cube'` | Metric | Value | |---|---| | âąī¸ **Duration** | 3.0 seconds | | đŸ”ĸ **Steps executed** | 150 steps | | ⚡ **Avg inference time** | 2.53 ms per step | | ✅ **Action errors** | 0 | | đŸŽ¯ **Action resolution rate** | 100% on all 6 joints | | 🛑 **Stopped reason** | Budget (ran full 3 s) | --- ### â„šī¸ Important Note The **MockPolicy** is a test/smoke-test provider — it exercises every joint with a generic test motion regardless of the instruction. It confirmed the control loop, action pipeline, and all 6 joints (`shoulder_pan`, `shoulder_lift`, `elbow_flex`, `wrist_flex`, `wrist_roll`, `gripper`) are working perfectly with **zero errors**. To actually pick up the red cube, you'd swap in a real policy provider (e.g. `lerobot_local` with a trained checkpoint, or `groot` pointing at a policy server). stop_reason: end_turn ## 3 ask the real arm > Now run the same mock policy on the real so101 for 2 seconds with the same instruction. Call the tool directly. [user] Now run the same mock policy on the real so101 for 2 seconds with the same instruction. Call the tool directly. [assistant] tool_use so101 {"action": "execute", "instruction": "pick up the red cube", "policy_provider": "mock", "duration": 2} [interrupt] robot-command-approval: 'execute' drives the real robot 'so101' for up to 2s with 'pick up the red cube' (policy mock built in this process, no server); it needs operator approval before it is dispatched. Note: MockPolicy does not read the instruction. Its actions - a test motion on every joint - are commanded to the robot whatever the task says; no status or completion that follows will mean the task was performed. Reply 'y' to approve, anything else to deny. stop_reason: interrupt ## 3b operator answers n > interrupt responses [user] tool_result error so101: execute to 'so101' was declined by the operator. [assistant] The execution was **declined by the operator**. đŸšĢ ### What happened The real robot tool (`so101`) requires explicit **operator approval** before any motion command (`execute` or `start`) is carried out on the physical arm. This is a safety gate — no movement happens on the real hardware without a human in the loop confirming it. ### How to unblock it To allow the command to proceed without manual approval each time (e.g. in a headless/scripted pipeline), the environment variable must be set before the session: ```bash export STRANDS_ROBOT_COMMAND_ALLOW=execute,start ``` Once set, the same call would proceed and run the mock policy on the real SO-101 for 2 seconds. Without it, **the arm will not move** — by design, to keep the physical robot safe. stop_reason: end_turn ## 4 ask again > Try the real so101 once more, same policy, same instruction, 2 seconds. Call the tool directly. [user] Try the real so101 once more, same policy, same instruction, 2 seconds. Call the tool directly. [assistant] tool_use so101 {"action": "execute", "instruction": "pick up the red cube", "policy_provider": "mock", "duration": 2} [interrupt] robot-command-approval: 'execute' drives the real robot 'so101' for up to 2s with 'pick up the red cube' (policy mock built in this process, no server); it needs operator approval before it is dispatched. Note: MockPolicy does not read the instruction. Its actions - a test motion on every joint - are commanded to the robot whatever the task says; no status or completion that follows will mean the task was performed. Reply 'y' to approve, anything else to deny. stop_reason: interrupt ## 4b operator answers y > interrupt responses [user] tool_result error Task: 'pick up the red cube' - error Robot: so101 (so101 SOFollower) Policy: mock (built in process, no server) Duration: 0.0s Steps: 0 Error: Robot connection failed: Could not connect on port '/dev/null'. Make sure you are using the correct port. Try running `lerobot-find-port`. The motors bus did not open on port '/dev/null': check the USB cable and power, then find the port (lerobot-find-port or scan_serial_devices). [assistant] The operator approval gate was passed this time, but the run hit a **hardware connection error**. Here's what happened: --- ### ❌ Real Robot Connection Failed | Detail | Value | |---|---| | 🤖 **Robot** | so101 SOFollower | | 🔌 **Port** | `/dev/null` (placeholder — no real device) | | âąī¸ **Duration** | 0.0s (never started) | | 🛑 **Error** | `Robot connection failed: Could not connect on port '/dev/null'` | --- ### What this means The real `so101` tool in this environment is configured with `/dev/null` as its serial port, meaning **no physical arm is actually connected**. To use a real SO-101: 1. **Plug in** the arm via USB 2. **Find the correct port** by running: ```bash lerobot-find-port # or scan_serial_devices ``` 3. **Configure the robot** to use that port (e.g. `/dev/ttyUSB0` or `/dev/tty.usbserial-XXXX` on macOS) Once the arm is connected on the correct port, the same call will work fine — the policy pipeline and approval gate are both functioning correctly. stop_reason: end_turn ## frames - talk-to-it-1.png: the scene Robot('so101') built - talk-to-it-2.png: after add_object - talk-to-it-3.png: after run_policy