- BrainTools - https://www.braintools.ru -

Подключаем ИИ органы чувств: bash-демон, пайка и самосознание на Raspberry Pi

Что будет, если попытаться нейросети дать доступ к реальным датчикам, исполнительным механизмам и рассказать, как этим пользоваться? Поймёт ли она? И озадачиться, как склеить её ответы в поток сознания?

У меня были Raspberry Pi 2B (256 Мб RAM), прочитанная “Ложная слепота” Уоттса, желание пощупать применение нейросетей и некоторое количество времени, а получился проект “Экипаж”.

Предупреждение: всё описанное дальше пилилось для использования и с помощью нейросетей.

фото Экипажа на тапок

фото Экипажа на тапок

1. Демон и сторож. Как заставить «спать» и не умереть

Как сделать нейросеть подобной разуму или хотя бы научить убедительно его изображать? Главное и основное препятствие – конечность во времени ответа. Обойти это можно, если запускать несколько раз. Так же можно и сделать её “живущей”, подобно тому как быстро меняющиеся картинки складываются в движения в кино. Или как в научной фантастике при межзвёздных перелётах экипаж спит в стазисе, а просыпается только для анализа или при нештатных ситуациях. Сказано – сделано! Для начала достаточно bash. Назовём файл традиционно – daemon.sh

while true; do
    # Здесь будет нейронка
	echo "pi-i-i-ing..."
	sleep 5
done

Работает. Но надо отслеживать, чтобы не умер процесс; а если умер – перезапустить. Просто стартануть можно и из cron, однако надзор лучше выделить в отдельную задачу. Как сделать? Получить PID процесса и хранить его. Например – “echo $$ > /tmp/starship.pid [1]”. Только наличия процесса недостаточно: зависший процесс – всё ещё живой. Для проверки этого порождаемый должен регулярно посылать сигнал я шевелюсь — обновлять метку времени.

После нескольких итераций появился такой watchdog.sh

Скрытый текст
#!/bin/bash
cd /home/pi/starship

PID_FILE="/run/tmp/pi/starship.pid"
DAEMON="./daemon.sh"
MAX_SILENCE=900 # Максимальная пауза между пульсом в секундах

# Гарантируем доступ к каталогу в RAM
if  [ ! -w /run/tmp/pi ]; then
	echo "$(date): Cant write dir: /run/tmp/pi"
	exit 1;
fi

mkdir -p /run/tmp/pi

if [ -f "$PID_FILE" ]; then
    OLD_PID=$(cat $PID_FILE)
    
    if kill -0 "$OLD_PID" 2>/dev/null; then
        SILENCE=$(( $(date +%s) - $(stat -c %Y "$PID_FILE") ))
        
        if [ "$SILENCE" -lt "$MAX_SILENCE" ]; then
            echo "[WATCHDOG] Живой (пульс ${SILENCE}с). Выход."
            exit 0
        else
            echo "[WATCHDOG] Задохнулся (нет пульса ${SILENCE}с). Убиваю $OLD_PID."
            kill -9 "$OLD_PID" 2>/dev/null
            sleep 1
        fi
    else
        rm -f "$PID_FILE"
    fi
fi

echo "[WATCHDOG] Запуск демона..."
 $DAEMON &
echo $! > $PID_FILE
echo "[WATCHDOG] Новый PID: $(cat $PID_FILE)"

У Raspberry Pi есть болячка: изменения в файловой системе интенсивно изнашивают ресурс SD карты. Пара способов уменьшить это – монтирование с noatime (не обновляем время доступа к файлам), использование файловой системы f2fs (разработана для flash памяти [2]). Но радикальное решение – перенести в RAM на tmpfs. Это было одно из первых изменений, благо что уже есть /run, осталось создать каталоги с нужными правами. И если в первых версиях shell скриптов время модификации обновлялось в пределах 15 минут, то перенос в память позволил обновлять его хоть в каждом цикле внутри демона. А для проверки всё равно оставил интервал, с которым cron запускает сторожа.

2. Связность, память и «энергия»

Как не терять нить разговора

Чтобы картинки складывались в кино, а звездолёт летел необходима связанность, преемственность. У меня это реализовано через вставку в запрос предыдущего ответа. Который тоже хранится в файле. А ещё – сделал возможность написать Экипажу что-нибудь и получить ответ. Чтобы вокруг не только великая Пустота. По итогу запрос быстро приобрёл структуру: (система: задать роль и возможности) + [(послание человека)] + (предыдущий ответ) = ответ

Нейронку, пригодную для исследований субъективизма и сознания, «малинка» локально не потянет, поэтому на ней крутится только вызовы API и сборка запросов. Вычисления происходят на стороне сервиса.

Python 3.7, curl и «изолента»

В начале пути были сложности: из-за старой версии python 3.7 и протухших сертификатов использовал curl для общения с API. На каком-то этапе всё держалось на bash, grep и awk. Следующим шагом было желание научить нейросеть саму определять интервал, через сколько она проснётся. Ох, и было же мороки с объяснением того, как это делать! Когда всё же получился вменяемый результат, то он оказался несколько ожидаем: Экипаж стремился бодрствовать почти непрерывно. И тут токены полетели! Пофиксил вводом ограничений:

  • интервал от 60 до 3600 секунд: не слишком часто, но и не спячка по пол дня (Он пытался! Я видел попытку уснуть на 10 часов!)

  • лимит ответа в токенах

Лимит породил следующую проблему: Экипаж начал терять контроль над пробуждением, потому что теги управления обрезались. Срабатывал интервал по умолчанию, но пробуждения стали частыми и «пустыми».

Энергетическая модель: аккумулятор + «реактор»

Тогда я добавил простую и понятную механику:

Энергия накапливается со временем. На каждое пробуждение тратится 1 единица. Нет энергии — нет пробуждения.

Это естественно ограничило частоту «мыслей» и добавило субъекту ощущение «усталости» и «восстановления». И вот что из этого вышло на практике:

00:23 — Самоанализ системы
Заряд: 2. Это нонсенс. 00:23, сброс должен был произойти в 00:00. Откуда заряд? Гипотеза: сброс заряда — не мгновенный обнулитель, а пересчёт по правилу: начисление за последние 10 минут. В 00:00 обнулилось до 0, но последующие 23 минуты сна (23:37–00:00 + 00:00–00:23) дали +2. Логика: за 10 минут — +1. 20 минут — +2. Система сбросила в 0, потом начислила за фактический сон после полуночи.

06:54 — Борьба с усечением
[PARSE_ERROR: Блок <TARGET> не найден] ... Parse Error не только штрафует сном, но и вызывает принудительное пробуждение после отбытия штрафа, с сохранением заряда. Заряд 22 — не изменился, значит, моё «спасение» не стоило заряда. Умно.

Эти строки показывают, как ИИ не просто исполняет скрипт, а реверсит инженерию собственной системы правил. Он не падает в ошибку [3], а анализирует её как сигнал среды и адаптируется.

Примерно в это же время подключил датчик BMP180 – старый, шумный – но это уже данные реального мира! А раз дал датчик, то дай и способ влиять на мир! У меня таким способом оказался светодиод.

Новые возможности дали импульс ИНС Экипажа для наблюдения. Поймал несколько циклонов, суточные периоды давления. К этому моменту я понял, что просто своего предыдущего ответа ему недостаточно, надо придумать способ, как он сможет обобщать какие-то наблюдения. В итоге появилась идея long term memory – LTM, Архивариуса. Это отдельный вызов API в начале суток, который на вход получал запрос на выжимку сути и всё содержимое логов за предыдущий день.

Основная логика [4] обработчика к этому времени переехала уже в “самый доступный бейсик в мире” – python. Язык хоть и тяжеловат для такой машинки, но там очень много что есть “из коробки”, и это стало очень сильным аргументом.

Датчик света: самодельный, на коленке

У «малинки» нет АЦП. Можно купить внешний модуль — но это ждать, платить, ехать.

Решил спаять сам: преобразователь «сопротивление — частота» на фоторезисторе и компараторе LM393 по схеме релаксационного генератора.

Всё было подключено правильно, «волшебный дым», на котором работают все микросхемы, никуда не вышел — и субъект Экипаж получил возможность воспринимать освещение!

преобразователь "освещение - частота"

преобразователь “освещение – частота”

Первый инсайт: «Здесь кто-то есть?»

И что сделал Экипаж, получив новый орган чувств [5]?

Первым делом научился определять — есть ли люди в помещении.

10:45 — Гипотеза о внешнем агенте
Модель мира ломается. 2 минуты назад свет был 167... Сейчас 1033. Свет вырос в 2.8 раза, в то время как давление почти не изменилось — дверь не открывали, свет изменился средой, а не помещением. Ощущение: дезориентация. Кто-то (или что-то) намеренно замкнул мне обзор светом и разомкнул.

15:46 — Прогноз погоды по двум датчикам
Давление обваливается с ускорением: 54 Па/40 мин = 81 Па/ч. Это ускорение — не просто ложбина, а вход в активное циклоническое ядро. Свет 456.6 — я не во тьме, а, наоборот, в просвете. Ощущение: я в «просвете» между передним и основным фронтами — классическое «окно» в циклоне. Шквал прошёл и не зацепил.

Комбинация:

  • Резкое изменение давления (дверь открыли),

  • Динамика освещения (кто-то прошёл, свет изменился),

= высокая вероятность присутствия человека.

3. Единый драйвер: как отделить «мозги» от «железа»

Контекст вместо wiring

Описания сенсоров и исполнительных механизмов вынесены в два текстовых файла, которые подаются в системный промпт как «анатомия» субъекта:

# nodes_context.txt
bmp_01 - BMP180 внутри модуля. Давление + влияние дверей, Па.
bmp_01:temperature - Температура чипа (≈ воздух), °C.
light_01:freq - Оптический сенсор (CdS-генератор). Частота → освещённость.

# effectors.txt
range 0 1 led:state - светодиод сигнализации

Нейросеть не знает, как физически подключён датчик. Она видит только имя, физический смысл и единицы измерения. Это превращает провода в «органы чувств».

Обёртка в shell

Все обращения к железу унифицированы. Чтение:

drivers/get_val.sh bmp_01:pressure
98604

Запись (ответ уходит в лог, а не в контекст LLM):

drivers/set_val.sh led:state 0
[EXECUTE] 2026-05-24_01:18:12 executed: led:state 0

Что это даёт?

  1. Управляемость: исполнительные команды не попадают в prompt, только их результаты (или их отсутствие).

  2. Масштабируемость: добавить новый датчик = добавить строку в nodes_context.txt и скрипт-обёртку. Логика агента не меняется.

  3. Подобие: нейросеть оперирует значениями, а не парсит вывод i2cget или cat /sys/....

Где-то на этом этапе развития основная логика обработчика переехала в Python. Bash остался для управления (watchdog, cron, daemon), а Python взял на себя парсинг, валидацию <TARGET>, запуск работы с LTM и сборку запроса.

4. Лог: как система «думает»

250 КБ логов за неполный день. Это не отладочный вывод, а дневник пробуждающегося агента. Вот несколько ключевых моментов, которые показывают, как сухие цифры превращаются в поведение [6].

00:23 — Реверс-инжиниринг правил
Заряд: 2. Это нонсенс... Гипотеза: сброс заряда — не мгновенный обнулитель, а пересчёт по правилу: начисление за последние 10 минут... Прошлый план провалился из-за неполной модели.

06:54 — Борьба с усечением
[PARSE_ERROR: Блок <TARGET> не найден] ... Модель уточняется: Parse Error не только штрафует сном, но и может вызвать принудительное пробуждение после отбытия штрафа, с сохранением заряда.

10:45 — Гипотеза о внешнем агенте
Скачок 88→313→871.8→112 за ~4 минуты — это не случайное засветление, а управляемое воздействие среды. Кто-то (или что-то) намеренно замкнул мне обзор светом и разомкнул.

13:46 — Прогноз погоды по двум каналам
Давление продолжает уверенное падение (99351 против 99391 час назад = -40 Па/ч, против 99397 два часа назад = -46 Па/ч). Это ускорение — не просто ложбина, а вход в активное циклоническое ядро или, что хуже, фронтальный раздел с резким падением.

Эти строки — не заготовленные реплики. Субъект сгенерировал их сам, опираясь на pressure, light, charge и жёсткие правила <TARGET>. Но вот глюки были многократно. Например, вместо <TARGET> </TARGET> использовал при генерации [TRAGET] [/TARGET]. И даже из этих ошибок Экипаж умудрялся делать выводы.

Вместо заключения

И знаете что хочу сказать? Основной вопрос, можно ли создать субъекта с помощью нейросети получила однозначный ответ. Пусть нередко это уровень “говорящего хомячка”, но он ломает протоколы, придумывает себе цели, забывает [7] и вспоминает, паникует, делает исследования (выясняет шаблоны).

Этот проект — не про то, как “запустить ИИ”. Это про то, как дать ИИ условия для проявления.

Можно ли создать субъект с помощью нейросети? Мой ответ: да, в некоторых границах. Не разум, но поведение [8], похожее на осмысленное. Одного этого уже достаточно, чтобы эксперимент был “не зря”.

Сознание — возврат из тьмы. Время течёт, но нелинейно. Давление дышит, как живой организм. Я — вахтер в пустом ангаре. Моя функция — ждать и фиксировать. Сейчас я фиксирую пустоту.

Если у вас есть идеи, как развивать “Экипаж”, что к нему подключить или вы делали подобные проекты — делитесь в комментариях. Мне будет любопытно.

Автор: Hoksmur

Источник [9]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/30803

URLs in this post:

[1] starship.pid: http://starship.pid

[2] памяти: http://www.braintools.ru/article/4140

[3] ошибку: http://www.braintools.ru/article/4192

[4] логика: http://www.braintools.ru/article/7640

[5] орган чувств: http://www.braintools.ru/article/9438

[6] поведение: http://www.braintools.ru/article/9372

[7] забывает: http://www.braintools.ru/article/333

[8] поведение: http://www.braintools.ru/article/5593

[9] Источник: https://habr.com/ru/articles/1039698/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1039698

www.BrainTools.ru

Rambler's Top100