事實證明,機械臂在許多需要速度、準(zhǔn)確性和安全性的應(yīng)用中非常有用且更高效。但對我來說,更重要的是,這些東西在工作時看起來很酷。我一直希望有一個機械臂可以幫助我完成日常工作,就像托尼·斯塔克在他的實驗室中使用的Dum-E和Dum-U一樣。可以看到這兩個機器人在制作鋼鐵俠套裝或使用攝像機拍攝他的作品時幫助他。其實阿呆也救過他一次命......這就是我想阻止它的地方,因為這不是粉絲頁面。除了虛構(gòu)的世界,還有許多由發(fā)那科、庫卡、電裝、ABB、安川等制造的很酷的現(xiàn)實世界機器人手臂。這些機械臂用于汽車,采礦廠,化學(xué)工業(yè)和許多其他地方的生產(chǎn)線。
因此,在本教程中,我們將在Arduino和MG995伺服電機的幫助下構(gòu)建自己的機械臂。機器人總共有4個自由度(DOF),不包括夾具,可以通過電位計控制。除此之外,我們還將對其進行編程,使其具有錄制和播放功能,以便我們可以記錄動作并要求機械臂根據(jù)需要多次重復(fù)它。聽起來很酷吧!!!所以讓我們開始構(gòu)建....
所需材料
Arduino Nano
5 MG-995伺服電機
5 電位器
性能板
伺服喇叭
螺母和螺釘
注意:機械臂的身體完全是3D打印機。如果您有打印機,則可以使用給定的設(shè)計文件打印它們。否則,使用提供的3D模型并使用木材或丙烯酸加工零件。如果你什么都沒有,那么你可以用紙板來建造簡單的機械臂。
3D打印和組裝機械臂
構(gòu)建這個機械臂最耗時的部分是在構(gòu)建它的身體時。最初,我開始使用Solidworks設(shè)計車身,但后來意識到Thingiverse上有許多很棒的設(shè)計,沒有必要重新發(fā)明輪子。因此,我瀏覽了設(shè)計,發(fā)現(xiàn)Ashing的機械臂V2.0將與我們的MG995伺服電機完美配合,并且完全適合我們的目的。
因此,請訪問他的Thingiverse頁面(上面給出的鏈接)并下載模型文件。總共有14個部分需要打印,所有這些部分的STL文件都可以從Thingiverse頁面下載。我使用Ultimaker的Cura 3.2.1軟體來切分STL檔案,並使用我的TEVO tarantula 3D列印機來列印它們。如果您想了解有關(guān)3D打印機及其工作原理的更多信息,可以閱讀有關(guān)3D打印入門指南的文章。
幸運的是,沒有一個部件有懸垂結(jié)構(gòu),因此不需要支撐。設(shè)計非常簡單,因此可以通過任何基本的3D打印機輕松處理。大約在打印 4.5 小時后,所有部件都可以組裝了。組裝說明再次由Ashing本身整齊地解釋,因此我不打算涵蓋它。
一個小提示是,您必須打磨/銼削零件的邊緣才能使電機適合。所有電機都將通過一點機械力舒適地安裝。要有耐心,如果電機看起來有點緊,請使用文件為電機騰出空間。您需要 20 個 3 毫米螺栓來組裝機器人 ARM。
安裝電機后,請確保它可以旋轉(zhuǎn)并到達所需的位置,然后再永久擰緊。組裝完成后,您可以繼續(xù)延長前三個伺服電機的電線。我用公對母的電線來延長它們并將其帶到電路板上。確保正確利用電線,以免它們在手臂工作時妨礙您。組裝好后,我的機械臂在下圖中看起來像這樣。
電路圖
MG995伺服電機以5V運行,Arduino板具有5V穩(wěn)壓器。因此,創(chuàng)建電路非常容易。我們必須將5 個伺服電機連接到 Arduino PWM 引腳,將 5 個電位計連接到 Arduino 模擬引腳以控制伺服電機。下面給出了相同的電路圖。
對于這個電路,我沒有使用任何外部電源。Arduino通過USB端口供電,板上的+5v引腳用于為電位計和伺服電機供電。在我們的機械臂中,在任何給定的時間實例中,只有一個伺服電機將處于運動狀態(tài),因此消耗的電流將小于 150mA,這可以通過 Arduino 板的板載穩(wěn)壓器提供。
我們有5個伺服電機和5個電位器分別控制它們。這 5 個電位計連接到 Arduino 板的 5 個模擬引腳 A0 至 A4。伺服電機由PWM信號控制,因此我們必須將它們連接到Arduino的PWM引腳。在Arduino Nano上,引腳D3,D5,D6,D9和D11僅支持PWM,因此我們將前5個引腳用于伺服電機。我使用性能板焊接連接,完成后我的板如下所示。如果需要,我還添加了一個桶形插孔,通過電池為設(shè)備供電。但是,它是完全可選的。
如果您完全不熟悉伺服電機和Arduino,那么建議您在繼續(xù)項目之前閱讀伺服電機基礎(chǔ)知識和使用Arduino控制伺服文章。
為機械臂編程Arduino
現(xiàn)在,有趣的部分是對Arduino進行編程,以允許用戶記錄使用POT所做的動作,然后在需要時播放。為此,我們必須為兩種模式對Arduino進行編程。一次是錄制模式,另一個是播放模式。用戶可以使用串行監(jiān)視器在兩種模式之間切換。可以在此頁面底部找到執(zhí)行相同操作的完整程序,您可以按原樣使用該程序。但下面我用小片段解釋了該程序,供您理解。
與往常一樣,我們通過添加所需的頭文件來開始程序。這里 Servo.h 頭文件用于控制伺服電機。我們有 5 個伺服電機,因此聲明了 5 個對象,為每個電機命名。我們還初始化將在程序中使用的變量。我已經(jīng)將它們?nèi)柯暶鳛槿值模绻信d趣優(yōu)化程序,可以更改它們的范圍。我們還聲明了一個名為 saved_data 的數(shù)組,顧名思義,該數(shù)組將保存機器人 ARM 的所有記錄動作。
#include //Servo header file
//Declare object for 5 Servo Motors?
Servo Servo_0;
Servo Servo_1;
Servo Servo_2;
Servo Servo_3;
Servo Gripper;
//Global Variable Declaration
int S0_pos, S1_pos, S2_pos, S3_pos, G_pos;
int P_S0_pos, P_S1_pos, P_S2_pos, P_S3_pos, P_G_pos;
int C_S0_pos, C_S1_pos, C_S2_pos, C_S3_pos, C_G_pos;
int POT_0,POT_1,POT_2,POT_3,POT_4;
int saved_data[700]; //Array for saving recorded data
int array_index=0;
char incoming = 0;
int action_pos;
int action_servo;
在空隙設(shè)置功能中,我們以 9600 波特率開始串行通信。我們還指定了伺服電機連接到的引腳。在我們的例子中,我們使用了使用附加函數(shù)指定的引腳 3、5、6、9 和 10。由于設(shè)置功能在啟動期間運行,我們可以使用它將機械臂設(shè)置為啟動位置。因此,我對所有五個電機的位置值進行了硬編碼。這些硬編碼值可以在以后根據(jù)您的喜好進行更改。在設(shè)置功能結(jié)束時,我們打印一個串行行,要求用戶按 R 或 P 執(zhí)行相應(yīng)的操作
void setup() {
Serial.begin(9600); //Serial Monitor for Debugging
//Decalre the pins to which the Servo Motors are connected to
Servo_0.attach(3);
Servo_1.attach(5);
Servo_2.attach(6);
Servo_3.attach(9);
Gripper.attach(10);
//Write the servo motors to intial position
Servo_0.write(70);
Servo_1.write(100);
Servo_2.write(110);
Servo_3.write(10);
Gripper.write(10);
Serial.println("Press 'R' to Record and 'P' to play"); //Instrust the user
}
我定義了一個名為 Read_POT 的函數(shù),它讀取所有 5 個電位器的模擬值并將其映射到伺服位置值。眾所周知,Arduino有一個8位ADC,它為我們提供0-1023的輸出,但伺服電機位置值的范圍僅為0-180。此外,由于這些伺服電機不是很精確,因此將它們驅(qū)動到極端的 0 端或 180 端是不安全的,因此我們將 10-170 設(shè)置為我們的極限。我們使用map函數(shù)將所有五個電機的0-1023轉(zhuǎn)換為10-170,如下所示。
void Read_POT() //Function to read the Analog value form POT and map it to Servo value
{
POT_0 = analogRead(A0); POT_1 = analogRead(A1); POT_2 = analogRead(A2); POT_3 = analogRead(A3); POT_4 = analogRead(A4); //Read the Analog values form all five POT
S0_pos = map(POT_0,0,1024,10,170); //Map it for 1st Servo (Base motor)
S1_pos = map(POT_1,0,1024,10,170); //Map it for 2nd Servo (Hip motor)
S2_pos = map(POT_2,0,1024,10,170); //Map it for 3rd Servo (Shoulder motor)
S3_pos = map(POT_3,0,1024,10,170); //Map it for 4th Servo (Neck motor)
G_pos ?= map(POT_4,0,1024,10,170); ?//Map it for 5th Servo (Gripper motor)
}
錄制模式代碼
在記錄模式下,用戶必須使用電位計控制機器人。每個 POT 對應(yīng)于一個單獨的電機,由于電位器是不同的,我們應(yīng)該將電機的位置和電機編號保存在saved_data陣列內(nèi)。讓我們看看如何使用記錄功能實現(xiàn)這一點。
消除伺服抖動問題
使用這些伺服電機時,每個人都可能遇到的一個常見問題是電機在工作時可能會抖動。這個問題有很多解決方案,首先你要弄清楚問題是出在電機的控制電路上,還是與寫入伺服電機的位置值有關(guān)。就我而言,我使用串行監(jiān)視器,發(fā)現(xiàn)servo_pos的值不是恒定的,有時會隨機上下抖動。
所以我對Arduino進行了編程,使其讀取兩次POT值并比較兩個值。僅當(dāng)兩個值相同時,該值才會被視為有效,否則該值將被丟棄。值得慶幸的是,這為我解決了抖動問題。還要確保 POT 牢固地安裝(我焊接了它)到 Arduino 的模擬引腳上。任何失去連接也會導(dǎo)致緊張。變量P_x_pos用于保存舊值,然后使用上面討論的Read_POT函數(shù)再次讀取和映射x_pos值。
Read_POT(); //Read the POT values ?for 1st time
//Save it in a varibale to compare it later
P_S0_pos = S0_pos;
P_S1_pos = S1_pos;
P_S2_pos = S2_pos;
P_S3_pos = S3_pos;
P_G_pos ?= G_pos;
Read_POT(); //Read the POT value for 2nd time
現(xiàn)在,如果值有效,我們必須控制伺服電機的位置。同樣在控制之后,我們必須將電機編號和電機位置保存在陣列中。我們本可以使用兩個不同的數(shù)組,一個用于電機編號,另一個用于其位置,但為了節(jié)省內(nèi)存和復(fù)雜性,我通過在將 pos 值保存到數(shù)組之前向 pos 值添加微分器值來組合它們。
if (P_S0_pos == S0_pos) //If 1st and 2nd value are same
{
Servo_0.write(S0_pos); //Control the servo
?
if (C_S0_pos != S0_pos) //If the POT has been turned
{
saved_data[array_index] = S0_pos + 0; //Save the new position to the array. Zero is added for zeroth motor (for understading purpose)
array_index++; //Increase the array index
}
?
C_S0_pos = S0_pos; //Saved the previous value to check if the POT has been turned
}
Sero_0的微分值為 0,Servo_1的微分值為 1000,同樣Servo_3為 3000,Gripper 為 4000。下面顯示了將微分器添加到位置值并保存到數(shù)組中的代碼行。
saved_data[array_index] = S0_pos + 0; //Save the new position to the array. Zero is added for zeroth motor (for understading purpose)
saved_data[array_index] = S1_pos + 1000; //1000 is added for 1st servo motor as differentiater
saved_data[array_index] = S2_pos + 2000; //2000 is added for 2nd servo motor as differentiater
saved_data[array_index] = S3_pos + 3000; //3000 is added for 3rd servo motor as differentiater
saved_data[array_index] = G_pos + 4000; //4000 is added for 4th servo motor as differentiater
播放模式代碼
用戶記錄saved_data中的動作后,他可以通過在串行監(jiān)視器中輸入“P”來切換到播放模式。在播放模式下,我們可以訪問保存在數(shù)組中的每個元素并拆分值以獲取電機編號和電機位置并相應(yīng)地控制它們的位置。
我們使用 for 循環(huán)來瀏覽數(shù)組的每個元素,直到保存在數(shù)組中的值。然后我們使用兩個變量action_servo和action_pos分別得到要控制的伺服電機的數(shù)量及其位置。要獲得伺服電機的數(shù)量,我們必須將其除以 1000 并得到位置,我們需要最后三位數(shù)字,這可以通過取模數(shù)獲得。
例如,如果數(shù)組中保存的值是 3125,則表示 3RD電機必須移動到 125 的位置。
for (int Play_action=0; Play_action
{
action_servo = saved_data[Play_action] / 1000; //The fist charector of the array element is split for knowing the servo number
action_pos = saved_data[Play_action] % 1000; //The last three charectors of the array element is split to know the servo postion
現(xiàn)在剩下的就是使用舵機編號并將其移動到獲得的伺服位置值。我使用開關(guān)盒進入相應(yīng)的伺服電機編號和寫入功能將伺服電機移動到該位置。開關(guān)外殼如下所示
switch(action_servo){ //Check which servo motor should be controlled
case 0: //If zeroth motor
Servo_0.write(action_pos);
break;
case 1://If 1st motor
Servo_1.write(action_pos);
break;
case 2://If 2nd motor
Servo_2.write(action_pos);
break;
case 3://If 3rd motor
Servo_3.write(action_pos);
break;
case 4://If 4th motor
Gripper.write(action_pos);
break;
主循環(huán)功能
在主循環(huán)功能內(nèi)部,我們只需要檢查用戶通過串行監(jiān)視器輸入的內(nèi)容并相應(yīng)地執(zhí)行播放模式的記錄模式。變量傳入用于保存用戶的值。如果輸入“R”,則記錄模式將被激活,如果按下“P”,則播放模式將由if條件語句執(zhí)行,如下所示。
void loop() {
if (Serial.available() > 1) //If something is recevied from serial monitor
{
incoming = Serial.read();
if (incoming == 'R')
Serial.println("Robotic Arm Recording Started......");
if (incoming == 'P')
Serial.println("Playing Recorded sequence");
}
if (incoming == 'R') //If user has selected Record mode
Record();
if (incoming == 'P') //If user has selected Play Mode
Play();
}
記錄和播放機器人手臂的工作
按照電路圖中所示進行連接,并上傳下面給出的代碼。通過計算機的USB端口為您的Arduino Nano供電,然后打開串行顯示器,您將受到此介紹消息的歡迎。
現(xiàn)在在串行監(jiān)視器中輸入 R,然后按回車鍵。請注意,應(yīng)選擇串行監(jiān)視器底部的換行符。進入后,機器人將進入錄制模式,您將看到以下屏幕。
此處顯示的信息可用于調(diào)試。從形式 69 開始的數(shù)字是伺服電機 0 到電機 5 的當(dāng)前位置。索引值適用于數(shù)組大小。請注意,我們使用的數(shù)組限制為 700,因此我們在超過該限制之前已完成記錄運動。錄制完成后,我們可以在串行監(jiān)視器中輸入P并按回車鍵,我們將進入播放模式,串行監(jiān)視器將顯示以下內(nèi)容。
在播放模式下,機器人將重復(fù)在錄制模式下完成的相同動作。這些動作將一次又一次地執(zhí)行,直到您通過串行監(jiān)視器中斷它。
/*?
? Robotic ARM with Record and Play option using Arduino?
? Code by: B. Aswinth Raj?
? Website: www.circuitdigest.com?
? Dated: 05-08-2018?
*/?
#include //Servo header file?
//Declare object for 5 Servo Motors ??
Servo Servo_0;?
Servo Servo_1;?
Servo Servo_2;?
Servo Servo_3;?
Servo Gripper;?
//Global Variable Declaration ?
int S0_pos, S1_pos, S2_pos, S3_pos, G_pos; ?
int P_S0_pos, P_S1_pos, P_S2_pos, P_S3_pos, P_G_pos;?
int C_S0_pos, C_S1_pos, C_S2_pos, C_S3_pos, C_G_pos;?
int POT_0,POT_1,POT_2,POT_3,POT_4;?
int saved_data[700]; //Array for saving recorded data?
int array_index=0;?
char incoming = 0;?
int action_pos;?
int action_servo;?
void setup() {?
Serial.begin(9600); //Serial Monitor for Debugging?
//Declare the pins to which the Servo Motors are connected to ?
Servo_0.attach(3);?
Servo_1.attach(5);?
Servo_2.attach(6);?
Servo_3.attach(9);?
Gripper.attach(10);?
//Write the servo motors to initial position ?
Servo_0.write(70);?
Servo_1.write(100);?
Servo_2.write(110);?
Servo_3.write(10);?
Gripper.write(10);?
Serial.println("Press 'R' to Record and 'P' to play"); //Instruct the user ?
}?
void Read_POT() //Function to read the Analog value form POT and map it to Servo value?
{?
? POT_0 = analogRead(A0); POT_1 = analogRead(A1); POT_2 = analogRead(A2); POT_3 = analogRead(A3); POT_4 = analogRead(A4); //Read the Analog values form all five POT?
? S0_pos = map(POT_0,0,1024,10,170); //Map it for 1st Servo (Base motor)?
? S1_pos = map(POT_1,0,1024,10,170); //Map it for 2nd Servo (Hip motor)?
? S2_pos = map(POT_2,0,1024,10,170); //Map it for 3rd Servo (Shoulder motor)?
? S3_pos = map(POT_3,0,1024,10,170); //Map it for 4th Servo (Neck motor)?
? G_pos ?= map(POT_4,0,1024,10,170); ?//Map it for 5th Servo (Gripper motor)?
}?
void Record() //Function to Record the movements of the Robotic Arm?
{?
Read_POT(); //Read the POT values ?for 1st time?
//Save it in a variable to compare it later?
? P_S0_pos = S0_pos;?
? P_S1_pos = S1_pos;?
? P_S2_pos = S2_pos;?
? P_S3_pos = S3_pos;?
? P_G_pos ?= G_pos;?
? ?
Read_POT(); //Read the POT value for 2nd time?
??
? if (P_S0_pos == S0_pos) //If 1st and 2nd value are same?
? {?
? ?Servo_0.write(S0_pos); //Control the servo?
? ??
? ?if (C_S0_pos != S0_pos) //If the POT has been turned ?
? ?{?
? ? ?saved_data[array_index] = S0_pos + 0; //Save the new position to the array. Zero is added for zeroth motor (for understading purpose)?
? ? ?array_index++; //Increase the array index ?
? ?}?
? ??
? ?C_S0_pos = S0_pos; //Saved the previous value to check if the POT has been turned ?
? }?
//Similarly repeat for all 5 servo Motors?
? if (P_S1_pos == S1_pos)?
? {?
? ?Servo_1.write(S1_pos);?
? ??
? ?if (C_S1_pos != S1_pos)?
? ?{?
? ? ?saved_data[array_index] = S1_pos + 1000; //1000 is added for 1st servo motor as differentiator ?
? ? ?array_index++;?
? ?}?
? ??
? ?C_S1_pos = S1_pos;?
? }?
? if (P_S2_pos == S2_pos)?
? {?
? ?Servo_2.write(S2_pos);?
? ??
? ?if (C_S2_pos != S2_pos)?
? ?{?
? ? ?saved_data[array_index] = S2_pos + 2000; //2000 is added for 2nd servo motor as differentiator ?
? ? ?array_index++;?
? ?}?
? ??
? ?C_S2_pos = S2_pos;?
? }?
? if (P_S3_pos == S3_pos)?
? {?
? ?Servo_3.write(S3_pos); ?
? ??
? ?if (C_S3_pos != S3_pos)?
? ?{?
? ? ?saved_data[array_index] = S3_pos + 3000; //3000 is added for 3rd servo motor as differentiater ?
? ? ?array_index++;?
? ?}?
? ??
? ?C_S3_pos = S3_pos; ? ?
? }?
? if (P_G_pos == G_pos)?
? {?
? ?Gripper.write(G_pos);?
? ??
? ?if (C_G_pos != G_pos)?
? ?{?
? ? ?saved_data[array_index] = G_pos + 4000; //4000 is added for 4th servo motor as differentiator ?
? ? ?array_index++;?
? ?}?
? ??
? ?C_G_pos = G_pos;?
? }?
? ?
?//Print the value for debugging ?
?Serial.print(S0_pos); ?Serial.print(" ?"); Serial.print(S1_pos); Serial.print(" ?"); Serial.print(S2_pos); Serial.print(" ?"); Serial.print(S3_pos); Serial.print(" ?"); Serial.println(G_pos);?
?Serial.print ("Index = "); Serial.println (array_index); ?
?delay(100); ?
}?
?
void Play() //Functon to play the recorded movements on the Robotic ARM?
{?
?for (int Play_action=0; Play_action
?{?
? ?action_servo = saved_data[Play_action] / 1000; //The fist character of the array element is split for knowing the servo number?
? ?action_pos = saved_data[Play_action] % 1000; //The last three characters of the array element is split to know the servo postion ?
? ?switch(action_servo){ //Check which servo motor should be controlled ?
? ? ?case 0: //If zeroth motor?
? ? ? ?Servo_0.write(action_pos);?
? ? ?break;?
? ? ?case 1://If 1st motor?
? ? ? ?Servo_1.write(action_pos);?
? ? ?break;?
? ? ?case 2://If 2nd motor?
? ? ? ?Servo_2.write(action_pos);?
? ? ?break;?
? ? ?case 3://If 3rd motor?
? ? ? ?Servo_3.write(action_pos);?
? ? ?break;?
? ? ?case 4://If 4th motor?
? ? ? ?Gripper.write(action_pos);?
? ? ?break;?
? ?}?
? ?delay(50);?
? ??
?}?
}?
?
void loop() {?
if (Serial.available() > 1) //If something is received from serial monitor ?
{?
incoming = Serial.read();?
if (incoming == 'R')?
Serial.println("Robotic Arm Recording Started......");?
if (incoming == 'P')?
Serial.println("Playing Recorded sequence");?
}?
if (incoming == 'R') //If user has selected Record mode?
Record();?
if (incoming == 'P') //If user has selected Play Mode ?
Play();?
}?
?
評論
查看更多