2013/09/02

Arduino練習:伺服馬達以Tower Pro SG90為例

這一篇介紹伺服馬達,以Tower Pro(輝盛)的小型伺服馬達SG90為例,價格便宜,在拍賣網站上大約80元,可作為初步練習之用。

我的板子是Arduino Uno R3,Arduino軟體開發環境是1.0.5版。



伺服馬達(servo motor),因常用於遙控模型飛機,所以又常稱為RC伺服機(RC Servo,Radio Control Servo,Remote Control Servo)、伺服馬達舵機。

伺服馬達裡含有直流馬達、齒輪箱、軸柄、以及控制電路,我們可透過訊號控制軸柄的停止角度,大概都是0到180度,但不同廠牌型號會有不同的範圍;經由齒輪箱降速後,變成適當可用的轉速,並且提供更高的轉矩(扭轉力)。

另外還有能連續轉動的伺服馬達,有些出廠時便能連續轉動,有些則是玩家自己動手改造,這種馬達透過訊號可控制轉動的速度。

一般伺服馬達有三條線,電源(紅色)、接地(黑或棕色)、訊號線(白、黃、橘、藍,甚至是黑色)。透過訊號線傳送PWM脈波來控制軸柄的停止位置旋轉角度,這個訊號脈波必須每秒重複50次(也就是50Hz),而脈衝持續時間長短便代表了馬達該將軸柄轉到什麼位置,範圍從1.0ms到2.0ms(millisecond,毫秒,千分之一秒),若想置中則是1.5ms;也可將1.0ms當做角度0度,那麼1.5ms會是90度,2.0ms則是轉到底180度。注意,也有可能反過來。
 
不過每個廠牌型號的伺服馬達可允許旋轉的角度各不相同,也就是說可接受的訊號脈衝範圍也不相同,必須查閱產品資料規格書,若超出範圍可能會損害伺服馬達。底下是Tower Pro SG90的規格

  • 重量:9g
  • 尺寸:23*12.2*29mm
  • 工作電壓:4.8V
  • 轉矩:1.8kg-cm,當工作電壓為4.8V時
  • 運轉速度:0.1秒 ∕ 60度 ,當工作電壓為4.8V時
  • 脈衝寬度範圍:500~2400µs 
  • 死頻帶寬度(dead band width):10µs
從中我們可知,可允許的脈衝範圍是500~2400µs,也就是0.5~2.4ms,比剛剛說的一般範圍還要寬,也就代表這個伺服馬達能旋轉的角度更大。另外有項值得一提的數據是死頻帶寬度,意思是說,因為訊號可能不穩上下起伏,當這一次脈衝寬度與上一次相差不超過死頻帶寬度時,伺服馬達便不會動作。

硬體線路很簡單,因為只有一個伺服馬達,我直接由Arduino的5V腳位供電。若想使用超過兩個,則應以另外的電源供電,而且要記得共同接地。

電路圖如下,除了電源5V與接地外,訊號線接往Arduino的數位腳位9。


照片如下,Tower Pro SG90的三條線顏色分別是黃、紅、棕,對應訊號、電源、接地。


Arduino內建操控伺服馬達的程式庫,已經將零零總總的東西通通包起來,使用方法非常簡單,讓我們能以非常簡單的方式操控伺服馬達的旋轉角度。因為需使用PWM,所以會影響原本的PWM腳位,使用此程式庫時,數位腳位9與10便無法輸出PWM,不管有無接伺服馬達,於是我們通常也會將伺服馬達的訊號線接在數位腳位9或10。

接下來是軟體的部份,先讓伺服馬達來回旋轉吧。

#include <Servo.h>

Servo myservo; // 建立Servo物件,控制伺服馬達

void setup()
{
  myservo.attach(9); // 連接數位腳位9,伺服馬達的訊號線
}

void loop()
{
  for(int i = 0; i <= 180; i+=1){
    myservo.write(i); // 使用write,傳入角度,從0度轉到180度
    delay(20);
  }
  for(int i = 180; i >= 0; i-=1){
    myservo.write(i);// 使用write,傳入角度,從180度轉到0度
    delay(20);
  }
}

不過Servo預設的脈衝寬度範圍是544到2400µs,跟SG90的500到2400差了一些些,我們可修正此點。

#include <Servo.h>

Servo myservo;

void setup()
{


  myservo.attach(9, 500, 2400); // 修正脈衝寬度範圍
  myservo.write(90); // 一開始先置中90度
  delay(3000);
}

void loop()
{
  for(int i = 500; i <= 2400; i+=100){
    myservo.writeMicroseconds(i); // 直接以脈衝寬度控制

    delay(300);
  }
  for(int i = 2400; i >= 500; i-=100){
    myservo.writeMicroseconds(i);

    delay(300);
  }
}

要注意的是,必須查詢伺服馬達的規格表,查知正確的脈衝寬度範圍,使用writeMicroseconds時也要小心,不可傳入超出範圍的值,否則會損壞伺服馬達

Servo程式庫中,還有read()可用,但只是讀取上次傳入write()的值;還有detach(),呼叫後伺服馬達就不動了,也就可以使用腳位9的PWM功能。


參考資料:

253 comments:

  1. 您好,我是正在學習ARDUINO的新手,最近也在玩這一顆伺服馬達,接法跟您一樣,可是我發現用USB供電跟DC-jack in(6顆3號電池7.2V)的行為不太一樣。以下是我的測試code:

    #include

    Servo myservo; // 建立Servo物件,控制伺服馬達

    void setup()
    {
    myservo.attach(6, 500, 2400); // 修正脈衝寬度範圍
    myservo.write(90);
    delay(1000);
    }
    void loop()
    {
    myservo.write(30);
    delay(1000);
    myservo.write(60);
    delay(1000);
    myservo.write(90);
    delay(1000);
    myservo.write(120);
    delay(1000);
    myservo.write(150);
    delay(1000);
    }
    在USB供電下狀況正常,可以在五個角度各停一秒,可是在DC in供電下,每次loop都只會在0~90度之間擺盪。
    我用電表量過兩者的供電,到馬達時都一樣是4.9~5V。但在去量D6 pin的電壓時,發現在USB下每個角度電壓是很穩定的在改變(30度=0.1V, 60度=0.2V...150度=0.5V), 但在DC in底下就只會在0~0.3V之間改變。不知道這個問題有解嗎?

    ReplyDelete
  2. 我也沒有太多經驗,底下只是猜測。因為現在在忙,沒辦法實際測試。

    你的「(6顆3號電池7.2V)」,可以確實輸出7.2V嗎?因為電池用久了,電壓就會下降。

    根據Arduino Uno的官方文件,http://arduino.cc/en/Main/ArduinoBoardUno,建議DC in輸入的電壓介於7~12V,若低於7V,板子就會不穩定;我通常是使用方形9V電池,您要不要試試看?

    ReplyDelete
    Replies
    1. 電池是新的充電電池,我量過電池供電都在7.2~7.3V,應該沒問題,只是我不知道外接電源時是否有需要在軟體加code或是在板子上加元件,也不確定外接電源會不會影響PWM輸出範圍,所以才想問一下是否有哪些額外設定。9V電池我也會試試,謝謝您囉!

      Delete
    2. 外接電源時,會經過Arduino的穩壓電路,變成5V再供給Arduino的電子零件,但若外接電源的電壓不夠高,穩壓後的電壓也就不夠,便會不穩定、出問題。

      據我所知,軟體程式部分並不需要修改或設定,不管是接USB或接電池。

      Delete
    3. 我剛剛重新試過一遍,線路一樣,但把與伺服馬達連接的腳為從9改成6。
      不管是用USB或9V電池,都能運作正常。5個角度、相隔1秒。

      手邊並沒有那麼多充電電池,沒辦法測試你的情況。
      你還可以加入更多的電池嗎,提高輸入電壓,只要小於12V就沒問題。

      老實說只是控制一個伺服馬達而已,我想不到其他問題。
      您要不要試試看其他腳位,譬如9。
      您的Arduino板子是哪一塊?


      下圖是我剛剛拍的照片,以9V電池供電。
      https://dl.dropboxusercontent.com/u/35616101/IMG_1799.jpg
      (這張圖幾天後就會刪除。)

      Delete
    4. 剛剛腳位也從6換到9,結果一樣 :(
      我是用mega 2560的板子,不知跟UNO有沒有差。
      我也用你的code做測試:
      for(int i = 30; i <= 150; i+=1){
      myservo.write(i); // 使用write,傳入角度,從0度轉到180度
      delay(20);
      }
      for(int i = 150; i >= 30; i-=1){
      myservo.write(i);// 使用write,傳入角度,從180度轉到0度
      delay(20);
      }
      USB供電時可正常可慢慢轉到每個角度,但一切到DC in就只會0度->100度->90度一直loop,真的很怪。

      Delete
    5. 沒用過mega 2560。看了mega 2560的文件,http://arduino.cc/en/Main/arduinoBoardMega2560
      外接電源建議電壓範圍也是7~12V。mega 2560性能比較強,或許比較耗電吧,或許。

      一切到DC in就怪怪的,根據你描述的情況,我也只能推論是外接電源的問題。

      請你再加入一兩顆電池吧,或是使用9V電池試試。

      Good luck.

      Delete
    6. 我買了一顆方形9V電池測試,結果也不正常,我想應該不是電壓的問題。
      後來做了最簡單的測試,就是起始時固定角度且不做loop:
      void setup()
      {
      myservo.attach(2); // 修正脈衝寬度範圍
      myservo.write(30);
      delay(1000);
      myservo.write(90);
      delay(1000);
      }
      USB供電下一切正常,停在90度定位,可是在DC in供電下,馬達竟然會自己進入反覆狀態,不斷在30~110度之間擺盪(角度也跟程式的有差),我覺得這已經不只是電壓問題而是邏輯問題。
      附帶說明一下,我的mega 2560是網拍上買的山寨版,雖然arduino已經是開源硬體且有參考線路,但山寨版有些用料或layout的問題或許會導致此異常現象,我已經決定再買一片正版uno來試試,也謝謝你的幫忙囉!

      Delete
    7. 嗯,那就沒辦法了,沒幫上忙。

      你之前發現「
      發現在USB下每個角度電壓是很穩定的在改變(30度=0.1V, 60度=0.2V...150度=0.5V), 但在DC in底下就只會在0~0.3V之間改變。


      這應該就是板子不正常的地方吧,從DC in輸入電源時就無法輸出穩定的、控制伺服馬達的訊號。

      沒幫上忙。祝你好運。

      Delete
  3. 您好,
    看了您的文章後, 也買了 Tower Pro(輝盛)的小型伺服馬達SG90 搭配 UNO R3,
    已經可以正常轉動, 但是發現Servo的90度中心點經常會變化, 尤其是關閉電源後再重新打開電源後,
    中心點位置就有機會會跑掉, 請問您有遇過這樣的問題嗎? (試過4顆 SG90 都一樣的狀況), 謝謝!

    ReplyDelete
    Replies
    1. 我的也會。不知道為什麼,馬達本身的問題?

      Delete
    2. Anonymous4/11/13 17:29

      我把2400改成2450就比較正了

      Delete
    3. 這...SG90規格寫著脈衝寬度範圍:500~2400µs,所以原因是每個馬達本身的小誤差囉?
      但您的問題不是「重新打開電源後、中心點位置就有機會會跑掉」嗎?這跟把2400改成2450...感覺沒關係耶。

      你是用myservo.attach(9, 500, 2450)嗎?
      我有空再試試看。

      Delete
  4. 我來發mail問問Tower Pro(輝盛),
    如果他們有回復我再將內容給您參考
    謝謝~

    ReplyDelete
  5. 你好,我是剛入門的新手,想跟您請教程式碼的一些問題,以下就是我的程式碼。
    A2腳位是接一個感測器,那我想將讀取電壓值的時間設為0.5秒,馬達作動時間分別為5秒及10秒,但是他讀取電壓值的時間只會跑5秒或10秒的部分,並不會跑0.5秒這個程式,想請問一下有什麼方法可以將0.5秒和5秒、10秒的程式隔開來??

    #include
    Servo myservo;

    int rain = 2;
    int ledPin = 13;
    int val = 0;

    void setup()
    {
    Serial.begin(9600);
    pinMode(ledPin, OUTPUT);
    pinMode(rain, INPUT);
    myservo.attach(9);
    myservo.write(0);
    }

    void loop()
    {
    val = analogRead(rain);
    Serial.println(val);
    delay(500);

    if(val<=600)
    {
    digitalWrite(ledPin, HIGH);
    delay(5000);
    myservo.write(180);
    }
    else if(val>600)
    {
    digitalWrite(ledPin, LOW);
    delay(10000);
    myservo.write(0);
    }

    }

    ReplyDelete
    Replies
    1. 可以呼叫millis(),它會回傳Arduino自從重置後經過的時間,單位是毫秒,型別是unsigned long,

      譬如說
      unsigned long timePrevious;
      void setup(){
      timePrevious = millis();
      }
      void loop(){
      unsigned long t = millis();
      if(t - timePrevious > 500){
      timePrevious = t;
      val = analogRead(rain);
      }
      }
      這樣也可以每500毫秒讀取一次A2腳位。

      再修改一下,應該可以達到你想要的功能吧。

      Delete
    2. This comment has been removed by the author.

      Delete
    3. 不好意思,請問可以描述一下
      unsigned long t = millis();
      if(t - timePrevious > 500){
      timePrevious = t;
      val = analogRead(rain);
      }
      這裡嗎...
      我不太了解另外增設 if 的效果是什麼

      Delete
    4. 每次在loop()裡呼叫millis時,就會取得目前時間,儲存在t。
      當目前時間t跟之前記錄的時間timePrevious相比,已經超過500毫秒時,
      才進入if裡面呼叫analogRead;並且更新timePrevious。

      Delete
    5. 好的,謝謝
      我會再試試看。

      Delete
    6. 不好意思我想再請教一下,
      因為我的val值固定介於1~1023之間,
      而它一開始val的值就是1023因此處於val>600這段程式裡面,
      所以時間會一直跑10000ms而不會去跑其它的部分,
      這樣子的話還有辦法將程式碼分開來執行嗎??

      Delete
    7. 用delay大概很難達到你想要的功能(應該)。
      一旦在loop函式裡呼叫delay,整支程式都停在那邊了。

      試著像我前面留言的那種寫法吧,使用millis並記錄上次的時間,比較前次這次的時間來控制要不要呼叫函式、執行功能。

      Delete
    8. 摁 我會試試看的
      謝謝

      Delete
    9. 也可以進階一點,使用計時器程式庫。
      譬如http://yehnan.blogspot.tw/2012/03/arduino.html

      Delete
  6. 不好意思我想請問一下 我剛買了這個馬達 但我完全是一個新手 我不知道要怎樣連到麵包板.. 那個插頭好像插不進去..

    ReplyDelete
    Replies
    1. 可到電子材料行,買兩頭都是公的「排針」。

      Delete
    2. 你好! 謝謝回覆!

      THX!

      Delete
  7. 你好,請問該如何改變伺服馬達轉速呢?

    ReplyDelete
    Replies
    1. 更改提供給伺服馬達的電壓大小,
      不過伺服馬達的工作電壓有其範圍,可能是4.8V~5V或到6V。

      通常會控制轉速的是直流馬達吧,譬如控制車輪的速度,
      不知道你為什麼要控制伺服馬達的轉速?

      Delete
    2. 謝謝你的回複,我要做一台血液搖擺機,功能需要能設定擺角及設定轉速.

      所以我能使用軟體改變輸出電壓嗎?或者有更好的方法嗎?謝謝

      Delete
    3. 血液搖擺機?

      伺服馬達的功能是,給定訊號(角度),就轉到該角度並保持住不動,一般來說不會讓它經常不斷地轉動,也很少控制其轉速吧。

      有些伺服馬達可連續轉動,給定不同角度的話,會影響它的轉速;但這種伺服馬達應該不是你要的。

      (莫非有特別的伺服馬達?我孤陋寡聞)

      我能想到的辦法:
      1. 外接電源,6V穩壓器,電晶體,用Arduino的PWM腳位控制電晶體,連帶控制通過的電流大小,這樣應該能影響轉速。
      2. 外接電源,6V穩壓器,可由電子訊號控制的可變電阻,製作分壓電路,根據Arduino輸出的訊號得到不同電阻、進而得到不同電壓。
      3. 從軟體解決,一單位時間內只轉動小小的角度,那麼就能控制在多少時間內要轉幾度。

      嘿,不過我沒試過控制伺服馬達的轉速,參考看看囉。

      Delete
  8. 我想讓我的伺服馬達每10分鐘做一個0~180~0的動作~~請問怎麼做

    ReplyDelete
    Replies
    1. 已經回答了。

      在loop最後面加上delay(10 * 60 * 1000);

      Delete
  9. This comment has been removed by the author.

    ReplyDelete
  10. 您好,我使用Arduino UNO版,使用5個伺服馬達,每個馬達分別測試都沒問題,但是要5個同時執行卻變成每個馬達都在抖動,無法正常操作,請問您知道這是哪裡的問題嗎?是電源供應不足造成的嗎?

    ReplyDelete
    Replies
    1. 應該是。

      關於UNO板的5V輸出功率,可參考這篇
      http://forum.arduino.cc/index.php?topic=53379.0

      Delete
    2. 我好像找到問題了!!
      電源方面似乎還是可以直接由Arduino UNO主板輸出,問題是同一時間attach太多個伺服馬達,測試後的結果2個是上限,再多就會不穩定,因此我換個寫法
      myservo.attach(9);
      myservo.write(50);
      myservo.detach();

      不要在setup() 裡面attach伺服馬達,在loop() 中要轉動馬達時,先attach要用的馬達,再設定轉動角度,當轉動完成時detach馬上釋放掉,就不會有同時間attach太多伺服馬達造成的不穩定現象。雖然這種寫法不太好,但至少目前解決問題了!

      Delete
    3. 呃,應該不是。一般Arduino板與Servo程式庫,應可控制至少12個伺服馬達。

      當你attach伺服馬達時,即便沒有呼叫write,Arduino還是會輸出訊號(某預設值),所以馬達仍會吃電源,所以你同時attach太多伺服馬達的話,電力就不夠。

      你的寫法並沒有不好,但條件是「同時只需轉動一個馬達」的情況下才能那樣寫。既然你同時只控制一個馬達,那麼當然只需提供一個馬達需要的電力即可。

      建議你還是使用外接電源。

      Delete
    4. 原來如此,感謝您的回答與建議!

      Delete
  11. This comment has been removed by the author.

    ReplyDelete
  12. 執行arduino範例sweep時,L燈號會退出又重來,servo會亂轉
    影片 https://www.youtube.com/watch?v=7d1HSAEFYm0
    請問這是什麼狀況?要怎麼解決@@

    ReplyDelete
    Replies
    1. 你的L燈會閃三次,這應該是NG板子的bootloader的行為,每次板子重置都會閃三次。

      所以看起來是因為電源不足以供應伺服馬達吧,造成馬達一轉就會重置。

      試著從外部提供足夠的電壓(5V?)與電流給馬達。

      Delete
    2. 影片中也有裝了9V電池喔
      所以才覺得奇怪
      而且有的馬達可以有的不行

      Delete
    3. 我是說從「外部」提供電源試試看,
      你的9V電池仍是經由板子的穩壓器,轉成5V後提供給板子與馬達。
      而我懷疑馬達一轉,電源就不夠力,於是板子就會reset。

      可以的馬達,是不是比較小呢?所以需要的電流比較小,於是沒問題。

      Delete
  13. 不好意思 我想請問一下
    我現在用飛機的遙控器無線傳輸給接收機,然後接收機再透過arduino將訊號傳給伺服馬達。
    但是伺服馬達會一直抖動,可以控制,但從讀取出來的信號發現不動搖桿,數值也會一直小幅度的跳動,認為可能是因此造成馬達抖動,我們已經排除是電源供應不穩或不夠的問題。
    直接讓接收機連接伺服馬達來遙控,就不會有抖動問題。
    想請問如何不讓伺服馬達抖動,或是怎麼讓訊號來源穩定?
    以下是我們的程式碼
    double TransmitterThrottle1=0;
    double TransmitterThrottle3=0;
    #include

    #define SERIAL_BAUDRATE 115200

    Servo servo1;
    Servo servo3;

    int val1;
    int val3;

    void setup()
    {
    pinMode (A0, INPUT);
    pinMode (A1, INPUT);
    servo1.attach(5);
    servo3.attach(6);

    Serial.begin(SERIAL_BAUDRATE);
    Serial.println("System Ready~");
    }

    void CheckTransmitter()
    {
    TransmitterThrottle1 = (pulseIn (A0, HIGH, 200000))/10;
    TransmitterThrottle3 = (pulseIn (A1, HIGH, 200000))/10;
    }

    void loop()
    {
    CheckTransmitter();
    Serial.print ("pulse1: ");
    Serial.println (TransmitterThrottle1);
    Serial.print ("pulse3: ");
    Serial.println (TransmitterThrottle3);

    val1 = TransmitterThrottle1;
    val3 = TransmitterThrottle3;
    val1 = map(val1, 110, 190, 0, 179);
    val3 = map(val3, 110, 190, 0, 179);
    servo1.write(val1);
    servo3.write(val3);

    delay(100);
    }

    ReplyDelete
    Replies
    1. 接收機傳出去的訊號是什麼呢?
      接收機是連接伺服馬達的吧,
      那麼你在程式裡讀取接收機的訊號時,
      譬如(pulseIn (A0, HIGH, 200000))/10與delay(100),
      這些是正確的寫法嗎?


      Delete
    2. 接收機傳出來給伺服馬達的訊號應該是PWM訊號,
      經由(pulseIn (A0, HIGH, 200000))讀出來的數值介於1100~1900。
      我們用接收機透過Arduino來控制伺服馬達是為了在程式碼中安插,
      受到感測器影響後的限制條件,if之類的情況。
      我有將delay(100)改為伺服馬達在用的delay(15),
      但伺服馬達的抖動頻率就加快了。

      抱歉我不確定(pulseIn (A0, HIGH, 200000))/10是不是正確的寫法,
      我參考別人讀取接收機訊號的方式,
      然後想說接收機可以直接接上伺服馬達控制,
      那應該就可以直接把程式碼讀到的訊號直接傳給伺服馬達來控制,
      結果是可以控制,但就造成不斷地抖動,訊號讀取出來很不穩定,
      我可以詢問其他讀取接收機訊號的程式碼嗎?可以讓訊號接收更穩定。

      我使用
      發射機 T6EX-2.4GHz
      發射頻率:72MHz低頻帶及72MHz高頻帶
      接收機 R617FS
      接收頻率:40MHz頻率、72MHz低頻帶及72MHz高頻帶
      中間頻率:455kHz

      感謝你的回覆。

      Delete
    3. 伺服馬達訊號的頻率是50Hz,也就是每20ms裡須有個訊號,每個訊號位於高電位的寬度介於1ms~2ms之間,而你的PWM訊號介於1.1~1.9,沒錯。
      所以把delay(100)改為delay(15)應該是對的。

      何謂抖動?請描述抖動的情況。兩個馬達都抖動?

      程式裡不是有印出收到的值嗎?
      Serial.println (TransmitterThrottle1);
      Serial.println (TransmitterThrottle3);
      那麼印出了哪些數值呢?

      先控制一個馬達試試看。

      Delete
    4. PS 我沒有飛機遙控器無線傳輸接收機的使用經驗。

      Delete
  14. 感謝指教。
    然後我們的伺服馬達,是在無推動遙控器搖桿的情況下,
    可能因為接收訊號不穩定,
    造成小幅度 高頻率的抖動。
    兩顆馬達都會抖動。
    即使指控制一顆也會有相同的抖動行為。
    如果將遙控器的搖桿置中,不再去更改發射訊號,
    那我這邊接收機,透過(pulseIn (A0, HIGH, 200000))/10印出來的值如下:
    pulse1: 152.00
    pulse3: 152.00
    pulse1: 150.00
    pulse3: 152.00
    pulse1: 151.00
    pulse3: 152.00
    pulse1: 152.00
    pulse3: 152.00
    pulse1: 151.00
    pulse3: 150.00
    pulse1: 151.00
    pulse3: 152.00
    pulse1: 150.00
    pulse3: 152.00
    pulse1: 151.00
    pulse3: 152.00
    pulse1: 151.00
    pulse3: 152.00
    pulse1: 150.00
    pulse3: 151.00
    pulse1: 151.00
    pulse3: 152.00
    pulse1: 150.00
    pulse3: 152.00
    pulse1: 152.00
    pulse3: 152.00
    pulse1: 151.00
    pulse3: 152.00
    pulse1: 150.00
    pulse3: 150.00
    pulse1: 152.00
    pulse3: 152.00
    像這樣小幅度的震盪。
    感謝你的回覆。

    ReplyDelete
    Replies
    1. int val1_old;
      int val3_old;
      void loop()
      {
      CheckTransmitter();
      Serial.print ("pulse1: ");
      Serial.println (TransmitterThrottle1);
      Serial.print ("pulse3: ");
      Serial.println (TransmitterThrottle3);

      val1 = TransmitterThrottle1;
      val3 = TransmitterThrottle3;
      val1 = map(val1, 110, 190, 0, 179);
      val3 = map(val3, 110, 190, 0, 179);
      if(abs(val1 - val1_old) > 5){
      val1_old = val1;
      servo1.write(val1);
      }
      if(abs(val3 - val3_old) > 5){
      val3_old = val3;
      servo3.write(val3);
      }

      delay(100);
      }

      如果改成這樣了,只有在新值跟舊值的差(絕對值)超過5時,才會叫伺服馬達旋轉,也就是去除小幅度的震盪。
      不過這麼一來,只有超過5的變化,才會有效。

      Delete
    2. 感謝你教我們輸入
      if(abs(val1 - val1_old) > 5){
      val1_old = val1;
      抖動的情況明顯降低很多了,
      但有時震盪範圍還是會大於5,
      所以我加大範圍到10,感覺就更穩定不抖動了,
      但有點像是降低操控靈敏度,增加訊號穩定度的感覺,
      可能還不夠完善,但對我們的性能要求算可以。
      反倒是很好奇為什麼接收器直接傳訊給伺服馬達,
      就可以這麼穩定且靈敏?
      但很感謝提供這段程式碼的改善方式,
      我們會在去官網查一下這段指令的詳細內容,
      感謝您的幫忙,謝謝。

      Delete
    3. > 但有點像是降低操控靈敏度,增加訊號穩定度的感覺,
      對,會有這樣的後果。

      > 為什麼接收器直接傳訊給伺服馬達,就可以這麼穩定且靈敏?
      應該是因為Arduino偵測訊號的部份,沒辦法非常準。

      Delete
  15. 您好,請問一下伺服馬達連接從arduino拉出的5V運作非常正常,但是接了外部電源卻完全沒有作用,會是什麼原因呢?

    板子透過USB連接電腦,伺服馬達接了手機旅充 (5V, 1.5A,也確認Arduino的ground也接了外部電源的地)

    ReplyDelete
    Replies
    1. 這麼奇怪?

      請提供照片吧。

      Delete
    2. https://dl.dropboxusercontent.com/u/24058771/P_20150523_194739_HDR.jpg
      照片在這裡,圖中是接了兩顆,但就算拔掉一顆也還是不會動

      Delete
    3. 嗯,好樣的,看不出問題。
      你拔掉的是哪一顆?

      確定麵包板底下在「將」的左右兩邊,是連起來的嗎?

      Delete
    4. 還真的是老舊麵包板接觸不良的問題......

      已解決,感謝~

      Delete
  16. 請問一下 利用 myservo.writeMicrosecond() 要如何輸出不同的多組pwm

    ReplyDelete
    Replies
    1. 自己控制時間,然後呼叫writeMicrosecond傳入不同的值。

      Delete
  17. 請問可以讓伺服馬達在來回旋轉的時候直接停止嗎

    ReplyDelete
    Replies
    1. 什麼?
      看不懂你的問題。

      Delete
    2. for(int i = 0; i <= 180; i+=1){
      myservo.write(i); // 使用write,傳入角度,從0度轉到180度
      delay(20);
      }
      for(int i = 180; i >= 0; i-=1){
      myservo.write(i);// 使用write,傳入角度,從180度轉到0度
      delay(20);
      }
      在這個循環的時候 讓他直接停止

      Delete
    3. 停止是想要固定在那個角度的話,須保持Servo物件的attach狀態;
      若不是,則要要detach,並且不再使用。

      Delete
  18. 您好,最近在摸索關於 Arduino 的設計與操作事項

    很多資訊的是參考版大的範例,所以要先謝謝你的分享

    再來就是要詢問一些問題

    如果我想利用搖桿模組控制伺服馬達的轉動角度

    譬如我搖桿X軸左右操縱的時候,此時馬達也會左右轉動

    停止操縱,馬達也會停止

    像此類概念設計,請問我程式碼應該怎麼下手

    不好意思,因為摸得不太透,所以想請教請教

    ReplyDelete
    Replies
    1. X軸是數位形式嗎?只有左跟右,與中間?
      那就讀取搖桿X軸的狀態,
      左就命令馬達左轉,右就右轉,中間就停止。

      不懂你想問些什麼。或許可以先用三個普通的按鍵來試試看。

      Delete
  19. 搖桿式這種

    http://www.eclife.com.tw/led/moreinfo_82970.htm


    他有PWM輸入,我想利用X軸的傾斜程度來控制伺服馬達

    ReplyDelete
    Replies
    1. > PWM輸入
      啥?在哪?

      那是類比搖桿,以analogRead讀取X或Y軸的值,先讀讀看,知道X軸搖到左、右、中間各會是大概多少值。
      讀取後,再對應到伺服馬達的角度。

      Delete
  20. 哈哈,這是類比的喔!

    好的,感謝你的建議

    我先試試看!!

    ReplyDelete
    Replies
    1. Z軸是個按鈕,所以是數位。

      Delete
  21. 哈哈,這是類比的喔!

    好的,感謝你的建議

    我先試試看!!

    ReplyDelete
  22. 我想請問說,我想讓兩顆馬達一顆進行正轉一顆進行反轉,該如何修改呢

    這是我寫的程式,裡頭只能控制一顆

    const int motorIn1 = 5;
    const int motorIn2 = 6;

    void setup()
    {
    pinMode( motorIn1,OUTPUT);
    pinMode( motorIn2,OUTPUT);
    }

    void loop()
    {
    for(int i = 150; i < 256 ;i+=10){
    analogWrite(motorIn1, i);
    analogWrite(motorIn2, 0);
    delay(150);
    }
    analogWrite(motorIn1, 0);
    analogWrite(motorIn2, 0);
    delay(2000);

    for(int i = 150; i < 256 ;i+=10){
    analogWrite(motorIn1, 0);
    analogWrite(motorIn2, i);
    delay(150);
    }
    analogWrite(motorIn1, 0);
    analogWrite(motorIn2, 0);
    delay(150);
    }

    ReplyDelete
    Replies
    1. 如果兩個馬達的轉動時間相同,也就是同時開始轉動、同時停止轉動,
      那就把控制第二個馬達的程式碼,放在每個delay之前即可。

      如果不同,那就應改成不使用delay的寫法。

      Delete
  23. 因為我有兩個馬達接arduino,發現腳位電流不穩後換成用繼電器穩壓,但是sg90的訊號線接到arduino,程式不變而繼電器也用程式改成digitalWrite(In1,HIGH),依然沒有反應,請問我該怎麼做?

    ReplyDelete
    Replies
    1. 請提供電路圖與程式碼。

      Delete
    2. https://drive.google.com/file/d/0B01ASocXW99pUGhyRm4tekM0RHM/view?usp=sharing
      直流馬達我是用繼電器模組,因為fritzing我找不到,所以我用電晶體跟繼電器拉了一個類似的作模擬
      我目前還在學習電子電路學相關知識,還請多多幫忙。
      謝謝

      Delete
    3. 你的伺服馬達的訊號線,沒接東西。

      Delete
    4. 伺服馬達通常可接受4.5~6V的運作電壓(視規格而定),
      你的電路圖裡的Power plug寫著7.5V。

      Delete
    5. This comment has been removed by the author.

      Delete
    6. 抱歉,我知道電晶體可以當作pwm模擬類比訊號從pin腳輸出給直流馬達的正極,但是我還是不懂如何在用繼電器模組當開關的情況下,用電晶體去控制伺服馬達的定角度,所以伺服馬達的訊號線不知道怎麼處理?

      Delete
    7. 建議你先照著這篇,以Arduino控制伺服馬達SG90。光一個SG90,從Arduino板子本身供電便已足夠。

      然後再加入穩壓器,從外部電池提供5V電源給伺服馬達;只有電源線部分改變,以及要共同接地,其他不變。
      另外也由外部電池與穩壓器供電給電路圖中的小馬達,連接線路加以控制。

      至於繼電器與電晶體,我認為應該不必使用吧,照你想要的功能來說。

      請參考拙作《Arduino輕鬆入門:範例分析與實作設計》,第9章介紹穩壓器,第10章介紹伺服馬達與直流馬達。

      Delete
    8. 謝謝老師的幫忙與建議,我有一個新問題,在溫濕感測器delay(1秒)顯示一次的情況下,我用監控視窗輸入1後,啟動馬達2秒後停止,結果馬達轉動2秒後,程式就停止了,顯示資料跟輸入控制都不能動,但我卻不知道原因?
      以下是我的程式碼
      void loop() {
      while (Serial.available() > 0) {//等待電腦輸入
      a = Serial.read();
      Serial.print(a);
      }
      if(a == 49)//如果輸入1
      {
      currentTime = millis();//取得開始執行程式到目前的時間
      previousTime = currentTime;//取得馬達開始轉動時間
      while(currentTime - previousTime < interval) {//輸入1後經過時間低於2000ms
      currentTime = millis();//取得開始執行程式到目前的時間
      digitalWrite(In1,HIGH);//馬達轉動
      Serial.print("after_time:");
      Serial.println(currentTime - previousTime);//輸入1後經過時間
      }
      digitalWrite(In1,LOW);//馬達停止
      a = 0;//清除輸入
      }
      int chk = DHT11.read(DHT11PIN);
      Serial.print("H");
      Serial.print((int)DHT11.humidity);//顯示濕度
      Serial.print("T");
      Serial.println((int)DHT11.temperature);//顯示溫度
      delay(1000);
      }

      Delete
    9. currentTime - previousTime < interval 永遠為真,
      所以while會是無窮迴圈。

      Delete
    10. 我先註解digitalWrite(In1,HIGH);,while跑2秒後,會離開迴圈,以下是執行輸入1後的結果,但是我如果讓馬達動起來可能只會顯示到after_time=490,程式就停下來了,監視視窗只會停留在after_time=490
      after_time:1999
      after_time:2000
      H58T28
      H61T22

      Delete
    11. > while跑2秒後,會離開迴圈
      這些行為不符合你貼的程式碼,想必你改過了。

      > 程式就停下來了
      什麼意思?一直看到after_time=490嗎?
      那就代表進入while無窮迴圈了

      Delete
    12. This comment has been removed by the author.

      Delete
    13. > while跑2秒後,會離開迴圈
      啊,抱歉,之前是我想錯了。迴圈裡有currentTime = millis(),所以不是無窮迴圈。

      > 只會顯示到after_time=490,程式就停下來了
      不明白為什麼會停下來,電源方面的問題吧,我猜。

      > digitalWrite(In1,HIGH);
      這是控制什麼?
      馬達是什麼馬達?

      Delete
    14. 控制直流馬達

      Delete
  24. 葉難老師您好:
    不好意思想請問一下,我想以開關控制伺服馬達的轉動與關閉。
    然而我現在的狀況是,按下開關馬達轉。
    但是我必須等到程式執行完,才能按關閉(這樣我按關閉不就沒意思了)
    所以,想請教一下我該如何撰寫
    才能在轉動的狀態馬上將伺服馬達關閉
    望您給我點方向
    感恩

    ReplyDelete
    Replies
    1. > 等到程式執行完,才能按關閉
      代表你控制伺服馬達的函式屬於阻斷式,到達指定位置才會回傳。

      請改用別套程式庫,譬如
      https://github.com/netlabtoolkit/VarSpeedServo

      Delete
  25. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. 資訊不足。
      你的電路、機械部分、程式碼?

      Delete
  26. Anonymous29/3/16 20:13

    請問我用陀螺儀mpu6050控制水平,我只在x軸接一個伺服馬達,伺服馬達可以控制他的開啟關閉嗎,像是用led控制一樣,給高電位和低電位的方法

    ReplyDelete
    Replies
    1. > 只在x軸接一個伺服馬達
      什麼?mpu6050直接接伺服馬達?

      應該用Arduino讀取mpu6050吧,然後根據讀到的數值做判斷,控制伺服馬達的開啟關閉。

      Delete
    2. Anonymous30/3/16 16:11

      對,用mpu6050接arduino,在利用伺伏馬達去接收mpu的數值,可是我想要讓伺伏馬達可以控制在有需要的時候開啟旋轉,其他不必要的話只是一直讀到值但是馬達不會轉動,不過這樣用會一直產生overflow的情形

      Delete
    3. 你說的是需求,
      請問有什麼問題呢?

      Delete
  27. Anonymous13/4/16 21:29

    我想問上面寫到內定頻寬是244~2400,然後如果我買的馬達頻寬比這個小,我程式照樣執行馬達是會毀損嗎,那要怎麼知道馬達頻寬,我上網查型號好像都查不太到頻寬範圍的資訊

    ReplyDelete
    Replies
    1. 244~2400? 哪來的?

      > 會毀損嗎
      有可能。 但一開始應會發出怪聲,趕緊拔掉,應該還好。

      > 查不太到頻寬範圍
      詢問原廠。 有時候根本查不到。 只能做實驗嘗試。

      Delete
    2. Anonymous13/4/16 22:39

      500~2400是arduino內建的嗎?頻寬範圍

      Delete
    3. 500~2400是我查到的,SG90的脈衝寬度範圍。

      Arduino程式庫Servo預設的範圍是544~2400。

      Delete
  28. #include
    #define SERVO_PIN1 9


    #define PW_LOWER_BOUND 1300 // PW是Pulse Width脈衝寬度
    #define PW_UPPER_BOUND 1700
    Servo s1 // 建立Servo物件
    void setup() {
    // 自行指定脈衝寬度上下限
    s1.attach(SERVO_PIN1, PW_LOWER_BOUND, PW_UPPER_BOUND);

    }
    void loop() {
    s1.writeMicroseconds(PW_LOWER_BOUND); // 直接指定脈衝寬度
    delay(1000);
    s1.writeMicroseconds((PW_LOWER_BOUND+PW_UPPER_BOUND)/2); // 直接指定脈衝寬度
    delay(1000);

    s1.writeMicroseconds(PW_UPPER_BOUND); // 直接指定脈衝寬度
    delay(1000);
    s1.writeMicroseconds((PW_LOWER_BOUND+PW_UPPER_BOUND)/2); // 直接指定脈衝寬度
    delay(1000);
    }

    上面程式執行到下列這一行時,伺服馬達會停止
    s1.writeMicroseconds((PW_LOWER_BOUND+PW_UPPER_BOUND)/2);




    接著我參考 http://playground.arduino.cc/Learning/TLC5940,下載程式庫https://github.com/PaulStoffregen/Tlc5940,
    開啟File→Sketchbook→Examples→Library-Tlc5940→Servos 如下

    /*
    This sketch sweeps a servo on channel 0.

    To connect a servo:
    1. Put a 2k-5k pull-up resistor (R0 below; I've tried with 3.3k) between the
    servo control output pin and +5v.
    2. Connect that same pin to the servo's control line like so

    servo data pin
    | _____
    OUTn ----+----[_____]---+5v
    R0

    Steve Pomeroy , 2009-01-20 */

    #include "Tlc5940.h"
    #include "tlc_servos.h"

    #define SERVO_CHANNEL 0
    #define DELAY_TIME 20

    void setup()
    {
    tlc_initServos(); // Note: this will drop the PWM freqency down to 50Hz.
    }

    void loop()
    {
    //for (int angle = 0; angle < 180; angle++) {
    tlc_setServo(SERVO_CHANNEL, 90);
    Tlc.update();
    delay(DELAY_TIME);
    //}
    //for (int angle = 180; angle >= 0; angle--) {
    // tlc_setServo(SERVO_CHANNEL, angle);
    // Tlc.update();
    // delay(DELAY_TIME);
    //}
    }

    接下來我只執行輸出90度,想讓伺服馬達停止,發現無法停止,我開啟程式庫裡面的tlc_servos.h,去修改上下界的脈衝寬度如下

    /* Copyright (c) 2009 by Alex Leone

    This file is part of the Arduino TLC5940 Library.

    The Arduino TLC5940 Library is free software: you can redistribute it
    and/or modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    The Arduino TLC5940 Library is distributed in the hope that it will be
    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with The Arduino TLC5940 Library. If not, see
    . */

    #ifndef TLC_SERVOS_H
    #define TLC_SERVOS_H

    /** \file
    TLC servo functions. */

    #include "Tlc5940.h"

    #ifndef SERVO_MAX_ANGLE
    /** The maximum angle of the servo. */
    #define SERVO_MAX_ANGLE 180
    #endif
    #ifndef SERVO_MIN_WIDTH
    /** The 1ms pulse width for zero degrees (0 - 4095). */
    #define SERVO_MIN_WIDTH 1300 //原本204修改成1300
    #endif
    #ifndef SERVO_MAX_WIDTH
    /** The 2ms pulse width for 180 degrees (0 - 4095). */
    #define SERVO_MAX_WIDTH 1700 //原本410修改成1700
    #endif
    #ifndef SERVO_TIMER1_TOP
    /** The top value for XLAT and BLANK pulses. This is with the div8 prescale,
    so
    \f$\displaystyle f_{PWM} = \frac{f_{osc}}{2 * 8 * SERVO\_TIMER1\_TOP} \f$
    The default is 20000, which corresponds to 50Hz. */
    #define SERVO_TIMER1_TOP 20000
    #endif
    #ifndef SERVO_TIMER2_TOP
    /** The top value for GSCLK pulses. Related to SERVO_TIMER1_TOP by
    \f$\displaystyle SERVO\_TIMER2\_TOP =
    \frac{2 * 8 * SERVO\_TIMER1\_TOP}{4096} - 1 \f$
    The default is 77. */
    #define SERVO_TIMER2_TOP 77
    #endif



    重新編譯後,發現仍然無法讓馬達停止,請問該如何修改呢?

    ReplyDelete
  29. > 上面程式執行到以下程式碼時,我的伺服馬達會停止
    > s1.writeMicroseconds((PW_LOWER_BOUND+PW_UPPER_BOUND)/2);

    什麼叫做會停止?伺服馬達轉到該位置後,本來就會停在那邊。

    > 只留下輸出90度,想讓伺服馬達停止,結果無法停止
    那伺服馬達怎麼了?繼續轉動?

    > #define SERVO_MIN_WIDTH 1300 //原本204,我修改成1300
    > #define SERVO_MAX_WIDTH 1700 //原本410,我修改成1700
    我想不該如此修改,因為它的單位應該不是us。
    但我也不知如何修改。

    ReplyDelete
  30. 因為我的伺服馬達是屬於連續轉動的馬達,如果用writeMicroseconds輸入脈衝1300,會一直順時鐘轉動,如果是1700,會一直逆時鐘轉動,如果是1500,則會停止轉動

    ReplyDelete
    Replies
    1. 試試吧,我也不確定。
      改成這樣:
      #define SERVO_MIN_WIDTH 266
      #define SERVO_MAX_WIDTH 348

      Delete
  31. 您的算法我大概了解

    (1300/20000)X4096=266.24
    (1700/20000)X4096=348.16

    改成上面兩個數字帶入還是一樣會慢速旋轉,

    如果將程式庫裡面的tlc_servos.h 內的改成以下
    #define SERVO_MIN_WIDTH 266
    #define SERVO_MAX_WIDTH 348

    則tlc_setServo(SERVO_CHANNEL,74) 才會停止


    如果將程式庫裡面的tlc_servos.h 內的改成以下
    #define SERVO_MIN_WIDTH 1300
    #define SERVO_MAX_WIDTH 1700

    則tlc_setServo(SERVO_CHANNEL,83) 才會停止


    雖然上面兩個方法可以讓馬達停止達到我的目的,還是想知道能不能校正成90度讓他停止以及tlc5940的程式庫有沒有函數
    可以直接輸入脈衝寬度

    如果不用tlc5940,直接用arduino 接腳9 接伺服馬達,然後直接輸入脈衝,執行 s1.writeMicroseconds(1500)就會完全停止,
    但是Arduino能接的伺服馬達腳位不夠用,所以才會用tlc5940,一個tlc5940可連接16個伺服馬達,二個tlc5940可連接32個伺服馬達

    ReplyDelete
  32. > 一樣會慢速旋轉
    那就要再調整啊。

    > 能不能校正成90度
    呃,慢慢調整、慢慢找,應該可以吧。
    因為我找不到
    #define SERVO_MIN_WIDTH 204
    #define SERVO_MAX_WIDTH 410
    為什麼是204跟410的算法。

    > 有沒有函數可以直接輸入脈衝寬度
    tlc_servos.h,看起來是沒有。
    也不清楚它裡頭的算法。

    ReplyDelete
    Replies
    1. 算法應該如下:
      1ms的話,4095 / 20 = 204.75,於是原本設為204。
      2ms的話,4095 / 20 * 2 = 409.5,於是原本設為410。
      如果相加除以2,兩者並不完全相等,或許這是造成你設為90度仍無法停止的原因。

      嗯,慢慢調囉。

      Delete
  33. 您好~
    因為最近在做專題的東西
    在使用藍牙控制LED跟伺服馬達
    您的文章對我一直有很大的幫助
    我有一個很大的困擾要請問您

    我用一個arduino的板子,好像無法同時控制LED跟馬達
    分開做出來就可以
    在一起控制就不行
    LED控制多了這個宣告後"myservo.attach(9);"
    變得不能控制

    麻煩您了Orz

    ReplyDelete
    Replies
    1. #include //匯入程式庫
      SoftwareSerial BT(8,9);//定義PIN8及PIN9,TX RX


      #include
      Servo myservo;


      int ledPin = 10,value =1 ,ledvalue;//LED
      //------------------------------------
      int n, up = 7, down = 6, Up, Down;;//開關
      //------------------------------------
      int flame = A1, BZ =4, val =0 ,flameled =5, x=0, y=5, BZdelay;//火感
      //------------------------------------
      int potpin =A2, potval =0, ledPin0 =3, i;//光感
      //------------------------------------
      int ledatuo = 12;//自動;
      //------------------------------------




      void setup()
      {
      Serial.begin(9600);//arduino接收率9600
      BT.begin(9600);//藍牙接收率9600
      //------------------------------
      pinMode(ledPin, OUTPUT);//設定pin10
      //------------------------------
      pinMode(down,INPUT);//開關
      pinMode(up,INPUT);//開關
      //------------------------------
      pinMode(BZ,OUTPUT);//蜂鳴器
      //------------------------------
      pinMode(ledPin0,OUTPUT);//光敏
      //------------------------------
      pinMode(ledatuo,OUTPUT);//自動
      //------------------------------
      }



      void loop()
      {
      Up = digitalRead(up);
      Down = digitalRead(down);
      if(Up > 0)
      {
      Serial.println(n);
      n = n + 2;
      if(n >= 255)
      {
      n = 255;
      }
      analogWrite(ledPin,n);
      delay(50);
      }
      if(Down > 0)
      {
      Serial.println(n);
      n = n - 5;
      if( n <= 0)
      {
      n = 0;
      }
      analogWrite(ledPin,n);
      delay(50);
      }
      //-------------------------------------------------------------------------------------------------------------觸控
      val = analogRead(flame);
      if(val>=850)
      {
      digitalWrite(flameled, LOW);
      digitalWrite(BZ, LOW);
      }
      else
      {
      if(val <=849 && val >=800) BZdelay=10;
      if(val <=800 && val >=650) BZdelay=5;
      if(val <=100 ) BZdelay=2;
      analogWrite(flameled, x);
      analogWrite(ledPin, x);
      analogWrite(ledPin0, x);

      x = x + y;
      if (x <= 0 || x >= 255)
      {
      y = -y ;
      }
      if(x == 255 )
      {
      digitalWrite(BZ, HIGH);
      }
      if(x == 0 )
      {
      digitalWrite(BZ, LOW);
      }
      delay(BZdelay);


      }


      //-------------------------------------------------------------------------------------------------------------防火

      }

      -----------------------------------------------------------------LED程式碼(1)

      Delete
  34. if(BT.available() == true||BT.available() == false)
    {

    if(BT.available() == true && BT.available() != false) //數據緩衝區
    {
    digitalWrite(ledatuo,LOW);
    value = BT.read();
    if(value >= 0 && value <=255 ) //如果有定義數據
    {
    Serial.println(value); //調適-再次確定數據是否有意義
    analogWrite(ledPin,value);//指定PWM給LED的亮度

    if(value == 1)
    {
    digitalWrite(ledPin,LOW);

    }
    if(value == 'a')
    {
    Serial.println("On");
    digitalWrite(ledPin,75);
    }
    if(value == 'b')
    {
    Serial.println("Off");
    digitalWrite(ledPin,0);
    }
    if(value == 'x')
    {
    potval=(225)-(analogRead(potpin)*4);//讀取感測器CDS的類比值並賦值給potval
    if(potval <= 115)
    {
    digitalWrite(ledPin0,LOW);

    }
    else if(potval >= 116)
    {
    analogWrite(ledPin0,potval); // 打開LED 並設置亮度(PWM 輸出最大值255)
    }
    }
    if(value == 'z')
    {
    digitalWrite(ledPin0,LOW);
    }
    }

    }
    //---------------------------------------------------------------------------------------
    if(BT.available() == false && BT.available() != true)
    {

    if(value == 0)
    {
    digitalWrite(ledPin0,LOW);
    digitalWrite(ledatuo,LOW);
    digitalWrite(ledPin,LOW);
    }
    if(value == 1)
    {
    potval=(225)-(analogRead(potpin)*4);//讀取感測器CDS的類比值並賦值給potval
    if(potval <= 129)
    {
    digitalWrite(ledPin0,LOW);
    digitalWrite(ledatuo,HIGH);
    //digitalWrite(ledPin,LOW);

    }
    else if(potval >= 130)
    {
    digitalWrite(ledatuo,HIGH);
    //digitalWrite(ledPin,LOW);
    analogWrite(ledPin0,(potval)); // 打開LED 並設置亮度(PWM 輸出最大值255)
    }
    }
    }
    }
    -----------------------------------------------------------------LED程式碼(2)(太常無法一次傳送,這段在LOOP裡面)

    ReplyDelete
  35. #include
    #include
    Servo myservo;

    SoftwareSerial BT (8,9);
    char command;

    int ang = 90;



    void setup()
    {
    BT.begin(9600);
    myservo.attach(11);
    myservo.write(90);
    }
    void loop()
    {

    if(BT.available()>0)
    {
    command=BT.read();
    switch(command)
    {
    case'a':ang += 30;if(ang>180) ang=180;
    myservo.write(ang);delay(20);
    break;

    case'b':
    myservo.write(0);
    break;

    case'c':ang += 5;if(ang>180) ang=180;
    myservo.write(ang);delay(20);
    break;

    case 'd':
    ang -= 5;if(ang<0) ang=0;
    myservo.write(ang);delay(20);
    break;


    }
    }
    }


    -----------------------------------------這是伺服馬達的程式

    ReplyDelete
  36. 咳咳,請把程式碼貼在專門的網站,譬如http://pastebin.com/

    然後再把網址告訴我。

    ReplyDelete
    Replies
    1. 請問可以給我您的信箱嗎
      我寄到您的信箱

      我最大的問題是LED跟馬達部分分開都可以正常運作
      但是不知道如何把兩個程式改成一個程式
      我試了一段時間但是還是沒有結果

      我的程式是想要用藍芽APP同時操控LED跟馬達
      真的很感謝您的回覆
      期待您的解答
      如果本來就不行的話也請您告訴我:D
      感謝您

      Delete
    2. > 請問可以給我您的信箱嗎
      不方便給。

      > 不知道如何把兩個程式改成一個程式
      我大致上看了,覺得應無問題。
      不知道你碰到什麼問題。

      Delete
    3. 請問您有試過把伺服馬達跟LED同時控制嗎?

      我在想辦法傳給您,我沒有用過網頁貼程式碼的...我試試看

      Delete
    4. 有。很多人都試過。

      Delete
    5. http://pastebin.com/BmHZ0Bsw

      Delete
    6. 請問看的到嗎

      Delete
    7. 可以。

      有何問題?

      Delete
    8. 我無法將兩個程式合在一起,可以幫我一夏嗎?

      Delete
    9. 恩恩...好的 謝謝您

      Delete
    10. 你嘗試寫的程式碼呢?
      覺得哪裡有問題。

      Delete
  37. 請問你程式碼裡的
    if(BT.available() == true || BT.available() == false)
    是什麼意思?

    還有
    if(BT.available() == true && BT.available() != false)
    是什麼意思?

    ReplyDelete
    Replies
    1. if(BT.available() == true || BT.available() == false)

      ---藍芽不管通不通都往下讀

      if(BT.available() == true && BT.available() != false)
      ---藍芽通&&藍芽不能不通

      if(BT.available() == false && BT.available() != true)
      ---藍芽不通&&藍芽不能通


      抱歉我寫得較亂,我是位新手,真的很感謝您的熱情回復Orz

      Delete
    2. available()回傳的是可供讀取的byte數量。

      Delete
    3. 那有需要修正嗎?:)

      Delete
    4. 拿available()回傳值跟true與false相比,很奇怪。

      Delete
    5. 請問我要怎麼寫才能讓程式判斷
      藍芽沒有連上時做甚麼
      藍芽連上時做甚麼
      ˊAˋ....我還在嘗試中

      Delete
    6. 藍牙有沒有連上,要下AT指令查詢才知道狀態。
      知道後,就是以if判斷囉。

      Delete
    7. AT指令要怎麼使用呢
      可以給我一個小範例嗎:D

      Delete
    8. 呃,你不是已經在使用藍牙模組了嗎?

      每個藍牙模組所定義的AT指令,不一樣相同。

      譬如
      AT+STATE 是查詢狀態,可能會回傳PAIRABLE可配對、CONNECTED已連線、等等。
      AT+UART 查詢傳輸速率。
      AT+LINK=xxxxxxx 連接到位址為xxxxxxx的另一個藍牙模組。
      等等

      Delete
    9. 是進入藍牙模駔裡面設定嗎
      不好意思我是個新手,在網路上學習得,問題挺多的

      Delete
    10. 讓藍牙模組進入AT模式,便可傳送AT指令進行設定。

      可先看看
      HC-05與HC-06藍牙模組補充說明(一)
      http://swf.com.tw/?p=693

      Config bluetooth baud rate w/ Arduino
      http://coopermaa2nd.blogspot.tw/2012/07/config-bluetooth-baud-rate-w-arduino.html

      等分享文章。

      Delete
    11. 謝謝您^ ^我去看看

      Delete
    12. 您好~我發現在定義馬達後(myservo.attach(11);)
      使用analogWrite的LED好像就不能使用了呢
      使用digitalWrite的LED可以使用

      Delete
    13. 控制伺服馬達要發出PWM,需要使用硬體計時器;
      使用時,若是Uno板,腳位9與10就無法使用analogWrite。

      Delete
    14. 那請問伺服馬達(不用硬體計時器)可以使用PWM控制嗎?

      myservo.attach(11); myservo.write(90);
      改成
      pinMode(11,OUTPUT);analogWrite(11,90);

      Delete
    15. 你試試看不就知道了。

      不行。
      伺服馬達PWM訊號的頻率,跟analogWrite輸出的頻率不同。

      Delete
    16. 所以要如何用myservo.write控制馬達又用analogWrite控制LED呢

      Delete
    17. 若是Arduino Uno,
      Servo程式庫使用的計時器會影響腳位9與10的analogWrite,
      請改用其他腳位。

      Delete
    18. 那我試著把腳位,9,10空出來不用

      Delete
    19. 感謝你:)問題是在這上面,我這還有一個mega板請問會有哪些腳也會像uno板一樣會影響而不能用的嗎?

      Delete
    20. 請查閱https://www.arduino.cc/en/Reference/Servo

      Delete
    21. Hi~你好~:D
      我用的板子是Mega2560 , LED 接腳在pin7 , 伺服馬達用SG90 ,接腳在pin10 ,遠端用藍牙控制馬達跟LED
      控制LED沒問題 控制馬達 馬達轉動的時候LED的亮度也會跟著改變, 請問為什麼呢?

      http://pastebin.com/kA1vu7bU

      Delete
    22. 什麼樣的改變?
      請描述馬達轉動跟LED亮度之間的關係。

      Delete
    23. > 馬達轉動的時候LED的亮度也會跟著改變
      從你的程式,看不出來為什麼。

      另外,
      程式有個byte Data[1];
      該陣列的大小是1,有效索引值只有0而已。
      但後面程式卻Data[1] = 'h',使用索引值1。

      Delete
    24. 謝謝你:D 那我改成byte Data[2]; 這樣可以嗎? 那個地方我用在溫溼度感測上面,先讀取一個信號確認進入後再量測

      LED假設我是調 15的亮度 , 開始調整馬達角度到 30度 轉動的時候LED亮度就不再是15了 ,請問是我在MIT App Inventor 2 上面設計錯誤嗎?

      Delete
    25. > 改成byte Data[2]; 這樣可以嗎?
      只解決一個問題。

      if(Data[0] = 't') 這是 = ,會把Data[0]改成't',是你要的嗎?
      你是要比較吧?使用 == ?
      如果要比較,那麼Data裡的資料從哪來呢?

      > 調整馬達角度到 30度
      怎麼調整?

      > 就不再是15了
      那會是多少?

      > 是我在MIT App Inventor 2 上面設計錯誤嗎?
      我怎麼知道。

      程式裡,有多處控制ledPin,可以自己寫上Serial.print類似的程式輸出訊息,
      這樣就知道到底執行何處改變LED亮度了,不是嗎?

      Delete
    26. Data的資料從手機端傳回來
      我是參考這邊http://blog.cavedu.com/programming-language/appinventor/%E9%9B%99a%E8%A8%88%E7%95%ABpart6%EF%BC%9Aarduino-%E5%82%B3%E5%85%A9%E7%AD%86%E8%B3%87%E6%96%99%E5%88%B0-android-%E6%89%8B%E6%A9%9F/

      用手機去調整轉的角度

      謝謝你我試試看用Serial.print

      Delete
    27. byte Data[1]; 並沒有從手機(藍牙)讀取資料。

      Delete
    28. 那要和改才能讓他讀取手機的資料呢?

      Delete
    29. 你程式裡不是已經有了。
      bt.read()

      Delete
    30. 哦哦~了解 感謝

      Delete
  38. > LED控制多了這個宣告後"myservo.attach(9);"
    > 變得不能控制
    看了你的程式碼,看不出問題所在。

    但請注意,你的SoftwareSerial已使用腳位8與9,
    不可再用來控制伺服馬達myservo.attach(9);

    ReplyDelete
    Replies
    1. 抱歉我忘了改 我把接腳改11!
      謝謝您的提醒:D

      Delete
  39. 您好
    想請問是否有類似的程式碼可以供學習
    我想做5個input
    1. 每個input分別會對應到1個servo角度 0(defualt), 36, 72, 108, 144, 180
    - 感謝板大,這篇文章幫助很大
    - 但SG90常常會不自主震動,不久後便會發燙,不知道是程式碼錯誤嗎
    2. 每個input要按照順序,假設是 從 input 1 HIGH然後LOW(Servo 36度), 其他同樣先HIGH再LOW 2(Servo 72度), 3(Servo 108度), 4(Servo 144度), 5(Servo 180度) 一個一個,同時間Servo也會根據階段轉向不同角度 0, 36, 72, ...
    - 假設如果變成 input 1 (Servo 36度)然後 直接跳input 3, 這樣Servo會轉回0度因為順序錯誤

    第二點的部分是否可以請板大幫忙提供建議的閱讀文章?
    我現在用if的方式會做不成功,還在尋找其他方式..

    謝謝
    Kevin

    ReplyDelete
    Replies
    1. 1. 碰到什麼問題嗎?
      不就是根據input、然後調整servo的角度。

      > 但SG90常常會不自主震動
      嗯,不該如此,程式碼呢?

      > 不久後便會發燙
      理由應該同上。不過你說發燙是多燙,電子零件運作時的溫度,通常會讓人手覺得很燙。

      2. 看不懂你寫的描述。


      Delete
  40. 為甚麼我造著你的電路接跟程式打我卻只能轉180度,無法從180度轉回0度?

    ReplyDelete
    Replies
    1. 馬達毀損 或 電路接錯 或 程式有誤,等等各種可能原因。

      Delete
  41. 請問如果使用此程式跟紅外線感應做結合車子過馬達打開車子不過馬達關閉程式
    怎麼修 改

    ReplyDelete
    Replies
    1. 你說的是需求,不是問題。
      請問碰到什麼問題嗎?

      Delete
  42. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. 外接電源或電池,若電壓不同,加入穩壓器。
      要注意穩壓器能夠輸出的電流上限,是否足夠供給你的馬達。

      Delete
    2. 我用電腦的外接電源,五個馬達都可以順利的控制,但是沒控制的其他馬達會產生顫抖的現象,請問這是什麼原因,如何解決呢

      Delete
    3. 我用可變電阻控制的

      Delete
    4. http://coopermaa2nd.blogspot.tw/2011/01/arduino-lab16.html

      Delete
    5. 參考上面那篇的

      Delete
    6. 印出analogRead讀到的值,看你沒控制可變電阻時,值是否不變。

      程式呢?

      Delete
    7. #include

      Servo myservo ;// 建立一個 Servo 物件
      Servo myservo1 ;
      Servo myservo2 ;
      Servo myservo3 ;
      Servo myservo4 ;

      int potpin = 0;// 可變電阻接在 Analog pin 0
      int potpin1 = 1;
      int potpin2 = 2;
      int potpin3 = 3;
      int potpin4 = 4;

      int val;// 儲存旋轉角度的變數
      int val1;
      int val2;
      int val3;
      int val4;

      void setup()
      {
      myservo.attach(9); // 食指接在 pin 9
      myservo1.attach(10); // 大拇指 10
      myservo2.attach(11); // 中指 11
      myservo3.attach(6); // 無名指 6
      myservo4.attach(5); // 小拇指 5
      }

      void loop()
      {
      val = analogRead(potpin); // 讀取可變電阻(數值介於 0 到 1023)
      val = map(val, 0, 1023, 0, 179); // 把 0 – 1023 的數值按比例縮放為 0 – 180 的數值
      myservo.write(val); // 設定 Servo 旋轉角度
      delay(15); // 等待 Servo 旋轉指定的位置

      val1 = analogRead(potpin1);
      val1 = map(val1, 0, 1023, 0, 179);
      myservo1.write(val1);
      delay(15);

      val2 = analogRead(potpin2);
      val2 = map(val2, 0, 1023, 0, 179);
      myservo2.write(val2);
      delay(15);

      val3 = analogRead(potpin3);
      val3 = map(val3, 0, 1023, 0, 179);
      myservo3.write(val3);
      delay(15);

      val4 = analogRead(potpin4);
      val4 = map(val4, 0, 1023, 0, 179);
      myservo4.write(val4);
      delay(15);
      }

      Delete
    8. This comment has been removed by the author.

      Delete
    9. 都跟網站的一樣

      Delete
    10. 請問要怎麼印出analogRead讀到的值

      Delete
    11. void setup(){
      Serial.begin(9600);
      }

      void loop(){
      val = analogRead(potpin);
      Serial.println(val);
      }

      > 沒控制的其他馬達會產生顫抖的現象
      嗯,還是看不出來為什麼。

      Delete
    12. 好像是馬達本身的問題
      試過接電源供應器以及放大器了
      還是有顫抖的現象

      Delete
    13. 嗯,真奇怪。真是深奧啊。

      沒幫上忙,orz。

      Delete
  43. 不好意思,請問一下伺服馬達可旋轉角度是多少
    自己現在程式是設定旋轉0到90度,電壓用3.3v
    可是馬達旋轉幾次後,就出問題了,現在旋轉角度都會跑掉
    已經準備換新的了,但是自己不知道問題在哪,是因為電壓沒用5v嗎?

    ReplyDelete
    Replies
    1. 伺服馬達通常都吃5~6V。
      最大旋轉角度,馬達各不相同。

      Delete
  44. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. 對。

      不過這塊板子的各GND已經連接在一起了,
      所以圖中板子上方的GND已連接到Arduino的GND,
      圖中板子右方的螺絲端子GND若連接到穩壓模組的GND,
      就可以了,你不必自己再接。

      你應該是看
      https://learn.adafruit.com/16-channel-pwm-servo-driver/hooking-it-up
      的吧?
      該網頁後面不是有示意圖嗎?

      Delete
  45. 請問如果我用https://img.ruten.com.tw/s1/7/f3/f7/21551405613047_274.jpg (16路舵機驅動板模組)控制多數伺服馬達,外接電源要把圖片中間IC右邊的VCC和GND接到穩壓模組輸出的VCC和GND,穩壓模組輸入的VCC和GND接電池的VCC和GND,這樣接對嗎?

    ReplyDelete
    Replies
    1. 圖片中間右邊,不是VCC,而是V+。
      板子的VCC是供應給晶片的電源。
      V+是供應給伺服馬達的電源。

      Delete
  46. 您好~
    新手報到,我想要單純用Arduino接我的飛控,飛控需要72MHz才能運作,請問我可以用writeMicroseconds的方法寫嗎?對writeMicroseconds不是很了解,擾請大大幫忙,謝謝~

    ReplyDelete
    Replies
    1. 沒經驗耶,建議你到別處詢問。

      你說的72MHz,應該是無線電波的頻率,跟writeMicroseconds沒關係。

      Delete
  47. 請問如果想控制五個伺服馬達,用兩個L298N接在Arduino uno上,剩下的一個馬達用Arduino供電,這樣腳位夠嗎

    ReplyDelete
    Replies
    1. 不懂為什麼這樣接。
      你不是已經用外接電源供電了嗎?
      L298N通常用來控制直流馬達吧。

      Delete
  48. const int button3=3; //宣告BUTTON為整數常數,並將其值設為3(數位PIN3)
    int btVoltage3=0; //將按鈕電位(btvoltage3)設為整數變數,初始值為0
    const int button4=4; //宣告BUTTON為整數常數,並將其值設為4(數位PIN4)
    int btvoltage4=0; //將按鈕電位(btvoltage4)設為整數變數,初始值為0

    #include
    Servo myservo; // 建立Servo物件,控制伺服馬達
    void setup()
    {
    myservo.attach(9); // 連接數位腳位9,伺服馬達的訊號線
    pinMode(button3,INPUT);//將button3腳位設為輸入模式
    pinMode(button4,INPUT);//將button4腳位設為輸入模式
    }
    void loop()
    {
    btVoltage3 = digitalRead(button3);//讀取按鈕腳位3目前的電位值
    btVoltage4 = digitalRead(button4);//讀取按鈕腳位4目前的電位值
    if(btVoltage3==HIGH& btVoltage4==LOW){
    for(int i = 0; i <= 180; i+=1){
    myservo.write(i); // 使用write,傳入角度,從0度轉到180度
    delay(20);
    } }
    else(btVoltage4==HIGH& btVoltage3==LOW){
    for(int i = 180; i >= 0; i-=1){
    myservo.write(i);// 使用write,傳入角度,從180度轉到0度
    delay(20);
    }
    }
    }
    一直編譯錯誤=====>'btVoltage4' was not declared in this scope
    可否賜教

    ReplyDelete
    Replies
    1. > 'btVoltage4' was not declared in this scope
      因為編譯器找不到這個變數。

      Delete
    2. 那要怎麼修改

      Delete
    3. 在適當的地方 定義該變數囉
      int btVoltage4=0;

      Delete
  49. This comment has been removed by the author.

    ReplyDelete
  50. This comment has been removed by the author.

    ReplyDelete
  51. 你好,我想請問:
    因為我最近在使用I2C-bus 24-channel的PWM控制板
    產品名稱:B24CH (是很新的產品,所以網路範例不多)
    該裝置的產品說明書中有提到控制伺服馬達轉動角度的值的設定
    產品說明書中稱那個值為【位置當量】
    這個部份我大概了解
    但他還提到一個可以修改的值叫做【速度當量】(預設為1)
    (就是他有兩個可修改的值去控制一個通道產生的PWM訊號,一個是基本的servo角度控制,另一個就是這個"速度當量")
    這個地方我很好奇,因為英文版的產品說明書這部分我不太懂
    所以我找到簡中版本的說明書(上面那些名詞就是此說明書中定義的)
    那我看了一下,他是否和接在該通道上的伺服馬達,其接受的訊號變化速度有關。
    就是說,那個值是不是可以控制伺服馬達在接受訊號後"轉動的速度"?
    不知道版主有沒有使用過這類產品?
    簡中說明書網址:
    http://pan.baidu.com/s/1kT3hCbH#path=%252F%25E4%25B8%25AD%25E6%2596%2587%252F%25E7%25A7%2581%25E6%259C%2589%252F(%25E8%2588%25B5%25E6%259C%25BA%25E6%258E%25A7%25E5%2588%25B6%25E5%2599%25A8%2520-%2520RC%25E4%25BF%25A1%25E5%258F%25B7%25E5%258F%2591%25E7%2594%259F%25E5%2599%25A8)SoftServo-B24CH%252F%25E6%2589%258B%25E5%2586%258C

    ReplyDelete
    Replies
    1. > 那個值是不是可以控制伺服馬達在接受訊號後"轉動的速度"?
      根據說明書,的確如此。

      文件中沒有「位置當量」與「速度當量」,而是當量位置和當量速度。
      我不懂為什麼要加上「當量」二字。

      > 有沒有使用過這類產品?
      用過別的。

      Delete
    2. 版主用過的產品也是能改轉動速度的嗎?
      因為我不知道值要寫多少,怕一個燒毀...
      版主如果有用過類似產品經驗的話,大概這個速度值是介於甚麼之間
      或是說版主知道這個值跟轉動的速度之間的關係嗎?

      Delete
    3. > 也是能改轉動速度的嗎?
      產品本身沒有,要自己控制。

      > 值要寫多少
      根據你給的說明書,預設值是0x01,往上慢慢調囉。
      看起來好像是1~250,但不確定。

      > 這個值跟轉動的速度之間的關係嗎?
      看說明書,值小應該代表速度慢,值大代表速度快。
      不過我不是100%確定。

      Delete