Python SDK for realtime 3D IMU data from the Auli.tech Cato. The Cato's standard HID mouse output is only 2D; this SDK exposes the full 3-axis accelerometer + gyroscope stream, fused orientation (quaternion / euler), and linear acceleration for robotics and research applications.
The protocol is a Python port of Auli's official imu_visualizer web demo. The device communicates over plain HID (USB or Bluetooth Classic), not BLE GATT — pair it at the OS level like any Bluetooth device and the SDK will find it.
pip install -e ".[examples]" # from a clone; [examples] adds matplotlib + websocketsNote: the SDK depends on the PyPI package
hidapi(imported ashid). Do not also install the PyPI package namedhid— both provide a module calledhidand shadow each other. If things break:pip uninstall hid && pip install --force-reinstall hidapi.
- Pair the Cato in System Settings → Bluetooth (or plug it in over USB).
- Grant your terminal app Input Monitoring permission in System Settings → Privacy & Security → Input Monitoring — required for HID access. Restart the terminal after granting.
Add a udev rule so you can open the device without root:
# /etc/udev/rules.d/99-cato.rules
KERNEL=="hidraw*", ATTRS{idVendor}=="1915", ATTRS{idProduct}=="52dd", MODE="0666"
from cato import Cato
with Cato() as cato: # finds the device, starts the stream
for sample in cato.samples():
print(sample.acc) # (x, y, z) m/s²
print(sample.gyro) # (x, y, z) deg/s
print(sample.quaternion) # (w, x, y, z) fused orientation
print(sample.linear_acc) # gravity-removed, world frame
print(sample.euler()) # (roll, pitch, yaw) degreesThree ways to consume data, all driven by one background reader thread:
# 1. Iterator — natural for logging/scripts
for sample in cato.samples(timeout=1.0):
...
# 2. Latest value — natural for render/control loops at their own rate
sample = cato.latest()
# 3. Callbacks — fired on the reader thread, keep them fast
cato.on_sample(lambda s: ...)
cato.on_status(lambda st: ...) # device status changes
cato.on_gesture(lambda g: ...) # gesture inference reports
cato.on_disconnect(lambda exc: ...)Other useful bits:
Cato.list_devices() # enumerate connected Catos
cato.reset_orientation() # re-zero the fused orientation
cato.send_command("sensor_stream on") # raw text-command channel
cato.get_info() # device info ("init" command)
cato.get_config("/global_info") # config subtrees
cato.get_profiles()
Cato(fusion=False) # raw-only, no Madgwick filterAll in examples/ — run with the device paired and powered on:
| Example | What it does |
|---|---|
01_print_stream.py |
Print live acc / gyro / euler at ~10 Hz |
02_csv_logger.py |
Log raw + fused samples to CSV until Ctrl-C |
03_live_plot.py |
Realtime matplotlib traces of acc & gyro |
04_gesture_monitor.py |
Print status changes and gesture inferences |
visualizer/serve.py |
3D orientation visualizer in your browser (like Auli's demo): Python streams fused data over a websocket to a three.js page |
python examples/visualizer/serve.py # then open http://localhost:8000 (opens automatically)- Frame: samples are given in the same Y-up reference frame as Auli's
imu_visualizer (device axes remapped to x/y/z; gyro y/z negated), because the
sensor-fusion constants were tuned in that frame. At rest,
acc ≈ (0, -9.81, 0). - Raw wire values:
sample.rawkeeps the untouched on-wire tuple(ts, acc_y, acc_z, acc_x, gyro_y, gyro_z, gyro_x). - Units: already physical on the wire — acc in m/s², gyro in deg/s.
- Orientation: IMU-only Madgwick filter (no magnetometer), so yaw is
relative, not absolute heading. Quaternions are
(w, x, y, z). - Sample rate: whatever the firmware streams; each sample carries the
device's
uint32timestamp (device_ts) and a hosttime.monotonic()stamp (host_ts).
For the curious (reverse-engineered from the official web demo):
- HID device, VID
0x1915/ PID0x52DD. - Report 7 (input): IMU stream —
<I6flittle-endian, timestamp + 6 floats. - Report 6 (feature): text-command channel ("OTA") with chunked framing —
commands like
sensor_stream on,init,config get /global_info. - Report 9 (input): status; must be answered with a heartbeat (report 10, one zero byte) or the device drops the link — the SDK does this automatically.
- Report 8 (input): gesture-inference results.
pip install -e ".[dev]"
pytest # protocol/fusion/OTA tests, no hardware neededMIT