之前我寫了arduino驅動步進電機帶動絲杆運動的博客,連結在下面:
arduino控制步進電機移動絲杆實現撥片架水平移動
既然已經可以驅動絲杆帶動撥片架運動了,接下就是需要驅動ST90S微型舵機來帶動撥片撥取貨物了,本博客就介紹arduino如何驅動ST90S微型舵機以及分享在驅動過程中所遇到的問題以及解決方法。
可以看到上圖中我們總共有5個撥片,每個撥片都是由一個舵機去控制的,因此我們總共需要5個小舵機,為了區分他們,我將其進行了標號:
#include <Servo.h>Servo UpLeftServo; Servo UpRightServo ; Servo DownLeftServo; Servo DownRightServo ; Servo DownMidServo ;arduino自帶舵機驅動的庫,叫Servo.h,因此在使用之前我們先要將其包含進來。定義好名字之後就要進行初始化了,前面我們定義了舵機控制對象,在這裡我們要將其進行初始化,函數就是XXX.attach(pin),XXX是定義的舵機名字,pin是該舵機的信號引腳,在我們用的板子中『D』開頭的信號引腳初始化的時候不用帶『D』,否則會提示找不到該引腳,還請注意:
void AngleInit(){ UpLeftServo.write(90); delay(20); DownLeftServo.write(90); delay(20); DownMidServo.write(90); delay(20); DownRightServo.write(90); delay(20); UpRightServo.write(90); }void setup() { for(uint8_t i = 1 ; i < 6;i++){ last_turn_angle[i] = 90; } UpLeftServo.attach(7); DownLeftServo.attach(10); DownMidServo.attach(A0); DownRightServo.attach(A3); UpRightServo.attach(A2); AngleInit(); Serial.begin(9600); while (Serial.read() >= 0) {};}因為舵機打角是連續的才比較穩定,因此我是讓舵機按照1°的變化趨勢進行打角。如果一次性舵機讓舵機打一個很大的角度,舵機很有可能會用力過猛,導致打到相應的角度之後進入一個抖動的狀態,如下圖所示:
同時還有一個問題,那就是如果我不知道某一時刻舵機的打角,那麼我按照1°打角變化的話,那就不知道是按1°遞增還是按1°遞減了,因此我就在初始化的時候都把舵機打到某一個特定的位置,我設定的是90°。
下層3個舵機角度與狀態關係:
那麼之後相對於給90°進行偏移打腳,即我們串口送給arduino板子的舵機打腳範圍是-90~90°時即可完成世界坐標下的0 ~ 180°打腳。但是這樣還是不夠,因為在後面打腳變化了之後我還需要知道上一次的打腳位置,因此我就設置了一個last_turn_angle數組,專門用來存放每個舵機上一次的打腳,並且根據上一次的打腳來打這次需要的角度,程序實現如下:
int last_turn_angle[6];void setup() { for(uint8_t i = 1 ; i < 6;i++){ last_turn_angle[i] = 90; }}void TestServo(){ send_sta = Serial.write("&"); char symbol; char angle; char steer_pos; uint8_t now_id; steer_pos = mov_cmd[0]; symbol = mov_cmd[1]; angle = mov_cmd[2]; if(symbol == '+'){ turn_angle = angle - '0'; }else if(symbol == '-'){ turn_angle = -(angle - '0'); } turn_angle *= 10; turn_angle = 90 + turn_angle; if(turn_angle > 177) turn_angle = 177; else if(turn_angle < 2) turn_angle = 2; now_id = (steer_pos - '0'); if(steer_pos == '1'){ServoCtl(UpLeftServo,last_turn_angle[now_id]);} else if(steer_pos == '2'){ServoCtl(DownLeftServo,last_turn_angle[now_id]);} else if(steer_pos == '3'){ServoCtl(DownMidServo,last_turn_angle[now_id]);} else if(steer_pos == '4'){ServoCtl(DownRightServo,last_turn_angle[now_id]);} else if(steer_pos == '5'){ServoCtl(UpRightServo,last_turn_angle[now_id]);} last_turn_angle[now_id] = turn_angle; Serial.println(turn_angle); send_sta = Serial.write("#");} void ServoCtl(Servo steer,uint8_t last_turn_angle){ int symbol = 0; if(turn_angle > last_turn_angle){ symbol = 1; }else{ symbol = -1; } for(uint8_t i = last_turn_angle;i != turn_angle; i += symbol){ steer.write(i); }}但是即使我做了這樣的1°變化緩衝操作之後,舵機打腳時還是會出現抖動的狀態,百思不得其解,遂上網搜索原因,有說servo庫所用定時器和串口定時器衝突的,有說沒有共地的,我都試了,但是沒有用,只好冷靜下來自己思考了。
靈感總是來得出乎意料,我想是不是因為我每次打腳都打到底的原因了呢?因為我每次都讓舵機打到0°或者180°,相當於是都打到了舵機的極點,這樣可能不夠穩定,因此我就想著給舵機打角限幅,限制到了2~177°,程序如下:if(turn_angle > 175) turn_angle = 175;else if(turn_angle < 5) turn_angle = 5;最終解決了問題,實現了舵機的穩定控制打角,效果如下:
因為程序是串口接收命令的,因此loop()裡面是這樣寫的:
void loop() { delay(50); while (Serial.available() > 0) { delay(100); send_sta = Serial.readBytes(mov_cmd,6); TestServo(); break; } while (Serial.read() >= 0){}}為了保證控制的穩定性,我在程序裡面設置了應答措施,當arduino板子接收到命令之後會通過send_sta = Serial.write("&");語句,也就是通過串口發送一個&符號告訴上層自己接收到了命令,當arduino完成了動作之後會通過send_sta = Serial.write("#");語句,也就是通過串口發送一個#符號告訴上層自己完成了命令。
具體的命令傳遞數組含義如下圖所示:我也在網上查了很多資料,有說板子型號沒選對的,有說沒有共地的,我都嘗試了一下,發現並沒有什麼用,最終還是通過插拔燒寫線和重啟arduino IDE來解決這個問題的,目前並不知道該問題的具體解決方法,但是通過上述操作還是順利的將程序燒寫進了arduino板子。
實際上,就算燒寫的時候arduino IDE報了如上錯誤,程序還是會被燒寫進arduino板子裡的。
《通過電機控制學習PID算法》本課程從最開始的電機部分再到核心的PID算法,一步一步循序漸進,通過這個課程的學習可以讓同學們認識到算法的重要性,同時本課程理論與實踐相結合,將難以理解的理論部分通俗的向大家分析講解。
點擊「閱讀原文」查看課程詳情