์ฌ๋ ํ์ฅ์ ์ํ ์๋์ ์ถ์ ๊ตฌ์กฐ ๋ก๋ด
My Favorite Production โญ
์๋์์ผ๋ก ๋ก๋ด ํ์ ์ค์๊ฐ ์ ์ดํ๋ DiSRHiT
- ํ๋ก์ ํธ ์๊ฐ
- ์์คํ ์ํคํ ์ฒ
- ์ฃผ์ ๊ธฐ๋ฅ
- ๊ธฐ์ ์คํ
- ํ๋์จ์ด ๊ตฌ์ฑ
- ์ค์น ๋ฐฉ๋ฒ
- ์ฌ์ฉ ๋ฐฉ๋ฒ
- ์ฝ๋ ๊ตฌ์กฐ
**DiSRHiT (Disaster Site Rescue Hand Tracking Robot)**๋ ์ฌ๋ ํ์ฅ์์ ๊ตฌ์กฐ ์์ ์ ์ํํ ์ ์๋ ์๋์ ์ ์ด ๋ก๋ด ์์คํ ์ ๋๋ค.
์ฌ๋ ํ์ฅ์์๋ 2์ฐจ ๋ถ๊ดด ์ํ์ผ๋ก ์ธํด ๊ตฌ์กฐ๋์์ด ์ง์ ์ ๊ทผํ๊ธฐ ์ด๋ ค์ด ์ํฉ์ด ๋ง์ต๋๋ค.
DiSRHiT๋ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์๊ฒฉ์ผ๋ก ์๋์๋ง์ผ๋ก ๋ก๋ด์ ์ ์ดํ ์ ์๋ ์์คํ
์ ๊ตฌํํ์ต๋๋ค.
- โ ์ง๊ด์ ์ ์ด: ์๊ฐ๋ฝ ๊ฐ๋๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ก๋ด์ ์ ๋ฌ
- โ ๋ฌด์ ํต์ : Socket ๊ธฐ๋ฐ ์๊ฒฉ ์ ์ด
- โ ์ ๋ฐ ์ ์ด: 11๊ฐ ์๋ณด ๋ชจํฐ ๋ ๋ฆฝ ์ ์ด
- โ ์ค์๊ฐ ์ฒ๋ฆฌ: MediaPipe ๊ธฐ๋ฐ ๋น ๋ฅธ ์ ์ถ์
- โ ์์ ์ฑ: ๋ฉํฐ์ค๋ ๋ฉ์ผ๋ก ์์ ์ ์ธ ๋ฐ์ดํฐ ์ ์ก
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ AI Computer (์ ์ด ์ปดํจํฐ) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ ์น์บ โ โ
โ โ โ โ โ
โ โ MediaPipe ์ ์ถ์ โ โ
โ โ โ โ โ
โ โ ์๊ฐ๋ฝ ๊ฐ๋ ๊ณ์ฐ (21 Landmarks)โ โ
โ โ โ โ โ
โ โ Socket Client (์ก์ ) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Socket (TCP/IP)
โ "action: 90: 90:90:..."
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Hand Robot (๋ก๋ด ํ) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Socket Server (์์ ) โ โ
โ โ โ โ โ
โ โ ๋ช
๋ น ํ์ฑ โ โ
โ โ โ โ โ
โ โ ๋ฉํฐ์ค๋ ๋ ๊ฐ๋ ๋ถ๋ฐฐ โ โ
โ โ โ โ โ
โ โ PCA9685 PWM ๋๋ผ์ด๋ฒ โ โ
โ โ โ โ โ
โ โ 11๊ฐ ์๋ณด ๋ชจํฐ ์ ์ด โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (Raspberry Pi) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
AI Computer (main_delay. py)
# MediaPipe๋ก 21๊ฐ ์ ๋๋๋งํฌ ์ถ์ถ
mp_hands = mp.solutions.hands
# ์๊ฐ๋ฝ ๊ฐ๋ ๊ณ์ฐ
joint_list = [
[1,5,6], # ์์ง
[0,9,10], # ๊ฒ์ง
[0,13,14], # ์ค์ง
[0,17,18], # ์ฝ์ง
# ... ์ด 9๊ฐ ๊ด์
]
# ๊ฐ๋ ๊ณ์ฐ (๋ฒกํฐ ๊ธฐ๋ฐ)
radians = np.arctan2(c[1]-b[1], c[0]-b[0]) - \
np.arctan2(a[1]-b[1], a[0]-b[0])
angle = np.abs(radians * 180.0 / np.pi)ํน์ง:
- 21๊ฐ ์ ๋๋๋งํฌ ์ค์๊ฐ ์ถ์
- 9๊ฐ ์ฃผ์ ๊ด์ ๊ฐ๋ ๊ณ์ฐ
- 180๋ ๋ฒ์ ์ ๊ทํ
- ํ๋ ์๋น ์ฒ๋ฆฌ ์๊ฐ: ~30ms
ํ๋กํ ์ฝ ์ค๊ณ:
๋ช
๋ น ํฌ๋งท:
"action:angle0:angle1:angle2:.. .:angle10"
์์:
"action:90:85:120:95:10: 15:20:25:45:50:60"
โโโฌโโ โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโ
๋ช
๋ น์ด 11๊ฐ ์๋ณด ๊ฐ๋
AI Computer โ Robot ํต์ :
# ์ก์ (AI Computer)
message = f"action:{': '.join(map(str, angles))}"
client_socket.send(message.encode())
# ์์ (Hand Robot)
data = client_socket.recv(1024)
command = data.decode().split(':')
# command[0] = 'action'
# command[1~11] = ๊ฐ ์๋ณด ๊ฐ๋ํน์ง:
- TCP/IP ์์ผ ํต์
- ๋ฉํฐ ํด๋ผ์ด์ธํธ ์ง์
- ์๋ฌ ํธ๋ค๋ง (ConnectionResetError)
- ์ปฌ๋ฌ ๋ก๊ทธ ์ถ๋ ฅ
PCA9685 PWM ์ ์ด:
class Servo_Controller_Class:
def __init__(self, Channel, ZeroOffset):
self.mChannel = Channel
self.m_ZeroOffset = ZeroOffset
# PCA9685 ์ด๊ธฐํ (I2C ์ฃผ์: 0x40)
self.mPwm = Adafruit_PCA9685.PCA9685(address=0x40)
self.mPwm.set_pwm_freq(60) # 60Hz
def SetPos(self, pos):
# ๊ฐ๋ โ PWM ํ์ค ๋ณํ
pulse = (650-150) * pos/180 + 150 + self.m_ZeroOffset
self.mPwm.set_pwm(self. mChannel, 0, int(pulse))PWM ๊ณ์ฐ:
ํ์ค ๋ฒ์: 150 ~ 650 (4096 ๋จ๊ณ ์ค)
๊ฐ๋ ๋ฒ์: 0ยฐ ~ 180ยฐ
๊ณต์: pulse = (650-150) ร (angle/180) + 150 + offset
๋์ ์ฒ๋ฆฌ:
# 11๊ฐ ์๋ณด๋ฅผ ๋์์ ์
๋ฐ์ดํธ
append_action_thread = []
for index in range(11):
thread = threading.Thread(
target=append_angle,
args=(index, command[index+1])
)
append_action_thread.append(thread)
# ๋ชจ๋ ์ค๋ ๋ ๋์ ์์
for thread in append_action_thread:
thread.start()Lock์ผ๋ก ๋๊ธฐํ:
action_lock = [threading.Lock() for _ in range(11)]
def append_angle(index, pos):
action_lock[index].acquire()
action_range[index][0] = int(float(pos))
action_lock[index]. release()| ๊ธฐ์ | ์ฉ๋ | ๋ฒ์ |
|---|---|---|
| Python | ๋ฉ์ธ ์ธ์ด | 3.8+ |
| MediaPipe | ์ ์ถ์ | 0.8.x |
| OpenCV | ์์ ์ฒ๋ฆฌ | 4.5.x |
| NumPy | ์์น ๊ณ์ฐ | 1.21.x |
| Socket | ๋คํธ์ํฌ ํต์ | Built-in |
| ๊ธฐ์ | ์ฉ๋ | ๋ฒ์ |
|---|---|---|
| Raspberry Pi | ๋ฉ์ธ ๋ณด๋ | 3B+ / 4 |
| Python | ์๋ฒ ์ธ์ด | 3.7+ |
| Adafruit PCA9685 | PWM ๋๋ผ์ด๋ฒ | I2C |
| RPi.GPIO | GPIO ์ ์ด | Latest |
| Socket | ์๋ฒ | Built-in |
| ๋ถํ | ์๋ | ์ฉ๋ |
|---|---|---|
| Raspberry Pi 3B+/4 | 1 | ๋ฉ์ธ ์ปจํธ๋กค๋ฌ |
| PCA9685 16์ฑ๋ PWM | 1 | ์๋ณด ์ ์ด |
| SG90 ์๋ณด ๋ชจํฐ | 11 | ์๊ฐ๋ฝ ๊ตฌ๋ |
| ์น์บ | 1 | ์ ์ถ์ ์ ๋ ฅ |
| 5V ์ ์ ๊ณต๊ธ | 1 | ์๋ณด ์ ์ |
| ์ ํผ ์์ด์ด | ๋ค์ | ์ฐ๊ฒฐ |
| ๋ธ๋ ๋๋ณด๋ | 1 | ํ๋กํ ํ์ |
11๊ฐ ์๋ณด ๋ชจํฐ:
[0] ์์ง ๊ธฐ์ ๋ถ
[1] ์์ง ์ฒซ์งธ ๋ง๋
[2] ์์ง ๋๋ง๋
[3] ๊ฒ์ง ๊ธฐ์ ๋ถ
[4] ๊ฒ์ง ์ฒซ์งธ ๋ง๋
[5] ๊ฒ์ง ๋๋ง๋
[6] ์ค์ง ์ฒซ์งธ ๋ง๋
[7] ์ฝ์ง ์ฒซ์งธ ๋ง๋
[8] ์๋ผ ์ฒซ์งธ ๋ง๋
[9] ์๋ชฉ ํ์
[10] ํ๊ฟ์น
git clone https://github.com/Deamonio/DiSRHiT.git
cd DiSRHiT/AI_computerpython3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activatepip install -r requirements.txtrequirements.txt:
mediapipe>=0.8.0
opencv-python>=4.5.0
numpy>=1.21.0
pygame>=2.0.0
gtts>=2.2.0ssh pi@<๋ผ์ฆ๋ฒ ๋ฆฌํ์ด_IP>git clone https://github.com/Deamonio/DiSRHiT.git
cd DiSRHiT/Hand_Robotpip3 install -r requirements.txtrequirements.txt:
RPi.GPIO>=0.7.0
adafruit-pca9685>=1.0.1sudo raspi-config
# Interfacing Options โ I2C โ Enable
sudo rebootsudo i2cdetect -y 1์ถ๋ ฅ ์์:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- โ PCA9685
# Raspberry Pi์์ ์คํ
cd DiSRHiT/Hand_Robot
python3 main. py์ถ๋ ฅ:
[Socket] Server started on 0.0.0.0:7672
[Socket] Waiting for connections...
IP ์ฃผ์ ํ์ธ:
hostname -I
# ์: 192.168.1.100IP ์ค์ :
# AI_computer/main_delay.py
HOST = '192.168.1.100' # Raspberry Pi IP
PORT = 7672์คํ:
cd DiSRHiT/AI_computer
python main_delay.py์ถ๋ ฅ:
[Socket] Connected to the Server
[Camera] Starting hand tracking...
์ ์ด ๋ฐฉ๋ฒ:
-
์น์บ ์์ ์์ ํด๊ธฐ
- MediaPipe๊ฐ 21๊ฐ ๋๋๋งํฌ ์ธ์
- ํ๋ฉด์ ์ ์ค์ผ๋ ํค ํ์
-
์๊ฐ๋ฝ ์์ง์ด๊ธฐ
- ๊ฐ ์๊ฐ๋ฝ ๊ฐ๋๊ฐ ์ค์๊ฐ ๊ณ์ฐ
- Socket์ผ๋ก ๋ก๋ด์ ์ ์ก
-
๋ก๋ด ํ ํ์ธ
- ๋ก๋ด ์๊ฐ๋ฝ์ด ๋์ผํ๊ฒ ์์ง์
- ์ง์ฐ ์๊ฐ: ~100ms
์ข ๋ฃ:
qํค: ํ๋ก๊ทธ๋จ ์ข ๋ฃCtrl+C: ๊ฐ์ ์ข ๋ฃ
DiSRHiT/
โโโ AI_computer/ # ์ ์ด ์ปดํจํฐ (Python 99.9%)
โ โโโ main_delay.py # ๋ฉ์ธ ์ ์ถ์ + Socket ํด๋ผ์ด์ธํธ
โ โโโ camera.py # ์น์บ ํ
์คํธ (์นด๋ฉ๋ผ 0)
โ โโโ camera2.py # ์น์บ ํ
์คํธ (์นด๋ฉ๋ผ 1)
โ โโโ requirements.txt # ์์กด์ฑ
โ
โโโ Hand_Robot/ # ๋ก๋ด ํ (Raspberry Pi)
โ โโโ main.py # Socket ์๋ฒ + ์๋ณด ์ ์ด
โ โโโ requirements.txt # ์์กด์ฑ
โ โโโ bin/ # ์ ํธ๋ฆฌํฐ ์คํฌ๋ฆฝํธ
โ โ โโโ i2cscan.py # I2C ์ฅ์น ์ค์บ
โ โ โโโ ftdi_urls.py # FTDI ์ฅ์น ํ์ธ
โ โ โโโ pyterm.py # ์๋ฆฌ์ผ ํฐ๋ฏธ๋
โ โโโ research_data/
โ โโโ socket_client.py # Socket ํ
์คํธ ํด๋ผ์ด์ธํธ
โ
โโโ profile_image. jpeg # ํ๋ก์ ํธ ์ด๋ฏธ์ง
โโโ README.md # ์ด ๋ฌธ์
โโโ test. md # ํ
์คํธ ๋ฌธ์
def draw_finger_angles(image, results, joint_list):
for hand in results.multi_hand_landmarks:
for joint in joint_list:
# 3๊ฐ ์ ์ผ๋ก ๊ฐ๋ ๊ณ์ฐ
a = np.array([hand.landmark[joint[0]].x,
hand.landmark[joint[0]].y])
b = np.array([hand.landmark[joint[1]].x,
hand.landmark[joint[1]].y])
c = np.array([hand.landmark[joint[2]].x,
hand.landmark[joint[2]].y])
# ๋ฒกํฐ ๊ฐ๋ ๊ณ์ฐ
radians = np.arctan2(c[1]-b[1], c[0]-b[0]) - \
np.arctan2(a[1]-b[1], a[0]-b[0])
angle = np.abs(radians * 180.0 / np.pi)
# 180๋ ์ด๊ณผ ์ ๋ณด์
if angle > 180. 0:
angle = 360 - angle
# ์๊ฐ๋ฝ๋ณ ๊ฐ๋ ๋ณด์
if cnt < 4: # ์์ง~์ฝ์ง
angle = 180 - angle
if cnt >= 4 and cnt <= 7: # ์ค๊ฐ ๋ง๋
if angle > 80:
angle = 180 - angle์๋ฆฌ:
- 3๊ฐ ๋๋๋งํฌ ์ถ์ถ (๊ด์ ์์ชฝ + ์ค์ฌ)
- 2๊ฐ ๋ฒกํฐ ์์ฑ
arctan2๋ก ๊ฐ ๋ฒกํฐ ๊ฐ๋ ๊ณ์ฐ- ๋ ๊ฐ๋ ์ฐจ์ด๋ก ๊ด์ ๊ฐ๋ ๋์ถ
- ์๊ฐ๋ฝ๋ณ ๋ณด์ ์ ์ฉ
def handle_client(client_socket, addr):
print(f"[Socket] Connected by: {addr[0]}:{addr[1]}")
while True:
try:
data = client_socket.recv(1024)
if not data:
break
# ๋ค๋ฅธ ํด๋ผ์ด์ธํธ์๊ฒ ๋ธ๋ก๋์บ์คํธ
for client in client_sockets:
if client != client_socket:
client. send(data)
# ๋ช
๋ น ํ์ฑ
command = data. decode().split(':')
if command[0] == 'action':
# 11๊ฐ ์๋ณด ๋์ ์
๋ฐ์ดํธ (๋ฉํฐ์ค๋ ๋ฉ)
threads = []
for index in range(11):
t = threading.Thread(
target=append_angle,
args=(index, command[index+1])
)
threads.append(t)
t.start()
except ConnectionResetError:
break
client_sockets.remove(client_socket)def SetPos(self, pos):
# ๊ฐ๋ (0~180ยฐ) โ PWM ํ์ค (150~650)
pulse = (650-150) * pos/180 + 150 + self.m_ZeroOffset
# PCA9685๋ก PWM ์ ํธ ์ ์ก
self. mPwm.set_pwm(self.mChannel, 0, int(pulse))PWM ํ์ด๋ฐ:
- ์ฃผํ์: 60Hz (16. 67ms ์ฃผ๊ธฐ)
- ํ์ค ๋ฒ์: 150~650 (4096 ๋จ๊ณ ์ค)
- 0ยฐ: 0. 61ms, 90ยฐ: 1.53ms, 180ยฐ: 2.45ms
์ฆ์:
[Socket][Error] Connection refused
ํด๊ฒฐ:
# 1. IP ์ฃผ์ ํ์ธ
hostname -I
# 2. ๋ฐฉํ๋ฒฝ ํ์ธ
sudo ufw allow 7672
# 3. ์๋ฒ ์ฌ์์
python3 main.py์ฆ์:
IOError: [Errno 2] No such file or directory
ํด๊ฒฐ:
# I2C ํ์ฑํ ํ์ธ
ls /dev/i2c*
# /dev/i2c-1 ์์ด์ผ ํจ
# I2C ์ฃผ์ ์ค์บ
sudo i2cdetect -y 1
# I2C ๋๊ตฌ ์ค์น
sudo apt-get install i2c-tools์ฆ์:
- ์๋ณด๊ฐ ๋ฏธ์ธํ๊ฒ ๋จ๋ฆผ
- ๊ฐ๋๊ฐ ๋ถ์์
ํด๊ฒฐ:
# 1. ์ ์ ๊ณต๊ธ ํ์ธ (5V 2A ์ด์)
# 2. ์บํจ์ํฐ ์ถ๊ฐ (1000ฮผF)
# 3. ๋ฐ๋์กด ์ค์
def SetPos(self, pos):
# ๊ฐ๋ ๋ณํ๊ฐ 2๋ ๋ฏธ๋ง์ด๋ฉด ๋ฌด์
if abs(pos - self.last_pos) < 2:
return
pulse = (650-150) * pos/180 + 150 + self.m_ZeroOffset
self.mPwm. set_pwm(self.mChannel, 0, int(pulse))
self.last_pos = pos์ฆ์:
FPS: 10~15 (๋ชฉํ: 30)
ํด๊ฒฐ:
# 1. ํด์๋ ๋ฎ์ถ๊ธฐ
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# 2. ๋ชจ๋ธ ๋ณต์ก๋ ๋ฎ์ถ๊ธฐ
mp_hands. Hands(
model_complexity=0, # 0: ๋น ๋ฆ, 1: ์ ํ
min_detection_confidence=0.5,
min_tracking_confidence=0.5
)
# 3. GPU ์ฌ์ฉ (๊ฐ๋ฅ ์)
mp_hands. Hands(
static_image_mode=False,
max_num_hands=1
)| ํญ๋ชฉ | ์์น | ๋น๊ณ |
|---|---|---|
| FPS | 25~30 | ์น์บ 30fps ๊ธฐ์ค |
| ์ง์ฐ ์๊ฐ | ~100ms | AI โ Robot |
| ํต์ ์๋ | <10ms | Local network |
| ์๋ณด ์๋ต | ~60ms | PWM 60Hz |
| ์ ํ๋ | ยฑ3ยฐ | ์๊ฐ๋ฝ ๊ฐ๋ |
AI Computer:
- CPU: 40~60% (i5 ์ด์)
- RAM: ~500MB
- GPU: MediaPipe (์ ํ)
Raspberry Pi:
- CPU: 20~30%
- RAM: ~200MB
- I2C ๋์ญํญ: ~100kbps
์๋๋ฆฌ์ค:
์ง์ง โ ๊ฑด๋ฌผ ๋ถ๊ดด โ 2์ฐจ ๋ถ๊ดด ์ํ
โ
DiSRHiT ํฌ์
โ ์๊ฒฉ ์กฐ์ข
โ
์์กด์ ํ์ โ ๋ฌผํ ์ ๋ฌ โ ๊ตฌ์กฐ
- ๋ฐฉ์ฌ๋ฅ ์ค์ผ ์ง์ญ
- ํํ ๋ฌผ์ง ์ ์ถ ํ์ฅ
- ํญ๋ฐ๋ฌผ ์ ๊ฑฐ
- ์ ๊ธฐ๋ฅ ํ๋ณต ํ๋ จ
- ์๊ฒฉ ์ฌํ ์น๋ฃ
- ์ ์์ง์ ๋ฐ์ดํฐ ์์ง
๊ธฐ์ฌ๋ ์ธ์ ๋ ํ์ํฉ๋๋ค! ๐
- Fork ์ด ์ ์ฅ์
- Feature ๋ธ๋์น ์์ฑ:
git checkout -b feature/AmazingFeature - ๋ณ๊ฒฝ์ฌํญ ์ปค๋ฐ:
git commit -m 'Add some AmazingFeature' - ๋ธ๋์น์ Push:
git push origin feature/AmazingFeature - Pull Request ์์ฑ
- ์์ ์ง์
- ํ ํผ๋๋ฐฑ ์ถ๊ฐ
- WebRTC ์์ ์คํธ๋ฆฌ๋ฐ
- ์์จ ์ฃผํ ๊ธฐ๋ฅ
- ์์ฑ ๋ช ๋ น ํตํฉ
์ด ํ๋ก์ ํธ๋ MIT License ํ์ ๋ฐฐํฌ๋ฉ๋๋ค.
[](mailto:hyun0810d@gmail. com)
ํ๋ก์ ํธ ๋งํฌ: https://github.com/Deamonio/DiSRHiT
| MediaPipe | Raspberry Pi | Adafruit | OpenCV |
|---|---|---|---|
| ์ ์ถ์ | ํ๋์จ์ด | PWM ๋๋ผ์ด๋ฒ | ์์ ์ฒ๋ฆฌ |
ํน๋ณ ๊ฐ์ฌ:
- ๐๏ธ Google MediaPipe - ์ ํํ ์ ์ถ์
- ๐ Raspberry Pi Foundation - ์ ๋ ดํ ์ปดํจํ ํ์
- โก Adafruit - ํ๋ฅญํ PCA9685 ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ๐๏ธ OpenCV Community - ์์ ์ฒ๋ฆฌ ๋๊ตฌ
Made with โค๏ธ and precision
"Saving lives through technology"
ยฉ 2025 Deamonio. All rights reserved.
