Skip to content
OpenServoCorePublic

About

Measurements and experiments behind the OpenServoCore firmware

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

notebooks

This is where I measure how a servo behaves, and where the firmware in open-servo-core gets its reasons from. A loop, a limit or a default over there should have a notebook in here showing the data behind it.

The stages

The work is split into stages. Each stage reads the results of the stages before it, and each stage is also one bench setup, so the reading order and the bench order are the same.

0. Instruments

Folder stages/0-instruments/. Targets the rulers. The DMM, the power resistors, the camera tag, the thermocouple and the clock that lines their logs up.

The later stages compare their results against these, so each one gets its own error bar first. No board and no servo on the bench.

1. Board

Folder stages/1-board/. Targets the controller board, one type at a time (dev board, swap board), latest revision only.

Known loads only, power resistors and an RL dummy, with the rail swept by the DPS. This gives the conversions from ADC counts to volts and amps, the sample timing, the noise floor and the board NTC curve. Shares a sitting with stage 0.

2. Motor

Folder stages/2-motor/. Targets the motor on its own, by type. Today that is a small brushed DC motor.

The bare motor sits in the printed motor rig, a base that seats the can with the leads under it and a wrench bar that holds the rotor by its pinion at one of twelve angles, with the DMM across its tabs. Resistance, inductance, the back-EMF constant and the no-load current are measured here with nothing else in the way, and the teardown at the end reads the copper on its own. Stage 3 is graded against these numbers. Same sitting as stage 1.

3. Servo

Folder stages/3-servo/. Targets the assembled servo, meaning the motor plus a gear train, a pot and a case.

Open loop throughout, with the camera tag on the horn and the horn lock for the held-rotor routes. Two groups of experiments live here. Electrical repeats the stage 2 measurements in situ, the way osc-cal has to do them, in one pass with one supply and no meter. Mechanical covers friction, inertia, the gear train, the pot and the servo under a known load, down to a crawl against the camera tag. The winding temperature constants are earlier work and are not re-measured; the winding's own resistance against its rest value is the thermometer. This is the long sitting, and the last one that captures open loop.

4. Estimators

Folder stages/4-estimators/. Targets the real-time estimators the firmware runs. Speed, position, current zero and winding temperature.

No new captures. Each estimator is designed offline over the stage 3 data and graded against the stage 0 instruments.

5. Control design

Folder stages/5-control/. Targets the control loops. Current, velocity, position and trajectory, including how a hold behaves.

The loops are designed from the measured plant and the estimators' noise and delay, over the stage 3 data. Each notebook ends with the loop the data supports, which is what the firmware carries, and the acceptance it has to meet in stage 7.

6. Safety design

Folder stages/6-safety/. Targets the limits and the faults. The current ceiling, the stall and position-error faults, the thermal derate and cutoff, the stop-hold limits.

Derived from stages 3 to 5, with the acceptance each limit and fault has to meet in stage 7, including on a sagging supply.

7. Validation

Folder stages/7-validation/. Targets the firmware as built from stages 4 to 6, mode by mode, on the servo of stage 3 and then on servos it was not designed on.

The closed-loop image runs each mode against the stage 0 instruments and the acceptance the design stages pre-registered: position, with its step, hold and tracking error against the tag; velocity, from a crawl to full speed against the tag, smooth at the crawl; torque and the current ceiling against the DMM; the thermal estimate and derate against the winding's own resistance read at rest; the faults and the stop-hold limits, and all of it on a sagging supply. A mode passes or fails against its number, and a failure goes back to the stage that designed it. This is the bench gate for a release, and it is re-run on each new servo type.

How an experiment is laid out

One folder with three files. A capture that drives the bench and writes a dataset under telemetry/, one notebook that reads the dataset and writes its result, and the results.json it wrote. In stages 0 and 1 the capture is a capture.py run on its own. In stage 2 the bench holds one state for several experiments at once, so each declares its capture as plans in a plan.py and python -m oscnb.sitting <state> runs every plan of a state in one sitting, with a manifest of what ran under telemetry/.../sittings/; the teardown's hand routes keep a capture.py. A notebook with no capture of its own may keep a module beside it that only it imports. Each stage folder has a README listing its experiments and how they chain.

Every notebook has the same sections, in the order a reader asks the questions. Purpose, why the number is needed and who uses it. Procedure, what the capture script does, step by step. Data, each file of the dataset shown on its own with its columns. Method, how the number is pulled out of the data, with the code. Results, the numbers in words and the write to the result file. Limitations, what the experiment does not cover. Open questions, each with the measurement that would settle it. Conclusion, the highlight: under which conditions the thing measured can be relied on, and what later experiments should take from it. References last, when a method leans on published work, numbered and cited in the text as [1]. Every code cell has a sentence above saying what it does, and every figure a sentence below saying what it shows.

Purpose

Derive the firmware's design

Which loops exist and how they are shaped, which limits and faults exist, and the defaults shared by all servos come out of the measurements here. The board constants are the only numbers copied into the firmware. Servo numbers like resistance, back-EMF constant, friction and thermal gains are reproduced by the tools on the user's own servo.

Build the methods behind the tools

The characterization, identification and calibration steps that osc-cal and the osc-web calibration flow run on a user's servo start here as experiments, in long form, with the model and its limits worked out on real data. An experiment is written for a type of thing, like a brushed DC motor or a pot, not for one servo model. The MG90 goes through first, the SG90 second to check the methods carry over.

Grade the tools

The notebooks hold the outside truth, a DMM, a camera, a thermocouple. The tools are scored against it, and the range where an experiment holds becomes the range where the tool stops.

Teach

A hobbyist without an EE degree should be able to follow along. The notebooks are committed with their outputs baked in, so they read straight from GitHub.

Equipment

What the experiments use, by the names the stage READMEs refer to. The scripts that drive the controlled ones live in equipment/.

DMM

An OWON B41T+ handheld multimeter, 4.5 digits, true RMS, with Bluetooth LE streaming and a K thermocouple input. It reads DC volts, ohms at about a milliamp, DC amps and temperature. equipment/b41t.py connects and logs every packet with the host time of arrival. Its Bluetooth address sits in equipment/registry.json so the script connects without a scan. Two bluez quirks the script handles on its own: a meter left connected stops advertising, so the script always disconnects on exit, and a stale discovery makes bluez refuse a new scan, which an adapter power cycle clears. A review with the measured accuracy is at lygte-info.dk. Amazon link: to add.

DPS

A FNIRSI DPS-150 bench supply, 0 to 30 V, 0 to 5 A, 150 W, fed from a 100 W USB-C PD adapter, scripted over USB serial through the fnirsi-dps150 package. equipment/dps150.py reads it, sets a voltage and a current limit, and switches the output. The voltage readback agrees with the DMM within millivolts. The current readback reads well under the true current and is not used as a measurement. Product page at Elektor. Amazon link: to add.

loads

Three uxcell aluminium-housed wirewound power resistors, 5 ohm 50 W 5 %. One is used bare, one with a capacitor across it, and the three in series make about 15 ohm. Their values come from the power-resistors experiment in stage 0, not from the label. A power inductor of about 1 mH rated 2 A or more, in series with one resistor, makes the RL dummy for the bridge-state experiment. Amazon links: to add.

thermocouple

A welded K-type thermocouple on twisted leads, read on the DMM's temperature input. Its offset, slope and time constant come from the thermocouple experiment in stage 0. Amazon link: to add.

camera

A phone filming two printed ArUco tags, one on the horn and one fixed beside it, plus a printed ChArUco board for the lens calibration. The pipeline lives in oscnb.fiducial. The phone model goes in the camera's registry entry, since the lens calibration belongs to it.

The original clips are not committed: a slow-motion phone clip runs to gigabytes, and the tag pipeline takes only the corners of two squares from each frame. A dataset holds the lens calibration, a corners table per clip, and a manifest with each original's size and hash. Each clip also has a 960 px real-time preview, a few megabytes, committed so that anyone replicating the experiment can see what was filmed and how. The originals stay on the host that captured them. The pipeline can be tried on the previews in place of the originals; the camera-tag notebook shows what that costs (a noise floor several times higher, the geometric error about the same).

rigs

Printed fixtures under rigs/, parametric CAD in build123d with the measured parts as their inputs. The motor rig of stage 2 seats the bare motor and holds its rotor at indexed angles. The servo rig of stage 3 holds the assembled servo and the board under the camera, with a tag mount on the stock horn, a bolted horn lock at mid travel, and a load arm, on one base that lies flat or stands on edge.

clapper

The servo board's STAT lamp, flashed from the host through the stat_flash_ticks register (osc clapper). Like a film clapper, every recording sees the same flash, so the camera's clip and the host's log can be lined up afterwards.

host

The computer running osc and osc-lab over the osc adapter, with the capture scripts in this repo. Python comes through uv, see Setup. On Linux, turn USB autosuspend off for the Bluetooth dongle (options btusb enable_autosuspend=0 in modprobe.d). A dongle that suspends between the DMM's packets resets on USB, comes back as a new adapter, and bluez forgets the meter.

Results

Each notebook writes one file next to itself, results.json, with the value, its bounds, the unit and the capture it came from. An experiment run on a servo and board pair writes results/<servo>__<board>.json in its folder instead, one file per pair. A later stage reads these files and nothing else from the stages before it.

Once a stage settles, its README opens with a TL;DR. It holds the constants a later stage reads, with their bounds, unit, confidence and notebook, then the key findings, the open questions and what the next stage takes. The constants table is not typed by hand. The stage's tldr.toml holds the hand-written parts of each row, the label, the result and key, the figures, what the bounds are and the confidence, and the numbers come from the result files. After a re-record or a spec change, uv run python -m oscnb.tldr rewrites the table between the tldr:constants markers in the README, and uv run pytest fails while a README is stale. A stage with no tldr.toml is skipped.

osc-lab

osc-lab is the little Rust tool in osc-lab/ that every capture records with. It has two commands. osc-lab sweep drives open-loop duty rungs and streams the raw TEL rows into sweep.csv. osc-lab burst takes the high-rate shunt bursts from rest into burst-K.csv. Both write a meta.json beside the data with the servo's constants at the time.

It is the only capture instrument. Nothing in it fits, identifies or calibrates. It is a raw instrument on purpose, so a dataset never goes through identification code on its way to disk. Everything else a capture script does to the servo, like get, set, status or baud, still goes through osc from the firmware repo.

To install it, you will need a nightly Rust toolchain (the rust-toolchain.toml in the folder picks it for you). Then run this in this folder:

cargo install --locked --path osc-lab

oscnb.osc finds it on the PATH, or at the path in the OSC_LAB environment variable.

Setup

You will need uv. Then uv sync in this folder installs Python 3.12 and the dependencies, uv run python equipment/dps150.py read checks the DPS, and uv run jupyter lab opens the notebooks.

The principles and rules this repo follows are in PRINCIPLES.md.

About

Measurements and experiments behind the OpenServoCore firmware

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages