anomaly detection for periodic textures

RPR

Ramanujan Periodicity Residual

Instead of asking “does this look different?”, we ask “does this still repeat the way it should?” — and because the detector knows which period broke, an agent can trace the defect to the machine component that caused it.

the blind spot

Two questions a detector can ask

“Does this look different?”

Embedding-based detectors compare a patch to known-good patches in feature space. A thin scratch across a regular grid barely moves an embedding — the pattern is broken, and the detector shrugs.

“Does this still repeat the way it should?”

Ramanujan sums give filters with a rare property: the period-q filter responds only to genuinely period-q structure — not its harmonics, not its neighbors. Break the repeat, and the right channel drops.

interactive — the actual math, in your browser

A Ramanujan Filter Bank you can poke

This is the same computation the detector runs: build FIR filters from the Ramanujan sums cq(n) (normalized for equal per-harmonic gain), convolve, square. Change the signal’s true period and watch the correct channel — and only that one — light up. The candidate set {1,2,3,4,6,8,12,24} is the divisor closure of the highly composite numbers ≤ 32: 8 periods instead of 32, 2× faster.

Input signal

Period-energy spectrum

how it fits together

From pixels to a physical cause

texture image camera on the line Ramanujan filter bank c_q(n) filters, rows + cols HCN period set ×2 faster periodicity signature per 32×32 patch vs memory bank of good anomaly heatmap cosine NN residual disrupted period q divisor-family attribution the part embeddings can’t give you physical mapping q · pixel pitch → mm ; belt ÷ mm → Hz Claude-powered agent process logs · incident history maintenance records ranked root causes every claim cites a number example: q=8 px × 0.5 mm/px = 4.0 mm 200 mm/s ÷ 4.0 mm = 50.0 Hz = gear mesh (2.0 Hz × 25 teeth) ✳
grid texture, 8 px pitch
grid · 8 px pitch · machine M-101
mesh texture, 12 px pitch
mesh · 12 px pitch · machine M-102
weave texture, 24 px pitch
weave · 24 px weft pitch · machine M-103
evidence

Validated before built on

The synthetic gate had to pass before anything else was written: defect scores spike ≈100× above clean regions, and the reported disrupted period is q = 8 — the texture’s true pitch — on the top patch and 9 of the top-10.

the payoff of knowing the period

An agent that argues with numbers

q = 8 px × 0.5 mm/px 4.0 mm repeat ÷ into 200 mm/s 50.0 Hz= gear mesh: 2.0 Hz × 25 teeth ✳
get_process_parameters("M-101")
  temperature_C: 82.1  belt_speed_mm_s: 200.0
convert_period_to_physical_frequency(8, "M-101")
  spatial_period_mm: 4.0  temporal_frequency_hz: 50.0
search_incident_history_by_frequency(50.0)
  INC-001 · 50.0 Hz · deviation 0.0% · same machine
get_maintenance_log("M-101")
  2026-06-28 · drive gear · non-OEM spare installed
top root cause · confidence 0.95

Drive gear tooth wear → tension flutter at gear-mesh frequency

  • 8 px × 0.5 mm/px = 4.0 mm; at 200 mm/s → 50.0 Hz
  • Incident INC-001 matched at 0.0% deviation
  • 50.0 Hz = machine’s gear-mesh frequency (2.0 Hz × 25 teeth)
  • Maintenance 2026-06-28: non-OEM drive gear installed

Recommended action: replace drive gear, re-tension belt.

Claude (claude-opus-4-8) runs a tool loop over four plant-data tools with a schema-constrained JSON answer. Every hypothesis must cite a parameter reading, a matched incident with its % deviation, or explicitly say no strong match. Without an API key, a deterministic matcher runs the same tools — the demo never breaks.

honest scope

What we claim — and what we don’t

Periodic textures only

RPR is scoped to grid / mesh / weave-like material. An embedding baseline ships alongside it as the second channel for everything else. We don’t claim wins on non-periodic objects.

Known misses, stated

26/27 correct verdicts on the demo set. The two misses: thin scratches on the coarse weave, just under the calibrated threshold. It’s in the README, not hidden.

Demo plant data

Process logs, incidents and maintenance records are mock data crafted for a self-consistent story. The agent tooling is real — swap backend/tools.py for live sources.