Алгоритм огня для адресных светодиодных матриц
Целью данной разработки является создание нового алгоритма для управления адресными цветными (RGB) светодиодными матрицами, для формирования эффекта горящего огня. Он может понадобиться для электрокаминов, умных светильников и т.п. Например, этот алгоритм будет использоваться в проекте «Умная фоторамка».
Конечно, похожие алгоритмы существуют. Некоторые используют анимации, другие случайные значения (например, шум Перлинга).
Аналоги
Анимации могут дать более реалистичный эффект, но постоянно повторяются и значит надоедают.
Алгоритмы на основе случайных чисел выглядят не вполне правдоподобно. Наиболее правдоподобный из таких алгоритмов использует маски для формированию цветовых пятен на основе случайных чисел. Работает он в пространстве HSV (тон, насыщенность, яркость). Это удобнее, чем в RGB (красный, зеленый, голубой), на котором работают светодиоды. Алгоритм довольно усложнен и плохо настраиваемый, т.к. необходимо под каждый размер матрицы нужно как-то задавать две матрицы масок для тона и насыщенности.
Физика пламени
Пламя можно представить в виде частиц, обладающих энергией. Они поднимаются в воздух из-за разницы температур, т.е. возникает движение вверх. По мере подъема (и удаления от источника энергии) они теряют свою энергию и в конце концов полностью гаснут. Возгорание происходит обычно очагово, т.е. в эпицентре энергия больше, чем по его краям.
Именно такое представление пламени было реализовано в алгоритме.
Модель возгорания
Модель возгорания можно описать нормальным законом распределения. Он имеет максимум по центру и симметричный спад к краям.
По такому закону могут распределяться энергии заряженных частиц. Причем вводится еще и диапазон изменений псевдослучайных чисел, задающих исходные энергии частиц пламени.
Затем на каждой итерации частицы должны перемещаться вверх со снижением энергии на определенный коэффициент.
Параметры цвета в зависимости от энергии
Цвет пламени определяет сгорающее вещество и может меняться. В большинстве случаев пламя древесины обладает красно-желтым оттенком. В самой горячей точке оно обладает высокой яркостью и желтым цветом. Затем при снижении яркости тон плавно перетекает в красный, а яркость снижается.
Это можно описать линейным законом трех характеристик цвета: тон, насыщенность, яркость.
Здесь подбираются начальные и конечные точки этих характеристик. Затем по модели они рассчитываются исходя из заданной энергии частицы.
Описание алгоритма
Здесь представлены параметры параметры алгоритма, а полный исходный код представлен в конце статьи.
// Параметры подключения #define LED_pin 13 #define x_size 11 #define y_size 8 // Параметры алгоритма #define fire_dT 100 // задержка обрабобтки модели огня в мс #define fire_loss 0.6 // коэффициент потери энергии #fire_smooth 0.5 // Коэффициент сглаженности распределения энергии частиц #define sig 1.5 // ширина пламени #define dlt 50 // общая мощность пламени #define mlt 600 // усиление возгорания #define H0 0 #define H255 60 #define S0 200 #define S255 255 #define V0 0 #define V255 255
Подключение к проекту:
void setup() { SetupMatrix(); } void loop() { LDfire(); }
Видеодемонстрация
Выводы
Алгоритм получился хорошо настраиваемый и можно получить несколько разных интересных эффектов. Вид пламени мне очень понравился и полностью устраивает.
Мне интересна обратная связь и если есть предложение по улучшению алгоритма, то оставьте их в комментариях под статьей, в сообществе ВК или под видео в YouTube.
Исходный текст программы Ардуино (алгоритм огня)
LD_fire.ino (поместить в каталог проекта)
// Алгоритм огня для светодиодной адресной матрицы // // Автор: Роман Исаков, 2020 // (с) LabData.ru // Параметры подключения #define LED_pin 13 #define x_size 11 #define y_size 8 // Параметры алгоритма #define fire_dT 100 // задержка обрабобтки модели огня в мс #define fire_loss 0.6 // коэффициент потери энергии #define fire_smooth 0.5 // Коэффициент сглаженности распределения энергии частиц #define sig 1.5 // ширина пламени #define dlt 50 // общая мощность пламени #define mlt 600 // усиление возгорания #define Hue 0 // Цвет пламени (0- красный) #define H0 0 #define H255 60 #define S0 200 #define S255 255 #define V0 0 #define V255 255 #include <Adafruit_GFX.h> #include <Adafruit_NeoMatrix.h> #include <Adafruit_NeoPixel.h> Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(x_size, y_size, LED_pin, NEO_MATRIX_BOTTOM + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE, NEO_RGB + NEO_KHZ800); //Картина возгорания byte fire_frame[y_size][x_size]; //Распределение вероятностей возгорания byte fire_prob[x_size]; // Функция настройки матрицы. Поместить в setup void SetupMatrix(){ matrix.begin(); matrix.clear(); matrix.show(); Generate_Prob(sig, mlt); // Создать распределение вероятностей (форма огня) } // Сгенерировать распределение вероятностей void Generate_Prob(float sigma, int mult) { for(int i=0; i<x_size; i++){ fire_prob[i] = round(mult*(1/(sigma*2.5))*exp(-sq(i-(x_size/2))/(2*sq(sigma)))); } } // Функция реализации алгоритма огня. Поместить в loop void LDfire() { static uint32_t old_T = 0; if (millis() - old_T > fire_dT) { // отследивание срабатывания old_T = millis(); // Цикл огня // 1. Сдвиг матрицы for (uint8_t y = y_size - 1; y >0; y--) { for (uint8_t x = 0; x < x_size; x++) { fire_frame[y][x] = fire_frame[y - 1][x]*fire_loss; } } // 2. Заполнение нижней строки псевдослучайными числами for (uint8_t x = 0; x < x_size; x++) { byte fire_old = fire_frame[0][x]; fire_frame[0][x] = round(fire_old + (random(fire_prob[x], fire_prob[x]+dlt)-fire_old)*fire_smooth); } // 3. Визуализация матрицы for (uint8_t y = 0 ; y < y_size-1 ; y++) { for (uint8_t x = 0; x < x_size; x++) { matrix.drawPixel(x, y, E2Color(fire_frame[y_size-1-y][x])); } } matrix.show(); } } // Функция расчета цвета из энергии частицы uint16_t E2Color(byte E) { byte H = Hue + map(E, 0, 255, H0, H255); byte S = constrain(map(E, 0, 255, S255, S0), 0, 255); byte V = constrain(map(E, 0, 255, V0, V255), 0, 255); return HSV2Color(H,S,V); } // Функция перевода из модели HSV в код цвета. uint16_t HSV2Color(uint8_t hue, uint8_t sat, uint8_t val) { uint8_t red, green, blue; byte h = ((24 * hue / 17) / 60) % 6; byte vmin = (long)val - val * sat / 255; byte a = (long)val * sat / 255 * (hue * 24 / 17 % 60) / 60; byte vinc = vmin + a; byte vdec = val - a; switch (h) { case 0: red = val; green = vinc; blue = vmin; break; case 1: red = vdec; green = val; blue = vmin; break; case 2: red = vmin; green = val; blue = vinc; break; case 3: red = vmin; green = vdec; blue = val; break; case 4: red = vinc; green = vmin; blue = val; break; case 5: red = val; green = vmin; blue = vdec; break; } return matrix.Color(green,red,blue); }
(с) Роман Исаков, 2020