Class project : Control Robodog by Sign language

2/2566 FRA500/625 Human-Robotics Interface – narongrat.usir

System Scenario

User แสดงท่าทางภาษามือ ไปที่ Webcam เพื่อ Hand tracking จากนั้น Webcam จะส่งท่าทางภาษามือ ไป Jetson Nano เพื่อแยกแยะว่าท่าทางภาษามือที่ส่งไปเป็นท่าอะไร จากนั้นก็จะควบคุม Robodog ให้ทำท่าทาง ตามภาษามือ ที่เรา Program ไว้

เก็บข้อมูลท่าภาษามือจากการเยี่ยมโรงเรียนโสตศึกษาทุ่งมหาเมฆ

ได้ไปสำรวจ User (บุคคลพิการการได้ยิน) เพื่อเก็บข้อมูลลักษณะท่าทางของภาษามือที่จะนำมาใช้ในการสั่ง Robodog Go1 EDU ได้ดังนี้

  • นั่ง

  • หมอบ หรือ รอ

  • ยืน

  • เดินซ้าย

  • เดินขวา

  • เดินหน้า

  • เดินถอยหลัง

  • เดินกลับหลังหัน

  • หยุด

  • กระโดด

ผลจากการไปสำรวจมา สังเกตได้ว่าลักษณธมือจะอยู่ที่ระดับอก ทำให้ไม่เหมาะสมกับการนำ Hololen2 มาใช้ นอกจากนี้ลักษณะท่าทางที่ใช้แม้มีความหมายเหมือนกัน แต่การทำท่าไม่เหมือนกัน

ออกแบบโปรแกรมภาษามือ

Hand Landmark Model

Hand Landmark Model จะตรวจจับ ตำแหน่งจุดสำคัญของพิกัดมือและข้อมือ 3 มิติ 21 จุดภายในบริเวณมือที่ตรวจพบ

Configuration Options

  • static_image_mode : หากตั้งค่าเป็นเท็จ โซลูชันจะถือว่าภาพที่อินพุตเป็นสตรีมวิดีโอ หากตั้งค่าเป็นจริง การตรวจจับมือจะทำงานบนทุกภาพที่นำเข้า ค่าเริ่มต้นเป็นเท็จ
  • max_num_hands : จำนวนมือสูงสุดที่จะตรวจจับ ค่าเริ่มต้นเป็น 2
  • model_complexity : ความซับซ้อนของโมเดลจุดสังเกตบนมือ: 0 หรือ 1 ค่าเริ่มต้นเป็น 1
  • min_detection_confidence : ค่าความเชื่อมั่นขั้นต่ำ ([0.0, 1.0]) จากแบบจำลองการตรวจจับมือจึงจะถือว่าการตรวจจับสำเร็จ ค่าเริ่มต้นเป็น 0.5
  • min_tracking_confidence : ค่าความเชื่อมั่นขั้นต่ำ ([0.0, 1.0]) จากแบบจำลองการติดตามจุดสังเกตสำหรับจุดสังเกตมือที่จะถือว่าติดตามได้สำเร็จ ค่าเริ่มต้นเป็น 0.5

Output

  • multi_hand_landmarks : การรวบรวมมือที่ตรวจพบ/ติดตาม โดยแต่ละมือจะแสดงเป็นรายการจุดสังเกตของเข็มนาฬิกา 21 จุด และจุดสังเกตแต่ละจุดประกอบด้วย x, y และ z x และ y ถูกทำให้เป็นมาตรฐานเป็น [0.0, 1.0] ตามความกว้างและความสูงของภาพตามลำดับ z แสดงถึงความลึกของจุดสังเกตโดยความลึกที่ข้อมือเป็นจุดกำเนิด และยิ่งค่ามีค่าน้อย จุดสังเกตก็จะอยู่ใกล้กล้องมากขึ้น
  • multi_hand_world_landmarks : การรวบรวมมือที่ตรวจพบ/ติดตาม โดยแต่ละมือจะแสดงเป็นรายการจุดสังเกตของเข็ม 21 เข็มในพิกัดโลก จุดสังเกตแต่ละจุดประกอบด้วย x, y และ z: พิกัด 3 มิติในโลกแห่งความเป็นจริงในหน่วยเมตร โดยมีจุดเริ่มต้นที่จุดศูนย์กลางเรขาคณิตโดยประมาณของเข็มนาฬิกา
  • multi_handedness : การรวบรวมความถนัดของมือที่ตรวจพบ/ติดตาม ค่า “ซ้าย” หรือ “ขวา”

ภาษามือที่นำมาใช้

ท่าทางที่นำมาใช้ในโปรแกรมภาษามือประกอบไปด้วย 5 ท่าทาง ได้แก่ ท่านั่ง ท่าเดิน ท่ารอ ท่าหยุด และท่ายืน ส่วนท่ากระโดดนั้นไม่สามารถทำได้เนื่องจากจับภาพยาก และ ท่านี้ Robodog ใช้พลังงานเยอะ จีงไม่เหมาะนำมาทำ

ในส่วนโปรแกรมใช้ภาษา Python และ Library OpenCV กับ Mediapipe โดย OpenCV ใช้ในการประมวลผลรูปภาพและวิดีโอ ส่วน Mediapipe ใช้ในการสร้าง hand landmark model ตามที่อธิบายข้างต้น ดังนั้นวิธีจะแยกท่าทางแต่ละท่าสามารถทำได้โดย การบอกตำแหน่งแต่ละข้อมือ ว่าอันไหนอยู่สูงต่ำ หรือ ขวาซ้าย จึงได้ภาพและวิดีโอตามนี้

ท่านั่ง

ท่ารอ

ท่าเดิน

ท่าหยุด

ท่ายืน

Interface with Robodog

Code ภาษามือ

import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_hands = mp.solutions.hands

# SET webcam input:
cap = cv2.VideoCapture(0)
if cap.isOpened():
    print("can open camera")
else:
    cap = cv2.VideoCapture(cv2.CAP_V4L2)
    print("can open camera")
    
with mp_hands.Hands(
    model_complexity=0,
    min_detection_confidence=0.6,  # 0 - 1 
    min_tracking_confidence=0.5) as hands:
    while cap.isOpened():
        success, image = cap.read()
        if not success:
            print("......")
            continue
        image = cv2.flip(image, 1)
        image.flags.writeable = False
        image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
        results = hands.process(image)

        image.flags.writeable = True
        image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)

        L = 0
        R = 0
        H = 1
        
        if results.multi_hand_landmarks:
            for hand_landmarks in results.multi_hand_landmarks:
                handIndex = results.multi_hand_landmarks.index(hand_landmarks)
                handLabel = results.multi_handedness[handIndex].classification[0].label
                
                handLandmarks = []
                for landmarks in hand_landmarks.landmark:
                    handLandmarks.append([landmarks.x, landmarks.y]) 

                if handLabel == "Left":
                    L = 1
                elif handLabel == "Right":
                    R = 2
                H = L + R     
                
                if H == 1 or H == 2:
                    
                    
                    
                    #Stop
                    if handLandmarks[0][1] > handLandmarks[1][1] and  handLandmarks[0][1] > handLandmarks[5][1] and handLandmarks[0][1] < handLandmarks[17][1]:
                            H = 6
                            
                if H == 3:
                    #Wait
                    if handLandmarks[0][1] > handLandmarks[1][1] or handLandmarks[0][1] < handLandmarks[1][1] and handLandmarks[0][1] > handLandmarks[5][1] and handLandmarks[0][1] > handLandmarks[9][1] and handLandmarks[0][1] > handLandmarks[13][1] and handLandmarks[0][1] > handLandmarks[17][1]:
                        if handLandmarks[8][1] < handLandmarks[6][1] and handLandmarks[12][1] < handLandmarks[10][1] and handLandmarks[16][1] < handLandmarks[14][1] and handLandmarks[20][1] < handLandmarks[18][1]:
                            if handLabel == "Left" and handLandmarks[4][0] < handLandmarks[8][0]:
                                H = 4
                            if handLabel == "Right" and handLandmarks[4][0] > handLandmarks[8][0]:
                                H = 4

                    #sit
                    if handLandmarks[0][1] > handLandmarks[5][1] and  handLandmarks[0][1] < handLandmarks[17][1]:
                        if handLandmarks[16][1] < handLandmarks[14][1] and  handLandmarks[20][1] < handLandmarks[18][1]:
                            H = 5

                    #stand
                    if handLabel == "Left" and handLandmarks[0][1] < handLandmarks[1][1] and handLandmarks[0][1] < handLandmarks[5][1] and handLandmarks[0][1] < handLandmarks[9][1] and handLandmarks[0][1] < handLandmarks[13][1] and handLandmarks[0][1] < handLandmarks[17][1]:
                        if handLandmarks[5][1] < handLandmarks[8][1] and handLandmarks[9][1] < handLandmarks[12][1] and handLandmarks[17][1] >= handLandmarks[20][1] and handLandmarks[2][1] < handLandmarks[5][1]:
                            H=7
                    if handLabel == "Right" and handLandmarks[0][1] < handLandmarks[1][1] and handLandmarks[0][1] < handLandmarks[5][1] and handLandmarks[0][1] < handLandmarks[9][1] and handLandmarks[0][1] < handLandmarks[13][1] and handLandmarks[0][1] < handLandmarks[17][1]:
                        if handLandmarks[5][1] < handLandmarks[8][1] and handLandmarks[9][1] < handLandmarks[12][1] and handLandmarks[17][1] >= handLandmarks[20][1] and handLandmarks[2][1] < handLandmarks[5][1]:
                            H=7

                    #walk
                    if handLandmarks[6][1] < handLandmarks[8][1] and handLandmarks[10][1] < handLandmarks[12][1] and handLandmarks[14][1] < handLandmarks[16][1] and handLandmarks[18][1] < handLandmarks[20][1]:
                        H=8
                
                
                mp_drawing.draw_landmarks(
                image,
                hand_landmarks,
                mp_hands.HAND_CONNECTIONS,
                mp_drawing_styles.get_default_hand_landmarks_style(),
                mp_drawing_styles.get_default_hand_connections_style()
                )
                
        if H == 1:
            cv2.putText(image, "Left", (500,50), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0,0,255), 2)
        elif H == 2:
            cv2.putText(image, "Right", (500,50), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0,0,255), 2)
        elif H == 3:
            cv2.putText(image, "Left-Right", (500,50), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0,0,255), 2)
        elif H == 4:
            cv2.putText(image, "Wait", (20,50), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (124,252,0), 2)
        elif H == 5:
            cv2.putText(image, "Sit", (20,50), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (124,252,0), 2)
        elif H == 6:
            cv2.putText(image, "Stop", (20,50), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (124,252,0), 2)
        elif H == 7:
            cv2.putText(image, "Stand", (20,50), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (124,252,0), 2)
        elif H == 8:
            cv2.putText(image, "Walk", (20,50), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (124,252,0), 2)

        if results.multi_hand_landmarks == None:
            H = 0 
        
        cv2.imshow("Sign Language Apps", image)
        #Exit Key ESC
        if cv2.waitKey(1) & 0xFF == 27:
            break
    cap.release()