
Привет, Хабр!
Недавно, вдохновившись моим опытом, мой товарищ решил приобщиться к моноколёсному комьюнити и купил моноколесо Inmotion V12 Pro. В целом колесо достаточно хорошее, но первая совместная поездка выявила одну проблему, а именно: практически невидимые задние сигналы. Этот недостаток не добавляет безопасности поездкам, поэтому, как всегда, я спешу исправить ситуацию. А что из этого вышло — читайте далее.
❯ Начало
Да, совместная поездка на моноколёсах без сигналов добавляет немного экстрима, но всё же лучше исправить ситуацию и наслаждаться безопасными поездками с чётко читаемыми сигналами. Так как временные рамки не позволяли купить готовый вариант умного стоп-сигнала, я решил собрать свой DIY-вариант, потратив на разработку и сборку менее двух часов. По традиции проект будет состоять из компонентов, которые уже есть у меня в наличии.
❯ Я его слепил из того, что было
Итак, так как было решено собирать умный сигнал, нам понадобятся следующие компоненты:
Красные яркие светодиоды 5 мм.
Как правило, они всегда лежат в запасе, купленные ещё в «нормальные времена». А если нет, то можно купить по цене семечек на Алиэкспрессе:

Микроконтроллер;
Здесь нужно компактное решение. У меня пару месяцев валялась без дела одна из вариаций платы ESP32-C3-Zero — будем использовать её:

Датчик положения в пространстве;
В данном проекте я буду использовать трёхосевой гироскоп MPU6050. У меня их «горка и маленькая кучка» — остались ещё с прошлых экспериментов:

Дополнительные компоненты.
N-канальный мосфет P0603BDG для управления матрицей светодиодов и несколько резисторов на 100 Ом.
❯ Как это работает
Ниже представлена принципиальная схема моего стоп-сигнала:

Принцип работы очень прост: для работы сигнала нам нужно отслеживать данные с акселерометра MPU6050 и при наличии отрицательного ускорения по оси Y зажигать матрицу светодиодов.
❯ Прошивка и отладка
Для дальнейших экспериментов собираем минимальный сет из датчика и отладочной платы. Затем, «абы как» припаиваем датчик и начинаем эксперименты:

И загрузив первую попавшуюся библиотеку MPU6050 начинаем эксперименты:

После установки библиотеки загружаем из примеров скетч MPU6050_Raw и закомментируем в нём следующие строки:
/*Print the obtained data on the defined format*/
#ifdef OUTPUT_READABLE_ACCELGYRO
//Serial.print("a/g:\t");
//Serial.print(ax); Serial.print("\t");
//Serial.print(ay); Serial.print("\t");
//Serial.print(az); Serial.print("\t");
//Serial.print(gx); Serial.print("\t");
//Serial.print(gy); Serial.print("\t");
//Serial.println(gz);
Serial.print(gy); // Выводим в консоль значения ускорения по оси Y
#endifДалее загружаем код в отладочную плату и запускаем в среде вывод значений в виде графика. Затем мы сможем наблюдать следующий результат, если подвигаем датчик по направлению оси Y вперед-назад:

Здесь мы наблюдаем положительное и отрицательное ускорение по оси Y. Данные при этом достаточно шумные, поэтому далее нам нужно их сгладить. Обычно это делается с помощью фильтра Калмана. После обработки сигнала датчика данным фильтром мы сможем наблюдать следующее:

Теперь мы можем использовать далее данные в нашей логике управления зажигания матрицы:
int target = 600; // Значение, за которым включается стоп-сигнал (отрицательное ускорение)
int fgy = (int)filter(gy);
Serial.println(fgy); // Выводим в порт данные ускорения по оси Y
if (fgy > target){
analogWrite(LED_pin, 255); // Зажикаем на максимум
} else{
analogWrite(LED_pin, 30); // Режим габарита
}По моей задумке, стоп-сигнал должен работать в двух режимах: габаритного огня (яркость 20%) и сигнала торможения (яркость 100%). Для этой цели и используется ШИМ-регулирование в представленном коде, а сам порог зажигания стоп-сигнала определяется переменной target, значение которой зависит от необходимой границы отрицательного ускорения.
Не буду ходить вокруг да около — ниже представлен итоговый код прошивки:
Код прошивки
/*
CYBEREX TECH, 2026
Стоп-сигнал с гироскопом MPU6050
*/
#include "I2Cdev.h"
#include "MPU6050.h"
/* MPU6050 default I2C address is 0x68*/
MPU6050 mpu;
//--------------------------------------------------------------------------------------------------------------*/
#define OUTPUT_READABLE_ACCELGYRO
// Пины подключения к датчику
#define sda_pin 12 // SDA 12
#define scl_pin 11 // SCL 11
#define LED_pin 5 // LED array MOSFET 5
int16_t ax, ay, az; // Переменные скорости по осям
int16_t gx, gy, gz; // Переменные ускорения по осям
// переменные фильтра Калмана
float varSt = 100; // среднее отклонение
float varProcess = 0.5; // скорость реакции на изменение (подбирается вручную)
float Pc = 0.0, G = 0.0, P = 1.0, Xp = 0.0, Zp = 0.0, Xe = 0.0;
// переменные фильтра Калмана
// Функция фильтрации
float filter(float val) {
Pc = P + varProcess;
G = Pc / (Pc + varSt);
P = (1 - G) * Pc;
Xp = Xe;
Zp = Xp;
Xe = G * (val - Zp) + Xp; // "фильтрованное" значение
return (Xe);
}
int target = 600; // Значение, за которым включается стоп-сигнал (отрицательное ускорение)
void setup() {
Wire.begin(sda_pin, scl_pin);
Serial.begin(115200);
/*Initialize device and check connection*/
Serial.println("Initializing MPU...");
mpu.initialize();
Serial.println("Testing MPU6050 connection...");
if(mpu.testConnection() == false){
Serial.println("MPU6050 connection failed");
while(true);
}
else{
Serial.println("MPU6050 connection successful");
}
/* Use the code below to change accel/gyro offset values. Use MPU6050_Zero to obtain the recommended offsets */
Serial.println("Updating internal sensor offsets...\n");
mpu.setXAccelOffset(0); //Set your accelerometer offset for axis X
mpu.setYAccelOffset(0); //Set your accelerometer offset for axis Y
mpu.setZAccelOffset(0); //Set your accelerometer offset for axis Z
mpu.setXGyroOffset(0); //Set your gyro offset for axis X
mpu.setYGyroOffset(0); //Set your gyro offset for axis Y
mpu.setZGyroOffset(0); //Set your gyro offset for axis Z
/*Print the defined offsets*/
Serial.print("\t");
Serial.print(mpu.getXAccelOffset());
Serial.print("\t");
Serial.print(mpu.getYAccelOffset());
Serial.print("\t");
Serial.print(mpu.getZAccelOffset());
Serial.print("\t");
Serial.print(mpu.getXGyroOffset());
Serial.print("\t");
Serial.print(mpu.getYGyroOffset());
Serial.print("\t");
Serial.print(mpu.getZGyroOffset());
Serial.print("\n");
/*Configure board LED pin for output*/
// pinMode(LED_BUILTIN, OUTPUT);
pinMode(LED_pin, OUTPUT);
}
void loop() {
/* Read raw accel/gyro data from the module. Other methods commented*/
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
#ifdef OUTPUT_READABLE_ACCELGYRO
int fgy = (int)filter(gy);
Serial.println(fgy); // Выводим в порт данные ускорения по оси Y
if (fgy > target){
analogWrite(LED_pin, 255); // Зажикаем на максимум
} else{
analogWrite(LED_pin, 30); // Режим габарита
}
#endif
delay(100);
}❯ Корпус
Для разработки корпуса я, как всегда, использую FreeCAD. Сам корпус имеет довольно простую конструкцию: сборка выполняется без единого болта, по принципу матрёшки. Ниже представлен скриншот проекта:

Печать корпуса выполнялась на моём 3D принтере Flashforge Adventurer 5M и заняла не более двадцати минут.
❯ Сборка стоп-сигнала
Самый интересный момент — когда твоя задумка воплощается в жизнь. Вставляем светодиоды в распечатанный корпус и припаиваем элементы согласно представленной выше схеме:

В состав корпуса входит разделяющая панель, на которую устанавливается датчик и отладочная плата:

Я очень торопился с реализацией и не предусмотрел в панели отверстия для болтов, поэтому фиксирующие датчик болты были просто вплавлены в пластик. Примерно так же было реализовано крепление платы, но в этот раз уже вплавлялись небольшие отрезки выводов, оставшиеся от ножек светодиодов, к которым позднее припаивалась отладочная плата:

Аналогичным способом крепится и мосфет. А так выглядит вся электроника в сборе:

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

Всё, стоп-сигнал собран:

Осталось разобраться с подключением: здесь всё просто. Моноколесо имеет USB-порт с функцией зарядки мобильных устройств, обеспечивающий достаточную мощность для работы стоп-сигнала. Его и будем использовать для питания. Для подключения используется разъем USB-A («папа»), который нужно максимально укоротить, чтобы он влез в отсек с портами при закрытой заглушке. Сам фонарь крепится на резиновую заглушку отсека моноколеса с помощью наноскотча, перед этим нужно обязательно обезжирить поверхность заглушки.

В итоге, после нескольких тестовых поездок, крепление стоп-сигнала на наноскотч оказалось очень надёжным решением.
❯ Итоги
Итоговым результатом я остался очень доволен! У меня были сомнения насчёт видимости фонаря в дневное время при прямом солнечном свете, но они не подтвердились. Стоп-сигнал очень хорошо видно даже на ярком солнце. Теперь поездки на моноколесе стали значительно комфортнее и безопаснее.

Само собой, этот стоп-сигнал можно применить не только на моноколесе, но и, например, на велосипеде. В этом случае стоит позаботиться о внешнем источнике питания. Да и данное решение будет экономически более целесообразным, чем покупка готовых решений, а удовольствие от сборки — бесценно!
На этой позитивной ноте можно и закончить статью! Спасибо за уделенное время! Интересных проектов и всех благ! А если остались вопросы или есть что добавить — добро пожаловать в комментарии.
Ссылки к статье:
Может быть интересно:

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале ↩






























