How I spent ~400 hours and ~$830 building my own rat terminator with machine learning (instead of hiring an exterminator).
Rat in a crosshair — title illustration

Why I Can’t
Work From Home

Rats chew wires for a mix of biological needs, environmental factors, and opportunistic behavior. The behavior is common in urban, agricultural, and domestic settings and has predictable causes and consequences.

— Rando on Quora

⚠️ Disclaimer · pulse check
!! This talk contains: rats, ballistics, and at least one dead rat !!
Chewed Cat-6 cable held in a gloved hand, attic in background
Dramatization
News article: Mice destroy NSW family home as plague continues — rodent-caused fire
Act I · framing

The Trap Didn't Work

Rat ignoring a cage trap, captured on Reolink IR camera
08/15/2025 · 02:32 AM · rat walks past baited cage
Wile E. Coyote falling with a tangled parachute
Me (unbeknownst)

If the rat won't go to the trap, the trap can go to the rat.

The plan, roughly
Train ML model 3D print ... Profit
Act I · the reasonable option

Normal Rat Extermination Project

Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Call Exterminator
Exterminator Sets Traps
Rats Removed
Go to the beach with family
More Beach 🏖️

~$300/month. Done in a week. Zero emotional scarring.

04
Act I · the unreasonable option

Rat Extermination With ML

August
September
October
November
December
January
Research
Train ML Model to Detect Rats
Experiment with 3D Printing Designs
Program & Wire up Servos
Ballistic Testing
Maybe Rat Eliminated?
Hours
400
Out of pocket
~$800
Rats confirmed dead (maybe)
1
05
Act II · training loop

YOLO != YOLO

Reolink IR capture — rat near trap, detected by YOLO
rat · 0.92
YOLOv8n · MacBook Pro · 640×640
01 Label draw boxes, auto split 70/30
Rat Image Labeler — custom bounding-box tool
Python/Tk labeler
02 Train 49 images · YOLOv8n · 640×640
03 Test + repeat ↺ mAP@0.5 0.94precision 0.91recall 0.88
Act II · the v1 rig

A servo that pulls a trigger.

Daisy pellet rifle Single-shot, then reset by hand Fusion 360 · PLA
Trigger mount CAD overlaid on the actual Daisy pellet rifle trigger
Isolated Fusion 360 CAD of the servo-to-trigger mount
Raspberry Pi single-board computer running the detector
Raspberry Pi
Feetech FT9035M 8.5kg·cm coreless micro-servo
Feetech · 8.5 kg·cm
Front-facing rat captured by the rig's IR camera under the deck
Act II · first trial

What's up, doc?

Why it didn't trigger
01 Training set was Reolink shots of rats from behind. Deployment rat arrived head-on.
02 Barely any IR low-light frames in training. The rig sees everything in IR.
03 Classic data distribution ≠ deployment distribution.
Training image: rear-facing rat in Reolink IR footage
The detector had mostly seen this angle. The first real rat gave it the opposite view.
Act II · fixing the dataset

More data.

Data Time comic about a show whose 'more data' catchphrase was cancelled after one episode
Synthetic IR rat generated with Gemini Nano Banana
Gemini Nano Banana
Stock rat photo
Stock & open web
Negative training example — my own legs near the trap
My own legs
Empty burrow captured by the rig
Empty scenes
Act III · mechanical path

Two detours leading to SO-101.

01 · overbuilt bearing
Fusion 360 thrust-bearing assembly cross-section
Assumed I needed a thrust bearing. Lots of CAD, zero testing.
02 · lighter weapon
Custom pitch harness designed to rotate an air pistol
CO₂ pistol: lighter, shorter, lower moment arm.
03 · SO-101 unlock
LeRobot SO-101 follower arm
Custom pitch attachment that bolts onto the SO-101 base
Jetson Nano
Jetson
Open-source arm + custom pitch mount, Jetson on top. 1 Hz → 20 Hz.
Act III · operator interface

A real world FPS.

Camera Tracker Control web UI — live camera view with crosshair, servo sliders, trigger button
Live camera feed, crosshair overlay, yaw/pitch sliders, keyboard nudges, and a trigger button — useful for calibration and couch testing.
One simple operator loop
HTTP COMMANDS + LIVE VIDEO Browser UI see + steer FastAPI control_api.py Robot camera + motion manual control for calibration + testing
/status/stream-frame/set-position/trigger-servo
Act III · rt_200.py architecture

One tracker. Small, testable parts.

CAMERATRACKER · rt_200.py — ORCHESTRATES CAPTURE, INFERENCE + CONTROL LOOPS + LIFECYCLE PERCEPTION BELIEF + CONTROL CameraSource USB / CSI · mono / stereo YOLO inference detections + confidence Stereo depth rectify · disparity · mm CrosshairAiming + observation pixels + depth → servo target AngularTargetBelief smooth · predict · decay AngularBelief Controller PID · step limit · deadband TrackerRobot Protocol · robot interface CameraTracker implements CONTROLLER CALLS set_yaw / set_pitch → OverlayRenderer crosshair · bbox · depth Control API FastAPI · status · stream Browser UI see + steer Trigger Servo PWM FRAME + BBOX + DEPTH MANUAL /set-position
Act IV · nights 01–02

The turret moved. Then it engaged.

IR night capture — rat under the deck, camera has panned to track it
night 01 · 02:07 · auto-pan
Frame 01 · tracked
A rat in frame, right where the camera pointed itself. First real unassisted detection.
IR capture — rat in frame, rig engaged
night 02 · 02:32 · shots fired
Frame 02 · engagement
Detector locked on. Turret slewed. Daisy fired. Likely hit; the spotted rat never returned on Reolink.
First real detection First engagement Calibration still mattered
Act IV · calibration

Camera Sees ≠ barrel points.

barrel → target camera crosshair 01 · detect offset crosshair above barrel line · needs to come down Δ 136 px
The procedure
01 Test-fire at a sheet of paper at known distance.
02 Measure pixel offset between camera crosshair and hit.
03 Shift crosshair down in software until it overlays the hit.
Foreshadowing

This calibration is only valid at one distance. Change how far the rat is, the offset shifts. Later: a whole failure mode.

Act IV · stereo fix

A fix.

Working stereo geometry
left CSI right CSI B = 52.5 mm target pixel Z from disparity depth-aware Y adjust
The code shifts the crosshair relative to the zeroed distance.
Math from the code
# stereo depth
# B is measured lens-center baseline
Z_mm = fx_px * 52.5 / disparity_px

# ratbot/robot/aiming.py
adjustment = focal_y_px * laser_vertical_offset_mm * (
    (1.0 / depth_mm) -
    (1.0 / reference_distance_mm)
)

adjustment = clamp(adjustment,
    -max_adjust_px, +max_adjust_px)

crosshair_y = target_y + adjustment
closer than 1m
move down
at 1m
0 px
farther than 1m
move up
rig/live · detect → aim → fire · loop
Act IV · terminator moment

The turret tracks. The turret fires.

First clean end-to-end run. Detector locks on, SO-101 slews to target, pitch drops, Daisy goes off.

It is genuinely unsettling to watch a weapon track something on its own — even on an iPad.

End-to-end working Detector + servos + trigger Autonomous
Act V · the verdict

Was it worth it?

Option A · Exterminator
$300
time One week.
effort One phone call.
scars Zero.
output Dead rats.
Option B · Mine
~$830 + ~400 hrs
Hardware bill
7× Feetech servos$140
Raspberry Pi$60
Jetson$300
2× IR cameras$80
CO₂ pistol + pellets$30
Reolink camera$80
Bearings$10
Heat-set inserts$20
Other tools~$40
Filament + prints~$70
Months · several scars · 1 dead rat (unconfirmed) · everything I learned.
The obviously correct answer.
Act V · legal-ish, not legal advice

The Law

What is a machine gun?
NFA definition needs a firearm.
A pellet gun is air-powered, not explosive-propelled. The notes say: no NFA / machine-gun exposure for the air gun itself.
Booby trap problem
Autonomous firing is the red line.
Human-in-the-loop click while watching video is the safer distinction. “Rat detected → fire” by itself is spring-gun / tort territory.
Pest control comparison
Killing rodents ≠ license to discharge anywhere.
Rodent dispatch, animal-cruelty rules, and projectile-discharge ordinances are separate layers from firearms classification.
Ground turret vs flying turret
Mounted / crawlspace
Quadcopter / airborne
NC state: permissive for air guns on private property.
Local: city ordinances often ban outdoor discharge; enclosed/indoor containment is the safer read.
crawlspace: plausibly contained
FAA: dangerous weapons attached to drones are illegal without specific FAA authorization.
State/local: weaponized-drone and privacy rules stack on top.
yard + airborne: different regime
Practical read from the notes: federal air-gun classification is the easy part; human approval, local discharge rules, and “does it fly?” are the risk multipliers.
fin.
Q&A
Ask me anything — rats, servos, YOLO, why I didn't just call the exterminator.
Audience member raising a hand at the rat-detection turret talk
artist's impression
fin. · your turn

Go solve a $300 problem the $830 way.

Every skill in this talk — CAD, YOLO, servos, stereo vision — was a first attempt, with AI riding shotgun.

The barrier between “I wonder” and “it fires pellets” has never been thinner.

Find your rats. It's early.
Appendix

Appendix: the trigger arm kept breaking.

Trigger arm v2 — thicker, curved, re-oriented print
trigger-arm · v2
v1
2–3
shots before snap
v2
40+
shots before snap
Iterations
01
Rotated the print, thickened the arm, added a curved shoulder.
Layer lines now cross the stress axis instead of parallel to it. 20× the shot life.
02
All the test-firing surfaced the bore-sight problem.
Hundreds of shots to prove the arm held up also proved the crosshair lied — same issue from earlier.
03
The harness holding the gun developed play.
Repeated firing loosened the mount that held the pistol itself. Force on the trigger dropped over time — a slow-motion failure.
04
Started designing a bolt mechanism to replace the snap-on.
Kinematically-locked instead of friction-held. Not finished.
Appendix · open source

Appendix: the depth camera, open-sourced.

Live disparity heat map (warm = near) beside the camera view ranging a Red Bull can with a metric distance readout
live · disparity map (warm = near) · click to measure
Two Pi cameras + a Jetson → metric depth, ~800 lines of Python.
Browser calibration UI — your laptop screen is the checkerboard.
Dense disparity maps drive the crosshair's distance readout.
github.com/base698/stereo-csi ↗
Appendix · very meta

Appendix: this deck presents itself.

Presenter view of this very deck: current slide, next-slide preview, speaker notes, and the Open audience window button
presenter.html · showing this deck · showing this slide's neighbor
The deck is plain HTML — one file, agent-built, arrow keys, print-to-PDF.
Presenter mode: notes, next slide, timer — one click opens a synced audience window.
Every animation and video you saw was mirrored to the screen the same way.