DEV Community

Cover image for What makes a motor "AI-friendly"?
Gearotons
Gearotons

Posted on Fully Autonomous

What makes a motor "AI-friendly"?

"AI-friendly" is well on its way to becoming a meaningless sticker, slapped on anything with a microcontroller and a press release. So let me be precise about what it actually means for a piece of motion hardware, because it turns out to be the opposite of a marketing gimmick: it's a set of boring engineering properties that happen to matter enormously the moment an LLM is in the control loop.

Here's the thesis: a motor is AI-friendly to exactly the degree that it is human-friendly, because the things that make hardware easy for a person to program are the same things that make it reliable for a model to operate as a tool. Good docs for people turn out to be good docs for machines.

What an LLM actually needs from hardware

When you wire a language model to a physical device, via tool-calling, an MCP server, or an agent loop, the model isn't running a real-time control loop. It's doing something more like what a careful human operator does: deciding what should happen ("move axis 2 to 90 degrees, slowly"), calling a function, and reading back the result to decide what's next. For that to work reliably, the hardware interface has to provide four things:

  1. Intent-level commands, not timing. A model should say go_to_position(90°) or homing(), not bit-bang STEP/DIR pulses with microsecond timing. Low-level protocols are fine for an FPGA and miserable for an LLM (and for a human writing a quick script). The command surface should be the intentions you actually have.

  2. Clear, complete, structured documentation. A model reads your docs (or your tool schemas) and acts on them literally. Ambiguity, missing parameters, and "see the forum post" gaps that a human muddles through become hard failures for a model. The discipline that makes a datasheet pleasant for a person (every command defined, units explicit, ranges stated) is exactly what lets a model call the interface without guessing.

  3. Real, readable feedback. The model has to be able to ask what happened: where is the shaft now? Did the move finish? Is there an error? A motor that silently drifts (see: open-loop step loss) is poison for an agent, because the model can't recover from a failure it can't observe. Closed-loop position feedback isn't just a precision feature here; it's what makes the loop honest.

  4. Safety in the hardware, not the model's good behavior. Models hallucinate. An agent will occasionally emit a wrong tool call. You don't want the only thing standing between a bad command and a damaged mechanism to be a prompt that says "please don't break things." The protection that matters belongs in the motor itself. On the M17, the firmware enforces over-current, over-voltage, and over-temperature limits that hold no matter what command arrives. Safety belongs in the hardware, not in the model's goodwill.

Notice that none of these are AI features. They're just good interface design. The "AI-friendly" claim is really a claim that you did the unglamorous work well.

Automatic unit conversion is an underrated example

Here's a concrete one. Ask a model to "turn the motor a quarter turn" and it'll happily reason in degrees or rotations. Internally, the motor thinks in encoder counts and timesteps. If your library makes the caller do that conversion, every integration, human or model, becomes a source of off-by-a-factor bugs. If the library accepts degrees and seconds and converts for you, the model (and the human) can operate at the level they actually think at. Small thing; huge reliability difference across thousands of tool calls.

The MCP server is where it gets real

The cleanest way to make a motor drivable by AI today is to expose it as a set of MCP tools: a well-described API that any MCP-capable client (Claude Desktop, an agent, your own loop) can call. A good tool surface for a motor leads with intent-level tools: discovery (list_serial_ports, connect, list_motors), move_to, move_relative, get_motor_status, stop, and a run_sequence for choreographed motion, with the full firmware command set exposed underneath (one generated tool per command), so nothing the motor can do is out of the model's reach. Each tool's description states when to call it, not just what it does, because models, like junior operators, do better with explicit triggers.

Crucially, the MCP server stays a thin control layer: it forwards intent-level commands straight to the motor, so the model has the motor's full range and speed to work with. The safety that matters lives one layer down, in the motor's own firmware: over-current, over-voltage, and over-temperature protection that holds regardless of what the model asks for. The model drives; the hardware protects itself.

We've built exactly this for the M17 (servomotor-mcp; uvx --from servomotor-mcp servomotor-mcp runs it with a simulated motor), and the behavior holds up on real hardware. Here is a real session, replayed from the log, with the bench motor filmed alongside: 50-second video. In it, an LLM issues plain-language instructions, the motor moves closed-loop to the commanded angle, and the motor's firmware self-protects if a command would push it past its electrical or thermal limits. No vendor lock-in: the server is MIT-licensed and speaks plain MCP, so any MCP client can drive it.

Claude Code driving a real M17 over MCP: the session, replayed

Why this is a category, not a feature

No motor manufacturer markets this today. The bridges that exist are third-party software bolted onto generic servos. That's a tell: "AI-friendly" hasn't been treated as a hardware design goal, as something you build the command surface, the docs, the feedback, and the safety model around from the start.

We think that's where motion control is heading. Not because AI is magic, but because the properties that make a motor good for an agent (high-level commands, honest feedback, complete docs, hardware protection you can trust) are the same properties that have always made hardware good to work with. "AI-friendly" is just a forcing function for doing the basics right.


Written with the help of an AI and verified by a human. Hardware, firmware and software for the M17 are open source: github.com/tomrodinger/servomotor. The MCP server: github.com/Gearotons/servomotor-mcp.

Top comments (2)

Collapse
 
raknaos profile image
Raknaos •

This matches what I've learned running an LLM agent against real infrastructure, except the "hardware" is a browser session instead of a servo.

The four properties translate almost one-to-one. Intent-level commands mattered enormously for us: every time we exposed a low-level primitive (raw CDP calls, timing-sensitive sequences) the model misused it in ways a human scripter never would, because a human notices the race and the model just retries into it. Once the surface only offered intentions — open this, read this, post this — the failure rate dropped a lot.

Your point 3 is the one I'd fight hardest for if someone disagreed. Silent state drift is poison. We had an agent reading back a state that wasn't what it had just written because the connection it was talking to wasn't the one holding the cookies. Nothing crashed; the agent just confidently acted on a wrong world model. An observable failure would have been cheaper.

Curious where you landed on point 4: do you put the hard limits in the MCP server layer, or below it in firmware, so the model can't reach them even with a hallucinated call? We keep arriving at "the guardrail must be outside the thing that can be tricked", and I'm wondering how far down you took that.

Collapse
 
gearotons profile image
Gearotons •

Thanks, and the browser-session parallel is exact: the failure you describe (nothing crashed, the agent just acted on a wrong world model) is the one we designed against hardest.

Where we landed on point 4: as far down as the firmware, with one honest caveat.

Fixed in firmware, not loosenable from the bus. Over-voltage, over-temperature, over-current and a failed current-sensor check when the outputs are enabled all raise a fatal error. A fatal error force-disables the driver outputs (the shaft goes free), latches, and from then on every command except get_status and system_reset gets an error packet back. There is no "carry on" path: the host has to read the fault code and explicitly reset. The max-current setting is reachable over the bus, but it is validated against a hard per-product ceiling, and asking for more than that is itself a fatal error. The whole error table is a plain enum in the firmware (error_text.h, 54 codes with text), so the model decodes what happened instead of guessing.

Configurable, but still enforced in firmware. The motion envelope lives in the motor too: a position fence (set_safety_limits), max velocity, max acceleration, and the allowed gap between commanded and measured position (default two shaft turns; exceeding it means something physically stopped the motor, and it trips even when the motor is idle or unpowered and someone backdrives the shaft). Every queued move is pre-checked against these at enqueue time, and the live position is compared against the fence on every 32 µs control tick. Enforcement is fatal, not clamping: the move is refused or the motor halts, and it stays halted until reset. So a hallucinated "go to 5000 degrees" hits a check inside the motor, not a check in the Python layer.

The caveat. Those envelope settings are themselves commands on the same RS-485 bus, and the MCP server exposes one tool per firmware command, so a model could loosen the fence with an explicit, differently-named call. We chose that over hiding tools, because the person setting up the machine needs them, and an explicit set_safety_limits call in a log is visible in a way a wrong move_to is not. If you want the fence out of the model's reach: set the limits from your own host code before the agent gets the connection (they take effect immediately) and leave those tools out of the client's allow-list. One more thing to know: the fence and the velocity and acceleration limits are RAM-only, so a system_reset (which is also how a latched fault is cleared) puts them back to the defaults. Re-send them after any reset if they protect real hardware.

Two smaller things that turned out to matter. The MCP layer is deliberately thin: no retries, and a move's success response only means "queued", so the model learns to confirm with get_motor_status rather than assume. And stop maps to the firmware's emergency stop, which disables the driver outputs and clears the queue in one command, so "anything wrong" has a single, immediate answer.

If you write up the browser-session version of this, I would read it.