Создаем нейросеть для роутера на Shell-скриптах: тернарная логика, стигмергия и MIPS без FPU

Вместо введения: почему нейросеть на роутере? Представьте ситуацию: вечер пятницы, вы садитесь смотреть фильм в 4K, а Wi-Fi начинает тормозить. Соседи тоже включили телевизоры, микроволновки, смартфоны — эфир забит. Роутер продолжает работать на максимальной мощности, создавая еще больше помех.

А что, если роутер сам понимал бы, что творится в эфире, и адаптировался? Что, если бы у него была нейросеть?

Звучит как научная фантастика для домашнего роутера за 1500 рублей? А вот и нет.

В этой статье я расскажу, как я создал TriTon — тернарную каскадную нейросеть для управления WiFi-роутером. Она работает на архитектуре MIPS без FPU, написана на чистом Shell (Busybox/ash) и не требует ничего, кроме стандартных утилит OpenWrt. Никакого Python, никакого TensorFlow, никакого облака.

Спойлер: она реально работает и умеет самообучаться.

Почему обычные роутеры тупят (и при чем здесь нейросети) Большинство роутеров используют примитивные алгоритмы выбора канала:

При старте сканируют эфир, выбирают наименее загруженный канал И всё. Дальше они сидят на этом канале годами, даже если вокруг всё изменилось.

Более дорогие решения (Cisco, Ubiquiti, TP-Link Omada) отправляют данные в облако, где крутые AI-алгоритмы анализируют обстановку и отдают команды роутерам. Это работает, но:

  1. Требует постоянного интернета
  2. Данные уходят на чужой сервер
  3. Есть задержка
  4. Нужна подписка

Я пошел другим путем — нейросеть прямо на роутере, без облака, без FPU, на голом Shell.

Как устроена нейросеть в голове (и почему это важно) В классическом понимании нейросеть — это матричные умножения, градиентный спуск и обратное распространение ошибки. Но мозг устроен иначе.

Биологический нейрон: Получает входные сигналы

Если сумма превысила порог — возбуждается (1) Если нет — тормозится (0) Иногда бывает состояние покоя или неопределенности

Никаких градиентов. Никаких производных. Простые пороги. Почему бы не сделать так же в коде? Так родилась идея тернарной логики — три состояния:

-1 — хорошо (зеленая зона)

0 — средне (желтая зона, требует проверки)

+1 — плохо (красная зона)

Три параметра WiFi, которые легко измерить: RSSI — уровень сигнала (dBm) Retry ratio — процент повторных передач Expected throughput — полезная скорость (Mbps)

Тернарная логика: превращаем числа в решения

Первый шаг — перевести непрерывные величины в три четких состояния.

Эти пороги я подобрал экспериментально на реальном роутере (MIPS MT7628). Они могут быть другими в вашей среде — и это нормально. 
Эти пороги я подобрал экспериментально на реальном роутере (MIPS MT7628). Они могут быть другими в вашей среде — и это нормально. 

Как это работает в коде:

evaluate_state() { local retry=$1 local throughput=$2 local rssi=$3 local score=0 # Оценка retry if [ $retry -lt 5 ]; then score=$((score - 1)) elif [ $retry -gt 15 ]; then score=$((score + 1)) fi # Оценка throughput if [ $throughput -gt 50 ]; then score=$((score - 1)) elif [ $throughput -lt 20 ]; then score=$((score + 1)) fi # Оценка RSSI if [ $rssi -gt -60 ]; then score=$((score - 1)) elif [ $rssi -lt -75 ]; then score=$((score + 1)) fi # Тернарный выход if [ $score -lt 0 ]; then echo "-1" # Хорошо elif [ $score -gt 0 ]; then echo "1" # Плохо else echo "0" # Средне fi }

Внимание: в Busybox нет поддержки <<< (here-string), поэтому конструкцию read var <<< $(command) пришлось заменить на:

stats=$(get_client_stats) MAX_RETRY=$(echo "$stats" | awk '{print $1}') MIN_THROUGHPUT=$(echo "$stats" | awk '{print $2}') MIN_RSSI=$(echo "$stats" | awk '{print $3}')

Каскадная архитектура: сначала канал, потом мощность

Классическая нейросеть — это один большой слой, который сразу выдает ответ. Я пошел другим путем — каскады.

Каскад 1: Выбор канала

Сначала роутер сканирует эфир командой iw dev wlan0 scan. Парсинг вывода — отдельный квест (честно говоря, самый муторный). Из каждого BSS (соседней точки доступа) извлекаем:

  • primary channel — на каком канале сидит
  • signal — уровень сигнала (RSSI)
  • station count — сколько клиентов подключено
  • channel utilisation — насколько загружен канал (0-255)
  • secondary channel offset — использует ли сосед ширину 40 МГц (HT40)

Каждый сосед получает тернарный вес по громкости:

  • RSSI > -60 dBm → +1 (громкий, сильно мешает)
  • RSSI < -75 dBm → -1 (тихий, почти не мешает)
  • Иначе → 0 (средний)

Для каждого канала суммируем:
Количество AP Суммарное количество клиентов Суммарную загруженность (utilisation) Суммарный вес соседей (RSSI-сумму)
Итоговая оценка канала:

  • -1 — чистый (можно заходить)
  • 0 — средний (терпимо)
  • +1 — грязный (лучше не заходить)

Код парсинга (упрощенно):

parse_scan() { awk ' BEGIN { RS="BSS "; FS="\n"; RSSI_GOOD=-60; RSSI_BAD=-75 } { if (NR==1) next; channel = 0; rssi_weight = 0; # ... парсинг полей ... if (channel >= 1 && channel <= 11) { bss_count[channel]++; rssi_sum[channel] += rssi_weight; } } END { for(c=1; c<=11; c++) { state = -1; if (bss_count[c] >= 3 || rssi_sum[c] >= 2) state = 0; if (bss_count[c] >= 5 || rssi_sum[c] >= 4) state = 1; printf("ch%d=%d\n", c, state); } }' "$scan_file" > "$stats_file" }

Каскад 2: Подбор мощности

После выбора канала нейросеть начинает подбирать оптимальную мощность передатчика:

  • Ставим минимальную мощность (5 dBm)
  • Ждем 30 секунд (клиенты должны стабилизироваться)
  • Измеряем RSSI, retry ratio, throughput клиентов
  • Если все три параметра в зеленой зоне (-1) — останавливаемся, мощность оптимальна
  • Если есть проблемы — повышаем мощность на 5 dBm
  • Повторяем до 20 dBm
CURRENT_POWER=$MIN_POWER while [ $CURRENT_POWER -le $MAX_POWER ]; do uci set wireless.radio0.txpower="$CURRENT_POWER" uci commit wireless wifi reload sleep $SETTLE_TIME stats=$(get_client_stats) MAX_RETRY=$(echo "$stats" | awk '{print $1}') MIN_THROUGHPUT=$(echo "$stats" | awk '{print $2}') MIN_RSSI=$(echo "$stats" | awk '{print $3}') if [ $MAX_RETRY -lt 15 ] && [ $MIN_THROUGHPUT -gt 20 ] && [ $MIN_RSSI -gt -75 ]; then echo "✅ Оптимальная мощность найдена: ${CURRENT_POWER} dBm" break fi CURRENT_POWER=$((CURRENT_POWER + 5)) done

Обучение через подкрепление (без градиентного спуска)

Нейросеть не просто действует по правилам — она учится на своих ошибках. Основной цикл мониторинга каждые 10 секунд проверяет качество связи:

while true; do sleep 10 stats=$(get_client_stats) MAX_RETRY=$(echo "$stats" | awk '{print $1}') MIN_THROUGHPUT=$(echo "$stats" | awk '{print $2}') MIN_RSSI=$(echo "$stats" | awk '{print $3}') STATE=$(evaluate_state $MAX_RETRY $MIN_THROUGHPUT $MIN_RSSI) if [ "$STATE" = "1" ]; then if [ $CURRENT_POWER -lt 20 ]; then # Повышаем мощность CURRENT_POWER=$((CURRENT_POWER + 5)) uci set wireless.radio0.txpower="$CURRENT_POWER" wifi reload # Ждем и проверяем, помогло ли sleep 30 stats=$(get_client_stats) NEW_RETRY=$(echo "$stats" | awk '{print $1}') if [ $NEW_RETRY -lt $MAX_RETRY ]; then # Успех — поощряем действие sed -i 's/power_increase=0/power_increase=1/' $WEIGHTS_FILE else # Неудача — наказываем sed -i 's/power_increase=0/power_increase=-1/' $WEIGHTS_FILE fi fi fi done

Веса хранятся в /tmp/weights.conf и влияют на будущие решения. Чем больше успешных повышений мощности, тем увереннее нейросеть будет повышать мощность в похожих ситуациях.

Стигмергия: как несколько роутеров договариваются без общения

Это самая интересная часть. В классических системах роутеры общаются через центральный контроллер или LAN. TriTon использует косвенную координацию через изменения в эфире.

Принцип такой же, как у муравьев с феромонными следами:

  1. Роутер А выбирает канал 6 и начинает работать
  2. Роутер Б сканирует эфир и видит: на канале 6 появилась новая AP с сильным сигналом
  3. Оценка канала 6 ухудшается (из -1 превращается в 0 или +1)
  4. Роутер Б выбирает другой свободный канал (1 или 11)
  5. Роутер В сканирует и видит занятость каналов 6 и 1 → выбирает оставшийся (11)

Результат: три роутера автоматически разъехались по непересекающимся каналам без единого отправленного пакета между ними. Это и есть эмерджентность — глобальная самоорганизация из простых локальных правил. Никакого центрального контроллера, никакой настройки, никакого LAN между роутерами.

Что получилось в итоге: реальные тесты Я протестировал нейросеть на роутере с MIPS MT7628 в жилом доме ( 40+ соседних AP в эфире).

Подбор оптимальной мощности на канале 9

Вывод: нейросеть нашла оптимальную мощность 10 dBm — минимальную, при которой качество остается высоким.
Смена канала при ухудшении При retry &gt; 15% нейросеть повышала мощность до 20 dBm. Если не помогало — сканировала эфир и переключалась на канал с состо
Вывод: нейросеть нашла оптимальную мощность 10 dBm — минимальную, при которой качество остается высоким. Смена канала при ухудшении При retry > 15% нейросеть повышала мощность до 20 dBm. Если не помогало — сканировала эфир и переключалась на канал с состо

Лог реальной работы:

2026-05-14 07:10:00 | MONITOR | power=10dBm | ch=9 | retry=18% | thr=28Mbps

2026-05-14 07:10:10 | POWER_UP | power=15dBm | ch=9 | retry=18% | thr=28Mbps

2026-05-14 07:11:10 | MONITOR | power=15dBm | ch=9 | retry=9% | thr=44Mbps

Повышение мощности помогло.

Стигмергия в действии

Два роутера с TriTon в одной комнате:

  1. Оба стартовали на канале 6
  2. Через 2 минуты один переключился на канал 1
  3. Еще через минуту второй переключился на канал 11
  4. Разъехались по непересекающимся каналам

Без единого LAN-кабеля между ними!

Технические тонкости: Busybox и MIPS

Самое сложное было заставить все работать на Busybox ash (не bash) и MIPS

  • Нет <<< (here-string) — заменили на stats=stats" | awk
  • Нет массивов в ash — используем eval для динамических переменных
  • Нет дробных чисел в сравнениях — преобразуем float в int через cut -d. -f1
  • awk в Busybox не поддерживает захват групп в match() — парсим через grep и sed

Пример костыля для throughput:

# В awk не работает match(... arr) # Пришлось через grep + sed throughput=$(echo "$client_data" | grep "expected throughput:" | sed 's/[^0-9.]//g')

Что дальше: планы и идеи

  1. Портирование на C — для встраивания в прошивку OpenWrt как стандартный пакет
  2. Интеграция с ubus — вместо парсинга iw через JSON API
  3. Web-интерфейс — чтобы настраивать пороги и смотреть графики
  4. Поддержка 5 GHz — сейчас только 2.4 GHz
  5. Адаптивные пороги — чтобы нейросеть сама подбирала пороги под свою среду

Заключение: зачем все это

TriTon — это не просто скрипт для роутера. Это:

  1. Доказательство концепции — нейросеть может работать без градиентного спуска и float
  2. Готовая система — работает на реальном железе, протестирована в бою
  3. Исследовательский проект — сочетает тернарную логику, каскадность, обучение через подкрепление и стигмергию
  4. Вклад в сообщество — OpenWrt получает инструмент, которого там не было

Нейросеть, которая работает на роутере за ~2000 рублей, без облака, без интернета, без FPU. На Shell-скриптах.

Хотите попробовать?

Скрипт доступен на GitHub: https://github.com/den4ik86/TriTon

Для установки достаточно:

wget https://github.com/den4ik86/TriTon/Triton.sh chmod +x Triton.sh ./Triton.sh

Вопросы, идеи, пулл-реквесты приветствуются! Спасибо за внимание!

33