Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Создание изображений с использованием одного светодиода

Компоненты и расходные материалы

Шаговый двигатель NEMA17
× 2
Драйвер терменвокса DRV8825 для шаговых двигателей для системы Theremino
× 2
устройство чтения карт SD
× 1
Arduino Mega 2560
× 1
Различные ремни ГРМ и клиновые колеса
× 1

Необходимые инструменты и машины

3D-принтер (общий)

Приложения и онлайн-сервисы

IDE Arduino
GIMP
Autodesk Fusion 360

Об этом проекте

Идея

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

Но что, если кто-то хочет создать более детальную картинку или использовать много разных цветов? Так мне пришла в голову идея построить 2-осевой станок с ЧПУ с одним светодиодом RGB, который может менять цвет и «раскрашивать» изображение.

План

Для работы этого проекта потребуются четыре основных компонента:2-осевой станок с ЧПУ, RGB-светодиод, SD-карта и камера, способная делать снимки с длинной выдержкой. Сначала Arduino Mega считывает SD-карту и находит растровое изображение для печати.

Затем он будет проходить по горизонтали и загораться соответствующими светодиодами, а также перемещаться по строке каждый раз, когда ширина изображения превышается. Наконец, он немного подождет, а затем выполнит поиск следующего растрового изображения и, наконец, остановится, когда больше нет изображений для создания.

Строительство буровой

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

Это позволяет настраивать длину каждой оси. На концах оси X есть торцевые крышки, напечатанные на 3D-принтере, одна из которых имеет крепление для шагового двигателя оси X и подшипника.

Чтение растровых изображений

Я выбрал формат файла растрового изображения из-за его простоты и легкости чтения. В зависимости от формата файла в самом файле есть несколько важных адресов, которые необходимо прочитать. Это 0x12 (ширина), 0x16 (высота), 0x1C (глубина цвета), 0xA (расположение данных пикселей) и, наконец, 0x36 (где обычно находятся данные пикселей).

Данные читаются кусками по два или четыре байта (16 или 32 бита), что также перемещает указатель на следующий адрес. Функция чтения просматривает и собирает все важные данные, включая смещения и размеры. Затем он просматривает и считывает каждый пиксель, строка за строкой.

Подготовка изображений

Поскольку для большинства камер максимальное время выдержки ограничено 30 секундами, существует ограничение в 288 пикселей, которые могут быть отображены за это время. Это соответствует примерно изображению 18 x 16. Чтобы создать свои изображения, я загрузил gimp и начал создавать очень простую пиксельную графику. Среди них были покебол, сердце и прыгающий Марио. Затем я поместил эти три изображения в каталог под названием «bitmaps» в корневом каталоге SD-карты. Программа считывает все изображения из этой папки.

Программа рисования

Поскольку у шаговых двигателей нет внутренней системы обратной связи по позиционированию, их положение необходимо отслеживать с помощью программного обеспечения. Программа, которую я написал, отслеживает положение светодиода с помощью системы координат, чтобы облегчить масштабирование. Когда Arduino Mega запускается, позиции шагового двигателя устанавливаются на 0, 0, а затем первое изображение обнаруживается и читается. Затем светодиодный индикатор мигнет пять раз, чтобы дать понять фотографу, что пора начинать съемку. Растровое изображение читается, сначала просматривая каждую строку, и в каждой строке читается каждый столбец. Зная текущую строку и столбец, шаговые двигатели можно перемещать в те же положения. В каждой позиции цвет светодиода меняется на цвет соответствующего пикселя.

(повторное) -Создание изображения

После того, как вставили SD-карту и подключили источник питания 12 В для двигателей, пришло время включить машину. На своей камере я установил время экспозиции 20 секунд, диафрагму F36, ISO 100 и компенсацию экспозиции -5 ступеней, чтобы минимизировать побочные эффекты. Первым нарисованным изображением был покебол, который можно увидеть здесь:

Хотя он немного расплывчатый, форма все же хорошо видна. Затем он создал растровое изображение сердца:

Поскольку это изображение было всего 9 на 9 пикселей, каждый отдельный пиксель намного менее определен. Напоследок я нарисовал прыгающего Марио:

На этом изображении есть сильные ореолы, в основном из-за обилия ярко окрашенных пикселей.

Будущие идеи для улучшений

Световые картины, которые я создал, оказались намного лучше, чем я изначально ожидал, но все же есть возможности для улучшений. Главное, что я хотел бы сделать, это уменьшить степень размытия, заставив светодиод двигаться в затемненном состоянии, а затем загораться только в неподвижном состоянии. Этот метод значительно улучшит четкость воссозданных изображений.

Код

  • Программа рисования света
Программа рисования света C / C ++
 // Функция чтения растрового изображения частично из Adafruit # include  #include  #include "DRV8825.h" #define MOTOR_STEPS 200 # define RPM 150 # define MICROSTEPS 4 // определения контактов # define STEPPER_X_DIR 7 # определить STEPPER_X_STEP 6 # определить STEPPER_X_EN 8 # определить STEPPER_Y_DIR 4 # определить STEPPER_Y_STEP 5 # определить STEPPER_Y_EN 12 # определить X 0 # определить Y 1 # определить X_DIR_FLAG -1 // 1 или -1 для изменения направления # определить Y_DIR_ 1 или -1 для изменения направления # define STEPS_PER_MM (3.75 * MICROSTEPS) // шаги, необходимые для перемещения на 1 мм # define SPACE_BETWEEN_POSITIONS 5 // 5 мм на ход # define R A0 # define G A1 # define B A2 #define SD_CS 22int currentPositions [] ={0, 0}; DRV8825 stepperX (MOTOR_STEPS, STEPPER_X_DIR, STEPPER_X_STEP, STEPPER_X_EN); DRV8825 stepperY (MOTOR_STEPS, STEPPER_Y_DIR, STEPPER_Y_STEP, STEPPER_Y_EN); настройка Serial00. init_steppers (); SD.begin (SD_CS); createBitmaps (); stepperX.disable (); stepperY.disable (); while (1);} void loop () {} void createBitmaps () {Файл dir =SD.open ("bitmaps"); while (true) {Растровое изображение файла =dir.openNextFile (); если (! растровое изображение) {перерыв; } paintBitmap (растровое изображение); задержка (15000); }} #define BUFFPIXEL 20void paintBitmap (файл bmpFile) {int bmpWidth, bmpHeight; uint8_t bmpDepth; uint32_t bmpImageOffset; uint32_t rowSize; // Не всегда =bmpWidth; может иметь заполнение uint8_t sdbuffer [3 * BUFFPIXEL]; // буфер пикселей (R + G + B на пиксель) uint8_t buffidx =sizeof (sdbuffer); // Текущая позиция в sdbuffer boolean goodBmp =false; // Устанавливается в true при правильном синтаксическом анализе заголовка boolean flip =true; // BMP сохраняется снизу вверх int w, h, row, col; uint8_t r, g, b; uint32_t pos =0, startTime =millis (); Serial.println (); Serial.print («Загрузка изображения»); Serial.print (bmpFile.name ()); Serial.println ('\' '); // Открыть запрошенный файл на SD-карте // Разобрать заголовок BMP if (read16 (bmpFile) ==0x4D42) {// Подпись BMP Serial.print ("Размер файла:"); Serial.println (read32 (bmpFile)); (недействительно) read32 (bmpFile); // Чтение и игнорирование байтов создателя bmpImageOffset =read32 (bmpFile); // Начало данных изображения Serial.print ("Смещение изображения:"); Serial.println (bmpImageOffset, DEC); // Чтение заголовка DIB Serial.print ("Размер заголовка:"); Serial.println (read32 (bmpFile)); bmpWidth =read32 (bmpFile); bmpHeight =read32 (bmpFile); if (read16 (bmpFile) ==1) {// # плоскости - должно быть '1' bmpDepth =read16 (bmpFile); // бит на пиксель Serial.print ("Битовая глубина:"); Serial.println (bmpDepth); if ((bmpDepth ==24) &&(read32 (bmpFile) ==0)) {// 0 =несжатый goodBmp =true; // Поддерживаемый формат BMP - продолжаем! Serial.print ("Размер изображения:"); Serial.print (bmpWidth); Serial.print ('х'); Serial.println (bmpHeight); // Строки BMP дополняются (при необходимости) до 4-байтовой границы rowSize =(bmpWidth * 3 + 3) &~ 3; // Если bmpHeight отрицательное, изображение идет сверху вниз. // Это не канон, но наблюдалось в дикой природе. если (bmpHeight <0) {bmpHeight =-bmpHeight; flip =false; } // Обрезка загружаемой области w =bmpWidth; h =bmpHeight; if (bmpWidth * bmpHeight> 290) {// Слишком большой Serial.println («Файл слишком велик для печати.»); возвращение; } для (uint8_t я =0; я <5; я ++) {analogWrite (R, 150); задержка (500); analogWrite (R, 0); задержка (500); } for (row =0; row  =sizeof (sdbuffer)) {// Действительно bmpFile.read (sdbuffer, sizeof (sdbuffer)); buffidx =0; // Установить индекс в начало} // Преобразовать пиксель из формата BMP в формат TFT, нажать для отображения b =sdbuffer [buffidx ++]; г =SDBuffer [buffidx ++]; г =SDBuffer [buffidx ++]; moveToPosition (столбец, строка); активировать светодиод (r, g, b); // оптимизировано! //tft.pushColor(tft.Color565(r,g,b)); } // конечный пиксель analogWrite (R, 0); analogWrite (G, 0); analogWrite (B, 0); } // конец строки развертки Serial.print ("Загружен в"); Serial.print (миллис () - время начала); Serial.println ("мс"); } // конец goodBmp}} bmpFile.close (); moveToPosition (0,0); if (! goodBmp) Serial.println ("Формат BMP не распознан.");} uint16_t read16 (Файл f) {uint16_t result; ((uint8_t *) &результат) [0] =f.read (); // LSB ((uint8_t *) &result) [1] =f.read (); // MSB возвращает результат;} uint32_t read32 (File f) {uint32_t result; ((uint8_t *) &результат) [0] =f.read (); // LSB ((uint8_t *) &result) [1] =f.read (); ((uint8_t *) &результат) [2] =f.read (); ((uint8_t *) &результат) [3] =f.read (); // MSB возвращает результат;} void activateLED (int r, int g, int b) {Serial.print (F ("LED имеет значение:")); Serial.print (r); Serial.print (","); Серийный отпечаток (г); Serial.print (","); Serial.println (б); analogWrite (R, r); analogWrite (G, g); analogWrite (B, b);} void moveToPosition (int x, int y) {int newPosX =(x-currentPositions [X]) * STEPS_PER_MM * X_DIR_FLAG * SPACE_BETWEEN_POSITIONS; int newPosY =(y-currentPositions [Y]) * STEPS_PER_MM * Y_DIR_FLAG * SPACE_BETWEEN_POSITIONS; stepperX.move (newPosX); stepperY.move (newPosY); currentPositions [X] =x; currentPositions [Y] =y; Serial.print ("Шаговые позиции:"); Serial.print (currentPositions [X]); Serial.print (","); Serial.println (currentPositions [Y]);} void init_steppers () {stepperX.begin (RPM); stepperX.setEnableActiveState (НИЗКИЙ); stepperX.enable (); stepperX.setMicrostep (МИКРОСТЕПС); stepperY.begin (об / мин); stepperY.setEnableActiveState (НИЗКИЙ); stepperY.enable (); stepperY.setMicrostep (MICROSTEPS);} 

Изготовленные на заказ детали и корпуса

Схема


Производственный процесс

  1. Что привело нас к использованию натуральных материалов в дизайне продуктов?
  2. Датчик движения с использованием Raspberry Pi
  3. Руководство менеджера по обслуживанию по созданию и использованию FMEA
  4. Отправка данных датчика от одного Arduino к другому с помощью Firebase
  5. Поменять местами две переменные Python, не используя третью
  6. Мелодия танца в гробу
  7. Переназначение старых пультов дистанционного управления
  8. Управление Arduino Rover с помощью Firmata и контроллера Xbox One
  9. 8-кратное светодиодное освещение со звуком
  10. Четвероногий Arduino