Arduino的馬達控制

1. 直流馬達與TT馬達(無編碼器)

1.1. 直流馬達工作原理

1.1.1. 工作原理

直流馬達(DC Motor)是一種將電能轉換為機械能的裝置。其基本原理基於「勞侖茲力」:當載有電流的導體(線圈)放置在磁場中時,會受到一個力的作用,從而產生轉矩,驅動馬達轉動。馬達內部的電刷和換向器結構確保線圈在不同旋轉角度下能持續朝同一方向轉動。

1.1.2. 轉速控制 (PWM)

要控制直流馬達的轉速,最常見的方法是使用脈衝寬度調變(Pulse-Width Modulation, PWM)
- 原理:PWM 技術不是改變電壓大小,而是快速地開關電源,並調整「開啟」時間佔總週期的比例(稱為工作週期 Duty Cycle)。
- 效果
- 高工作週期(如90%):馬達獲得的平均功率較高,轉速較快。
- 低工作週期(如20%):馬達獲得的平均功率較低,轉速較慢。
- Arduino 實作:Arduino 的 analogWrite(pin, value) 函式就是用來產生PWM訊號的。value 的範圍是 0(0% 工作週期,停止)到 255(100% 工作週期,全速)。

1.1.3. 方向控制的挑戰 與 馬達驅動器的必要性

理論上的方向控制:
要改變馬達的旋轉方向,最直觀的方法就是將供應給馬達的電源正負極交換。如果你將一個直流馬達接到電池上,然後將電線反接,你會發現馬達立刻反向旋轉。

實務上的挑戰:
這個「手動換線」的方法在自走車、機器手臂等需要動態改變方向的專案中是完全不可行的。我們不可能在程式跑到一半時,親手去拔插電線。我們需要的是一種能讓 Arduino 用程式邏輯訊號來控制電流方向的方法。

解決方案:馬達驅動器 (Motor Driver)
這就是為什麼馬達驅動器是不可或缺的。它一次解決了控制直流馬達的兩大核心問題:

  1. 功率放大 (Power Amplification)
    Arduino 的 I/O 接腳輸出電流非常小(約 20-40mA),遠不足以驅動馬達(通常需要數百mA甚至數A)。馬達驅動器從外部電源獲取足夠的電力,並根據 Arduino 的訊號來驅動馬達,就像一個由程式控制的「大功率水龍頭」。直接將馬達接到 Arduino 會燒毀開發板!

  2. 方向控制 (Direction Control)
    馬達驅動器內部整合了稱為 H-Bridge (H橋) 的精密電路。這個電路就像一個由程式控制的「雙向道閘門」,可以根據 Arduino 送來的 HIGHLOW 訊號,來決定電流要「從左到右」還是「從右到左」流過馬達,從而實現程式化的正反轉控制。

總結來說,馬達驅動器扮演著「翻譯官」兼「大力士」的角色:它將 Arduino 微弱的「指令」(邏輯訊號)轉譯成強大的「動作」(大電流輸出),並能依指令決定動作的方向。

1.2. TT馬達(無編碼器)介紹

1.3. 直流馬達與TT馬達比較

特性 一般直流馬達 (無齒輪箱) TT馬達 (帶減速齒輪箱)
轉速 非常高 較低
扭力 非常低 較高
應用場景 風扇、飛輪、需要高速旋轉的場合 輪型機器人、履帶車、需要力量的場合
控制 轉速不易在低速時穩定控制 轉速相對穩定,適合入門自走車
選擇建議 如果你需要高速旋轉,例如做一個小風扇。 如果你需要驅動輪子或任何需要力量的結構,請選擇TT馬達。

2. Arduino與馬達控制器

2.1. H-Bridge原理:馬達控制的核心

H-Bridge (H橋) 是所有直流馬達驅動器的核心電路。它的名字來自其電路結構形狀像一個大寫的 "H"。

2.1.1. 電路結構

H橋由四個電子開關(Q1, Q2, Q3, Q4)組成。在現代驅動器中,這些開關通常是電晶體(Transistors),由 Arduino 發出的邏輯訊號控制其開關狀態。馬達(M)被放置在H的中央橫槓上。

graph TD subgraph H-Bridge Circuit VCC(Power +) GND(Ground -) Q1(Q1: 左上開關) Q2(Q2: 右上開關) Q3(Q3: 左下開關) Q4(Q4: 右下開關) M((M)) VCC --|> Q1 VCC --|> Q2 Q1 -- 馬達左端 --> M Q2 -- 馬達右端 --> M M -- 馬達左端 --> Q3 M -- 馬達右端 --> Q4 Q3 --|> GND Q4 --|> GND end

2.1.2. 工作模式詳解

1. 正轉 (Forward)
- 狀態:開關 Q1Q4 閉合,Q2Q3 斷開。
- 電流路徑:電流從 Power+ -> Q1 -> 馬達 (左至右) -> Q4 -> Ground-
- 結果:馬達向一個方向旋轉。

2. 反轉 (Reverse)
- 狀態:開關 Q2Q3 閉合,Q1Q4 斷開。
- 電流路徑:電流從 Power+ -> Q2 -> 馬達 (右至左) -> Q3 -> Ground-
- 結果:馬達向相反方向旋轉。

3. 自由滑行 (Coast / Freewheeling)
- 狀態:所有開關 Q1, Q2, Q3, Q4 全部斷開。
- 電流路徑:沒有電流路徑。馬達與電路完全斷開。
- 結果:馬達會因自身的慣性繼續旋轉,直到摩擦力使其自然停止。這就像空檔滑行。

4. 電磁煞車 (Dynamic Brake)
- 狀態:上方開關 Q1, Q2 斷開,下方開關 Q3, Q4 閉合。
- 電流路徑:馬達的兩個接腳被 Q3Q4 短路在一起。
- 結果
- 由於慣性,仍在旋轉的馬達會像一個小型發電機一樣產生感應電動勢。
- 這個電壓在短路的迴路中產生電流。
- 根據勞侖茲力,這個電流會產生一個與旋轉方向相反的扭力,從而迅速地讓馬達停下來。這提供了比自由滑行更快的煞車效果。

2.1.3. 重要警告:直通 (Shoot-through)

絕對不能讓同一側的開關(例如 Q1Q3,或 Q2Q4)同時閉合。這會造成從 Power+ 直接通過兩個開關流向 Ground-短路,產生巨大電流,瞬間燒毀馬達驅動器。專業的馬達驅動晶片(如L298N)內部都設計有「死區時間」或保護邏輯,以防止這種情況發生。

2.2. 馬達控制器介紹

2.2.1. 馬達控制器種類

除了 L298N,市面上還有許多不同的馬達驅動器,各有優劣:
- L298N:經典、便宜、耐用,但效率較低、體積大、會發熱。適合初學者。
- TB6612FNG:比 L298N 更現代、效率更高、體積小。是目前許多專案的首選。
- DRV8833:與 TB6612FNG 類似,體積小、效率高,常用於小型機器人。
- 馬達擴充板 (Motor Shield):直接插在 Arduino 上的擴充板,通常整合了 TB6612FNG 或類似晶片,接線非常方便,但價格較高。

2.2.2. 馬達控制器選型指南

  1. 馬達數量:你需要控制幾個馬達?L298N、TB6612FNG、DRV8833 通常都能控制2個。
  2. 工作電壓:你的馬達需要多高的電壓?檢查驅動器的電壓範圍是否匹配。
  3. 最大電流:這是最重要的參數。檢查驅動器能持續提供及瞬間提供的最大電流是否大於你的馬達堵轉時的電流,否則會燒毀驅動器。
  4. 效率與發熱:L298N 是電晶體結構,效率低、易發熱;TB6612FNG 等是 MOSFET 結構,效率高、發熱少。
  5. 體積與便利性:擴充板最方便,但犧牲了靈活性;L298N體積最大。

2.3. L298N 完整接線圖 (以L298N為例)

這張圖展示了如何連接 Arduino UNO、L298N、兩個TT馬達以及一個外部電池盒,構成一個典型的雙輪驅動車基礎。

graph TD subgraph Arduino_UNO D9((PWM ~9)) D8((8)) D7((7)) D6((PWM ~6)) D5((5)) D4((4)) GND_A[GND] end subgraph L298N_Motor_Driver ENA((ENA)) IN1((IN1)) IN2((IN2)) ENB((ENB)) IN3((IN3)) IN4((IN4)) VCC_L298N((12V)) GND_L298N((GND)) OUT1((OUT1)) OUT2((OUT2)) OUT3((OUT3)) OUT4((OUT4)) end subgraph External_Power Battery[電池盒 6V] P_BAT[+] N_BAT[-] end subgraph Motors MotorA[左馬達] MotorB[右馬達] end %% Arduino to L298N connections D9 -- PWM控制左輪轉速 --> ENA D8 -- 左輪方向 --> IN1 D7 -- 左輪方向 --> IN2 D6 -- PWM控制右輪轉速 --> ENB D5 -- 右輪方向 --> IN3 D4 -- 右輪方向 --> IN4 %% Power connections P_BAT -- 電源正極 --> VCC_L298N N_BAT -- 電源負極 --> GND_L298N GND_L298N -- 建立共地 --> GND_A %% Motor connections OUT1 -- 馬達線 --> MotorA OUT2 -- 馬達線 --> MotorA OUT3 -- 馬達線 --> MotorB OUT4 -- 馬達線 --> MotorB classDef arduino fill:#66ccff,stroke:#333,stroke-width:2px; classDef l298n fill:#ffcc66,stroke:#333,stroke-width:2px; classDef power fill:#cc99ff,stroke:#333,stroke-width:2px; classDef motor fill:#99ff99,stroke:#333,stroke-width:2px; class Arduino_UNO arduino class L298N_Motor_Driver l298n class External_Power power class Motors motor

2.4. 兩個馬達的控制邏輯

假設 IN1, IN2 控制左輪,IN3, IN4 控制右輪。轉速由 ENA, ENB 的PWM值決定。

動作 IN1 IN2 IN3 IN4 說明
前進 HIGH LOW HIGH LOW 兩個馬達都向前轉
後退 LOW HIGH LOW HIGH 兩個馬達都向後轉
左轉 LOW HIGH HIGH LOW 左輪後退,右輪前進 (原地旋轉)
右轉 HIGH LOW LOW HIGH 左輪前進,右輪後退 (原地旋轉)
停止 LOW LOW LOW LOW 兩個馬達停止 (自由滑行)

註:左轉和右轉也可以透過單邊輪子轉動、另一邊停止的方式實現,會是畫弧線轉彎而非原地旋轉。


3. 程式範例

這個範例程式會讓自走車重複執行「前進2秒 -> 停止1秒 -> 右轉0.5秒 -> 停止1秒」的動作。

// ===== Pin Definitions =====
// Motor A (Left)
const int enA = 9;   // 左輪轉速控制 (PWM)
const int in1 = 8;   // 左輪方向控制
const int in2 = 7;   // 左輪方向控制

// Motor B (Right)
const int enB = 6;   // 右輪轉速控制 (PWM)
const int in3 = 5;   // 右輪方向控制
const int in4 = 4;   // 右輪方向控制

// ===== Speed Setting =====
int motorSpeed = 200; // 設定馬達轉速 (0-255)

// ===== Setup Function =====
void setup() {
  // 將所有控制腳位設定為輸出模式
  pinMode(enA, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(enB, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);

  // 程式開始時,先讓馬達停止
  stopMotors();
}

// ===== Main Loop =====
void loop() {
  // 1. 前進 2 秒
  moveForward(motorSpeed);
  delay(2000);

  // 2. 停止 1 秒
  stopMotors();
  delay(1000);

  // 3. 右轉 0.5 秒
  turnRight(motorSpeed);
  delay(500);

  // 4. 停止 1 秒
  stopMotors();
  delay(1000);
}

// ===== Custom Functions =====

// 前進
void moveForward(int speed) {
  // 左輪前進
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
  analogWrite(enA, speed);

  // 右輪前進
  digitalWrite(in3, HIGH);
  digitalWrite(in4, LOW);
  analogWrite(enB, speed);
}

// 後退
void moveBackward(int speed) {
  // 左輪後退
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
  analogWrite(enA, speed);

  // 右輪後退
  digitalWrite(in3, LOW);
  digitalWrite(in4, HIGH);
  analogWrite(enB, speed);
}

// 原地右轉
void turnRight(int speed) {
  // 左輪前進
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);
  analogWrite(enA, speed);

  // 右輪後退
  digitalWrite(in3, LOW);
  digitalWrite(in4, HIGH);
  analogWrite(enB, speed);
}

// 原地左轉
void turnLeft(int speed) {
  // 左輪後退
  digitalWrite(in1, LOW);
  digitalWrite(in2, HIGH);
  analogWrite(enA, speed);

  // 右輪前進
  digitalWrite(in3, HIGH);
  digitalWrite(in4, LOW);
  analogWrite(enB, speed);
}

// 停止
void stopMotors() {
  // 左輪停止
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
  analogWrite(enA, 0);

  // 右輪停止
  digitalWrite(in3, LOW);
  digitalWrite(in4, LOW);
  analogWrite(enB, 0);
}

程式解說

  1. Pin Definitions:在程式最上方用常數(const int)定義所有接腳,方便未來修改與閱讀。
  2. setup()
    • 使用 pinMode() 將所有控制腳位設為 OUTPUT
    • 在程式一開始呼叫 stopMotors(),確保裝置啟動時不會暴衝。
  3. loop()
    • 主迴圈是動作的編排區。它依序呼叫自訂的動作函式(如moveForward)和 delay() 來實現一個簡單的自動控制流程。
  4. Custom Functions (自訂函式)
    • 將「前進」、「後退」、「左轉」、「右轉」、「停止」等基本動作封裝成獨立的函式。
    • 這麼做的好處是讓 loop() 中的邏輯更清晰,並且可以重複使用這些動作。
    • 每個函式內部根據之前討論的 控制邏輯表,透過 digitalWrite() 設定方向,並用 analogWrite() 設定速度。
    • stopMotors() 函式將所有方向設為 LOW,並將PWM速度設為 0,徹底停止馬達。

4. 常見問題與除錯 (Troubleshooting)

  1. 問題:馬達完全沒反應

    • 檢查電源:L298N 的 VCC 是否接到外部電源?外部電源是否有電?
    • 檢查共地:L298N 的 GND 是否同時連接到外部電源負極和 Arduino 的 GND?這是最常見的錯誤。
    • 檢查跳線帽:L298N 上的 ENAENB 跳線帽是否插著?如果拔掉了,需要手動將 ENAENB 接到 HIGH 或 PWM 腳位。
    • 檢查 5V EN 跳線帽:L298N 上的 5V EN 跳線帽是否插著?如果拔掉了,需要手動為 5V 腳位提供邏輯電源。
  2. 問題:馬達轉向或轉速不如預期

    • 檢查 IN 接腳IN1/IN2IN3/IN4 的接線是否與程式碼中的定義一致?HIGH/LOW 的組合是否正確?
    • 檢查 ENA/ENB 接腳ENAENB 是否接到 Arduino 的 PWM 腳位(有 ~ 符號的)?如果接到普通數位腳位,將無法調速。
    • 檢查馬達接線:嘗試交換 OUT1/OUT2OUT3/OUT4 的接線,看看轉向是否反了。如果反了,可以交換接線或修改程式碼邏輯。
  3. 問題:Arduino 板上的燈變暗或重啟

    • 原因:這通常是電流問題。你可能不小心將馬達電源(L298N 的 VCC)接到 Arduino 的 5V 腳位了。馬達啟動時的大電流會拉低 Arduino 的電壓,導致重啟。
    • 解決永遠使用外部電源供應馬達電力。