Search-and-rescue robots and pet trackers both rely on a simple truth: a person is easy to find if they carry a radio, and hard to find if you rely on cameras. Phones already broadcast Bluetooth. We wanted a robot dog that finds a person by radio alone, around corners and through visual occlusions, using nothing more than signal strength and its own legs.
What it doesSignalHound is a Unitree Go2 that homes in on a person by Bluetooth signal strength. The person carries a phone advertising a BLE beacon. A Nordic nRF7002-DK riding on the dog scans continuously and measures the beacon's RSSI, then rebroadcasts its measurements over BLE so the laptop needs no cable. RSSI gives strength, not direction, so the dog infers direction by moving: it takes a short step, stops, re-measures, and compares. Signal up, keep going; signal flat or down, turn and probe; after a few points it fits a plane to its recent odometry-plus-RSSI history and steers along the gradient. When the signal drops after a peak it drives back to the strongest spot and stops. Everything runs through a single safety layer: tiny velocity bursts, hard speed caps, a stop after every burst, Ctrl+C stops the dog, and a search time and move budget. A terminal dashboard shows the live RSSI, filtered value, robot pose, search state and every decision.
How we built itFirmware: a Zephyr application on the nRF5340 inside the nRF7002-DK (active BLE scanning, name filter, continuous scan window, batched RSSI rebroadcast in manufacturer data), plus the hci_ipc controller on the network core, built from upstream Zephyr and flashed over J-Link. Laptop: Python. A radio layer (serial or BLE via bleak) with a rolling median and EMA filter; a Go2 layer on the Unitree WebRTC data channel (StandUp, BalanceStand, Move, StopMove, live sport-mode telemetry); a centralized motion guard; and a homing controller with an explicit state machine. We built a 2D radio simulator with path loss, noise, packet loss and a multipath-ripple model, and used it to test every algorithm change before touching the dog. Twelve-to-sixteen-case convergence matrices decided each tuning choice. We used an AI coding assistant heavily for firmware bring-up, simulation and the control loop.
Simulator resultsEvery algorithm change was tested in the simulator before it went on the dog. Each choice was decided by a 12–16 case convergence matrix (from our hackathon log):
- Plain sweep with 3-step probes: 16/16 found, 36 moves
- Shorter post-turn probes: 15/16, with 8 m runaways
- Two-sided probing: 11–14/16, with 12–17 m runaways (rejected)
- Plane-fit gradient steering with 5 dB multipath ripple: 10/12 → 12/12 found; average moves 44 → 36; worst wander 8.4 → 4.0 m
- "Passed it" return-to-best reflex: 12/12 found, mean stop distance 1.4 m
After the event we reran the final controller on harder cases: 12 targets 4 m away in every direction, 5 dB ripple, 3 dB noise and 10% packet loss.
Aryan: hardware setup and all on-robot testing, Go2 networking and credentials, phone beacon and DK mounting, running and observing every field trial, submission. Ray: Go2 connection verification / team coordination / demo staging. Both: algorithm decisions from the field logs.
Challenges we ran intoAlmost everything. The DK's console lands on the second J-Link virtual COM port. The board's drag-and-drop programmer doesn't work on nRF5340, so we flashed both cores over J-Link. Extended advertising in the host made scan start fail against the upstream controller. BlueZ only surfaces one advertisement update every two seconds per device, so the DK batches its last sixteen samples with a running index. The Go2's access point name changes every boot and has no internet, which kept cutting our tooling off, so we routed internet over a phone tether with a pinned default route. A power bank kept auto-switching off under the DK's tiny load. And the physics: at four meters indoors, multipath ripple of ±5 dB hides a 2 dB/m gradient, and a hand-held phone is body-shadowed by 10 to 20 dB, so the dog passed within a meter of the person and kept searching until we changed the stop criterion.
Testing and results (real hardware)RSSI vs. distance (phone advertising every 100 ms, 191 samples):
- Phone touching the DK: −22 to −24 dBm (±1 dB)
- Arm's length: −38 to −41 dBm (±3 dB)
- On a nearby table: −57 to −62 dBm (±5 dB)
- Walking away around the hall: −75 to −86 dBm, about one packet every 5 s. Below about −78 dBm packets go missing rather than just getting weaker.
Motion primitives on the Go2:
- Forward burst (0.2 m/s for 0.5 s): moved 0.10 m and stopped cleanly.
- Rotate burst (0.5 rad/s for 0.5 s): turned about 8°, about 55% of the commanded angle because the dog ramps up, with under 2 cm of drift.
Field runs, and what each one taught us:
- Run 1: the DK was hand-held, not on the dog. The pipeline worked end to end (radio → decision → motion → stop), but it wasn't a real closed loop. We changed it to accept thin sample windows and to count only silence as "signal lost".
- Run 2: the phone was taped flat on the dog. The baseline was −87 dBm because the dog's body and battery blocked about 20 dB. We moved the receiver clear of the body and added a weak-baseline warning.
- Run 3: the DK and power bank rode on the dog, and the person held the phone. Forward readings were flat, so the dog turned left and gained 6 dB. We learned that the first turn with one omnidirectional antenna is a coin flip, so we made wrong guesses cheap.
- Run 4: 16 moves over about 6 m, ending in a loop. RSSI rippled between −60 and −73 dBm with no consistent gradient, which is indoor multipath. We added plane-fit gradient steering.
- Run 5: 39 autonomous moves in 3.8 minutes over about 12 m. The dog boxed around the person and was stopped by the operator at −51.3 dBm and rising. We set the arrival threshold to −52 dBm and added the return-to-best reflex.
A real closed loop on real hardware: the dog's movement decisions came from live RSSI measured by the Nordic board, with no cameras and no map. In our last full run the dog boxed around the person over 39 autonomous moves and was closing in at −51 dBm and rising when we stopped it. The Nordic board is the sensor, not a decoration, and it runs untethered on a power bank. Every motion primitive is bounded and stops cleanly. Every field run is logged decision by decision and fed back into the simulator.
What we learnedRSSI is a noisy scalar field, not a range. Time-averaging fixes packet noise but not multipath, which is a function of position; only moving and averaging over space defeats it. The first turn with one omnidirectional antenna is a coin flip, so the algorithm has to make wrong guesses cheap rather than trying to avoid them. Stop criteria matter as much as search: a body-shadowed beacon never reads as strong as you expect. And simulate before you move a robot; most of our tuning came from a 200-line mock.
What's next for our projectA foil reflector behind the antenna to give the dog a directional pattern, which turns the search into "spin, sniff, go" (our simulator says 8 dB of front/back contrast takes convergence from 20% to 90%). Frontier-style exploration for larger spaces. Using the Go2's obstacle sensors for avoidance. A second receiver for true bearing estimation. Fixing false early stops on multipath peaks, which our post-event simulation exposed.







Comments