--- title: 機器人專題 tags: arduino --- ## 簡介 此課程我們使用 4DOF Mechanical Arm Kit V2.0 機器手臂套組來模擬怪手的機器運作原理,同時運用 Arduino 程式控制相關的操作,並以木塊當作夾取的物品。 --- ## 使用材料 1. Arduino Uno V4.0 開發板 2. 擴充版 3. 機器手臂組 4. 馬達 5. 蘑菇鈕 6. 電池盒、電池 7. 一些電線和螺絲、螺帽 8. 藍芽模組 ([操作介面app](https://play.google.com/store/apps/details?id=com.keyestudio.keyes_arm_123&pli=1)) --- 詳細材料規格如下圖: ![](https://i.imgur.com/1AMvIU7.jpg) --- ## 製作過程 --- ### 組裝步驟 依照參考資料中的組裝步驟進行組裝,組裝完成後依照下表進行接線,並且編寫程式。 | 部件 | 連接腳位 | | --------------- | ----------- | | 遙控器Left X軸 | A2 | | 底座馬達 | A0 | | 遙控器Left Y軸 | A3 | | 前手臂馬達 | A1 | | 遙控器Right X軸 | A5 | | 夾爪馬達 | 7(數字腳位) | | 遙控器Right Y軸 | A4 | | 後手臂馬達 | 6 | --- ### 程式編寫思維 1. 引入 Servo.h 函式庫 ```arduino=1 #include<Servo.h> ``` --- 2. 宣告四個變數 L_X, L_Y, R_X, R_Y,分別代表四個搖桿的讀值。 ```arduino=3 int L_X =0; int L_Y =0; int R_X =0; int R_Y =0; ``` --- 3. 宣告四個變數 pos_LX, pos_LY, pos_RX, pos_RY,分別代表四個伺服馬達的初始位置。 ```arduino=9 int pos_LX = 90; int pos_LY = 60; int pos_RX = 90; int pos_RY = 45; ``` --- 4. 宣告 Servo 型別的四個變數 MOTOR_LX, MOTOR_LY, MOTOR_RX, MOTOR_RY,分別代表四個伺服馬達。 ```arduino=15 Servo MOTOR_LX,MOTOR_LY,MOTOR_RY,MOTOR_RX; ``` --- 5. 分別定義四個函數`bottom()` `rear_hand()` `claw()` `rear_arm()` ,用來控制前手臂馬達、後手臂馬達、底座馬達、夾爪馬達,並且對個別馬達的轉動角度進行校正,最後個別設定馬達的延遲時間,因為篇幅的關係,底下的程式只列出其中馬達的函式,其他三個函式可見頁尾的完成程式碼區。 ```arduino=17 void bottom(){ L_X = analogRead(A2); // 搖桿向左 if(L_X<50){ if(pos_LX < 0){ pos_LX = 0; } else{ pos_LX=pos_LX-1; MOTOR_LX.write(pos_LX); delay(10); } } // 搖桿向右 if(L_X>650){ if(pos_LX > 180){ pos_LX = 180; } else{ pos_LX++; MOTOR_LX.write(pos_LX); delay(10); } } } ``` --- 6. 在 setup() 函式中,宣告並初始化四個伺服馬達,以及設定與電腦的溝通路徑和速度,如下: ```arduino=139 void setup() { MOTOR_LX.attach(A0); MOTOR_LY.attach(A1); MOTOR_RX.attach(7); MOTOR_RY.attach(6); Serial.begin(9600); } ``` --- 7. 在 loop() 函式中,利用 Serial.println() 指令印出 pos_RY 的值,並依序執行前面定義的四個函式,最後延遲 10 毫秒。重複進行以上動作,如下: ```arduino=149 void loop(){ Serial.println(pos_LY); claw(); rear_arm(); bottom(); rear_hand(); } ``` --- ## 遇到的困難 --- 1. 底座組裝問題 原本按照組裝說明書一步步的安裝,但後來準備安裝開發板時發現無法安裝,後來仔細研究組裝的方位才發現原來一開始的底板裝錯方向,導致需要拆開重新組裝,造成進度嚴重落後 --- 2. 馬達組裝問題 再安裝馬達的時候裝錯方向,導致編寫程式時,馬達超出轉動角度的限制,造成馬達過熱,後來經由微調程式改變馬達的角度建置,校正馬達回歸正常的運轉角度。 --- 3. 組裝電路失誤 剛開始我們以為每個腳位皆可接受類比訊號,後來發現只有 Analog pin 的腳位才可以接收 4. RX TX 占用問題 如果項目中需要使用TX,RX口,那麼在上傳的時候一定不能把這兩個插上,否則會報錯,而且是各種奇怪的錯誤,典型就是會顯示:項目上傳出錯。 解決:等項目上傳成功後,再將TX,RX口插上。就好了 參考: [串口通信或TX/RX顯示上傳項目出錯](https://www.twblogs.net/a/5ef4daa7a383f019dfff0961) --- ## 參考資料 ::: spoiler 手臂組裝與按鈕 <iframe src="https://drive.google.com/file/d/1vnT4L-yzVJtonMbBu-Fe49ow11-V5m-a/preview" width="640" height="480" allow="autoplay"></iframe> ::: :::spoiler 延伸模組 <iframe src="https://drive.google.com/file/d/1etJxT14MhvON35VsX2gAK0TfDcTT6pIA/preview" width="640" height="480" allow="autoplay"></iframe> ::: --- ## 操作影片 :::spoiler 搖桿控制 <iframe src="https://drive.google.com/file/d/1xb5TNAyGyFC_sEF2SgTz1Tc26aD2tcOG/preview" width="640" height="480" allow="autoplay"></iframe> ::: :::spoiler 藍芽模組控制 <iframe src="https://drive.google.com/file/d/1VUJ5it1U1nlBNwX_bN8sfEG3L4oI-xgY/preview" width="640" height="480" allow="autoplay"></iframe> ::: --- ## 分工表 | 項目 | 31312 邱紹瑋 | 31316 張閔盛 | 31322 黃冠霖 | | ----------------- |:------------:|:------------:|:------------:| | 組裝機械手臂 | O | O | O | | 電路連接 | O | O | O | | 編寫程式碼 | O | | | | 簡報製作 | O | O | O | | Hackmd 文字稿製作 | O | | | | 上臺分享 | | O | O | --- ## 機械手臂完整程式碼: :::spoiler 遙感控制 ```arduino= #include<Servo.h> int L_X =0; int L_Y =0; int R_X =0; int R_Y =0; int pos_LX = 90; int pos_LY = 60; int pos_RX = 90; int pos_RY = 45; Servo MOTOR_LX,MOTOR_LY,MOTOR_RY,MOTOR_RX; void bottom(){ L_X = analogRead(A2); // 搖桿向左 if(L_X<50){ if(pos_LX < 0){ pos_LX = 0; } else{ pos_LX=pos_LX-1; MOTOR_LX.write(pos_LX); delay(10); } } // 搖桿向右 if(L_X>650){ if(pos_LX > 180){ pos_LX = 180; } else{ pos_LX++; MOTOR_LX.write(pos_LX); delay(10); } } } void rear_hand(){ L_Y = analogRead(A3); // 搖桿向下 if(L_Y > 700){ if(pos_LY < 0){ pos_LY = 0; } else{ pos_LY=pos_LY-1; MOTOR_LY.write(pos_LY); delay(15); } } // 搖桿向上 if(L_Y < 300){ if(pos_LY > 180){ pos_LY = 180; } else{ pos_LY++; MOTOR_LY.write(pos_LY); delay(15); } } } void claw(){ R_X = analogRead(A5); // 搖桿向左 if(R_X<=300){ if(pos_RX < 0){ pos_RX = 0; } else{ pos_RX=pos_RX-1; MOTOR_RX.write(pos_RX); delay(10); } } // 搖桿向右 if(R_X>700){ if(pos_RX > 180){ pos_RX = 180; } else{ pos_RX++; MOTOR_RX.write(pos_RX); delay(10); } } } void rear_arm(){ R_Y = analogRead(A4); // 搖桿向下 if(R_Y > 700){ if(pos_RY > 90){ pos_RY = 90; } else{ pos_RY=pos_RY+1; MOTOR_RY.write(pos_RY); delay(15); } } // 搖桿向上 if(R_Y < 400){ if(pos_RY < 0){ pos_RY = 0; } else{ pos_RY--; MOTOR_RY.write(pos_RY); delay(15); } } } void setup() { // put your setup code here, to run once: MOTOR_LX.attach(A0); MOTOR_LY.attach(A1); MOTOR_RX.attach(7); MOTOR_RY.attach(6); Serial.begin(9600); } void loop() { // L_Y = analogRead(A3); // Serial.print("L_X:"); // Serial.println(L_X); // Serial.print("L_Y:"); // Serial.println(L_Y); Serial.println(pos_LY); claw(); rear_arm(); bottom(); rear_hand(); } ``` ::: :::spoiler 藍芽控制 ```arduino= #include<Servo.h> int L_X =0; int L_Y =0; int R_X =0; int R_Y =0; int pos_LX = 90; int pos_LY = 60; int pos_RX = 90; int pos_RY = 45; char val; Servo MOTOR_LX,MOTOR_LY,MOTOR_RY,MOTOR_RX; void left_XL(){ pos_LX++; MOTOR_LX.write(pos_LX); delay(10); // 搖桿向左 if(pos_LX < 0){ pos_LX = 0; } } void left_XR(){ pos_LX--; MOTOR_LX.write(pos_LX); delay(10); // 搖桿向右 if(pos_LX > 180){ pos_LX = 180; } } void left_YB(){ // 搖桿向下 pos_LY=pos_LY-1; MOTOR_LY.write(pos_LY); delay(10); if(pos_LY < 0){ pos_LY = 0; } } void left_YF(){ // 搖桿向上 pos_LY++; MOTOR_LY.write(pos_LY); delay(10); if(pos_LY > 180){ pos_LY = 180; } } void right_XL(){ // 搖桿向左 pos_RX=pos_RX-1; MOTOR_RX.write(pos_RX); //delay(10); if(pos_RX < 0){ pos_RX = 0; } } void right_XR(){ // 搖桿向右 pos_RX++; MOTOR_RX.write(pos_RX); //delay(10); if(pos_RX > 60){ pos_RX = 60; } } void right_YB(){ // 搖桿向下 if(pos_RY > 90){ pos_RY = 90; } pos_RY=pos_RY+1; MOTOR_RY.write(pos_RY); delay(10); } void right_YF(){ // 搖桿向上 if(pos_RY < 0){ pos_RY = 0; } pos_RY--; MOTOR_RY.write(pos_RY); delay(10); } void setup() { // put your setup code here, to run once: MOTOR_LX.attach(A0); MOTOR_LY.attach(A1); MOTOR_RX.attach(7); MOTOR_RY.attach(6); Serial.begin(9600); } void loop() { // L_Y = analogRead(A3); // Serial.print("L_X:"); // Serial.println(L_X); // Serial.print("L_Y:"); // Serial.println(L_Y); //Serial.println(pos_LY); // 一般控制 // claw(); // rear_arm(); // bottom(); // rear_hand(); // 藍芽控制 Serial.println(pos_RX); if(Serial.available()){ val = Serial.read(); Serial.println(val); } switch(val) { case 'L': while(val != 'S'){ if(Serial.available()){ val = Serial.read(); Serial.println(val); } left_XL(); } break; //claw opens case 'R': while(val != 'S'){ if(Serial.available()){ val = Serial.read(); Serial.println(val); } left_XR(); } break; //claw closes case 'F': while(val != 'S'){ if(Serial.available()){ val = Serial.read(); Serial.println(val); } left_YF(); } break; //left servo swings forward case 'B': while(val != 'S'){ if(Serial.available()){ val = Serial.read(); Serial.println(val); } left_YB(); } break; //left servo swings back ward case 'f': while(val != 'S'){ if(Serial.available()){ val = Serial.read(); Serial.println(val); } right_YF(); } break; //right servo stretches out case 'b': while(val != 'S'){ if(Serial.available()){ val = Serial.read(); Serial.println(val); } right_YB(); } break; //right servo draws back case 'l': while(val != 'S'){ if(Serial.available()){ val = Serial.read(); Serial.println(val); } right_XL(); } break; //base servo turns left case 'r': while(val != 'S'){ if(Serial.available()){ val = Serial.read(); Serial.println(val); } right_XR(); } break; //base servo turns right } } ``` ::: --- ## 作品照片 --- ![](https://i.imgur.com/EfVP1BO.jpg) --- ![](https://i.imgur.com/HKv0JMj.jpg)