← Back to projects
KANGAROOTUNING / FEASIBILITY STUDY SOLO BUILD · MAR–MAY 2026

Kangaroo
Tuning

A sound-based wildlife guidance simulation, built to test one question before any hardware gets bought: can directional sound plausibly steer a kangaroo herd away from a construction road and into a safe corridor?

Simulation-Validated · Field-Unvalidated
Three-band acoustic field diagram A directional emitter on the left projects three acoustic bands toward a safe corridor on the right, crossing a road boundary in between. ROAD CORRIDOR EMITTER HERD
social cue, 500Hz–2kHz mid-freq guide, 2–10kHz ultrasonic repeller, >20kHz 50×50 grid · distance attenuation + wind distortion
The Problem

Fencing works. It also destroys the habitat it's meant to protect.

Construction sites near wildlife corridors kill kangaroos when herds wander onto active roads. The usual fix is physical exclusion — fencing, which is expensive, disrupts the very corridor it's next to, and doesn't scale to sites with large herds.

The alternative under test here is acoustic: directional sound emitters tuned to a kangaroo's hearing range, aiming to nudge herds toward a safe exit corridor instead of walling them out. Nobody had field access, a hardware budget, or live kangaroos to test that on. So the question got answered in simulation first — build the behavior and physics model, and see whether acoustic guidance is even plausible before buying a single directional speaker.

What the research actually says

  • Eastern grey hearing range: 1–18kHz, peak sensitivity 2–3.5kHz
  • Foot-thump below 7kHz signals predator threat — raises vigilance, not approach
  • No confirmed attraction sound exists in the literature for this species
  • Commercial deterrents (Roo Guard, Shu Roo, Roobadge) show mixed or no proven results
System Overview

What actually got built

A 2D grid simulation with a real behavioral model underneath it — not a toy. Every kangaroo carries stress, curiosity, herd cohesion, a panic threshold, and a velocity, and reacts to a physically modelled sound field rather than a scripted path.

environment50×50 grid — construction zone, road boundary, safe corridor, obstacles, worker noise sources
agent modelper-kangaroo stress, curiosity, herd cohesion, panic threshold, velocity
acoustic field3-band model — mid-freq guide, social cue, ultrasonic repeller — with distance attenuation and wind distortion
controllersrule-based baseline (shipped) + Gymnasium-compatible PPO environment scaffold (untrained)
interfaceCLI — status / demo / interactive / test, plus a real-time ASCII map renderer
testspytest, 3 stages, documented expected outcomes — including the ones expected to fail
Acoustic state ownership diagram AcousticField computes the real sound values. AcousticEnvironment holds a cached copy read by each KangarooAgent. A sync call is required to keep the cache current. AcousticField computes real values AcousticEnvironment holds cached copy KangarooAgent reads the cache update_acoustic_fields() never called — the bug
Build Timeline

Nine weeks, four real work sessions

08 MAR 2026
Scaffold: environment, agent model, acoustic field, CLI
First working build. Stage 1 tests at this point: stress response and herd behavior passed; acoustic gradient influence and ultrasonic repulsion failed — the sound field wasn't reaching the agents yet.
12 APR 2026
Documentation overhaul
Rewrote the README against a proper template, fixed typos. No behavior changes — a legibility pass before the bug-hunting session that followed.
27 APR 2026
The bug-fixing day
Found and fixed the acoustic sync bug that had kangaroos moving blind since day one, three separate bugs in the rule-based controller, wired in the real RLController import, and added the CLI entrypoint. Most of the case study below happened in this single session.
03–06 MAY 2026
Cleanup and close-out
Deduplicated RL_ACTION_MAP into config.py, removed dead state fields from SoundEmulator, fixed a control-flow bug in the RL action dispatch, and closed the POC with final documented test results.
Bug Log

Four bugs worth explaining

Picked for what they teach, not just that they got fixed. All four are real commits from 27 April – 6 May.

Kangaroos were navigating blind kangaroo_agent.py

Root cause: two objects held the same data. AcousticField computed the real sound values every step; AcousticEnvironment held a cached copy that agents actually read from. The method that synced the cache, update_acoustic_fields(), existed and worked — it was just never called during a simulation step. Every kangaroo read an all-zero sound field, every step, for the entire early build. That's why Stage 1's acoustic tests failed on 8 March.

def step_all(self, acoustic_environment, acoustic_field):
+ acoustic_environment.update_acoustic_fields(acoustic_field)
herd_center = self.get_herd_center()
for agent in self.agents:
Why it's worth tellingA one-line fix, but the failure mode is a real category: a cache-invalidation bug. Nothing crashed. Nothing threw. The simulation ran fine and produced confidently wrong behavior for weeks, because no one checked whether the numbers a component reads are the same numbers being computed elsewhere.
Checking a key exists instead of what it's set to controller_rule_based.py

Root cause: the action dict always carried every key, with a default value when unset. if "activate_cue" in action is True whenever the key is present — which was always — so the social cue, ultrasound, and intensity branches fired every single step regardless of whether the controller had actually decided to activate them.

- if "activate_cue" in action:
+ if action["activate_cue"]:
acoustic_field.set_social_cue(action["activate_cue"], 0.3)
Why it's worth tellingin checks presence, not truthiness. Easy to write, easy to miss in review, and it silently turns a conditional into an unconditional — three separate branches, same mistake, same commit.
The beam that never stopped spinning controller_rule_based.py

Root cause: get_action() computed an absolute target angle toward the corridor. apply_action() treated it as a relative delta and added it to the current beam angle every step. The beam angle grew without bound instead of tracking the corridor, and the herd's starting position was hardcoded into the angle calculation on top of that, so it never re-aimed as the herd moved.

- current_angle = acoustic_field.beam_angle
- new_angle = current_angle + action["adjust_beam_direction"]
+ new_angle = action["adjust_beam_direction"]
Why it's worth tellingThe variable name adjust_beam_direction was ambiguous about absolute vs. relative — renamed at the call site to make the contract obvious rather than leaving a comment that would drift from the code.
Dispatch logic that ran for the wrong controller simulation_runner.py

Root cause: two bugs, same commit, same root cause — control flow drifted out of the block it belonged in. Controller type detection used hasattr(controller, 'model') as a stand-in for "is this the RL controller," which is fragile — any object exposing a .model attribute for any reason would get routed down the RL path. Separately, the RL action-dispatch block was placed after the if/else instead of inside it, so it executed unconditionally, including for the rule-based controller, whose actions are a dict where the dispatch expected an integer key. TypeError, every step.

- if hasattr(self.controller, RLController):
+ if isinstance(self.controller, RLController):
action = self.controller.get_action(state)
+ self._apply_rl_action(action)
+ action_name = config.RL_ACTION_MAP.get(action, "increase_intensity")
Why it's worth tellinghasattr checks are a common shortcut for type detection in Python, but they detect shape, not identity. isinstance says what the object actually is.
Test Readout

Where the POC actually landed

StageResultNotes
1 — Behavior model 4/4 Pass Acoustic gradient influence, ultrasonic repulsion, stress response, herd behavior — core mechanics confirmed working.
2 — Rule-based controller 3/4 1 Fail Near-road response, stress reduction, and baseline pass. Corridor guidance fails: starting distance of 40 grid units exceeds effective beam range at current attenuation settings — documented as a parameter limitation, not a code bug.
3 — RL scaffold 2 Pass / 3 Skip Environment interface and RL-vs-rule comparison pass. Training convergence, safe-exit-rate, and generalization tests skipped — no trained model exists yet.
Known Limitations

What this doesn't prove

  • Behavioral parameters aren't field-validated. Stress accumulation, curiosity response, and acoustic sensitivity curves are estimated from desk research about kangaroo hearing, not measured from real animals.
  • No confirmed attraction sound exists in the literature. The mid-frequency guide beam is a working hypothesis, not an established fact — this is the central untested assumption the whole approach rests on.
  • Habituation is unmodeled. Real animals adapt to repeated artificial stimuli over time; the simulation doesn't account for that.
  • Outdoor sound dispersion and construction background noise are unresolved — the acoustic model runs in a clean simulated field.
What's Next

Before any hardware gets built

  • Train the PPO controller — target >80% safe exit rate
  • Sensitivity analysis on curiosity, herd cohesion, and gradient strength
  • Baseline comparison: random walk vs. acoustic-guided, to prove the influence is real and not noise
  • Monte Carlo runs across multiple seeds for statistical validation
  • Hardware prototype — Raspberry Pi, directional speaker, PIR sensor — only once simulation results hold up
  • Field testing — requires a wildlife permit and ethics clearance
POC Verdict

Acoustic guidance is plausible in simulation and unproven in the field. That's not a hedge — it's the actual, disciplined output of a proof-of-concept: don't spend a hardware budget or ask for a wildlife permit until the physics and behavior model earn it. The model earned a "maybe, worth prototyping." It didn't earn a "yes."

1,577 lines · 6 python modules · 3 test stages KangarooTuning · closed 06 May 2026