Попала мне в руки матрица из программируемых светодиодов WS2812, 16 х 16 = 256 диодов. На гибкой печатке. "Нужно что то с этим делать" - шепнуло мироздание. Интернет услужливо подсунул большой и успешный проект Алекса Гайвера - Огненную лампу.
Вроде бы все просто - бери и делай. Но я не хотел повторять его проект, плюс мне не совсем нравился эффект пламени, реализованный в лампе Гайвера. И описанные в видео алгоритмы я попробовал, но они не возбудили меня. Штирлиц шел по улицам Берлина. Что то выдавало в нём советского разведчика. То ли фуражка с красной звездой, то ли парашют за спиной....
Когда то один товарищ из Симбирска сказал - "Мы пойдем другим путем". Ну вот и я решил попробовать придумать свой алгоритм пламени. Получится - будет хорошо, не получится - фиг с ним, передерём Гайверовский. Хотя передерастом быть не хочется, поэтому надо придумать свой.
Что нужно для начала? Придумать схему устройства и развести под схему печатную плату.
Светодиодов много - целых 256 штук, управление диодами завязано на жесткие тайминги. 1,25 мкс на один бит данных. Или 30 мкс на 24 бита - RGB-цвет. На 256 диодов надо 7.68 мс. И на протяжении этих почти 8 миллисекунд в сторону даже чихнуть нельзя. (Точнее, можно, но быстро-быстро). А поскольку генерация огня связана еще и с кучей математики - выбор пал на 32-битную платформу, на народный камушек STM32F103C8. Там и памяти достаточно, что бы "развернуть" массив данных для матрицы в линейный массив 24 байта на диод, и SPI есть, что бы выплюнуть этот массив в диодную матрицу. И есть DMA, что бы отправка данных выполнялась независимо от текущих задач МК... И тактовая частота до 72 МГц позволит быстро просчитывать кадры.
Добавляем хотелки - управление с ИК-пульта, несколько бортовых кнопок, интеграция с умным домом.... В результате родилась схема, печатка отправилась в производство. Пока китайцы делают печатку и она едет в Украину, продолжаю эксперименты с эффектами и алгоритмами.
(Зачем там умный дом? Да почти ни зачем. Просто лично мне удобно. Плюс будет добавлено управление отключаемой розеткой. Ибо 256 диодов даже в погашенном состоянии жрут 150 ма тока.)
Долго сказка сказывается , быстро дело делается. 23 дня и платы у меня.
Поиск подходящего светорассеивателя - та еще проблема. Сначала мы с коллегой купили по стеклянной вазе. Но ваза прозрачная, нужно матировать. Обклеивать калькой - некрасиво. Попробовал белую бумагу - слишком непрозрачно. Да и вопрос об основании и креплении стекла к оному остается. В процессе поисков наткнулся на форуме у Гайвера на ссылку - там для особо ленивых можно даже купить полный комплект для лампы Гайвера. Я же взял БП на 4 китайских ампера и пластиковый плафон с пробковым основанием.
В качестве несущих элементов были применены канализационные трубы d=50мм, стяжки, обрезки какого то пластика и прочие подручные материалы.
Готовая плата была разрезана на три части, собрана и закреплена в трубе.
Еще хочу предупредить, что матовый плафон, за счет того, что он находится на некотором расстоянии от матрицы диодов, кардинально меняет восприятие отображаемого эффекта.
Это следует учитывать при разработке эффектов.
И ещё. Я не знаю, как нормально снять видео работающей лампы. В темноте - идет жуткий пересвет, особенно при съемке телефоном. В хорошем освещении - исчезают полутона и опять же, остаются только цветные пятна.
Но я попробую все же показать, что у меня получилось. А ниже опишу идею алгоритма.
Что бы иметь возможность более точно просчитывать математику движущихся частиц (например, смещение на нецелое число пикселей) и при этом не уползать в вычисления с плавающей запятой, я принял простое решение - умножить все координаты на 256 и работать с целыми числами. А уже при выводе данных на матрицу конечные координаты делить на 256. Поскольку контроллер у нас 32-разрядный - ему пофиг, что суммировать - 16+1, 4096+256 или даже 1048576+65536. 32 бита - это 32 бита. Да и на 256 делить легко - отбросить младший байт - и вуаля!
В качестве пламени была принята концепция искры и её следа. (Как сказал какой то политический деятель доинтернетной эпохи - Из искры возгорится пламя).
Искра характеризуется семью параметрами - текущие координаты X, Y, время жизни t, вертикальное приращение координаты dY, горизонтальное приращение ±dX, цвет и яркость.
С цветом работать будем в понятиях цветового пространства HSV. Hue - цветовой круг (0-360), яркость Value от 0 до 255. Насыщенность в эффекте пламени постоянна и максимальна.
Цвет определяется как базовый + случайный зазор. Не меняется на протяжении времени жизни искры.
Для классического пламени, после ряда экспериментов, применен базовый оттенок = 357 - это практически красный, немножко сдвинутый в сторону фиолетового, что б добавить чуточку синей компоненты. Максимальный зазор у меня - 28. Т.е. 357+28 = 385. 385-360=25. 25 - это что то в районе оранжевого цвета. С учетом того, что оранжевый в схеме RGB - это красный плюс 1/2 зеленого - получилось нормально. Если зазор продлевать в сторону желтого цвета - получаются неестественные блики.
Опять же, если выбрать стартовый цвет и зазор другие - можно получить разные виды пламени, зелёное, синее... Если убрать насыщенность - будет белое пламя.
Координаты X, Y - координата Y при создании искры всегда 0. Координата X - сначала я думал выбирать ее случайно, в пределах ширины матрицы. Но как оказалось, для имитации пламени достаточно 16 искр, по числу столбцов матрицы. И стартовая координата X численно равна номеру искры. Т.е. каждому рабочему - по фабрике, а каждой искре - по столбцу матрицы.
Все параметры, как и цвет, подобраны экспериментально, по максимальной похожести эффекта на реальное пламя.
Время жизни - это число кадров (циклов расчета), в течении которых искра живет. Выбирается случайно, от 10 до 22 кадров. По истечении времени жизни генерируется новая искра.
Приращение dY - от 1/2 до 1. (Тут и далее я буду оперировать вещественными координатами, что бы не вносить путаницу. В реальной математике всё умножено на 256, что б работать с целыми числами).
Приращение dX - от -1/8 до +1/8.
Яркость. Тут я предположил, что как в реальном пламени, чем горячее (и ярче) язык пламени, тем он и быстрее. Ну и обратное - чем быстрее язык пламени, тем он ярче. Соответственно, яркость искры прямо пропорциональна её вертикальной скорости. Итого - яркость Value = 255 * dY, где dY = 1/2 .. 1.
Этап первый - "лента".
В течении жизни искры её яркость уменьшается до 0.В каждом кадре рассчитываются новые координаты искры, ее яркость и в соответствии с этими координатами на поле матрицы зажигается соответствующий светодиод. При этом зажженные на прошлых циклах светодиоды не стираются, а остаются гореть. Т.е. искра оставляет за собой след - "ленту" с плавно уменьшающейся к концу ленты яркостью свечения.
Ниже - пример зажигания искры, тут dX = 0, после окончания времени жизни поле очищается и начинает стартовать новая искра.
dY, цвет, время жизни - случайные.
Тут и далее частота кадров значительно уменьшена, что бы была видна механика алгоритма.
К сожалению, на видео плохо видно уменьшение яркости каждого следующего пикселя. Камера телефона считает себя умнее всех :(
Но в реальности - пиксели имеют плавное уменьшение яркости.
Добавим dX.
Этап второй - угасание.
Теперь надо подумать, как гасить след искры.
Оптимальным вариантом оказалось в каждом кадре уменьшать яркость свечения всех пикселей матрицы на константу. Я подобрал константу таким образом, что бы пиксель с максимальной яркостью 255 угасал до нуля за приблизительно 40 кадров.
Вот здесь показано попеременное движение искры вверх и, после окончания времени жизни, угасание всех пикселей. Когда все пиксели погаснут - генерируется новая искра. dX = 0, что б не отвлекал.
За счет того, что шаг уменьшения яркости при движении искры вверх больший, нежели шаг уменьшения яркости пикселей при угасании, как раз и получается эффект угасания сверху вниз.
Теперь попробуем одновременное движение искры и угасание поля. dX = 0.
Все тоже самое, но три искры.
Ну теперь можно попробовать включить все 16 искр и случайный dX.
Этап третий - размытие.
Для большей достоверности я добавил размытие по горизонтали. При зажигании искры в очередном кадре слева и справа от искры зажигаются её "клоны", того же цвета, но с половинной яркостью. Если слева или справа уже светится какой то пиксель, то клон его заменит, если яркость пикселя ниже яркости "клона".
Ну и уже пора в демках включить нормальную скорость (около 60 кадров в сек).
Одна искра с нормальной скоростью, с размытием.
Этап четвертый, финальный. Все 16 искр.
Полноценная имитация - все 16 искр. Если накрыть плафоном - будет видео из начала статьи.
Эффекты
После реализации пламени другие эффекты оказались такими простыми....
Горизонтальная и вертикальная радуги, светлячки, прыгающие огоньки...
Эффект плазмы на основе шума Перлина.
Добавлено отображение текущего времени (из сети умного дома). Таймер автовыключения и случайные эффекты.
В принципе, лампа может работать и как автономный светильник. Засим, если вдруг кого заинтересует схема - могу выложить.
-------------------
Исходники эффекта огня.
Отличная работа, спасибо большое! Как всегда очень четко сделано и понятно описано.
ОтветитьУдалитьБеру на вооружение. ))
( никакой политики - см Википедию "Значение:
что-либо узнав, какой-либо новый приём, метод и т.п., начать им пользоваться" )
Редко нынче попадаются полезные и приятные, для чтения, материалы. Но тут... Спасибо за шикарную дозу эстетического кайфа!
ОтветитьУдалитьСпасибо.
УдалитьЯ выкладывал это с тайной мыслью, что кому то пригодится.
Знаю, что dadigor чего то мутит сэтими адресныме ледами.
И судя по комментам в ютубе к самому последнему видео, SottNick уже пытается запустить процедуру.
А если помимо эфектов периодически выводить еще и время, бегущей строкой?
ОтветитьУдалитьВремя выводится по нажатию кнопки на пульте или лампе. Ибо и без лампы часов в комнате достаточно.
УдалитьПопробовал Ваш алгоритм на 328 Меге. Не сразу, но заработало. Огонь пожалуй, даже более живой, по ощущениям, чем на лампе Гайвера получился. Сужу по роликам, так как самой лампы у меня нет и " в живую" не могу сравнить. Спасибо! Теперь бы еще с Перлином разобраться. Судя по хабру, штука адская и, похоже, без математики с плавающей точкой не обойтись.
ОтветитьУдалитьзачем плавающая точка?
УдалитьУмножили на 256 и погнали целочисленную.
У меня в лампе перлин есть. Плазма на нем сделана. Целочисленный.
Круто! Про умножение понятно :) оно у Вас и в огне используется - удобно. С Перлином сама векторно-скалярная математика не очень понятна пока. Да и очень уж эти матрицы прожорливы оказались. Похоже авр их еле тянет. На радуге у меня плеер отказывается работать, хрипит, трещит и циклится. Удивляюсь, как они на ардуине-то работают...
ОтветитьУдалитькинул в скайп. единственное, это СТМ32 - и там 32-битная математика работает с той же скоростью, что и 8-битная.
УдалитьСпасибо большое!
ОтветитьУдалить