亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

使用一个 LED 创建图像

组件和用品

NEMA17 步进电机
× 2
Theremino 驱动器 DRV8825 用于 Theremino 系统的步进电机
× 2
SD 读卡器
× 1
Arduino Mega 2560
× 1
各种同步带和V型轮
× 1

必要的工具和机器

3D 打印机(通用)

应用和在线服务

Arduino IDE
GIMP
Autodesk Fusion 360

关于这个项目

想法

看了几个视频,看了很多关于光绘的文章,我决定试一试。光绘涉及使用具有很长曝光时间的相机来捕捉小光源。这允许单个光在单个图像中串成长条纹。

但是如果有人想要创建更详细的图片或使用许多不同的颜色怎么办?这就是我想出的想法来构建一个 2 轴 CNC 机器,该机器具有单个 RGB LED,可以改变颜色并“绘制”图像。

计划

该项目需要四个主要组件才能工作:一台 2 轴 CNC 机器、RGB LED、SD 卡和一个能够进行长时间曝光拍摄的相机。首先,Arduino Mega 会读取 SD 卡并找到要打印的位图。

然后,它会水平穿过并点亮相应的 LED,同时每次超过图像宽度时也会向下移动一行。最后,它会稍等片刻,然后搜索下一个位图,最后在没有更多图像要创建时停止。

建造钻机

由于我在设计和制造 CNC 机床方面的经验,这一步并不太难。我想做一些模块化的东西,也可以为其他项目扩展,所以我选择了一个简单的设计,使用两个同步带连接到沿着平行铝挤压件移动的横杆上。

这使每个轴的长度可以非常定制。 X 轴的末端有 3D 打印的端盖,其中一个端盖用于安装 X 轴步进电机和轴承。

读取位图

我选择位图文件格式是因为它简单且易于阅读。根据文件格式,文件本身有几个重要地址必须读取。它们是 0x12(宽度)、0x16(高度)、0x1C(颜色深度)、0xA(像素数据的位置),最后是 0x36(像素数据通常所在的位置)。

以两个或四个字节(16 或 32 位)的块读取数据,这也将指针前进到下一个地址。 read 函数遍历并获取所有重要数据,包括偏移量和大小。然后它逐行读取每个像素。

准备图像

由于大多数相机的曝光时间最长为 30 秒,因此在这段时间内可以显示的总像素数限制为大约 288 个。这相当于大约 18 x 16 的图像。为了制作我的图像,我加载了 gimp 并开始创建非常简单的像素艺术。其中包括一个精灵球、一颗心和一个跳跃的马里奥。然后我把这三张图片放到了SD卡根目录下一个叫“bitmaps”的目录下。该程序从此文件夹中读取所有图像。

绘画程序

由于步进电机没有内部定位反馈系统,它们的位置必须由软件跟踪。我编写的程序使用网格系统跟踪 LED 的位置,以便轻松缩放。当 Arduino Mega 启动时,步进器位置设置为 0、0,然后找到并读取第一个图像。然后,LED 闪烁五次,让摄影师知道几乎可以开始拍摄了。位图的读取方式是首先循环遍历每一行,然后在每一行中读取每一列。通过了解当前的行和列,步进电机可以移动到相同的位置。在每个位置,LED 会更改为相应像素的颜色。

(re)-创建图像

插入SD卡并为电机插入12v电源后,就可以打开机器了。在我的相机上,我将其设置为 20 秒的曝光时间、F36 的光圈、100 的 ISO 和 -5 档的曝光补偿,以最大限度地减少重影效果。绘制的第一张图片是一个精灵球,见这里:

虽然有点模糊,但还是可以清楚地看到形状。然后它创建了一个心脏位图:

由于此图像只有 9 x 9 像素,因此每个单独的像素都不太明确。最后,我画了一张马里奥跳跃的图:

这张图片重影重,主要是因为色彩鲜艳的像素很多。

未来的改进思路

我创作的光绘效果比我最初想象的要好得多,但仍有改进的空间。我想做的主要事情是通过让 LED 在变暗时移动,然后仅在静止时点亮来减少模糊量。这种技术将大大提高重建图像的清晰度。

代码

  • 光绘计划
光绘程序C/C++
//部分来自Adafruit的位图读取功能#include #include #include "DRV8825.h"#define MOTOR_STEPS 200#define RPM 150#define MICROSTEPS 4//引脚定义#define STEPPER_X_DIR 7#define STEPPER_X_STEP 6#define STEPPER_X_EN 8#define STEPPER_Y_DIR 4#define STEPPER_Y_STEP 5#define STEPPER_Y_EN 12#define X 0#define Y 1#define X_DIR_FLAG -1 //1 或-FLAG_1 翻转方向 //1 或-FLAG_1 翻转方向1 或 -1 翻转方向#define STEPS_PER_MM (3.75 * MICROSTEPS) //移动 1mm 所需的步数#define SPACE_BETWEEN_POSITIONS 5 //每次移动 5mm#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);120 setup(10begin);120 init_steppers(); SD.begin(SD_CS); createBitmaps(); stepperX.disable(); stepperY.disable(); while(1);}void loop() {}void createBitmaps(){ File dir =SD.open("bitmaps"); while(true){ 文件位图 =dir.openNextFile();如果(!位图){ 中断;油漆位图(位图);延迟(15000); } }#define BUFFPIXEL 20void paintBitmap(File bmpFile){ int bmpWidth, bmpHeight; uint8_t bmpDepth; uint32_t bmpImageOffset; uint32_t 行大小; // 并非总是 =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 =毫秒(); Serial.println(); Serial.print("加载图像'"); Serial.print(bmpFile.name()); Serial.println('\''); // 打开 SD 卡上请求的文件 // 解析 BMP 头 if (read16(bmpFile) ==0x4D42) { // BMP 签名 Serial.print("File size:"); Serial.println(read32(bmpFile)); (无效)read32(bmpFile); // 读取并忽略创建者字节 bmpImageOffset =read32(bmpFile); // 图像数据开始 Serial.print("Image Offset:"); Serial.println(bmpImageOffset, DEC); // 读取 DIB 头 Serial.print("Header size:"); Serial.println(read32(bmpFile)); bmpWidth =read32(bmpFile); bmpHeight =read32(bmpFile); if (read16(bmpFile) ==1) { // # 平面 -- 必须是 '1' bmpDepth =read16(bmpFile); // 每像素位数 Serial.print("Bit Depth:"); Serial.println(bmpDepth); if ((bmpDepth ==24) &&(read32(bmpFile) ==0)) { // 0 =未压缩的 goodBmp =true; // 支持的 BMP 格式——继续! Serial.print("图片尺寸:"); Serial.print(bmpWidth); Serial.print('x'); Serial.println(bmpHeight); // BMP 行被填充(如果需要)到 4 字节边界 rowSize =(bmpWidth * 3 + 3) &~3; // 如果 bmpHeight 为负,则图像为自上而下的顺序。 // 这不是经典,而是在野外观察到的。如果(bmpHeight <0){ bmpHeight =-bmpHeight;翻转 =假; } // 要加载的裁剪区域 w =bmpWidth; h =bmpHeight; if(bmpWidth*bmpHeight>290){ //Too large Serial.println("文件太大无法打印。");返回; } for(uint8_t i=0; i<5;i++){analogWrite(R, 150);延迟(500);模拟写入(R,0);延迟(500); } for (row =0; row=sizeof(sdbuffer)) { // 实际上 bmpFile.read(sdbuffer, sizeof(sdbuffer)); buffidx =0; // 将索引设置为开始 } // 将像素从 BMP 格式转换为 TFT 格式,推送显示 b =sdbuffer[buffidx++]; g =sdbuffer[buffidx++]; r =sdbuffer[buffidx++]; moveToPosition(col, row);激活LED(r,g,b); // 优化! //tft.pushColor(tft.Color565(r,g,b)); } // 结束像素analogWrite(R, 0);模拟写入(G,0);模拟写入(B,0); } // 结束扫描线 Serial.print("Loaded in "); Serial.print(millis() - startTime); Serial.println("毫秒"); } // 结束goodBmp } } bmpFile.close(); moveToPosition(0,0); if (!goodBmp) Serial.println("BMP 格式无法识别。");}uint16_t read16(File f) { uint16_t result; ((uint8_t *)&result)[0] =f.read(); // LSB ((uint8_t *)&result)[1] =f.read(); // MSB 返回结果;}uint32_t read32(File f) { uint32_t result; ((uint8_t *)&result)[0] =f.read(); // LSB ((uint8_t *)&result)[1] =f.read(); ((uint8_t *)&result)[2] =f.read(); ((uint8_t *)&result)[3] =f.read(); // MSB 返回结果;}void activateLED(int r, int g, int b){ Serial.print(F("LED has value of:")); Serial.print(r); Serial.print(", "); Serial.print(g); Serial.print(", "); Serial.println(b);模拟写入(R,R);模拟写入(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);当前位置[X] =x;当前位置[Y] =y; Serial.print("步进位置:"); Serial.print(currentPositions[X]); Serial.print(", "); Serial.println(currentPositions[Y]);}void init_steppers(){ stepperX.begin(RPM); stepperX.setEnableActiveState(LOW); stepperX.enable(); stepperX.setMicrostep(MICROSTEPS); stepperY.begin(RPM); stepperY.setEnableActiveState(LOW); stepperY.enable(); stepperY.setMicrostep(MICROSTEPS);}

定制零件和外壳

示意图


制造工艺

  1. 是什么让我们重新在产品设计中使用天然材料?
  2. 使用 Raspberry Pi 的运动传感器
  3. 创建和使用 FMEA 的维护经理指南
  4. 使用 Firebase 将传感器数据从一个 Arduino 发送到另一个
  5. 在不使用第三个的情况下交换两个 Python 变量
  6. 棺材舞曲
  7. 重新利用旧遥控器
  8. 使用 Firmata 和 Xbox One 控制器控制 Arduino Rover
  9. 8x LED 声音照明
  10. Arduino Quadruped
  11. 使用 LED 灯 (Li-Fi) 的数据传输
  12. 使用 Bolt 和 Arduino 控制 LED 亮度