Skip to main content
Early tutorial (stub). The flow and node configuration below are complete enough to build and test end-to-end. Screenshots and the shareable template link will be added as the template is published.
By the end of this tutorial you’ll have a Waveshare UGV Beast that listens to a spoken command, has a model turn it into a short driving plan, and executes that plan — built entirely in the visual Workflow editor, no scripts. Prefer to build it in Python instead? See the SDK version: Control a UGV rover with your voice. Same idea, different surface.

Architecture at a glance

The idea: voice → text → a JSON plan → each step is checked against the commands the rover actually understands → the rover executes it. The model reasons; a fixed contract controls the motion. Cyberwave runs the listening nodes (Audio Track, Audio Assistant) on the device and the thinking nodes in the cloud — you don’t wire that split yourself.

Prerequisites


Step 1: Create the workflow

Create a new workflow (name it UGV Rover Voice Agent) and add both twins to it: the microphone twin (audio in) and the UGV Beast twin (the rover to drive). You’ll wire nine nodes left to right. Each node’s inputs are set in the inspector — some are fixed values (# tab), some reference another node’s output using the </> expression tab with {node-name.output} syntax.

Step 2: Capture the voice — Audio Track → Audio Assistant

Audio Track (the trigger) listens to the microphone and emits short audio clips. Audio Assistant trims that stream down to actual speech (voice-activity detection), so the transcriber isn’t fed silence.
Speak into the mic and open Executions — a run should fire, and Audio Assistant should show is_speaking: true with a captured speech segment.

Step 3: Transcribe — Call Model (speech-to-text)

Add a Call Model node and pick a speech-to-text model (e.g. Faster Whisper Small EN for English). Output: result = the transcript, e.g. "back up a little, then turn left".

Step 4: Plan — Call Model (planner)

Add a second Call Model node and pick a capable cloud LLM or VLM (e.g. GPT-5.4, Gemini 3 Pro, or Claude). This node turns the sentence into a strict JSON driving plan. Set the Prompt to expression mode (</>) and paste the planner prompt below. The last line pulls in the live transcript with {call-model.result}.
Leave Image URL empty. It’s for pictures, not text — the transcript belongs in the prompt. (Wire Image URL to a camera frame only if you want a vision variant.)
Output: result = the JSON plan as text.

Step 5: Read the plan — JSON Parser

Add a JSON Parser to turn the plan text into structured data. LLM fix repairs the occasional chatty wrapper ("Sure, here's...") around the JSON so parsing doesn’t fail.

Step 6: Know the rover’s vocabulary — Twin

Add a Twin node pointing at the UGV Beast. It reports the exact commands the rover accepts — the safe vocabulary you’ll match against. Output used later: control_actuations (e.g. move_forward, move_backward, turn_left, turn_right, stop).

Step 7: Run each step — Loop

Add a Loop so the plan’s steps execute one at a time.
Point array_data at the actions list, not the whole json_data object. A loop needs an array [ … ] to iterate.
The loop exposes {loop.item} (the current step) and {loop.index} each pass.

Step 8: Guardrail — Fuzzy Matcher

Wire Loop → Fuzzy Matcher. This snaps the model’s word ("backward") to the rover’s real command ("move_backward"), and returns empty if nothing is close — so a bad transcription can’t drive the rover. Outputs: matched (the real command), match (true/false), score.

Step 9: Drive — Virtual Controller

Wire Fuzzy Matcher → Virtual Controller. This publishes the matched command to the rover. Use Virtual Controller (not Send Controller Command) because it accepts a dynamic command that changes every step.
Motion duration. Virtual Controller has no duration field, so how long each command drives is governed by the UGV Beast Controller policy. Test a single forward in simulation (Step 10) and confirm the rover moves briefly and stops. If it keeps going, add an explicit stop step between moves.

Step 10: Test in simulation

Switch the toolbar to SIMULATE — your voice is real, but the rover drives as a 3D twin in the viewer, so nothing physical can go wrong.
  1. Confirm the mic twin is streaming and the Beast is visible in the viewer.
  2. Say a single command: “move forward for three seconds.”
  3. Open Executions → latest run and walk the nodes:
Then work through progressively harder commands:
For “back up a little, then turn left” the Loop runs twice, Fuzzy Matcher returns move_backward then turn_left, and the twin reverses then turns in the viewer.

Troubleshooting


Step 11: Go live

Once simulation is clean, switch the toolbar to LIVE to drive the physical Beast. The graph is unchanged — only the target flips.

The one idea to take away

The model never controls speed, and never sends a command directly to the rover. It only chooses a direction and a duration, and every command is validated against the rover’s real vocabulary before it moves. That’s what makes an LLM safe to put in a robot’s control loop: the model reasons, a fixed contract acts.

Next steps

The SDK version

Build the same agent in Python with the Cyberwave SDK.

Workflow nodes

Every node used here, with inputs, outputs, and where it runs.

Workflows reference

Triggers, the editor, and running in sim or live.

Add a camera (vision)

Wire a camera frame into the planner for obstacle-aware driving.