feat(SNNBot): thermometer speed encoding for precise lead discrimination
3-bit speed encoding couldn't distinguish speed 3 from speed 5 — 9° of lead error baked into the input. Thermometer coding with 8 bits gives 1-bit Hamming distance between adjacent speeds. MAX_K back to 128. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# SNNBot — SNN core + aiming loop prototype (issue #152).
|
# SNNBot — SNN core + aiming loop prototype (issue #152).
|
||||||
# 80-input population-coded → 12 LIF hidden → polar-coded (sin/cos) output decoded via atan2.
|
# 80-input population-coded → 12 LIF hidden → polar-coded (sin/cos) output decoded via atan2.
|
||||||
# Inputs: relVelDir [0-35], speed [36-38], distance thermometer [39-46]. Raw bearing removed (irrelevant to lead).
|
# Inputs: relVelDir [0-35], speed thermometer [36-43], distance thermometer [44-51]. Raw bearing removed (irrelevant to lead).
|
||||||
# No movement, no firing. SuperSpike three-factor rule on all weights (issue #158).
|
# No movement, no firing. SuperSpike three-factor rule on all weights (issue #158).
|
||||||
# Binary reservoir alternative toggled via USE_RESERVOIR const (issue #159).
|
# Binary reservoir alternative toggled via USE_RESERVOIR const (issue #159).
|
||||||
#
|
#
|
||||||
@@ -325,9 +325,10 @@ proc toBinaryInput(bearing: float, velDir: float, velSpeed: float, distance: flo
|
|||||||
## Physics-based encoding for lead generalization (issue #152).
|
## Physics-based encoding for lead generalization (issue #152).
|
||||||
## Raw bearing removed — irrelevant to lead offset.
|
## Raw bearing removed — irrelevant to lead offset.
|
||||||
## Bits 0-35: relVelDir = (velDir - bearing) normalized to [-180,180), 36 bins × 10°, 5 active (center ± 2 neighbors).
|
## Bits 0-35: relVelDir = (velDir - bearing) normalized to [-180,180), 36 bins × 10°, 5 active (center ± 2 neighbors).
|
||||||
## Bits 36-38: speed level (3 bits, center ± 1 neighbor).
|
## Bits 36-43: speed thermometer (8 bits): speedInt bits set from bit 36 upward, 0-8 active.
|
||||||
## Bits 39-46: distance thermometer, 8 bands of ~125px in [0,1000].
|
## Adjacent speeds differ by 1 bit (Hamming-1), giving smooth generalization.
|
||||||
## Bits 47-79: unused (sparse is fine for popcount similarity).
|
## Bits 44-51: distance thermometer, 8 bands of ~125px in [0,1000].
|
||||||
|
## Bits 52-79: unused.
|
||||||
## ponytail: physics-based; revert to absolute bearing if convergence regresses
|
## ponytail: physics-based; revert to absolute bearing if convergence regresses
|
||||||
result = [0'u64, 0'u64]
|
result = [0'u64, 0'u64]
|
||||||
|
|
||||||
@@ -342,17 +343,18 @@ proc toBinaryInput(bearing: float, velDir: float, velSpeed: float, distance: flo
|
|||||||
let idx = (vCenter + offset + 36) mod 36 # bits 0-35
|
let idx = (vCenter + offset + 36) mod 36 # bits 0-35
|
||||||
result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64))
|
result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64))
|
||||||
|
|
||||||
# Speed: bits 36-38, center ± 1 neighbor clamped to [0,2]
|
# Speed thermometer: bits 36-43; set bits 36..(36+speedInt-1)
|
||||||
let sCenter = clamp(int(velSpeed), 0, 2)
|
# speedInt=0 → no bits; speedInt=8 → all 8 bits set
|
||||||
for offset in -1 .. 1:
|
let speedInt = clamp(int(velSpeed + 0.5), 0, 8)
|
||||||
let idx = 36 + clamp(sCenter + offset, 0, 2)
|
for b in 0 ..< speedInt:
|
||||||
|
let idx = 36 + b
|
||||||
result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64))
|
result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64))
|
||||||
|
|
||||||
# Distance thermometer: bits 39-46, 8 bands of ~125px over [0,1000]
|
# Distance thermometer: bits 44-51, 8 bands of ~125px over [0,1000]
|
||||||
# All bits up to the current band set (inclusive)
|
# All bits up to the current band set (inclusive)
|
||||||
let band = clamp(int(distance / 125.0), 0, 7)
|
let band = clamp(int(distance / 125.0), 0, 7)
|
||||||
for b in 0 .. band:
|
for b in 0 .. band:
|
||||||
let idx = 39 + b
|
let idx = 44 + b
|
||||||
result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64))
|
result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64))
|
||||||
|
|
||||||
# ── Main loop ─────────────────────────────────────────────────────────────────
|
# ── Main loop ─────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import std/[bitops, math]
|
|||||||
const
|
const
|
||||||
INPUT_BITS* = 80
|
INPUT_BITS* = 80
|
||||||
WORDS_IN* = 2 # 80 bits → 2 × uint64 (128 bits, only 80 used)
|
WORDS_IN* = 2 # 80 bits → 2 × uint64 (128 bits, only 80 used)
|
||||||
MAX_K* = 64
|
MAX_K* = 128
|
||||||
MIN_SIM* = 3
|
MIN_SIM* = 3
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|||||||
Reference in New Issue
Block a user