条状LED灯墙 | Filment LED wall

使用条状LED制作的屏幕

效果演示 | Demo

系统架构 | Architecture

电脑 –串口–> 单片机 –GPIO–> 串联灯光模块

硬件 | Hardware

  • Arduino UNO 开发板
  • JXI5020 LED 驱动器
  • 38mm 3V 2700K LED陶瓷灯丝

上位机代码 | Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import cv2
import serial
import time
import pyrealsense2 as rs
import matplotlib.pyplot as plt
import numpy as np

# 屏幕设置
screenWidth = 3
screenHeight = 3

pixelWidth = screenWidth * 4
pixelHeight = screenHeight * 4

# Open Serial port
port = 'COM16'
ser = serial.Serial(port, 115200, timeout=0.5)
time.sleep(2)
print("Serial port {} opened".format(port))

def sendCmd(cmd):
ser.write((str(cmd) + '\n').encode())
return

# Realsense D415i setting
pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
pipeline.start(config)

while True:
frames = pipeline.wait_for_frames()
depth_rs = frames.get_depth_frame()

# 格式转换
depth = np.asanyarray(depth_rs.get_data())

# 获取灰度深度图最大4米
dimg_gray = cv2.convertScaleAbs(depth, alpha=255/4000)

# 镜像
dimg_gray = cv2.flip(dimg_gray, 1)

# 裁剪成正方形
gray_part = dimg_gray[0:480, 80:540]

# 缩放,降低分辨率
gray_blur = cv2.blur(gray_part, (30, 30))
gray_rescaled = cv2.resize(gray_blur, (pixelWidth, pixelHeight))
ret, gray_thresh = cv2.threshold(gray_rescaled, 100, 255, cv2.THRESH_BINARY_INV)

# 电脑端显示
gray_rescaled_show = cv2.resize(gray_thresh, (480, 480), interpolation=cv2.INTER_AREA)
color_depth = cv2.applyColorMap(gray_part, cv2.COLORMAP_JET)
cv2.imshow('Color Depth', color_depth)
cv2.imshow('Gray Blur', gray_blur)
cv2.imshow('Gray Rescaled', gray_rescaled_show)

# 向下位机输出16个像素
def displayUnit(unitRow, unitCol, image):
for row in range(unitRow * 4 + 4 - 1, unitRow * 4 - 1, -1):
for col in range(unitCol * 4 + 4 - 1, unitCol * 4 - 1, -1):
if image[row][col] > 0:
sendCmd(0)
else:
sendCmd(1)

# 向下位机发送数据
if screenHeight % 2 == 0: # 屏幕高度为偶数
for unitRow in range(screenHeight):
if unitRow % 2 == 0: # 偶数行正向遍历
for unitCol in range(screenWidth):
displayUnit(unitRow, unitCol, gray_thresh)
else: # 奇数行反向遍历
for unitCol in range(screenWidth - 1, -1, -1):
displayUnit(unitRow, unitCol, gray_thresh)
else: # 屏幕高度为奇数
for unitRow in range(screenHeight):
if unitRow % 2 == 0: # 偶数行反向遍历
for unitCol in range(screenWidth - 1, -1, -1):
displayUnit(unitRow, unitCol, gray_thresh)
else: # 奇数行正向遍历
for unitCol in range(screenWidth):
displayUnit(unitRow, unitCol, gray_thresh)

# 向下位机发送同步信号
sendCmd(2)

if cv2.waitKey(1) == ord('q'):
break

cv2.destroyAllWindows()

下位机代码 | Arduino

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#define LED_OE  9
#define LED_LE 6
#define LED_CLK 3
#define LED_SDI 5

void setup() {
Serial.begin(115200);

pinMode(LED_OE,OUTPUT);
pinMode(LED_LE,OUTPUT);
pinMode(LED_CLK,OUTPUT);
pinMode(LED_SDI,OUTPUT);

digitalWrite(LED_OE,HIGH);
digitalWrite(LED_LE,LOW);
digitalWrite(LED_SDI,HIGH);
digitalWrite(LED_CLK,LOW);
Serial.println("Init done!");

digitalWrite(LED_OE,LOW);
}

void loop()
{
// Read command from serial port
while (Serial.available() > 0) {
int cmd = Serial.parseInt();
if (Serial.read() == '\n')
readCmd(cmd);
}
}

void readCmd(unsigned int cmd) {
switch(cmd) {
case 0: // Black Pixel
digitalWrite(LED_SDI,1);
digitalWrite(LED_CLK,HIGH);
digitalWrite(LED_CLK,LOW);
break;
case 1: // White pixel
digitalWrite(LED_SDI,0);
digitalWrite(LED_CLK,HIGH);
digitalWrite(LED_CLK,LOW);
break;
case 2: // Output
digitalWrite(LED_LE,HIGH);
digitalWrite(LED_LE,LOW);
break;
}
}

参考资料 | Reference

乒乓显示器: https://www.youtube.com/watch?v=EZEMK-C-nSo&t=99s