---
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))
---
詳細材料規格如下圖:

---
## 製作過程
---
### 組裝步驟
依照參考資料中的組裝步驟進行組裝,組裝完成後依照下表進行接線,並且編寫程式。
| 部件 | 連接腳位 |
| --------------- | ----------- |
| 遙控器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
}
}
```
:::
---
## 作品照片
---

---
