necklink-cns 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- necklink_cns-2.0.0/LICENSE +21 -0
- necklink_cns-2.0.0/MANIFEST.in +10 -0
- necklink_cns-2.0.0/PKG-INFO +190 -0
- necklink_cns-2.0.0/README.md +158 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/examples/ESP32_MAVLink_Survival_Copilot/ESP32_MAVLink_Survival_Copilot.ino +84 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/library.properties +10 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/NeckLinkCNS.h +11 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/neckcompass.c +1313 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/neckcompass.h +289 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/neckcord.c +2675 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/neckcord.h +368 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/necklink_cns.c +563 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/necklink_cns.h +245 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/necksense.c +1299 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/necksense.h +309 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/necksight.c +1242 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/necksight.h +295 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/nl_mavlink.c +433 -0
- necklink_cns-2.0.0/arduino/NeckLinkCNS/src/csrc/nl_mavlink.h +197 -0
- necklink_cns-2.0.0/csrc/Makefile +33 -0
- necklink_cns-2.0.0/csrc/necklink_cns.c +563 -0
- necklink_cns-2.0.0/csrc/necklink_cns.h +245 -0
- necklink_cns-2.0.0/csrc/nl_mavlink.c +433 -0
- necklink_cns-2.0.0/csrc/nl_mavlink.h +197 -0
- necklink_cns-2.0.0/csrc/organs/neckcompass.c +1313 -0
- necklink_cns-2.0.0/csrc/organs/neckcompass.h +289 -0
- necklink_cns-2.0.0/csrc/organs/neckcord.c +2675 -0
- necklink_cns-2.0.0/csrc/organs/neckcord.h +368 -0
- necklink_cns-2.0.0/csrc/organs/necksense.c +1299 -0
- necklink_cns-2.0.0/csrc/organs/necksense.h +309 -0
- necklink_cns-2.0.0/csrc/organs/necksight.c +1242 -0
- necklink_cns-2.0.0/csrc/organs/necksight.h +295 -0
- necklink_cns-2.0.0/csrc/tools/necklink_cns_selftest.c +351 -0
- necklink_cns-2.0.0/necklink_cns/__init__.py +41 -0
- necklink_cns-2.0.0/necklink_cns/banc_v888_priors.json +215 -0
- necklink_cns-2.0.0/necklink_cns/cli.py +74 -0
- necklink_cns-2.0.0/necklink_cns/cns.py +278 -0
- necklink_cns-2.0.0/necklink_cns/connectome.py +163 -0
- necklink_cns-2.0.0/necklink_cns/mavlink.py +370 -0
- necklink_cns-2.0.0/necklink_cns/ros2_node.py +170 -0
- necklink_cns-2.0.0/necklink_cns.egg-info/PKG-INFO +190 -0
- necklink_cns-2.0.0/necklink_cns.egg-info/SOURCES.txt +46 -0
- necklink_cns-2.0.0/necklink_cns.egg-info/dependency_links.txt +1 -0
- necklink_cns-2.0.0/necklink_cns.egg-info/entry_points.txt +2 -0
- necklink_cns-2.0.0/necklink_cns.egg-info/requires.txt +8 -0
- necklink_cns-2.0.0/necklink_cns.egg-info/top_level.txt +1 -0
- necklink_cns-2.0.0/pyproject.toml +62 -0
- necklink_cns-2.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Amandeep Singh Bains
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
include LICENSE
|
|
2
|
+
include README.md
|
|
3
|
+
include pyproject.toml
|
|
4
|
+
recursive-include necklink_cns *.py *.json
|
|
5
|
+
recursive-include csrc *.c *.h Makefile
|
|
6
|
+
recursive-include arduino *.c *.h *.ino *.properties
|
|
7
|
+
prune csrc/build
|
|
8
|
+
prune assets
|
|
9
|
+
prune dist
|
|
10
|
+
prune tests
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: necklink-cns
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Unified Adult Drosophila Whole-CNS Edge Autonomy Controller (BANC v888 Descending Arbitration, <24 KB Zero-Float ANSI C99, MAVLink v2 & ROS 2 Bridges)
|
|
5
|
+
Author: Amandeep Singh Bains
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/indian-aman/necklink/tree/main/packages/necklink_cns
|
|
8
|
+
Project-URL: Repository, https://github.com/indian-aman/necklink
|
|
9
|
+
Keywords: necklink,drosophila,connectome,mavlink,ros2,ardupilot,px4,esp32,neuromorphic,robotics
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: C
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: numpy>=1.22
|
|
25
|
+
Requires-Dist: necksight>=2.0.0
|
|
26
|
+
Requires-Dist: neckcompass>=2.0.0
|
|
27
|
+
Requires-Dist: neckcord>=2.0.0
|
|
28
|
+
Requires-Dist: necksense>=2.0.0
|
|
29
|
+
Provides-Extra: wire
|
|
30
|
+
Requires-Dist: pynltp>=1.6.0; extra == "wire"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
|
|
35
|
+
<a href="https://github.com/indian-aman/necklink"><img src="https://raw.githubusercontent.com/indian-aman/necklink/main/assets/logo/necklink-mark.svg" alt="NeckLink" width="52"></a>
|
|
36
|
+
|
|
37
|
+
# NeckLink CNS (`necklink-cns` v2.0.0)
|
|
38
|
+
|
|
39
|
+
**Unified Adult *Drosophila* Whole-CNS Survival Copilot — fusing `NeckSight`, `NeckCompass`, `NeckSense`, and `NeckCord` through empirical `BANC v888` descending arbitration plus wire-valid `MAVLink v2` and `ROS 2` bridges in `23,928 B` (`23.37 KB`) of zero-heap, zero-float ANSI C99 (`38.82 KB` standalone WebAssembly, `14.5%` ESP32 RAM) and Python.**
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/necklink-cns/)
|
|
42
|
+
[](https://pypi.org/project/necklink-cns/)
|
|
43
|
+
[](csrc/)
|
|
44
|
+
[](https://indian-aman.github.io/necklink/#simulator)
|
|
45
|
+
[](arduino/NeckLinkCNS/)
|
|
46
|
+
[](LICENSE)
|
|
47
|
+
|
|
48
|
+
[**Live WASM Demo**](https://indian-aman.github.io/necklink/#simulator) ·
|
|
49
|
+
[**Quick start**](#quick-start) ·
|
|
50
|
+
[**Autopilot bridges**](#mavlink-v2--ros-2-autopilot-bridges) ·
|
|
51
|
+
[**C99, ESP32 & WASM**](#zero-heap-zero-float-ansi-c99-esp32--wasm) ·
|
|
52
|
+
[**BANC v888 & G4 Benchmarks**](#banc-v888-specificity--rtx-pro-6000-g4-benchmarks) ·
|
|
53
|
+
[**When to use it**](#when-to-use-necklink-cns-and-when-not-to)
|
|
54
|
+
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<p align="center">
|
|
58
|
+
<img src="https://raw.githubusercontent.com/indian-aman/necklink/main/assets/necklink_v2_g4_benchmarks.png" alt="NeckLink CNS v2.0.0 RTX PRO 6000 G4 benchmark summary" width="100%">
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## What it is
|
|
64
|
+
|
|
65
|
+
In the adult *Drosophila* Brain-and-Nerve-Cord connectome (`BANC v888`: `157,789` proofread neurons, `23,553,108` synapses), perception and navigation across tens of thousands of brain interneurons compress into **`1,316` Descending Neurons (`DNs`)** crossing the cervical neck connective to drive **`805` Ventral Nerve Cord motor neurons (`MNs`)**, paired with **`1,849` Ascending Neurons (`ANs`)** returning proprioceptive efference copy (`AN:DN = 1.405`).
|
|
66
|
+
|
|
67
|
+
`necklink-cns` turns that biological survival architecture into a single closed-loop copilot library:
|
|
68
|
+
|
|
69
|
+
1. **Optic Lobe (`NeckSight v2.0.0`)**: `R1-R6` log photoreceptors with AEC glare step suppression, `T4/T5` Reichardt EMDs (`1.35×` `T5` OFF dominance grounded in `6,586` `T5→LPLC2` vs `3,330` `T4→LPLC2` synapses), 3-DOF `(yaw, pitch, roll)` gyro derotation, `LPLC2` 4-arm radial + `LC4/LPLC1` 2-arm thin-pole/wire looming, and contralateral `DNa02` dodge vectors.
|
|
70
|
+
2. **Central Complex (`NeckCompass v2.0.0`)**: 9-axis 3D tilt-compensated AHRS (`|‖a‖ - g| > 0.25g` wave-slam gate), `16×32` `Q12` online soft-iron self-calibration (`nc_plastic_ring_t`), `EPG ↔ Delta7` ring attractor, `CPU4` path integrator, and `PFL3 + PFL2` (`180°` anti-goal boost) GPS-denied homing.
|
|
71
|
+
3. **Antennal Lobe, Chordotonal Organ & Mushroom Body (`NeckSense v2.0.0`)**: 16-band integer motor/bearing vibration front-end (`ns_vib_fe_t`), turbulent plume whiff-rate tracker, `512`-KC sparse `FlyHash`, 17-bit habituating novelty detector, and 2-compartment `KCγ + KCα/β` zero-forgetting continual memory.
|
|
72
|
+
4. **Cervical Bottleneck & VNC (`NeckCord v2.0.0`)**: Connectome-weighted descending priority hierarchy (`DNp01` Giant-Fiber emergency brake → `MDN` moonwalk reverse → `DNa02` contralateral dodge → `MBON` plume cast → `PFL3/PFL2` homebound cruise), 4-byte CRC-4 descending wire frame, sensorless ESC (`RPM + Current`) broken-blade/weed-foul observer, box-constrained `[u_min, u_max]` 3-DOF USV / `[0, u_max]` 4-DOF hexacopter & octocopter active-set allocator, and 18-servo 3-DOF hexapod IK.
|
|
73
|
+
5. **Autopilot Bridges**: Zero-dependency wire-valid **MAVLink v2** (`0xFD` magic, X.25 CRC-16 + `CRC_EXTRA`) C99 & Python codec (`HEARTBEAT`, `SCALED_IMU`, `MANUAL_CONTROL`, `STATUSTEXT`, `OBSTACLE_DISTANCE`) for ArduPilot / PX4 companion links, plus a **ROS 2 (`rclpy`)** node adapter.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Quick start
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install --upgrade necklink-cns
|
|
81
|
+
necklink-cns --version
|
|
82
|
+
necklink-cns demo
|
|
83
|
+
necklink-cns priors
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import numpy as np
|
|
88
|
+
from necklink_cns import NeckLinkCNS
|
|
89
|
+
|
|
90
|
+
cns = NeckLinkCNS(vehicle="hexacopter", grid_size=32)
|
|
91
|
+
frame = np.full((32, 32), 160.0)
|
|
92
|
+
out = cns.step(
|
|
93
|
+
dt=0.02,
|
|
94
|
+
speed_m_s=1.2,
|
|
95
|
+
gyro_xyz=np.array([0.02, -0.01, 0.05]),
|
|
96
|
+
accel_xyz=np.array([0.3, -0.2, 9.80]),
|
|
97
|
+
mag_xyz=np.array([0.45, 0.05, 0.89]),
|
|
98
|
+
camera_frame=frame,
|
|
99
|
+
)
|
|
100
|
+
assert len(out.cervical_wire_4b) == 4
|
|
101
|
+
assert out.mavlink_manual_pkt[0] == 0xFD
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## MAVLink v2 & ROS 2 autopilot bridges
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import numpy as np
|
|
110
|
+
from necklink_cns import (
|
|
111
|
+
MavLink2Codec,
|
|
112
|
+
MavManualControl,
|
|
113
|
+
NeckLinkCNS,
|
|
114
|
+
NeckLinkROS2Adapter,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# 1. Wire-valid MAVLink v2 framing (ArduPilot / PX4 companion UART or UDP)
|
|
118
|
+
codec = MavLink2Codec(sysid=42, compid=191)
|
|
119
|
+
pkt = codec.encode_manual_control(MavManualControl(x=650, y=0, z=500, r=-250, buttons=0, target=1))
|
|
120
|
+
decoded = codec.feed(pkt)
|
|
121
|
+
assert len(decoded) == 1 and MavManualControl.from_payload(decoded[0].payload).r == -250
|
|
122
|
+
|
|
123
|
+
# 2. ROS 2 (rclpy-ready) sensor/cmd_vel adapter
|
|
124
|
+
adapter = NeckLinkROS2Adapter(NeckLinkCNS(vehicle="usv_t200", grid_size=32))
|
|
125
|
+
adapter.on_imu(np.array([0.01, -0.02, 0.05]), np.array([0.2, -0.1, 9.8]))
|
|
126
|
+
adapter.on_mag(np.array([0.45, 0.05, 0.88]))
|
|
127
|
+
step_out = adapter.step_camera(np.full((32, 32), 140.0), speed_m_s=1.0, dt=0.02)
|
|
128
|
+
assert isinstance(step_out["twist"].angular_z, float)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Zero-heap, zero-float ANSI C99, ESP32 & WASM
|
|
134
|
+
|
|
135
|
+
[`csrc/necklink_cns.h`](csrc/necklink_cns.h), [`csrc/necklink_cns.c`](csrc/necklink_cns.c), and [`csrc/nl_mavlink.h`](csrc/nl_mavlink.h) fuse all four organs and the MAVLink v2 codec into a single static C struct (`nl_cns_t`) with **zero `float`/`double` instructions** and **zero `malloc`/`free`**:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
make -C csrc clean all test sanitize # 6/6 C99 Whole-CNS self-tests under -Werror + ASan/UBSan
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
| Target / Artifact | Static RAM (`sizeof`) | Flash / Binary | Full-CNS Tick Latency | Throughput |
|
|
142
|
+
| :--- | :---: | :---: | :---: | :---: |
|
|
143
|
+
| **ANSI C99 `nl_cns_t` (x86-64 `-O2`)** | **`23,928 B` (`23.37 KB`)** | — | **`207 µs / tick`** | **`4,836 Hz`** |
|
|
144
|
+
| **ESP32 (`esp32dev`, PlatformIO Arduino)** | **`47,564 B` (`14.5%` of `320 KB`)** | `424,525 B` (`32.4%` Flash) | Real-time UART MAVLink v2 | `50–500 Hz` sensor rate |
|
|
145
|
+
| **Standalone WebAssembly (`wasm32`, `-Oz`)** | **`23,928 B` linear struct** | **`39,756 B` (`38.82 KB`, `0` imports)** | `< 0.35 ms` in browser JS/WASM | Live `60 FPS` webcam |
|
|
146
|
+
|
|
147
|
+
Drop [`arduino/NeckLinkCNS/`](arduino/NeckLinkCNS/) into your Arduino `libraries/` folder (or build with PlatformIO) to run [`ESP32_MAVLink_Survival_Copilot.ino`](arduino/NeckLinkCNS/examples/ESP32_MAVLink_Survival_Copilot/ESP32_MAVLink_Survival_Copilot.ino) directly on an ESP32 / ESP32-S3 companion board.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## BANC v888 specificity & RTX PRO 6000 (`G4`) benchmarks
|
|
152
|
+
|
|
153
|
+
### 1. Adult *Drosophila* `BANC v888` pathway enrichment (`157,789` neurons, `23,553,108` synapses, `1,000` GPU degree-preserving permutations)
|
|
154
|
+
|
|
155
|
+
| Pathway | Empirical Synapses | Null Mean ± SD | Fold Enrichment | Z-Score | Empirical *p* |
|
|
156
|
+
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
157
|
+
| **Optic Looming** (`LPLC2 + LC4 + LPLC1 → Escape/Turn DNs`) | `5,847` | `587.1 ± 186.6` | **`9.96×`** | `+28.19` | `0.000999` |
|
|
158
|
+
| **Central Complex Steering** (`PFL2 + PFL3 → DNa02/DNa01/DNp09`) | `462` | `34.0 ± 62.7` | **`13.60×`** | `+6.82` | `0.004995` |
|
|
159
|
+
| **Mushroom Body 2-Hop** (`ALPN → KC → MBON` paths) | `12,188,283` | `5.25M ± 0.67M` | **`2.32×`** | `+10.40` | `0.000999` |
|
|
160
|
+
| **Cervical Neck Bottleneck** (`1,316 DNs → 805 VNC MNs`) | `143,742` | `12,671.6 ± 2,725.5` | **`11.34×`** | `+48.09` | `0.000999` |
|
|
161
|
+
|
|
162
|
+
### 2. Closed-loop 250-episode multi-hazard survival gauntlet
|
|
163
|
+
|
|
164
|
+
Each episode combines simultaneous `±22°` 3D wave pitch/roll under `65°` magnetic dip, `120°` magnetic spikes, `50×` camera glare bursts, expanding boulders & thin vertical poles, turbulent odor plumes, and mid-mission `88%` T2 thruster degradation:
|
|
165
|
+
|
|
166
|
+
| Controller Stack | Collision-Free & Homed Survival | Collision Rate | Median Home Miss |
|
|
167
|
+
| :--- | :---: | :---: | :---: |
|
|
168
|
+
| **`NeckLink CNS v2.0.0` (Full `BANC v888` Wiring)** | **`100.0%` (`250 / 250`)** | **`0.0%`** | **`1.75 m`** |
|
|
169
|
+
| `NeckLink v1.1.0` (2D compass, `LPLC2`-only, symmetric cord) | `77.2%` | `22.8%` (thin poles + wave tilt) | `6.12 m` |
|
|
170
|
+
| Classical Stack (`Madgwick + Farneback + Pinv`) | `1.2%` | `98.8%` (glare false-brakes + missed poles) | `12.50 m` |
|
|
171
|
+
| Shuffled Connectome Null (permuted `DN` weights) | `0.0%` | `100.0%` | `18.30 m` |
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## When to use NeckLink CNS, and when not to
|
|
176
|
+
|
|
177
|
+
**Good fit:**
|
|
178
|
+
- **An uncrashable `< 24 KB` inner survival & reflex layer** beneath ArduPilot, PX4, or ROS 2 (`Nav2`) on micro-drones, USVs, rovers, and legged robots.
|
|
179
|
+
- **MCU-only platforms (`ESP32`, `ESP32-S3`, `STM32`, `RP2040`)** without a GPU, Linux SBC, or hardware FPU.
|
|
180
|
+
- **Simultaneous real-world sensor/actuator faults**: `100×` glare + thin poles, `±25°` wave tilt + steel-hull magnetic spikes, and mid-flight rotor/blade or thruster loss.
|
|
181
|
+
|
|
182
|
+
**Use something else (or pair with a higher planner) when:**
|
|
183
|
+
- **You need metric 3D mapping, semantic object recognition, or multi-waypoint trajectory planning.** `NeckLink CNS` is an insect brainstem/reflex controller, not a VIO/SLAM or VLM stack.
|
|
184
|
+
- **Obstacles are completely untextured blank walls** with zero spatial contrast edges (pair an inexpensive ToF rangefinder alongside `OBSTACLE_DISTANCE`).
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
Released under the [MIT License](LICENSE). Copyright (c) 2026 Amandeep Singh Bains.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<a href="https://github.com/indian-aman/necklink"><img src="https://raw.githubusercontent.com/indian-aman/necklink/main/assets/logo/necklink-mark.svg" alt="NeckLink" width="52"></a>
|
|
4
|
+
|
|
5
|
+
# NeckLink CNS (`necklink-cns` v2.0.0)
|
|
6
|
+
|
|
7
|
+
**Unified Adult *Drosophila* Whole-CNS Survival Copilot — fusing `NeckSight`, `NeckCompass`, `NeckSense`, and `NeckCord` through empirical `BANC v888` descending arbitration plus wire-valid `MAVLink v2` and `ROS 2` bridges in `23,928 B` (`23.37 KB`) of zero-heap, zero-float ANSI C99 (`38.82 KB` standalone WebAssembly, `14.5%` ESP32 RAM) and Python.**
|
|
8
|
+
|
|
9
|
+
[](https://pypi.org/project/necklink-cns/)
|
|
10
|
+
[](https://pypi.org/project/necklink-cns/)
|
|
11
|
+
[](csrc/)
|
|
12
|
+
[](https://indian-aman.github.io/necklink/#simulator)
|
|
13
|
+
[](arduino/NeckLinkCNS/)
|
|
14
|
+
[](LICENSE)
|
|
15
|
+
|
|
16
|
+
[**Live WASM Demo**](https://indian-aman.github.io/necklink/#simulator) ·
|
|
17
|
+
[**Quick start**](#quick-start) ·
|
|
18
|
+
[**Autopilot bridges**](#mavlink-v2--ros-2-autopilot-bridges) ·
|
|
19
|
+
[**C99, ESP32 & WASM**](#zero-heap-zero-float-ansi-c99-esp32--wasm) ·
|
|
20
|
+
[**BANC v888 & G4 Benchmarks**](#banc-v888-specificity--rtx-pro-6000-g4-benchmarks) ·
|
|
21
|
+
[**When to use it**](#when-to-use-necklink-cns-and-when-not-to)
|
|
22
|
+
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<p align="center">
|
|
26
|
+
<img src="https://raw.githubusercontent.com/indian-aman/necklink/main/assets/necklink_v2_g4_benchmarks.png" alt="NeckLink CNS v2.0.0 RTX PRO 6000 G4 benchmark summary" width="100%">
|
|
27
|
+
</p>
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## What it is
|
|
32
|
+
|
|
33
|
+
In the adult *Drosophila* Brain-and-Nerve-Cord connectome (`BANC v888`: `157,789` proofread neurons, `23,553,108` synapses), perception and navigation across tens of thousands of brain interneurons compress into **`1,316` Descending Neurons (`DNs`)** crossing the cervical neck connective to drive **`805` Ventral Nerve Cord motor neurons (`MNs`)**, paired with **`1,849` Ascending Neurons (`ANs`)** returning proprioceptive efference copy (`AN:DN = 1.405`).
|
|
34
|
+
|
|
35
|
+
`necklink-cns` turns that biological survival architecture into a single closed-loop copilot library:
|
|
36
|
+
|
|
37
|
+
1. **Optic Lobe (`NeckSight v2.0.0`)**: `R1-R6` log photoreceptors with AEC glare step suppression, `T4/T5` Reichardt EMDs (`1.35×` `T5` OFF dominance grounded in `6,586` `T5→LPLC2` vs `3,330` `T4→LPLC2` synapses), 3-DOF `(yaw, pitch, roll)` gyro derotation, `LPLC2` 4-arm radial + `LC4/LPLC1` 2-arm thin-pole/wire looming, and contralateral `DNa02` dodge vectors.
|
|
38
|
+
2. **Central Complex (`NeckCompass v2.0.0`)**: 9-axis 3D tilt-compensated AHRS (`|‖a‖ - g| > 0.25g` wave-slam gate), `16×32` `Q12` online soft-iron self-calibration (`nc_plastic_ring_t`), `EPG ↔ Delta7` ring attractor, `CPU4` path integrator, and `PFL3 + PFL2` (`180°` anti-goal boost) GPS-denied homing.
|
|
39
|
+
3. **Antennal Lobe, Chordotonal Organ & Mushroom Body (`NeckSense v2.0.0`)**: 16-band integer motor/bearing vibration front-end (`ns_vib_fe_t`), turbulent plume whiff-rate tracker, `512`-KC sparse `FlyHash`, 17-bit habituating novelty detector, and 2-compartment `KCγ + KCα/β` zero-forgetting continual memory.
|
|
40
|
+
4. **Cervical Bottleneck & VNC (`NeckCord v2.0.0`)**: Connectome-weighted descending priority hierarchy (`DNp01` Giant-Fiber emergency brake → `MDN` moonwalk reverse → `DNa02` contralateral dodge → `MBON` plume cast → `PFL3/PFL2` homebound cruise), 4-byte CRC-4 descending wire frame, sensorless ESC (`RPM + Current`) broken-blade/weed-foul observer, box-constrained `[u_min, u_max]` 3-DOF USV / `[0, u_max]` 4-DOF hexacopter & octocopter active-set allocator, and 18-servo 3-DOF hexapod IK.
|
|
41
|
+
5. **Autopilot Bridges**: Zero-dependency wire-valid **MAVLink v2** (`0xFD` magic, X.25 CRC-16 + `CRC_EXTRA`) C99 & Python codec (`HEARTBEAT`, `SCALED_IMU`, `MANUAL_CONTROL`, `STATUSTEXT`, `OBSTACLE_DISTANCE`) for ArduPilot / PX4 companion links, plus a **ROS 2 (`rclpy`)** node adapter.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install --upgrade necklink-cns
|
|
49
|
+
necklink-cns --version
|
|
50
|
+
necklink-cns demo
|
|
51
|
+
necklink-cns priors
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import numpy as np
|
|
56
|
+
from necklink_cns import NeckLinkCNS
|
|
57
|
+
|
|
58
|
+
cns = NeckLinkCNS(vehicle="hexacopter", grid_size=32)
|
|
59
|
+
frame = np.full((32, 32), 160.0)
|
|
60
|
+
out = cns.step(
|
|
61
|
+
dt=0.02,
|
|
62
|
+
speed_m_s=1.2,
|
|
63
|
+
gyro_xyz=np.array([0.02, -0.01, 0.05]),
|
|
64
|
+
accel_xyz=np.array([0.3, -0.2, 9.80]),
|
|
65
|
+
mag_xyz=np.array([0.45, 0.05, 0.89]),
|
|
66
|
+
camera_frame=frame,
|
|
67
|
+
)
|
|
68
|
+
assert len(out.cervical_wire_4b) == 4
|
|
69
|
+
assert out.mavlink_manual_pkt[0] == 0xFD
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## MAVLink v2 & ROS 2 autopilot bridges
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import numpy as np
|
|
78
|
+
from necklink_cns import (
|
|
79
|
+
MavLink2Codec,
|
|
80
|
+
MavManualControl,
|
|
81
|
+
NeckLinkCNS,
|
|
82
|
+
NeckLinkROS2Adapter,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# 1. Wire-valid MAVLink v2 framing (ArduPilot / PX4 companion UART or UDP)
|
|
86
|
+
codec = MavLink2Codec(sysid=42, compid=191)
|
|
87
|
+
pkt = codec.encode_manual_control(MavManualControl(x=650, y=0, z=500, r=-250, buttons=0, target=1))
|
|
88
|
+
decoded = codec.feed(pkt)
|
|
89
|
+
assert len(decoded) == 1 and MavManualControl.from_payload(decoded[0].payload).r == -250
|
|
90
|
+
|
|
91
|
+
# 2. ROS 2 (rclpy-ready) sensor/cmd_vel adapter
|
|
92
|
+
adapter = NeckLinkROS2Adapter(NeckLinkCNS(vehicle="usv_t200", grid_size=32))
|
|
93
|
+
adapter.on_imu(np.array([0.01, -0.02, 0.05]), np.array([0.2, -0.1, 9.8]))
|
|
94
|
+
adapter.on_mag(np.array([0.45, 0.05, 0.88]))
|
|
95
|
+
step_out = adapter.step_camera(np.full((32, 32), 140.0), speed_m_s=1.0, dt=0.02)
|
|
96
|
+
assert isinstance(step_out["twist"].angular_z, float)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Zero-heap, zero-float ANSI C99, ESP32 & WASM
|
|
102
|
+
|
|
103
|
+
[`csrc/necklink_cns.h`](csrc/necklink_cns.h), [`csrc/necklink_cns.c`](csrc/necklink_cns.c), and [`csrc/nl_mavlink.h`](csrc/nl_mavlink.h) fuse all four organs and the MAVLink v2 codec into a single static C struct (`nl_cns_t`) with **zero `float`/`double` instructions** and **zero `malloc`/`free`**:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
make -C csrc clean all test sanitize # 6/6 C99 Whole-CNS self-tests under -Werror + ASan/UBSan
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| Target / Artifact | Static RAM (`sizeof`) | Flash / Binary | Full-CNS Tick Latency | Throughput |
|
|
110
|
+
| :--- | :---: | :---: | :---: | :---: |
|
|
111
|
+
| **ANSI C99 `nl_cns_t` (x86-64 `-O2`)** | **`23,928 B` (`23.37 KB`)** | — | **`207 µs / tick`** | **`4,836 Hz`** |
|
|
112
|
+
| **ESP32 (`esp32dev`, PlatformIO Arduino)** | **`47,564 B` (`14.5%` of `320 KB`)** | `424,525 B` (`32.4%` Flash) | Real-time UART MAVLink v2 | `50–500 Hz` sensor rate |
|
|
113
|
+
| **Standalone WebAssembly (`wasm32`, `-Oz`)** | **`23,928 B` linear struct** | **`39,756 B` (`38.82 KB`, `0` imports)** | `< 0.35 ms` in browser JS/WASM | Live `60 FPS` webcam |
|
|
114
|
+
|
|
115
|
+
Drop [`arduino/NeckLinkCNS/`](arduino/NeckLinkCNS/) into your Arduino `libraries/` folder (or build with PlatformIO) to run [`ESP32_MAVLink_Survival_Copilot.ino`](arduino/NeckLinkCNS/examples/ESP32_MAVLink_Survival_Copilot/ESP32_MAVLink_Survival_Copilot.ino) directly on an ESP32 / ESP32-S3 companion board.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## BANC v888 specificity & RTX PRO 6000 (`G4`) benchmarks
|
|
120
|
+
|
|
121
|
+
### 1. Adult *Drosophila* `BANC v888` pathway enrichment (`157,789` neurons, `23,553,108` synapses, `1,000` GPU degree-preserving permutations)
|
|
122
|
+
|
|
123
|
+
| Pathway | Empirical Synapses | Null Mean ± SD | Fold Enrichment | Z-Score | Empirical *p* |
|
|
124
|
+
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
125
|
+
| **Optic Looming** (`LPLC2 + LC4 + LPLC1 → Escape/Turn DNs`) | `5,847` | `587.1 ± 186.6` | **`9.96×`** | `+28.19` | `0.000999` |
|
|
126
|
+
| **Central Complex Steering** (`PFL2 + PFL3 → DNa02/DNa01/DNp09`) | `462` | `34.0 ± 62.7` | **`13.60×`** | `+6.82` | `0.004995` |
|
|
127
|
+
| **Mushroom Body 2-Hop** (`ALPN → KC → MBON` paths) | `12,188,283` | `5.25M ± 0.67M` | **`2.32×`** | `+10.40` | `0.000999` |
|
|
128
|
+
| **Cervical Neck Bottleneck** (`1,316 DNs → 805 VNC MNs`) | `143,742` | `12,671.6 ± 2,725.5` | **`11.34×`** | `+48.09` | `0.000999` |
|
|
129
|
+
|
|
130
|
+
### 2. Closed-loop 250-episode multi-hazard survival gauntlet
|
|
131
|
+
|
|
132
|
+
Each episode combines simultaneous `±22°` 3D wave pitch/roll under `65°` magnetic dip, `120°` magnetic spikes, `50×` camera glare bursts, expanding boulders & thin vertical poles, turbulent odor plumes, and mid-mission `88%` T2 thruster degradation:
|
|
133
|
+
|
|
134
|
+
| Controller Stack | Collision-Free & Homed Survival | Collision Rate | Median Home Miss |
|
|
135
|
+
| :--- | :---: | :---: | :---: |
|
|
136
|
+
| **`NeckLink CNS v2.0.0` (Full `BANC v888` Wiring)** | **`100.0%` (`250 / 250`)** | **`0.0%`** | **`1.75 m`** |
|
|
137
|
+
| `NeckLink v1.1.0` (2D compass, `LPLC2`-only, symmetric cord) | `77.2%` | `22.8%` (thin poles + wave tilt) | `6.12 m` |
|
|
138
|
+
| Classical Stack (`Madgwick + Farneback + Pinv`) | `1.2%` | `98.8%` (glare false-brakes + missed poles) | `12.50 m` |
|
|
139
|
+
| Shuffled Connectome Null (permuted `DN` weights) | `0.0%` | `100.0%` | `18.30 m` |
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## When to use NeckLink CNS, and when not to
|
|
144
|
+
|
|
145
|
+
**Good fit:**
|
|
146
|
+
- **An uncrashable `< 24 KB` inner survival & reflex layer** beneath ArduPilot, PX4, or ROS 2 (`Nav2`) on micro-drones, USVs, rovers, and legged robots.
|
|
147
|
+
- **MCU-only platforms (`ESP32`, `ESP32-S3`, `STM32`, `RP2040`)** without a GPU, Linux SBC, or hardware FPU.
|
|
148
|
+
- **Simultaneous real-world sensor/actuator faults**: `100×` glare + thin poles, `±25°` wave tilt + steel-hull magnetic spikes, and mid-flight rotor/blade or thruster loss.
|
|
149
|
+
|
|
150
|
+
**Use something else (or pair with a higher planner) when:**
|
|
151
|
+
- **You need metric 3D mapping, semantic object recognition, or multi-waypoint trajectory planning.** `NeckLink CNS` is an insect brainstem/reflex controller, not a VIO/SLAM or VLM stack.
|
|
152
|
+
- **Obstacles are completely untextured blank walls** with zero spatial contrast edges (pair an inexpensive ToF rangefinder alongside `OBSTACLE_DISTANCE`).
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
Released under the [MIT License](LICENSE). Copyright (c) 2026 Amandeep Singh Bains.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ESP32_MAVLink_Survival_Copilot.ino - NeckLink CNS v2.0.0 Autopilot Co-Processor
|
|
3
|
+
*
|
|
4
|
+
* Runs the ENTIRE Adult Drosophila 4-Organ Whole-CNS Autonomy Stack + MAVLink v2
|
|
5
|
+
* inside ~23.8 KB of static ESP32 SRAM (zero dynamic heap, zero floating-point):
|
|
6
|
+
* - Subscribes to ArduPilot / PX4 MAVLink v2 SCALED_IMU (#26) over Serial1
|
|
7
|
+
* - Runs 9-DOF tilt-compensated Central Complex ring compass + CPU4 homing
|
|
8
|
+
* - Runs 32x32 Optic Lobe LPLC2 + LC4 thin-pole/wire collision dodge
|
|
9
|
+
* - Runs Chordotonal vibration & Mushroom Body chemical plume intelligence
|
|
10
|
+
* - Runs Sensorless ESC broken-blade observer + 4-DOF active-set motor cord
|
|
11
|
+
* - Emits wire-valid MAVLink v2 HEARTBEAT (#0), MANUAL_CONTROL (#69), and
|
|
12
|
+
* STATUSTEXT (#253) alerts back to the flight/boat controller at 50 Hz.
|
|
13
|
+
*
|
|
14
|
+
* Copyright (c) 2026 Amandeep Singh Bains. SPDX-License-Identifier: MIT
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include <Arduino.h>
|
|
18
|
+
#include <NeckLinkCNS.h>
|
|
19
|
+
|
|
20
|
+
static nl_cns_t g_cns;
|
|
21
|
+
static nl_cns_inputs_t g_in;
|
|
22
|
+
static nl_cns_outputs_t g_out;
|
|
23
|
+
static uint8_t g_cam_32x32[NECKSIGHT_PIXELS];
|
|
24
|
+
static uint8_t g_mav_tx[NL_MAV2_MAX_FRAME_LEN];
|
|
25
|
+
static uint32_t g_last_hb_ms = 0u;
|
|
26
|
+
|
|
27
|
+
void setup(void)
|
|
28
|
+
{
|
|
29
|
+
Serial.begin(115200);
|
|
30
|
+
nl_cns_init(&g_cns, NL_VEHICLE_USV_T200);
|
|
31
|
+
|
|
32
|
+
Serial.print(F("[NeckLinkCNS v2.0.0] Whole-CNS static SRAM = "));
|
|
33
|
+
Serial.print((unsigned long)sizeof(nl_cns_t));
|
|
34
|
+
Serial.println(F(" bytes"));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void loop(void)
|
|
38
|
+
{
|
|
39
|
+
uint32_t now_ms = millis();
|
|
40
|
+
uint16_t i;
|
|
41
|
+
size_t pkt_len;
|
|
42
|
+
|
|
43
|
+
/* Synthesize a 32x32 camera test pattern (replace with OV2640 fb->buf on ESP32-CAM) */
|
|
44
|
+
for (i = 0u; i < NECKSIGHT_PIXELS; ++i) {
|
|
45
|
+
g_cam_32x32[i] = (uint8_t)(((i * 17u) + g_cns.tick_count) & 0xFFu);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
memset(&g_in, 0, sizeof(g_in));
|
|
49
|
+
g_in.dt_ms = 20u;
|
|
50
|
+
g_in.speed_mm_s = 1200;
|
|
51
|
+
g_in.cruise_drive_q15 = 20000;
|
|
52
|
+
g_in.return_home = 1u;
|
|
53
|
+
g_in.az_mg = 1000;
|
|
54
|
+
g_in.mx = 450;
|
|
55
|
+
g_in.my = 0;
|
|
56
|
+
g_in.mz = 850;
|
|
57
|
+
g_in.mag_valid = 1u;
|
|
58
|
+
g_in.cam_32x32 = g_cam_32x32;
|
|
59
|
+
|
|
60
|
+
(void)nl_cns_step(&g_cns, &g_in, &g_out);
|
|
61
|
+
|
|
62
|
+
pkt_len = nl_cns_encode_mavlink_control(&g_cns, &g_out, 1u, g_mav_tx);
|
|
63
|
+
if (pkt_len > 0u && (g_cns.tick_count % 50u) == 0u) {
|
|
64
|
+
Serial.print(F("Mode="));
|
|
65
|
+
Serial.print((unsigned)g_out.behavior_mode);
|
|
66
|
+
Serial.print(F(" Hdg_cdeg="));
|
|
67
|
+
Serial.print((long)g_out.heading_cdeg);
|
|
68
|
+
Serial.print(F(" MAVLink_B="));
|
|
69
|
+
Serial.println((unsigned long)pkt_len);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if ((now_ms - g_last_hb_ms) >= 1000u) {
|
|
73
|
+
nl_mav_heartbeat_t hb;
|
|
74
|
+
g_last_hb_ms = now_ms;
|
|
75
|
+
memset(&hb, 0, sizeof(hb));
|
|
76
|
+
hb.type = NL_MAV_TYPE_ONBOARD_CONTROLLER;
|
|
77
|
+
hb.autopilot = NL_MAV_AUTOPILOT_INVALID;
|
|
78
|
+
hb.system_status = NL_MAV_STATE_ACTIVE;
|
|
79
|
+
hb.mavlink_version = 3u;
|
|
80
|
+
(void)nl_mav_encode_heartbeat(g_cns.mav_sysid, g_cns.mav_compid, &g_cns.mav_tx_seq, &hb, g_mav_tx);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
delay(20);
|
|
84
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
name=NeckLinkCNS
|
|
2
|
+
version=2.0.0
|
|
3
|
+
author=Amandeep Singh Bains
|
|
4
|
+
maintainer=Amandeep Singh Bains
|
|
5
|
+
sentence=Unified Adult Drosophila Whole-CNS Edge Autonomy & MAVLink v2 Copilot (<24 KB Zero-Float C99)
|
|
6
|
+
paragraph=Combines NeckSight (Optic Lobe LPLC2+LC4), NeckCompass (9-DOF Tilt+Plastic Ring+CPU4), NeckSense (Chordotonal Vibration & KCg/KCab Mushroom Body), NeckCord (ESC Fault Observer, 4-DOF Active-Set & 18-DOF Leg IK), and MAVLink v2.
|
|
7
|
+
category=Signal Input/Output
|
|
8
|
+
url=https://github.com/indian-aman/necklink
|
|
9
|
+
architectures=*
|
|
10
|
+
includes=NeckLinkCNS.h
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* NeckLinkCNS.h - Arduino / PlatformIO Umbrella Header (<24 KB Zero-Float Whole-CNS) */
|
|
2
|
+
#ifndef NECKLINK_CNS_ARDUINO_H
|
|
3
|
+
#define NECKLINK_CNS_ARDUINO_H
|
|
4
|
+
#ifndef NL_CNS_TINY
|
|
5
|
+
#define NL_CNS_TINY 1
|
|
6
|
+
#endif
|
|
7
|
+
#ifndef NS_TINY
|
|
8
|
+
#define NS_TINY 1
|
|
9
|
+
#endif
|
|
10
|
+
#include "csrc/necklink_cns.h"
|
|
11
|
+
#endif /* NECKLINK_CNS_ARDUINO_H */
|