The idea
Pre-teach the Piper a small set of named saved poses for a fixed workspace, and let a model sequence them. No dataset, no VLA — the fixed pose set is your deterministic contract.Prerequisites
- An AgileX Piper twin, paired over CAN and jogging under a teleop/pose
controller (see the Piper quickstart —
including
can0up at 1 Mbps). - A microphone twin that streams audio.
- Saved poses defined for your workspace:
home,over_object,grasp,lift,over_target,release, plus gripperopen_gripper/close_gripper(see Twin saved poses).
Step 1: Create the workflow
Create a workflow (Piper Voice Agent) and add the microphone and AgileX Piper twins. Wire nodes left to right; set inputs on the# (fixed) or </>
(expression) tabs using {node-name.output}.
Step 2: Capture the voice — Audio Track → Audio Assistant
| Node | Field | Value |
|---|---|---|
| Audio Track | Twin | your microphone twin |
| Audio Track | Buffer preset | speech-to-text |
| Audio Assistant | audio | {audio-track.audio} |
| Audio Assistant | Modality | voice_assistant |
Step 3: Transcribe — Call Model (speech-to-text)
| Field | Mode | Value |
|---|---|---|
audio | </> | {audio-assistant.audio} |
| Model | — | a speech-to-text model |
result = the transcript.
Step 4 (optional): See the workspace — Data Source
For a VLM planner, add a Data Source node:data_type: twin_image, sensor =
the Piper’s camera. Output image_url. Skip for a plain LLM on a fixed workspace.
Step 5: Plan the poses — Call Model (planner)
Add a second Call Model (LLM or VLM). Set Prompt to</> and paste the
Piper pose planner; the last line inlines the transcript.
Piper pose planner prompt
Piper pose planner prompt
image_url ← {data-source.image_url}. Output: result =
the JSON plan.
Step 6: Read the plan — JSON Parser
| Field | Mode | Value |
|---|---|---|
json_data | </> | {call-model-2.result} |
| LLM fix enabled | — | on |
Step 7: The arm’s vocabulary — Twin
Add a Twin node pointing at the AgileX Piper twin → it reports the valid step names for matching. Output:control_actuations.
Step 8: Run each step — Loop
| Field | Mode | Value |
|---|---|---|
array_data | </> | {json-parser.json_data.steps} |
Step 9: Guardrail — Fuzzy Matcher
Wire Loop → Fuzzy Matcher.| Field | Mode | Value |
|---|---|---|
| Uncertain String | </> | {loop.item.action} |
| Source of Truth | </> | {twin.control_actuations} |
| Advanced → Score Threshold | # | 80 |
Your saved-pose names must match the eight step names in the prompt. If they
differ, rename them (in the prompt or the poses) so the matcher resolves them.
Step 10: Confidence gate — Conditional
| Field | Mode | Value |
|---|---|---|
left_value | </> | {fuzzy-matcher.match} |
| operator | — | equal |
right_value | # | true |
Step 11: Move the arm — Virtual Controller
Wire Conditional (true) → Virtual Controller (dynamic command per step).| Field | Value |
|---|---|
| Twin | AgileX Piper |
| Command source | Source Node → {fuzzy-matcher.matched} |
| Controller Policy | your teleop/pose controller |
Step 12: Test in simulation
Switch to SIMULATE (voice real, arm simulated):- Confirm the arm is at zero pose (and the camera streams, if using a VLM).
- Say “put the block in the cup.”
- Walk Executions: STT → JSON pose plan → Loop per step → matcher resolves each pose → Virtual Controller fires per step → the twin executes.
home.
Step 13: Go live
Once simulation is clean, switch to LIVE. The graph is unchanged. Keep the workspace clear and be ready to stop.The one idea to take away
The model never sets joint angles and never commands the arm directly — it only sequences pre-taught poses, each validated against the arm’s real vocabulary. The model reasons; a fixed contract acts.Next steps
Piper quickstart
Set up and calibrate the Piper before adding voice.
SO-101 voice agent
The same template on the SO-101 arm.
UGV voice agent
The same template on a mobile rover.
Workflow nodes
Every node used here, with inputs and outputs.