影像辨識技術結合Q1ARM機器手臂進行物品分類

前言

本篇的專題教學主要是透過Microsoft LOBE.ai的影像分類神經網路訓練平台進行影像分類模型的訓練,匯出的影像分類神經網路模型將會以Tensorflow-Lite格式進行後續延伸的機器手臂影像分類辨識的操作,本篇教學採用的機器手臂是採用香港的一個Maker所設計的Q1-ARM機器手臂進行機械的運作,這位香港Maker朋友所設計的Q1-ARM機構非常簡單可靠,機械的動作也非常地確實,非常適合所有想嘗試製作機器手臂控制的初學者進行實作,本篇教學也會針對Q1-ARM的機構簡單介紹,而Q1-ARM相關的實作教學基於尊重原作者的智產權,以下提供原作者分享的連結https://wikifactory.com/@jason-workshop/q1-arm-mark-2,請各位有興趣的朋友可以至該原廠網站參考相關實作教學文件,本篇教學的主題將會區分成兩個部份的章節主題進行教學,分別如下:

  1. Q1-ARM的控制核心Linkit-7697微控器的程式設計
  2. 嵌入式(或個人電腦)Python控制核心的程式設計

希望透過以上兩個章節主題,讓每個有興趣實作影像辨識機器手臂控制的朋友都能夠實作出專屬於自己的「人工智慧機器手臂」。完整程式碼請由 cavedu github 取得

分類 技術教學文 標籤 LOBE.ai、Tensorflow-Lite、Classification、Python、Q1ARM
撰寫/攝影 曾俊霖
前情提要 在此之前CAVEDU部落格有一篇「Raspberry Pi 4 之輸送帶影像辨識分類機電整合專題」,該篇主要是透過Teachable Machine進行影像分類神經網路的訓練,並且匯出Tensorflow-Lite模型至樹莓派進行輸送帶的分類操作,透過樹莓派的GPIO的控制,進行伺服馬達與直流輸送帶馬達的控制。
時間 6小時以上 材料表 1.      Q1-ARM

2.      Linkit-7697

3.      Robot Shield V2 Servo Control Board for LinkIt 7697

4.      嵌入式系統或個人電腦

 


本篇專題教學,主要是要針對影像辨識的結果進行機器手臂的動作操作,由於機器手臂是透過Linkit-7697微控器進行控制,本身受限於微控器的運算效能不足以進行影像辨識運算,因此必須結合能夠進行神經網路模型運算的嵌入式系統(或個人電腦)運算能力較強大的平台進行操作,因此本篇專題教學是一個跨平台操作的系統,以下列出這個跨平台操作的系統架構圖。

一、Q1-ARM的操作概念說明

Q1-ARM是一個具有5個自由度的機器手臂,五個自由度分別是:底座(左右旋轉)、下臂(上下旋轉)、上臂(上下旋轉)、腕部(上下旋轉)、夾爪(開閉旋轉),基本上都是透過SG-90伺服馬達進行機構的運動,因此無法乘載過重的負載,但用於機器手臂基礎控制教學是非常合用的,相關機器手臂的組裝檔案連結如下:組裝說明

Q1-ARM是透過聯發科的Linkit-7697微控器進行控制,在本篇專題教學,我們將會採用Arduino IDE開發環境進行程式的設計,完整的Arduino IDE的原始程式碼為 q1arm_serial.ino,請由此取得

以下簡要介紹重要程式片段:

透過標準的Servo.h程式庫進行伺服馬達的設定,總共有5個伺服馬達,控制信號分別接至Linkit-7697的D16、D11、D9、D8、D7數位信號接腳。 D16:底座旋轉 D11:下臂 D9:上臂 D8:腕部 D7:抓爪
#include <Servo.h>
Servo __myservo16;
Servo __myservo11;
Servo __myservo9;
Servo __myservo8;
Servo __myservo7;
透過Serial.readStringUntil函數擷取UART串列資料,判斷 決定命令字串是否結束。 String_to_Int(str, 2):擷取字串2欄資料(分別是第0欄與第1欄,第0欄設定「馬達編號」,第1欄設定「馬達轉動角度」。
if (Serial.available()) {
// 讀取傳入的字串直到"\n"結尾
str = Serial.readStringUntil('\n');

String_to_Int(str, 2);
Serial.println(SA[0]);
Serial.println(A[1]);
}
  if (SA[0] == "數值") {
int PRE_angle = __myservo16.read();
if (A[1] >= PRE_angle) {
for (int angle=PRE_angle;angle<=A[1];angle++){
__myservo16.write(angle);
delay(20);
}
Serial.println("Servo_OK");
}
if (A[1] < PRE_angle) {
for (int angle=PRE_angle;angle>=A[1];angle--){
__myservo16.write(angle);
delay(20);
}
Serial.println("Servo_OK");
}
}

擷取字串第0欄位(SA[0]),判斷控制哪個伺服馬達。擷取字串第1欄位(A[1]),判斷伺服馬達的轉動角度。程式會讀取伺服馬達目前的初始角度狀態,決定增加或減少角度。角度控制透過for迴圈逐漸增減伺服馬達轉動角度,防止機器手臂因伺服馬達轉動速度太快導致機器手臂整體晃動不穩的狀況發生。

void String_to_Int(String temp, int count)
{
int index;

index = temp.indexOf(',');
SA[0] = temp.substring(0, index);

for (int i = 1; i < count; i++) {
temp = temp.substring(index + 1, temp.length());
index = temp.indexOf(',');
SA[i] = temp.substring(0, index);
A[i] = SA[i].toInt();
}
}

判斷來自UART接收的字串內容,透過.indexOf(‘,’)判斷各字串的分隔符號。
透過.substring(0, index)取出字串欄位內的資料。

由上述的程式運作可知,Linkit-7697透過USB-UART介面進行串列資料的接收,這樣的操作方式,其實非常適合進行跨平台的操作,只要能夠收發UART資料便可以進行操控。以下便要介紹命令機器手臂動作的運算中心的操作概念。

 

二、嵌入式系統(或個人電腦)運算操作的概念說明

本篇專題教學採用的Python程式運算主要是處理影像分類神經網路模型運算,這部分的運算由於有著較大的運算負荷,因此需要由具有較佳運算較能的嵌入式系統(如:樹莓派、Jetson系列、ASUS-Tinker系列等)或是個人電腦進行操作,此外,為了降低運算的負荷,對於訓練後匯出的神經網路模型,本篇專題教學將以Tensorflow-Lite模型進行後續的影像推論應用。

以下列出整個操作影像辨識與控制機器手臂的流程:

對於影像分類神經網路模型的訓練,本篇專題教學將是以Microsoft LOBE.ai平台進行神經網路模型的訓練,LOBE.ai的操作教學請參考本篇教學:微軟 LOBE ai – 離線訓練影像辨識模型!

此外,由於本篇專題教學為了提高訓練用影像資料集的品質,因此設計了一個蒐集影像照片的Python程式,這個Python主要的功能是能夠在即時影像畫面中指定一個特定的區域,或稱ROI(Region of Interest)區域,進行影像資料的蒐集,可以大幅降低訓練用資料集的照片不必要的影像干擾,使用者可以透過鍵盤的上下左右鍵進行ROI區域的位置,也可以透過鍵盤的「+」與「-」按鍵進行ROI區域的大小調整,在選定好特定的監控區域後按下「Enter」鍵確定拍照區域,接下來便可以按「空白鍵」進行拍照的動作,依照LOBE.ai的運作特性,我們建議針對每個要辨識的類別,約莫進行30張照片的蒐集,並且指定正確的分類資料夾名稱,拍照程式的程式碼為PHOTO_ARM.py,請由此取得。

請注意,操作拍照程式時,必須要確定機器手臂也確實連接至嵌入式系統(或個人電腦)上,並且確定攝影機連接的編號,拍照的檔案都是以Photo開頭命名的JPG圖檔,拍照完成後使用者必須確實轉存到指定的資料夾中。

嵌入式系統Python程式操作的方式:

python3 PHOTO_ARM.py --video 攝影機編號 --com UART連接埠編號
#例如
python3 PHOTO_ARM.py --video 0 --com 5

個人電腦透過Anaconda虛擬環境的操作方式:

 

python PHOTO_ARM.py --video 攝影機編號 --com UART連接埠編號
#例如
python PHOTO_ARM.py --video 0 --com 5

在完成拍照後,請將拍照的照片轉存到自行定義的類別資料夾中,類別資料夾名稱可以自行定義,但必須採用英文或數字,且名稱不可以有空格,在進行LOBE.ai平台運作的時候,也建議以資料集(Data-Set)的方式進行資料集匯入,請參考以下三張畫面截圖。

LOBE.ai在完成匯入訓練資料集與神經網路模型訓練後,本篇專題教學採用Tensorflow-Lite的模型進行匯出模型應用。

在完成匯出Tensorflow-Lite神經網路模型後,便可以進行後續影像辨識控制機器手臂的動作,這部分的程式必須確定以下三件事情都有確實完成連接或設定:

  1. Webcam攝影機完成連接於嵌入式系統(或個人電腦),並且確認攝影機連接編號。
  2. Q1-ARM完成連接於嵌入式系統(或個人電腦),並且確認UART串列通訊埠的編號。
  3. ai訓練後的神經網路模型資料夾與Python程式存放在同一個資料夾中。

 

影像辨識控制機器手臂Python程式,主要是由4個部分構成,分別是:

  1. UART串列資料傳輸設定
  2. Tensorflow-Lite模型匯入
  3. 影像分類推論
  4. 推論結果控制機器手臂

上述的4個部分,系統運算負荷最大的部分主要是在影像分類推論,主要原因在於程式需要多次確認影像辨識的結果,在穩定辨識多次(次數可以由程式內指定)後,才會進行機器手臂的控制操作,以避免機器手臂的錯誤動作。影像辨識控制機器手臂的程式碼為LOBE_WEBCAM_ARM.py,請由此取得。

嵌入式系統Python程式操作的方式:

python3 LOBE_WEBCAM_ARM.py --video 攝影機編號 --com UART連接埠編號 --model "模型資料夾"

例如:

python3 PHOTO_ARM.py --video 0 --com 5 --model "ARM_MODEL_LOBE TFLite"

 

個人電腦透過Anaconda虛擬環境的操作方式:
python LOBE_WEBCAM_ARM.py --video 攝影機編號 --com UART連接埠編號 --model "模型資料夾"

例如:

python PHOTO_ARM.py --video 0 --com 5 --model "ARM_MODEL_LOBE TFLite"

以下針對LOBE_WEBCAM_ARM.py的程式內容,進行簡要說明:

設定影像辨識分類的名稱,這部分必須要和LOBE.ai訓練神經網路模型時,各指定的類別名稱一致。
Condition_1 = 'OBJ_1'
Condition_2 = 'OBJ_2'
Condition_3 = 'OBJ_3'
Condition_4 = 'OBJ_4'
Condition_Other = 'Other'
get_prediction是影像推論的函式
def get_prediction(image, interpreter, signature):
影像推論的程式片段(略)
return outputs
#image是即時影像資料
#interpreter是神經網路模型
#signature是模型參數檔內容
process_image是影像預處理函式
def process_image(image, input_shape):
#影像前處理的程式片段(略)
return image.reshape(input_shape).astype(np.float32)
#image是即時影像資料
#input_shape是輸入影像的解析度資訊
    COM_PORT = args.com  #COM_PORT變數設定UART通訊連接埠的編號
BAUD_RATES = 9600 #BAUD_RATES變數設定RAUD為9600BPS
ser = serial.Serial(COM_PORT, BAUD_RATES) # 建立 ser 物件啟動序列通訊
進行影像辨識
if (times==1) and (key_flag == 0) and (Motion_Busy ==0):
prediction = get_prediction(image, interpreter, signature)
針對Condition_1條件1進行穩定辨識次數的計數。Condition_n條件n,以此類推。
if (classification_flag == 1) and (Label_name == Condition_1) and (prob > 0.5):
Condition_1_count = Condition_1_count + 1
Condition_2_count = 0
Condition_3_count = 0
Condition_4_count = 0
Condition_1連續穩定辨識次數達50次時開始進行機器手臂伺服馬達的動作設定。 Condition_n,以此類推。
if (Condition_1_count == 50):
Motion_Busy = 1
伺服馬達動作角度設定片段程式(略)
Motion_Busy = 0
”降低影像辨識推論的運算負荷,times>=10:表示每10個Frame進行一次影像辨識推論。”
偵測各種按鍵
read_dir_key = cv2.waitKeyEx(1)
if (read_dir_key != -1):
print(read_dir_key)
#進行各種按鍵偵測的動作程式片段(略)
# 按 q鍵 或 Esc鍵 結束程式
elif ((read_dir_key == 113) or (read_dir_key == 27)):
key_detect = 1
即時繪製影像辨識框
if (key_flag == 1):
cv2.rectangle(image_src,
(box_left,box_top),(box_left+scale,box_top+scale),
(0,255,0),2)
elif (key_flag == 0):
cv2.rectangle(image_src,
(box_left,box_top),(box_left+scale,box_top+scale),
(0,0,255),2)
繪製辨識框中心的連線,並且標示角度值。
box_center_x = box_left + int(scale / 2)
box_center_y = box_top + int(scale / 2)

dw = int(frame_width / 2) - box_center_x
dh = frame_height - box_center_y
angle = (math.atan(dw / dh)) / math.pi * 180

cv2.line(image_src,
(int(frame_width / 2),frame_height),
(box_center_x,box_center_y),
(0,255,255),2)

cv2.putText(image_src, str(round(angle,3)),
(int(frame_width * 0.6),30), cv2.FONT_HERSHEY_SIMPLEX, 1,
(0,255,255), 6, cv2.LINE_AA)
cv2.putText(image_src, str(round(angle,3)),
(int(frame_width * 0.6),30), cv2.FONT_HERSHEY_SIMPLEX, 1,
(0,0,0), 2, cv2.LINE_AA)
顯示即時影像畫面
cv2.imshow('Detecting_1 ....',image_src)

即時影像辨識與機器手臂的控制,影片連結如下

 


相關文章

 

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *