惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 【当耐特】
Stack Overflow Blog
Stack Overflow Blog
V
Visual Studio Blog
小众软件
小众软件
The Cloudflare Blog
T
Tailwind CSS Blog
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
美团技术团队
WordPress大学
WordPress大学
罗磊的独立博客
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
Last Week in AI
Last Week in AI
月光博客
月光博客
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
Google Developers Blog
GbyAI
GbyAI
B
Blog
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
Hugging Face - Blog
Hugging Face - Blog
博客园 - 叶小钗

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания
Лампа плавного пуска
aabzel · 2026-06-13 · via Все публикации подряд на Хабре

Пролог
У меня было множество вело фар и всегда меня раздражало то, что фара включается практически мгновенно. Глаза даже не успевают приспособиться и это доставляет существенный дискомфорт. При этом на работе я много лет как раз разрабатывал ECU-прошивки для управления кузовной электроникой. В связи с этим я принял решение разработать себе прототип вело фары с плавным включением яркости для вело-фары.

Постановка задачи
Разработать прототип вело-фары с плавным включением и плавным отключением. Яркость должна изменяться постепенно. Обеспечить возможность дистанционного включения лампы (например по IR пульту). Сделать возможность регулирования яркости свечения через инкрементный энкодер. Настройки интенсивности свечения сохранять в энергонезависимой памяти NVRAM. Попробовать два алгоритма регулирования яркости : PWM и PDM.

Аппаратная часть

В качестве отладочной платы для прототипа я по-прежнему выбрал с JZ-F407VET6.

Распиновка для сборки прототипа такая

GPIO

Pull

Wire Name

Dir

Comment

PE0

Up

Encoder A

in

Инкрементный энкодер

PE1

Up

Encoder B

in

Инкрементный энкодер

PA6

up

IR sensor

in

TSOP-36

PA9

no

USART1_TX

out

CLI

PA10

up

USART1_RX

in

CLI

PA0

none

TIM5_CH1

out

PWM

PA3

none

TIM9_CH2

out

PWM

PE4

none

DeltaSigma

out

PDM

В роли драйвера 12-вольтового LED я выбрал микросхему H-моста DRV8870. Вот так выглядит отладочная плата для этой микросхемы.

Нагрузкой является LED сборка со встроенными резисторами. Также нужен цоколь для LED лампы.

На малых свечениях лампа выглядит как радиоактивный изотоп

На малых свечениях лампа выглядит как радиоактивный изотоп

Весь прототип выглядит вот так

В чем трудность управления LED фонарями?

1) Главная особенность LED лампы (в отличие от ламп накаливания) в том, что она полностью без инерционная. То есть, если с нуля подать скважность 100%, то лампа в самом деле мгновенно и ослепительно включится на 100%. Выглядит как вспышка. Для меня это как раз нежелательный сценарий. Я хочу сделать искусственную инерционность LED лампы. Чтобы хрусталики глаз могли приспособиться к постепенному нарастанию яркости. Как же мне этого добиться?

2) Вторая особенность LED в том, что LEDы не так-то просто плавно включить. Уже при 0.7 % заполнения LED включается и это тоже визуально воспринимается как резкое включение. Хочется как-то сделать акцент именно на начальной фазе нарастания яркости и на фазе окончательного потухания. Добавить программную инерционность вокруг нулевого заполнения.

Программная часть

Прежде чем писать прошивку надо определиться с тем по какому алгоритму следует производить нарастание яркости. Простой линейный закон изменения яркости - это не так эффектно. Сначала я попробовал экспоненциальный закон нарастания яркости. С потуханием все хорошо, однако с включением плохо. На прототипе это выглядело как резкое включение.

В коде он выглядит так

Экспоненциальное нарастание и спад яркости LED-а
import matplotlib.pyplot as plt
import numpy as np

max_illum=100.0
scale = 0.5
time_s = np.arange(1, 7)
time_max=10
time_s = np.linspace(0, time_max, 100)
illumination_climax = max_illum*(1.0-  np.exp(-scale*time_s))
illumination_decay =  max_illum* np.exp(-scale*time_s)

plt.plot(time_s, illumination_climax)
plt.plot(time_s, illumination_decay)
 
plt.title('Illumination change')
plt.xlabel('Time,[s]')
plt.ylabel('Light level, [Lx]')
plt.grid()
plt.show()

Затем я попробовал реализовать нарастание и снижение яркости при помощи логистической кривой. Логистическая функция выражается формулой ниже, где k - коэффициент крутизны наклона кривой

 Логистическая функция

Логистическая функция

Вот ее графики

Можно поэкспериментировать с различными параметрами переходного процесса отрисовывая графики: длительность переходного процесса и коэффициент в степени.

Код построения логистической кривой
import matplotlib.pyplot as plt
import numpy as np

max_illum=100.0
scale = 0.95
time_s = np.arange(1, 7)
time_max=10
time_s = np.linspace(0, time_max, 100)
time1_s=time_s-time_max/2
illumination_climax = max_illum*1.0/(1.0 +  np.exp(-scale*time1_s))
illumination_decay =   max_illum-illumination_climax
 
#illumination_decay1 =   max_illum* np.exp(-scale*time_s)

plt.plot(time_s, illumination_climax)
plt.plot(time_s, illumination_decay)
 
plt.title('Illumination change')
plt.xlabel('Time,[s]')
plt.ylabel('Light level, [Lx]')
plt.grid()
plt.show()

Этот переход уже что надо, однако надо помнить, что у меня есть еще и инкрементный энкодер для ручного управления яркостью. Функциональное управление яркостью согласно логистической кривой без обратной связи перебивает алгоритм управления яркостью по квадратурному энкодеру. Как же разрешить это противоречие?

PID регулятор

Чтобы решить эту проблему я обратился к PID регулятору. PID-регулятор — это программа, которая читает показания датчика и управляет мощностью так, чтобы значение датчика стало тем, что вы задали. Что, если энкодером и кнопками задавать целевое значение для PID регулятора? При этом PID регулятор уже сам будет как-то делать желаемый переходной процесс. Главная роль тут отводится интегральной части регулятора. Ее вклад в финальное заполнение и будет вносить основной результат. Однако и тут облом. Дело в том, что сразу после установки нового целевого значения яркости образуется максимальная ошибка (погрешность). Это приводит к интенсивному нарастанию яркости на начальном этапе, которое как раз и портит весь визуальный эффект. Как же быть?

Чтобы сделать медленное изменение на начальном и конечном участке я слегка модернизировал PID регулятор. Добавил еще один параметр (смешение): отклонение от прошлой цели. Смещение противоположно ошибке. Смещение плюс погрешность дают расстояние, которое надо пройди регулятору от предыдущей цели к новой цели.

Чтобы появилась возможность вычислять смещение, надо при каждой установке нового целевого значения регулятора запоминать предыдущее значение цели last_target. Вот так.

bool pid_target_set(uint8_t num, float target) {
    bool res = false;
    PidHandle_t* Node = PidGetNode(num);
    if(Node) {
        res = is_float_equal_absolute(target, Node->target,0.0001);
        if(!res) {
            LOG_INFO(PID, "PID%u,Set,Target:%f->%f", num, Node->target, target);
            Node->last_target = Node->target; // !!!!!!!
            Node->target = target;
            res = true;
        }
    } else {
        LOG_ERROR(PID, "NodeErr,PID%u", num);
    }
    return res;
}

Затем на каждой итерации PID регулятора вычислять абсолютное значение смещения shift = |out - last_target|

Благодаря вычислению смещения можно на каждой итерации PID регулятора вычислять такое приращение к интегральной части, которое в результате и сделает нам S-образный переходной процесс (см зелёный график d ). Дифферециальная часть PID регулятора тут и вовсе не нужна. Коэффициент D можно обнулить. Однако, чтобы процесс начался нужно сделать ненулевой пропорциональный коэффициент. Код ядра PID регулятора получается такой.

static bool pid_proc_value_ll(PidHandle_t* const Node) {
    bool res = false;
    if(Node) {
        Node->shift = Node->last_target - Node->out;
        Node->d_sum = 0.0f;
        if(0.0 < Node->error) {
            Node->d_sum = MATH_MIN(fabsf(Node->shift),Node->error);
        } else {
            Node->d_sum = MATH_MAX(-fabsf(Node->shift),Node->error);
        }
        Node->error_sum += Node->d_sum;
   
        Node->out = 0.0f;
        Node->out += Node->p * Node->error;
        Node->out += Node->i * Node->error_sum;
        
        LOG_DEBUG(PID, "%s", PidNodeToStr(Node));
        res = true;
    }
    return res;
}

Вот так это работает в реальности. Перед Вами график заполнения PWM от времени прочитанный из реального UART лога. Как видно, это S-образный переходной процесс, который рассчитывается в потоковом режиме.

Управление заполнением PWM от времени

Управление заполнением PWM от времени

Варьируя интегральный коэффициент можно управлять скоростью переходного процесса.

Управление яркостью на низком уровне

Второй вопрос, что выбрать для непосредственного регулирования яркости: PWM или PDM?

Проблема PWM в том, что он не позволяет непрерывно включаться с нуля и включает ярость скачком уже на 0.7% заполнения. Поэтому я попробовал запрограммировать программный PDM.

Настроил прерывания по таймеру 1 c частотой 20kHz. В обработчике прерываний стал делать вычисление PDM семплов.


bool delta_sigma_isr_proc_one_ll(DeltaSigmaHandle_t* Node) {
    bool res = false;
    Node->error = Node->target - Node->dac_out;
    Node->sum_error += Node->error;
    Node->pdm = adc_1bit(Node->sum_error, Node->comparator_middle);

    res = gpio_logic_level_set(Node->Pad, (GpioLogicLevel_t)Node->pdm);
    Node->dac_out = dac_1bit(Node->pdm, Node->min, Node->max);
    Node->sample_cnt++;
    return res;
}

Однако это дело не улучшило. PDM дает заметное мерцание на малой яркости. В результате я выбрал PWM, который по крайней мере не мерцает.

В результате, прошивка приняла такую структуру.

Возможные приложения прошивки драйвера LEDов
--Тестер автомобильных светодиодных лампочек
--Вело фара
--Торшеры для чтения
--Люстры
--Настольные светильники
--Плавное включение может пригодиться для управления подсветкой смартфонов при выходе из режима блокировки экрана. Это добавит эргономики.
--Можно включать LED лампы на малое свечение ночью, чтобы сбивать с толку комаров.
--S-образный переходной процесс может быть как нельзя кстати для управления шаговыми двигателями (ШД). Там как раз запрещено скачкообразное изменение скорости управляющего воздействия, чтобы не потерять отработку шагов на обмотках статора.

Итог

Удалось сделать прототип электроники и прошивки для велосипедной фары, который позволяет производить плавное управление яркостью LED при помощи инкрементного энкодера. Также заложен алгоритм плавного включения и отключения фары. Удалось сделать S-образный переходной процесс при помощи слегка модернизированного PI-регулятора.

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

Ссылки

Вопросы:
1) Как при помощи PID регулятора сделать S-образный переходной процесс?
2) Как избавиться от мерцания при PDM управлении свечением?
3) Существуют ли в продаже миниатюрные отладочные платы с STM32, GPIO разъемами и драйвером hi-side ключей, чтобы управлять автомобильными LEDами.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовали 9 пользователей. Воздержался 1 пользователь.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовали 11 пользователей. Воздержавшихся нет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовали 10 пользователей. Воздержавшихся нет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовали 10 пользователей. Воздержавшихся нет.