手勢控制LED亮度專案教程 | MicroPython | 機器視覺 | RTL8722DM_MINI

RTL8722DM_MINI + MicroPython + 機器視覺

嗨!如果你還沒有看過之前的演示影片,請點擊上面的影片 :point_up_2:

計算機視覺雖然已經存在了很長一段時間,但是在微控制器(MCU)上運行機器視覺的應用還是有點不太實際,因為它需要大量的計算,還會消耗大量的電力,因此最好的方法是將與 CV 相關的計算移到更強大的電腦上,例如我們的筆電或云服務器,通過IoT的技術將數據下載到MCU中,讓 MCU來 實時控制傳感器/執行器。

因此這個專案主要有 2 塊組成,

  • 機器視覺算法和視頻採集 → PC
  • 運行MicroPython, 進行無線數據傳輸和 LED 控制 → RTL8722DM_MINI

接下來我們會聚焦每一個重要的組成部分。


1. 視頻捕捉和運行機器視覺算法

為了實現手勢識別,我選擇了眾所周知的 OpenCVMediaPipe 庫,因為它們都是在 Github 上的開源項目,而且它們都支援Python語言開發——這意味著我們可以使用 Python 來快速實現我們的邏輯並完成原型設計。

我這裏使用的代碼主要來自名為 Murtaza 的 Youtuber,只要在他的網站上註冊帳戶,你就可以免費地獲得他的代碼,但是因為我還沒有獲得開源他的代碼的許可,因此這裏我建議大家可以在點擊觀看他的手部追蹤以及 手勢控制 的影片,從影片中查看他的代碼並了解他是如何實現這些功能的。

當然,我自己也添加並編輯了的原始 Python 腳本,好讓它只運行我需要的任務,並幫助我通過 TCP socket將數據發送到我的 RTL8722DM_MINI上。下面的圖就是在我的 PC 上運行的所有邏輯的流程圖。

Untitled Diagram


程式碼

以下就是完整的代碼。

# Computer Vision related Code partially adopted from https://www.youtube.com/c/MurtazasWorkshopRoboticsandAI/featured
# Credit goes to him for create the module and example


import cv2
import time
import numpy as np
import math
import socket
import mediapipe as mp

########## Module #############
class handDetector():
    def __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5):
        self.mode = mode
        self.maxHands = maxHands
        self.detectionCon = detectionCon
        self.trackCon = trackCon

        self.mpHands = mp.solutions.hands
        self.hands = self.mpHands.Hands(self.mode, self.maxHands,
                                        self.detectionCon, self.trackCon)
        self.mpDraw = mp.solutions.drawing_utils

    def findHands(self, img, draw=True):
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        self.results = self.hands.process(imgRGB)
        # print(results.multi_hand_landmarks)

        if self.results.multi_hand_landmarks:
            for handLms in self.results.multi_hand_landmarks:
                if draw:
                    self.mpDraw.draw_landmarks(img, handLms,
                                               self.mpHands.HAND_CONNECTIONS)
        return img

    def findPosition(self, img, handNo=0, draw=True):

        lmList = []
        if self.results.multi_hand_landmarks:
            myHand = self.results.multi_hand_landmarks[handNo]
            for id, lm in enumerate(myHand.landmark):
                # print(id, lm)
                h, w, c = img.shape
                cx, cy = int(lm.x * w), int(lm.y * h)
                # print(id, cx, cy)
                lmList.append( [id, cx, cy])
                if draw:
                    cv2.circle(img, (cx, cy), 15, (255, 0, 255), cv2.FILLED)

        return lmList



############## Variables ##################
wCam, hCam = 640, 480
pTime = 0
minBri = 0
maxBri = 1
briArd =0


############## Declararion ##################

cap = cv2.VideoCapture(0) # default camera is 0, if you have another cam, you can set to 1
cap.set(3, wCam)
cap.set(4, hCam)
detector = handDetector(detectionCon=0.7)



########## Step 1 ###########
# Start a TCP server and bind to port 12345
# Use ipconfig to check the IP address of your PC
s = socket.socket()
print ("Socket successfully created")
port = 12345
s.bind(('', port))
print("socket binded to %s" % (port))
s.listen(5)
print ("socket is listening")
c, addr = s.accept()
print('Got connection from', addr)


######### Step 2 ###############
# Image capture and processing using mediapipe and opencv
while True:
    success, img = cap.read()
    img = detector.findHands(img)
    lmList = detector.findPosition(img, draw=False)
    if len(lmList) != 0:
        x1, y1 = lmList[4][1], lmList[4][2]
        x2, y2 = lmList[8][1], lmList[8][2]
        cx, cy = (x1 + x2) // 2, (y1 + y2) // 2

        cv2.circle(img, (x1, y1), 15, (255, 0, 255), cv2.FILLED)
        cv2.circle(img, (x2, y2), 15, (255, 0, 255), cv2.FILLED)
        cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), 3)
        cv2.circle(img, (cx, cy), 15, (255, 0, 255), cv2.FILLED)

        length = math.hypot(x2 - x1, y2 - y1)
        #print(length)

        # Hand range 50 - 300

        brightness = np.interp(length, [50, 300], [minBri, maxBri])

        briArd = np.around(brightness,2)

        #print(briArd, brightness, length)

        if length < 50:
            cv2.circle(img, (cx, cy), 15, (0, 255, 0), cv2.FILLED)

    # Print FPS
    cTime = time.time()
    fps = 1 / (cTime - pTime)
    pTime = cTime
    cv2.putText(img, f'FPS: {int(fps)}', (40, 50), cv2.FONT_HERSHEY_COMPLEX,
                1, (255, 0, 0), 3)

    # Display image
    cv2.imshow("Img", img)
    cv2.waitKey(1)

    # Sending the distance between our thumb and index wirelessly to IoT device
    c.sendall(str(briArd).encode())
    print("send data success")
    print(briArd, str(briArd))
    #c.close()



2.運行MicroPython來無線傳輸數據並控制LED亮度

我選擇 MicroPython 的原因是因爲這樣我就不用使用其他編程語言了 :laughing: :stuck_out_tongue_winking_eye: 所有的開發都可以用Python來完成~

如果有人不瞭解的話 – MicroPython是專為微控制器設計的 Python 3 解釋器,而RTL8722DM_MINI 已經支援 MicroPython。

使用MicroPython,我們就可以控制微控制器上的所有外設,以RTL8722DM_MINI爲例,我們只需13行的Python程式碼就可實現以下的功能,

  1. 連接到Wi-Fi
  2. 啟動一個TCP客戶端
  3. 通過PWM控制LED亮度

(MicroPython 的程式碼在這裡 :point_down:

程式碼

import socket
from wireless import WLAN
from machine import PWM
wifi = WLAN(mode = WLAN.STA)
wifi.connect(ssid = "yourNetwork", pswd = "password") # change the ssid and pswd to yours
c = socket.SOCK()
# make sure to check the server IP address and update in the next line of code
c.connect("192.168.0.106", 12345) 
p = PWM(pin = "PA_23")
while True:
	data = c.recv(512)
	f_brightness= float(data[:3])
	print(f_brightness)
	p.write(f_brightness)



總結

借助 Wi-Fi 和其他無線通訊方式,RTL8722DM_MINI 等支援物聯網的設備可以成為一個 AIoT 專案中不可或缺的一部分。使用 Python 語言,更是可以加速產品原型設計。這也是我在這個專案中感受最深的地方。

其實RTL8722DM_MINI 還有很多其他的有趣的功能還沒有用上,比如Audio CodecUltra-Low PowerBLE5.0,這些功能都很有潛力,可以讓我們用RTL8722DM_MINI來搭建一個強大的多媒體物聯網節點,并且可以部署在外面很長時間。如果有有興趣的朋友,也可以一起來溝通討論,如何來最大化地使用這個開發板 :wink:

1 Like