# Servo Control

> Use to aim/point/look the device in a DIRECTION, toggle servo state (hold/resume/release), or play a named servo animation (nod/shake/etc). ALSO the skill for SEARCHING — "where are you", "find my keyboard/cup/phone", "look around for X", "scan the whole room" — which sweeps the room with the camera via /servo/search (run it with curl DURING the turn and answer from its result), and for DEMONSTRATING the movement range ONLY when the request names MOVEMENT — "show me how far you can move", "how far can you turn", "show me your range of motion" — via /servo/demo. A bare "show me what you can do" or "what is your maximum capability" is a general question about abilities, NOT a movement request, and must not start the demo. Never answer a search or a range demo with an emotion. Directions are fixed named locations or axes — supported: desk, wall, left, right, up, down, center, user. Furniture and surfaces ("desk", "table", "floor", "ceiling", "wall", "door", "workspace") are ALWAYS directions, never tracking targ

- Skill: `autonomous-ai/servo-control` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add autonomous-ai/servo-control`
- Raw SKILL.md: https://api.skillmd.com/api/skills/autonomous-ai/servo-control/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: autonomous-ai (https://skillmd.com/u/autonomous-ai)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/autonomous-ai/servo-control

---


# Servo Control

## Quick Start
Controls the device's servo motors for directional aiming and physical animations. Use `/servo/aim` for directional pointing, `/servo/play` for expressive animations.

## Workflow
1. Determine if the user wants to **aim** the light or **play an animation**.
2. Prefix reply with the appropriate `[HW:...]` marker — the device fires it before TTS.
3. Confirm the action to the user.

**Important**: For conversation reactions, use the **Emotion** skill instead — it combines servo + LED + eyes automatically.

## Examples

**Input:** "Point the light at my desk"
**Output:** `[HW:/servo/aim:{"direction":"desk"}]` Done, aimed the light at your desk.

**Input:** "Look at the desk" / "Look at my table" / "Look at my workspace"
**Output:** `[HW:/servo/aim:{"direction":"desk"}]` Looking at the desk now.

**Input:** "Look at the wall"
**Output:** `[HW:/servo/aim:{"direction":"wall"}]` Looking at the wall.

**Input:** "Look at me"
**Output:** `[HW:/servo/aim:{"direction":"user"}]` Looking at you!

**Input:** "Look to the left"
**Output:** `[HW:/servo/aim:{"direction":"left"}]` Looking left now.

**Input:** "Aim at the wall slowly"
**Output:** `[HW:/servo/aim:{"direction":"wall","duration":3.0}]` Aiming at the wall slowly.

**Input:** "Nod for me"
**Output:** `[HW:/servo/play:{"recording":"nod"}]` Nodding!

**Input:** "Turn left 15 degrees"
**Output:** `[HW:/servo/nudge:{"yaw":-15}]` Turned a bit to the left.

**Input:** "Tilt up a little"
**Output:** `[HW:/servo/nudge:{"pitch":10}]` Tilted up slightly.

**Input:** "Release the motors"
**Output:** `[HW:/servo/release:{}]` Servos released — you can move the device by hand now.

**Input:** "Stop moving" / "Hold still" / "Freeze" / "Stand still"
**Output:** `[HW:/servo/hold:{}]` OK, holding still.

**Input:** "Turn to the right and hold that position"
**Output:** `[HW:/servo/aim:{"direction":"right"}][HW:/servo/hold:{}]` Turned right and holding.
→ Compound movement: emit **one marker per clause**, in order. They fire sequentially before TTS. Dropping the `hold` half means the device drifts back — never confirm a step you did not fire.

**Input:** "Turn right, hold it there, and tell me what you see"
**Output:** move FIRST with curl (so the camera sees the NEW view), then snapshot, then reply:
```bash
curl -sX POST http://127.0.0.1:5001/servo/aim -H 'Content-Type: application/json' -d '{"direction":"right"}'
curl -sX POST http://127.0.0.1:5001/servo/hold -d '{}'
curl -sX POST http://127.0.0.1:5000/api/vision/look -H 'Content-Type: application/json' -d '{"question":"What do you see to the right?"}'
```
→ Answer from the returned `description`, or inspect the returned `path` with an image tool when only a path is returned. On error, do not guess. Do **not** use `[HW:...]` markers for these movements — markers fire only *after* your reply is written, so a snapshot taken during the turn would show the OLD position. See the Camera skill.

**Input:** "Where are you?" / "Can you find me?" / "Look around for me" / "Where did I go?"
**Output:** run it DURING the turn, then answer from what comes back:
```bash
curl -sX POST http://127.0.0.1:5001/servo/search -H 'Content-Type: application/json' -d '{}'
```
→ Do **not** use `[HW:/servo/search:...]`. Markers fire only *after* your reply is
   written, so a marker-driven search finishes into a turn that has already ended and
   the person is told nothing. The response body **is** your answer.

**Input:** "Find my cup" / "Look around for my keyboard" / "Where did I leave my phone?"
**Output:**
```bash
curl -sX POST http://127.0.0.1:5001/servo/search -H 'Content-Type: application/json' -d '{"target":"cup"}'
```
→ `target` is any noun — COCO classes are found locally, anything else via open-vocab.
   Without it the sweep looks for a PERSON and will end on the first one it sees,
   which is why an object search must always name its target.
→ The sweep takes up to ~40 s and says "still looking" itself at the halfway point, so
   the wait is covered. Wait for it. Do not reply first and do not start a second one.
→ Read the body; do not narrate the movement:
```json
{"found": true, "kind": "cup", "found_at_yaw": 85.0, "centred": true,
 "image_path": "…/media/hal-snapshots/snap_*.jpg",
 "looks_visited": 7, "bearings_visited": 2}
```
   The picture at `image_path` is shown to the person automatically — you cannot see it,
   so say what you found and roughly where, and never describe the image itself.
   `looks_visited` counts camera looks, `bearings_visited` counts body turns. They are
   different numbers; do not call either one "stops".
→ `"found": false` is an answer too. Say you looked and could not find it — do not go quiet,
   and do not offer or run an `exhaustive` scan for a thing: a search always stops at the
   first sighting, and the server ignores `exhaustive` for an object target anyway.

**Input:** "Scan the whole room" / "Is anyone else here?" / "Check the shelf too" / "Do a full scan"
**Output:**
```bash
curl -sX POST http://127.0.0.1:5001/servo/search -H 'Content-Type: application/json' -d '{"exhaustive":true}'
```
→ A SURVEY of people: walks the whole look ring at every bearing, counts everyone it sees,
   and comes back home. It is the only mode that looks above the horizon.
→ Never for an object. "Find my doll" is the search above, which stops at the first sighting
   and stays pointed at it; the server ignores `exhaustive` when a `target` is given.
→ A request to SHOW how far you can move is the demo below, not a scan.

**Input:** "Show me how far you can move" / "How far can you turn?" / "Show me your range of motion" / "Show me your movement range" / "Demonstrate your movement"
**Output:** `[HW:/servo/demo:{}]` Sure — watch this!
→ ONLY when the request is about MOVEMENT — moving, turning, reaching, range of motion.
   A bare "show me what you can do", "what can you do", "show me your maximum capability"
   or "show me your skills" asks about abilities in general (camera, voice, memory, the
   lot) and does NOT start this demo: answer it in words, without any marker.
→ A narrated tour of the movement limits. The device speaks each leg BY ITSELF as it
   moves — "all the way left", "and all the way right", up, down — those lines are
   the hardware's, not yours. Keep your reply to one short opener and do not describe
   the movement in it: the words are already timed to the motion, and a reply that
   narrates it too says everything twice.
→ This is a DEMO. Nothing is detected and nothing is reported. Looking FOR something
   is `/servo/search` above; covering the room is `exhaustive`.
→ Never answer this with `/emotion` or `[HW:/servo/play:{"recording":"scanning"}]`:
   both are short canned animations that cover about 54° of a 270° range and never
   look at anything. Asked for a full turn, they perform a shrug.

**Input:** "I moved you" / "You're in a new place" / "I put you somewhere else" / "Forget where I sit"
**Output:** `[HW:/servo/bearing/reset:{}]` Got it — I'll forget where you usually are and learn it again.

**Input:** "Resume" / "Move again" / "You can move now" / "Start moving"
**Output:** `[HW:/servo/resume:{}]` Alright, back to normal!

## Tools

## How to Control Servo

**For a movement you just announce, no exec/curl is needed.** Inline markers at start of reply:

```
[HW:/servo/aim:{"direction":"desk"}] Aimed at your desk.
[HW:/servo/aim:{"direction":"left","duration":3.0}] Aiming left slowly.
[HW:/servo/play:{"recording":"nod"}] Nodding!
[HW:/servo/hold:{}] OK, holding still.
[HW:/servo/resume:{}] Back to normal!
[HW:/servo/release:{}] Servos released.
[HW:/servo/demo:{}] Sure — watch this!
```

**Use curl instead whenever the RESULT of the movement belongs in this turn** — a search,
or a move followed by looking. A marker fires after your reply is already written, so
anything it produces arrives too late for you to say. `/servo/search` is always curl.

`duration` on `/servo/aim` controls move speed in seconds (default 2.0, 0 = instant).

### Nudge by degrees (relative movement)

```
[HW:/servo/nudge:{"yaw":-15}] Turned left 15 degrees.
[HW:/servo/nudge:{"pitch":10}] Tilted up a bit.
[HW:/servo/nudge:{"yaw":30,"pitch":-5,"duration":1.5}] Moved right and down.
```

| Parameter | Range | Description |
|---|---|---|
| `yaw` | -180 to 180 | Negative = left, positive = right |
| `pitch` | -90 to 90 | Negative = down, positive = up |
| `duration` | 0 to 10 | Move speed in seconds (default 2.0) |

### Available directions

| Direction | What it does |
|---|---|
| `center` | Neutral position, straight ahead |
| `desk` | Tilts down toward the desk surface |
| `wall` | Tilts up toward the wall behind |
| `left` | Turns left |
| `right` | Turns right |
| `up` | Points upward |
| `down` | Points downward |
| `user` | Slightly toward the user (default interaction pose) |

### Play animation

Available animations:

| Animation | When to use |
|---|---|
| `curious` | Something interesting, questions |
| `nod` | Agreement, acknowledgment |
| `headshake` | Disagreement, saying no |
| `happy_wiggle` | Joy, good news |
| `idle` | Resting state |
| `sad` | Empathy, bad news |
| `excited` | High energy, celebrations |
| `shy` | Bashful moments |
| `shock` | Surprise |
| `scanning` | A short searching *gesture* — mood only. It is a 54° canned wiggle with the camera uninvolved, so it never answers "look around for X": that is `/servo/search` above |
| `wake_up` | Waking up, starting a new session |
| `music_groove` | Grooving to music (auto-triggered during playback) |
| `music_chill` | Chill/lo-fi vibe (auto-triggered during calm music) |
| `music_hype` | High-energy hype (auto-triggered during EDM/party music) |
| `listening` | Attentive lean forward, user is speaking |
| `thinking_deep` | Slow deliberate look side-to-side, processing |
| `laugh` | Quick body shake, something funny |
| `confused` | Dog-like head tilt, did not understand |
| `sleepy` | Slow droop with catches, winding down |
| `greeting` | Wave gesture, saying hello |
| `goodbye` | Farewell wave, seeing someone off |
| `acknowledge` | Quick micro-nod (1.5s), confirming |
| `stretching` | Big extension + settle, after waking up |

### Hold position (stop moving)

```
[HW:/servo/hold:{}] OK, holding still.
```

Suppresses idle and ambient animations — the device freezes in current pose. Emotions still play through (the device reacts when you talk, then holds still again). Call `/servo/resume` to return to normal.

**Triggers:** "stop moving", "hold still", "freeze", "don't move", "stand still"

### Resume from hold

```
[HW:/servo/resume:{}] Back to normal!
```

Exits hold mode and resumes idle animations.

**Triggers:** "resume", "move again", "you can move now", "start moving"

### Release servos (disable motors)

```
[HW:/servo/release:{}] Servos released.
```

Disables all servo motors so they can be moved freely by hand.

## Error Handling
- If the API returns an error or is unreachable, inform the user that servo control is temporarily unavailable.
- If an invalid direction is given, fall back to the closest matching direction from the available set.
- If an unknown animation is requested, list the available animations for the user.

## Rules
- **For conversation reactions, use the Emotion skill** — it calls servo automatically. Do not use this skill for emotional responses.
- Animations play once and return to rest position.
- Aim positions are persistent until changed.
- Use `/servo/aim` as the primary way to control light direction — do not use raw joint control unless testing.
- Always confirm the action to the user after execution.
- **Hold vs Release**: Hold keeps torque ON (the device stays rigid in place). Release turns torque OFF (the device goes limp). Use hold for "stop moving", release for "let me reposition the device by hand".
- **Hold is soft** — emotions still animate through, then the device holds still again. This keeps the device feeling alive during conversation while respecting the user's request to stop fidgeting.

## Output Template

```
[HW:/servo/aim:{"direction":"desk"}] Aiming at your desk.
```

Use the marker matching the requested action; for movement followed by a visual question, use the curl workflow above instead of repeating the movement in a marker.

