控制全攻略:從PWM原理到高級(jí)玩法與常見故障排查)
如果你玩ESP32有一陣子了很難不碰到舵機(jī)控制這道坎。做智能小車要轉(zhuǎn)方向做機(jī)械臂要抬關(guān)節(jié)做云臺(tái)要偏航俯仰背后都是同一個(gè)東西在干活舵機(jī)。而我見過太多人卡在“代碼能編譯、舵機(jī)就是不聽話”這個(gè)階段要么抖動(dòng)、要么轉(zhuǎn)不到位、要么接上就重啟。這篇ESP32舵機(jī)控制全攻略就是要把這些坑一次性填平從PWM信號(hào)原理、舵機(jī)選型、供電設(shè)計(jì)到ESP32驅(qū)動(dòng)代碼、多舵機(jī)管理、平滑運(yùn)動(dòng)、串口指令控制一路講到高級(jí)玩法。新手上手能照著接線跑通第一個(gè)例子有基礎(chǔ)的人也能從高級(jí)部分找到優(yōu)化思路。1. 項(xiàng)目整體拆解為什么偏偏用ESP32玩舵機(jī)先說方案選型。單片機(jī)世界里能做舵機(jī)控制的板子很多但ESP32有幾個(gè)特性讓它特別適合干這活一是PWM資源多內(nèi)置LEDCLED Control控制器最多支持16路PWM輸出不需要外掛擴(kuò)展芯片就能驅(qū)動(dòng)十來個(gè)舵機(jī)二是引腳數(shù)量足夠多給每個(gè)舵機(jī)分配獨(dú)立信號(hào)線很寬松三是主頻高240MHz跑復(fù)雜控制邏輯、多舵機(jī)插值運(yùn)算都不吃力四是自帶Wi-Fi和藍(lán)牙做完本地控制還能接上位機(jī)、接手機(jī)這個(gè)后面講高級(jí)應(yīng)用會(huì)用到。我最初做舵機(jī)控制其實(shí)用的是普通開發(fā)板引腳少、PWM通道也少接兩個(gè)舵機(jī)就得精打細(xì)算頻率稍微不對(duì)整個(gè)程序就亂了。換成ESP32之后最大的感受是“從省著用變成隨便用”16個(gè)PWM通道、4組定時(shí)器、微秒級(jí)脈寬精度這些參數(shù)放在舵機(jī)控制場(chǎng)景里非常寬裕。這套方案要解決的核心問題其實(shí)可以歸納成三類。第一類是“轉(zhuǎn)不起來”舵機(jī)不動(dòng)作、角度偏、或者上電就卡死這多半是硬件接線和供電的問題。第二類是“轉(zhuǎn)不準(zhǔn)”角度誤差、回中偏差、兩路舵機(jī)動(dòng)作不同步這是PWM信號(hào)和控制邏輯的問題。第三類是“轉(zhuǎn)不漂亮”舵機(jī)像開關(guān)一樣啪地彈過去而不是平滑運(yùn)動(dòng)這是運(yùn)動(dòng)規(guī)劃的問題。大部分人做到第一類就放棄了實(shí)際上后兩類才是讓項(xiàng)目看起來“專業(yè)”的分水嶺。這篇攻略采用的思路是從底層原理講起再做最小系統(tǒng)驗(yàn)證最后往上疊加功能。我不會(huì)一上來就丟一堆代碼而是先把“為什么舵機(jī)要50Hz PWM”“為什么脈寬是0.5ms到2.5ms”“為什么必須共地”這些底層邏輯講清楚這樣后面排查問題才有依據(jù)。2. 舵機(jī)控制的核心PWM信號(hào)、選型與供電2.1 角度是靠“脈寬”而不是“占空比”決定的先聊最容易被忽略的基礎(chǔ)。標(biāo)準(zhǔn)模擬舵機(jī)的控制信號(hào)是一路周期為20ms的PWM方波也就是50Hz。舵機(jī)真正關(guān)心的是高電平持續(xù)多久這個(gè)“高電平持續(xù)時(shí)間”叫脈寬單位是微秒。絕大多數(shù)標(biāo)準(zhǔn)舵機(jī)的脈寬映射關(guān)系是這樣的0.5ms脈寬對(duì)應(yīng)0°1.5ms對(duì)應(yīng)90°2.5ms對(duì)應(yīng)180°。換算成角度就是脈寬(μs) 500 (角度 / 180.0) * 2000反過來說如果要讓舵機(jī)停在45°那脈寬就是500 45 / 180 * 2000 1000μs也就是1ms。這里有個(gè)很多人踩過的坑一聽到PWM就只關(guān)心占空比。比如你用50Hz頻率、50%占空比去驅(qū)動(dòng)舵機(jī)得到的高電平是10ms遠(yuǎn)超過2.5ms舵機(jī)要么直接打死要么狂抖。占空比是比值而舵機(jī)讀的是絕對(duì)值。所以一定要記住頻率固定50Hz、脈寬才是控制量。有些數(shù)字舵機(jī)支持更高頻率比如333Hz甚至部分總線舵機(jī)完全不走PWM協(xié)議。選舵機(jī)前先確認(rèn)類型標(biāo)準(zhǔn)模擬舵機(jī)就老老實(shí)實(shí)按50Hz來。2.2 常見舵機(jī)型號(hào)怎么選不是扭矩越大越好我常用的是常見的SG90小舵機(jī)和MG996R金屬齒輪大舵機(jī)這兩個(gè)型號(hào)足夠說明選型邏輯。SG90是塑料齒輪重量9g左右扭矩大約1.8kg·cm適合做小車轉(zhuǎn)向、云臺(tái)俯仰、小型機(jī)械臂的末端關(guān)節(jié)。MG996R是金屬齒輪重量55g左右扭矩能達(dá)到11kg·cm左右適合做機(jī)械臂大臂、底盤轉(zhuǎn)向這類需要承受較大負(fù)載的關(guān)節(jié)。選型核心看三點(diǎn)。第一是扭矩余量。舵機(jī)的峰值扭矩要大于關(guān)節(jié)實(shí)際負(fù)載的2到3倍。如果關(guān)節(jié)需要1kg·cm的力用SG90的1.8kg·cm勉強(qiáng)夠但長(zhǎng)時(shí)間接近滿負(fù)荷運(yùn)轉(zhuǎn)會(huì)發(fā)熱嚴(yán)重、壽命驟降。這時(shí)候就該上MG996R或者更高一級(jí)的舵機(jī)。第二是工作電壓。SG90標(biāo)稱4.8VMG996R可以支持4.8V到6.8V。電壓越高扭矩越大但同時(shí)電流也上去。很多人直接把舵機(jī)接到ESP32的3.3V引腳上結(jié)果連空載都轉(zhuǎn)不動(dòng)道理就在這。常規(guī)做法是給舵機(jī)一個(gè)5V的獨(dú)立電源電壓不足時(shí)再考慮升壓或換更高電壓舵機(jī)。第三是齒輪材質(zhì)。金屬齒輪抗沖擊、壽命長(zhǎng)但價(jià)格高、重量大、電流需求也高塑料齒輪便宜、輕巧適合低速輕載場(chǎng)景。高速碰撞場(chǎng)景比如機(jī)器狗、機(jī)械臂快速擺動(dòng)別用塑料齒輪容易掃齒。2.3 供電設(shè)計(jì)最容易翻車的環(huán)節(jié)沒有之一我調(diào)試舵機(jī)項(xiàng)目時(shí)踩過最狠的坑就是供電。一個(gè)SG90空載電流大概100mA到200mA堵轉(zhuǎn)時(shí)能飆到650mA甚至更高M(jìn)G996R堵轉(zhuǎn)電流可以超過2A。而大部分ESP32開發(fā)板的板載穩(wěn)壓器能穩(wěn)定輸出的電流也就500mA左右還要分給主控和Wi-Fi。結(jié)果就是舵機(jī)一啟動(dòng)電壓瞬間被拉低ESP32直接重啟或者舵機(jī)在原地嗡嗡發(fā)抖。正確接法分兩路走ESP32用USB供電或獨(dú)立的3.3V/5V供電舵機(jī)用獨(dú)立的5V電源供電兩者之間必須共地。所謂共地就是把外部電源的負(fù)極和ESP32的GND引腳連在一起。如果不共地PWM信號(hào)的電平參考點(diǎn)不一致舵機(jī)收到的信號(hào)可能完全亂掉。如果你只是測(cè)試一個(gè)SG90用5V 1A的適配器就夠如果是兩個(gè)MG996R建議上5V 3A以上的電源或者直接用2S鋰電池加5V穩(wěn)壓模塊/UBEC??傊鏅C(jī)電源寧大勿小尤其不要圖省事直接從ESP32開發(fā)板的Vin引腳甚至3.3V引腳取電。3. 硬件連接與最小系統(tǒng)搭建3.1 三根線的正確接法標(biāo)準(zhǔn)舵機(jī)引出來三根線顏色約定一般是棕色或黑色是地線GND紅色是電源線VCC橙色或黃色是信號(hào)線Signal。信號(hào)線接ESP32的GPIO引腳電源線接外部5V正極地線接外部電源負(fù)極同時(shí)和ESP32的GND相連。注意信號(hào)線絕對(duì)不要接到5V上ESP32的GPIO是3.3V邏輯直接接5V輕則IO口損壞重則燒芯片。某些舵機(jī)尤其是工業(yè)舵機(jī)需要5V邏輯信號(hào)那就要加電平轉(zhuǎn)換電路不能用3.3V直驅(qū)。推薦優(yōu)先使用的信號(hào)引腳有GPIO13、GPIO12、GPIO14、GPIO27、GPIO26、GPIO25。這幾個(gè)引腳沒有上電時(shí)序限制也沒有板載外設(shè)占用是舵機(jī)控制的“安全區(qū)”。需要避開的是GPIO0BOOT引腳、GPIO1和GPIO3串口、GPIO6到GPIO11連接板載Flash等。3.2 布線實(shí)踐信號(hào)線要短地線要粗常見場(chǎng)景里舵機(jī)裝在機(jī)械臂末端信號(hào)線要走一兩米這時(shí)就容易出問題。舵機(jī)信號(hào)線最好控制在20cm以內(nèi)越短越穩(wěn)。如果必須長(zhǎng)距離走線用屏蔽線或者雙絞線并且GPIO上可以加一個(gè)1kΩ串聯(lián)電阻降低信號(hào)反射和干擾。電源線路也要注意。舵機(jī)啟動(dòng)瞬間電流很大如果電源線太細(xì)線路壓降會(huì)把5V拉到4V以下舵機(jī)自然沒勁。我習(xí)慣用18AWG以上的線做舵機(jī)電源線或者直接焊線到舵機(jī)端而不是用杜邦線。實(shí)測(cè)過用普通杜邦線帶兩個(gè)MG996R同時(shí)動(dòng)作線會(huì)發(fā)熱電壓跌落很明顯。最后說一句多舵機(jī)項(xiàng)目里建議給每個(gè)舵機(jī)電源端加一個(gè)100μF到470μF的電解電容靠近舵機(jī)電源輸入位置。這個(gè)電容能在啟動(dòng)瞬間補(bǔ)充電流能明顯減少電壓跌落導(dǎo)致的抖動(dòng)。4. 開發(fā)環(huán)境配置與舵機(jī)庫選型4.1 安裝Arduino Core for ESP32與ESP32Servo庫軟件側(cè)我用的是Arduino IDE加Espressif官方Arduino Core。安裝好核心之后還需要裝一個(gè)關(guān)鍵庫ESP32Servo。這個(gè)庫是專門為ESP32適配的舵機(jī)控制庫底層封裝了LEDC的PWM設(shè)置并把控制接口抽象成類似普通開發(fā)板Servo庫的寫法。在Arduino庫管理器里搜索“ESP32Servo”直接安裝作者是某個(gè)社區(qū)開發(fā)者這已經(jīng)是ESP32舵機(jī)控制事實(shí)上最常用的庫。它能同時(shí)管理十幾路舵機(jī)支持微秒級(jí)脈寬寫入還內(nèi)置了平滑移動(dòng)方法后面的高級(jí)應(yīng)用會(huì)用到。如果不用這個(gè)庫直接用ESP32自帶的LEDC API寫PWM也行但手寫的代碼要多處理定時(shí)器分組、通道分配這些細(xì)節(jié)對(duì)新手不友好。我的建議是標(biāo)準(zhǔn)舵機(jī)控制場(chǎng)景一律用ESP32Servo只有需要非常規(guī)頻率或自定義波形的特殊需求才直接用LEDC API。4.2 ESP32Servo庫與LEDC API的關(guān)系理解一下底層邏輯。ESP32的LEDC模塊有16個(gè)通道分成4組定時(shí)器每組定時(shí)器最多帶8個(gè)通道。同組定時(shí)器的通道必須共用相同的頻率和分辨率。ESP32Servo庫會(huì)把舵機(jī)請(qǐng)求的50Hz設(shè)置到某個(gè)定時(shí)器組然后把通道分配給舵機(jī)。這里有個(gè)容易踩雷的地方同一組定時(shí)器里所有通道頻率必須相同如果你在一個(gè)項(xiàng)目里又用LEDC做LED呼吸燈頻率比如5kHz又用ESP32Servo驅(qū)動(dòng)舵機(jī)50Hz兩者如果落在同一組定時(shí)器上會(huì)互相干擾表現(xiàn)為舵機(jī)亂抖、燈頻閃。解決辦法是手動(dòng)指定servo庫使用不同的定時(shí)器或者干脆舵機(jī)和LED呼吸燈用不同的LEDC定時(shí)器組。我一般在多任務(wù)項(xiàng)目里習(xí)慣拆成兩組一組定時(shí)器專門給舵機(jī)另一組定時(shí)器給其它PWM設(shè)備這樣互不干擾。5. 基礎(chǔ)驅(qū)動(dòng)代碼從write到自定義脈寬5.1 最小驅(qū)動(dòng)代碼裝好庫之后跑通第一個(gè)舵機(jī)最簡(jiǎn)單#include ESP32Servo.h Servo myServo; const int servoPin 13; void setup() { myServo.attach(servoPin, 500, 2400); // 引腳, 最小脈寬μs, 最大脈寬μs myServo.write(90); // 回到中位 } void loop() { for (int angle 0; angle 180; angle 1) { myServo.write(angle); delay(15); } for (int angle 180; angle 0; angle - 1) { myServo.write(angle); delay(15); } }attach函數(shù)的三個(gè)參數(shù)分別是信號(hào)引腳、最小脈寬和最大脈寬。默認(rèn)值是544到2400μs對(duì)應(yīng)0°和180°。為什么我寫500到2400因?yàn)镾G90的實(shí)際行程范圍通常到不了2.5ms用2400作為180°端點(diǎn)在接近兩端時(shí)更不容易頂死。實(shí)測(cè)中我發(fā)現(xiàn)用默認(rèn)值直接write(0)和write(180)時(shí)有些舵機(jī)會(huì)發(fā)出“咔咔”的頂齒聲那是舵機(jī)機(jī)械限位已經(jīng)到了但信號(hào)還在驅(qū)動(dòng)它繼續(xù)往死里頂。把脈寬范圍縮小一點(diǎn)反而更安全代價(jià)只是犧牲一點(diǎn)點(diǎn)角度范圍換來的是齒輪和保護(hù)。5.2 用writeMicroseconds精確控制如果你需要非常精確地控制某一個(gè)中間角度或者想讓舵機(jī)停在5°、10°這種位置的精細(xì)點(diǎn)上靠write(angle)可能不夠細(xì)因?yàn)閹靸?nèi)部還要做一次浮點(diǎn)到脈寬的轉(zhuǎn)換。更直接的方法是writeMicrosecondsmyServo.writeMicroseconds(1500); // 90°位置 myServo.writeMicroseconds(1000); // 45°位置 myServo.writeMicroseconds(2000); // 135°位置這里有個(gè)好處你可以繞過角度整數(shù)直接給任意脈寬實(shí)現(xiàn)亞度級(jí)的位置控制。還有一個(gè)實(shí)際用途校準(zhǔn)舵機(jī)的中位。不同品牌舵機(jī)的90°位置對(duì)應(yīng)的脈寬可能略有偏差有的在1480μs有的在1520μs。用一個(gè)可以微調(diào)脈寬的程序?qū)崪y(cè)找出舵機(jī)真正回中的脈寬再把這個(gè)值作為基準(zhǔn)能顯著提升多舵機(jī)項(xiàng)目的精度。5.3 多舵機(jī)同步控制多舵機(jī)控制有兩種思路一種是每個(gè)舵機(jī)獨(dú)立attach然后逐個(gè)write另一種是所有舵機(jī)循環(huán)一個(gè)時(shí)間片讓它們?cè)谝曈X上“同步”運(yùn)動(dòng)。第一種寫法的參考代碼#include ESP32Servo.h Servo servo[6]; int pins[6] {13, 12, 14, 27, 26, 25}; void setup() { for (int i 0; i 6; i) { servo[i].attach(pins[i], 500, 2400); servo[i].write(90); } } void loop() { for (int a 0; a 180; a 2) { for (int i 0; i 6; i) { servo[i].write(a); } delay(20); } }注意我在loop里不是把一個(gè)舵機(jī)從0轉(zhuǎn)到180再轉(zhuǎn)下一個(gè)而是所有舵機(jī)同時(shí)逐步變化。舵機(jī)物理響應(yīng)有一個(gè)時(shí)間過程多個(gè)舵機(jī)同時(shí)收到位置更新指令時(shí)它們?cè)谝曈X上是同步開始運(yùn)動(dòng)的這就是“偽同步”。如果真要讓多個(gè)舵機(jī)精確到同一時(shí)刻到達(dá)某個(gè)角度單靠write是不夠的需要計(jì)算每個(gè)舵機(jī)的速度和加速度讓它們?cè)谕粫r(shí)間窗內(nèi)到達(dá)目標(biāo)這就引出下一步的平滑運(yùn)動(dòng)。6. 高級(jí)應(yīng)用平滑運(yùn)動(dòng)、串口控制與多路擴(kuò)展6.1 用插值實(shí)現(xiàn)平滑運(yùn)動(dòng)直接write會(huì)讓舵機(jī)以最大速度“彈”到目標(biāo)位置在機(jī)械臂上看起來非常生硬而且還容易因?yàn)閼T性造成末端抖動(dòng)。解決思路是中間插值把一段行程切成很多小步每步之間加延時(shí)讓舵機(jī)看起來是“勻速滑過去”。void smoothMove(Servo s, int startAngle, int endAngle, int durationMs) { int steps 30; int delta endAngle - startAngle; for (int i 0; i steps; i) { int angle startAngle delta * i / steps; s.write(angle); delay(durationMs / steps); } }這個(gè)接口看起來簡(jiǎn)單但實(shí)際使用有幾個(gè)細(xì)節(jié)。第一是steps太少會(huì)有頓挫感太多則舵機(jī)追不上指令動(dòng)作反而出現(xiàn)滯后。我實(shí)踐經(jīng)驗(yàn)是30步左右對(duì)大多數(shù)模擬舵機(jī)剛剛好。第二是durationMs的最小值有物理限制普通模擬舵機(jī)從0°轉(zhuǎn)到180°大約需要0.3到0.5秒如果你強(qiáng)行把durationMs設(shè)成200ms舵機(jī)追不上最后還是全速?zèng)_過去。第三是這種簡(jiǎn)單線性插值沒有加減速機(jī)械臂高速啟動(dòng)、急停時(shí)依然會(huì)有沖擊感真正的順滑要在起始和結(jié)束段做S曲線加減速即速度先慢、中間快、末尾再慢。S曲線實(shí)現(xiàn)也不復(fù)雜就是在插值時(shí)不再等比例分配角度而是按一個(gè)正弦或多項(xiàng)式曲線分配比例。比如用正弦函數(shù)float t (float)i / steps; float eased (1.0 - cos(t * PI)) / 2.0; int angle startAngle (int)(delta * eased);這個(gè)公式的好處是起步和收尾速度都是0中間自然加速機(jī)械動(dòng)作明顯柔和很多。6.2 串口指令控制把舵機(jī)交給上位機(jī)項(xiàng)目做大了之后舵機(jī)不可能總在固定的循環(huán)里跑你需要在上位機(jī)或者另一塊主控上隨時(shí)指定某個(gè)舵機(jī)轉(zhuǎn)到某個(gè)角度。最簡(jiǎn)單的實(shí)現(xiàn)就是串口協(xié)議。#include ESP32Servo.h Servo myServo; String input ; void setup() { Serial.begin(115200); myServo.attach(13, 500, 2400); } void loop() { while (Serial.available()) { char c Serial.read(); if (c \n) { int angle input.toInt(); if (angle 0 angle 180) { myServo.write(angle); Serial.println(OK: String(angle)); } else { Serial.println(ERR); } input ; } else { input c; } } }協(xié)議格式定義為直接發(fā)整數(shù)如90換行確認(rèn)?;刈xOK或ERR方便上位機(jī)確認(rèn)指令到達(dá)。這個(gè)協(xié)議很短但擴(kuò)展性很好如果要控制多個(gè)舵機(jī)可以約定成1:90表示1號(hào)舵機(jī)到90°解析時(shí)用split處理冒號(hào)即可。再往上走可以用ESP32的Wi-Fi功能把舵機(jī)控制做成一個(gè)HTTP接口或者WebSocket服務(wù)。上位機(jī)不需要連串口線直接在瀏覽器里拖動(dòng)滑塊遠(yuǎn)程控制機(jī)械臂。這部分本質(zhì)上是把串口字符換成了網(wǎng)絡(luò)請(qǐng)求控制邏輯完全一樣但應(yīng)用場(chǎng)景一下子就打開了。6.3 多舵機(jī)數(shù)量擴(kuò)展PCA9685還是繼續(xù)用ESP32引腳如果舵機(jī)數(shù)量超過10個(gè)直接占用GPIO就不太劃算了因?yàn)檫€要留引腳給傳感器、編碼器、顯示屏。這種時(shí)候我傾向于用PCA9685舵機(jī)驅(qū)動(dòng)板。它通過I2C接口控制16路PWM整個(gè)板子只占ESP32的兩個(gè)引腳SDA和SCL卻能接16個(gè)舵機(jī)擴(kuò)展多塊板子還能繼續(xù)堆通道數(shù)。PCA9685的接線也很簡(jiǎn)單VCC接5VGND共地SDA接GPIO21SCL接GPIO22ESP32的默認(rèn)I2C引腳。軟件上裝Adafruit PWM Servo Driver庫初始化時(shí)設(shè)置頻率為50Hz然后通過setPWM(channel, 0, pulseWidth)控制對(duì)應(yīng)舵機(jī)。這里有個(gè)關(guān)鍵參數(shù)PCA9685內(nèi)部是12位分辨率占空比數(shù)值范圍0到4095。50Hz周期20ms那么1ms脈寬對(duì)應(yīng)的計(jì)數(shù)是4095 * 1 / 20 ≈ 2051.5ms對(duì)應(yīng)307。換算公式pulseCount 脈寬(ms) * 4095 / 20用這個(gè)庫驅(qū)動(dòng)舵機(jī)時(shí)記得給PCA9685單獨(dú)供電它板載的穩(wěn)壓器一般是給邏輯部分用的舵機(jī)電流還是從外部5V走。6.4 定時(shí)器分組與自定義PWM突破庫的邊界ESP32Servo庫很好用但它隱藏了定時(shí)器分配細(xì)節(jié)。如果你需要完全掌控PWM輸出或者是想在同一塊板上同時(shí)輸出多種頻率可以直接使用LEDC APIconst int freq 50; const int resolution 16; const int channel 0; const int pin 13; ledcSetup(channel, freq, resolution); ledcAttachPin(pin, channel); // 0°對(duì)應(yīng)脈寬0.5ms20ms周期duty 2^16 * 0.5 / 20 1638 ledcWrite(channel, 1638); // 90°對(duì)應(yīng)1.5ms脈寬duty 65535 * 1.5 / 20 4915 ledcWrite(channel, 4915);用16位分辨率時(shí)計(jì)數(shù)范圍0到6553550Hz下每個(gè)計(jì)數(shù)單位約0.3μs的脈寬精度足夠精細(xì)。但要注意16位分辨率下LEDC的實(shí)際分頻系數(shù)是有限制的某些頻率組合可能導(dǎo)致實(shí)際輸出頻率偏離設(shè)定值。我實(shí)測(cè)下來50Hz配16位在ESP32上工作正常如果你需要高速PWM把分辨率降到12位或更低反而更穩(wěn)定。這個(gè)方案適合的場(chǎng)景舵機(jī)需要非標(biāo)準(zhǔn)脈寬范圍、需要和別的PWM設(shè)備并行、或者需要在中斷里動(dòng)態(tài)改脈寬的情況。普通控制還是用ESP32Servo更省心。7. 常見問題與排查技巧實(shí)錄7.1 舵機(jī)抖動(dòng)或嗡嗡響怎么查舵機(jī)抖動(dòng)永遠(yuǎn)是供電問題排在第一位信號(hào)問題排在第二位。先看電源舵機(jī)供電電壓是否穩(wěn)定有沒有用獨(dú)立電源地線有沒有共地電源線是否太細(xì)。用萬用表實(shí)時(shí)量一下舵機(jī)電源端的電壓如果動(dòng)作瞬間電壓掉到4.5V以下基本就是供電不足。排除供電后再看信號(hào)線線是否太長(zhǎng)、是否靠近電機(jī)或電感元件、有沒有和電源線綁在一起走線。我遇到過一次舵機(jī)在某個(gè)特定角度狂抖查了很久發(fā)現(xiàn)是控制線和大電流導(dǎo)線平行走了40cm干擾信號(hào)了。把導(dǎo)線分開、加上屏蔽后解決。還有一個(gè)容易忽略的點(diǎn)PWM頻率。如果你用ESP32Servo庫頻率被固定在50Hz不需要管但如果你手寫了LEDC配置頻率設(shè)錯(cuò)比如設(shè)成500Hz舵機(jī)就會(huì)瘋狂抖動(dòng)并尖銳鳴叫。檢查ledcSetup里的freq參數(shù)。7.2 舵機(jī)不動(dòng)但發(fā)燙先聽聲音。如果舵機(jī)通電后內(nèi)部馬達(dá)在較勁發(fā)出悶悶的嗡聲說明舵機(jī)在努力往某個(gè)方向轉(zhuǎn)但被卡住了。常見原因是齒輪卡死、連桿過緊、或者電源電壓不夠?qū)е买?qū)動(dòng)不了。這時(shí)候立即斷電手動(dòng)轉(zhuǎn)動(dòng)舵機(jī)搖臂看是否順暢排除機(jī)械卡頓。另一種情況是信號(hào)根本沒進(jìn)去。用示波器或邏輯分析儀看GPIO有沒有方波輸出沒有示波器的話先用Serial.print打印一下程序運(yùn)行狀態(tài)確認(rèn)程序確實(shí)跑到了attach和write那幾行。我吃過一次虧初始化代碼寫在setup里然后loop里有個(gè)死循環(huán)跳不出來舵機(jī)當(dāng)然紋絲不動(dòng)。7.3 接上舵機(jī)ESP32就重啟這是最經(jīng)典的電源坑。舵機(jī)啟動(dòng)瞬間的電流沖擊拉低了整個(gè)系統(tǒng)的電壓ESP32的穩(wěn)壓器檢測(cè)到欠壓直接復(fù)位。排查方法把舵機(jī)的電源線和ESP32分開用獨(dú)立5V電源給舵機(jī)共地后如果問題消失那就確認(rèn)了是供電不足。如果臨時(shí)沒有獨(dú)立電源有一個(gè)應(yīng)急辦法在舵機(jī)電源引腳旁邊加一個(gè)大電容比如1000μF電解電容能緩解瞬間浪涌。但這只是緩兵之計(jì)長(zhǎng)期跑項(xiàng)目一定要獨(dú)立供電。7.4 多個(gè)舵機(jī)動(dòng)作互相干擾現(xiàn)象是動(dòng)A舵機(jī)時(shí)B舵機(jī)也跟著抖或者兩個(gè)舵機(jī)位置錯(cuò)亂。這大概率是定時(shí)器組沖突。前面講過ESP32的LEDC分4組定時(shí)器同組共享頻率。如果兩個(gè)舵機(jī)恰好被分配到同組定時(shí)器而且你在程序里又手動(dòng)去改了這個(gè)組里某一個(gè)通道的頻率就會(huì)把整組通道帶偏。解決辦法ESP32Servo庫里有setTimerHz相關(guān)配置或者在attach前查清楚通道和定時(shí)器分配。更省事的方法是把所有用到不同頻率的PWM設(shè)備用同一組頻率驅(qū)動(dòng)比如LED呼吸燈直接舍棄或者單獨(dú)用另一個(gè)LEDC定時(shí)器組、讓舵機(jī)全部走某一個(gè)組。7.5 實(shí)測(cè)排查順序表根據(jù)我大量排查經(jīng)驗(yàn)列一張速查表按順序檢查能省下大量時(shí)間現(xiàn)象第一排查項(xiàng)第二排查項(xiàng)第三排查項(xiàng)舵機(jī)完全不動(dòng)信號(hào)線是否接對(duì)GPIO供電是否到位代碼是否執(zhí)行到write舵機(jī)抖動(dòng)供電電壓是否穩(wěn)定信號(hào)線是否受干擾頻率是否50Hz舵機(jī)角度不準(zhǔn)脈寬范圍是否匹配機(jī)械安裝是否松動(dòng)舵機(jī)是否磨損接舵機(jī)即重啟供電是否獨(dú)立電容是否缺失電源功率是否夠多舵機(jī)互相影響定時(shí)器組是否沖突共地是否可靠電源線是否過細(xì)這張表不一定覆蓋所有情況但能解決日常八成問題。我后來做舵機(jī)項(xiàng)目都是先按表格過一遍再?zèng)Q定要不要深入排查程序邏輯。8. 一些親測(cè)好用的工程習(xí)慣最后聊點(diǎn)代碼之外的東西。第一個(gè)習(xí)慣是所有舵機(jī)項(xiàng)目先把電源做成“可斷”的舵機(jī)電源獨(dú)立開關(guān)方便程序燒錄時(shí)斷開舵機(jī)電源。否則每次燒錄時(shí)舵機(jī)突然回中機(jī)械臂會(huì)猛甩一下危險(xiǎn)不說還容易撞壞結(jié)構(gòu)件。第二個(gè)習(xí)慣是給每路舵機(jī)定義一個(gè)邏輯編號(hào)結(jié)構(gòu)體統(tǒng)一管理角度范圍、當(dāng)前角度、目標(biāo)角度。項(xiàng)目變大以后你會(huì)發(fā)現(xiàn)所有舵機(jī)直接寫死在代碼里根本維護(hù)不了用結(jié)構(gòu)體數(shù)組加上統(tǒng)一的MoveTo接口上層邏輯會(huì)清晰很多。第三個(gè)習(xí)慣是API設(shè)計(jì)上盡量保留writeMicroseconds而不是只用write。角度write確實(shí)直觀但當(dāng)你需要做舵機(jī)校準(zhǔn)時(shí)脈寬是更底層的真實(shí)控制量。我習(xí)慣在配置里保存每路舵機(jī)的脈寬零點(diǎn)和范圍運(yùn)行時(shí)全部用脈寬控制需要角度時(shí)再臨時(shí)換算。這樣換了舵機(jī)型號(hào)后只需改配置不改代碼。ESP32加舵機(jī)這個(gè)組合還有個(gè)擴(kuò)展空間很大的方向接上MPU6050陀螺儀做閉環(huán)把舵機(jī)當(dāng)成云臺(tái)的執(zhí)行器姿態(tài)數(shù)據(jù)作為反饋?zhàn)鲆粋€(gè)真正意義上的穩(wěn)定云臺(tái)。這已經(jīng)不是單純PWM控制的問題了而是控制算法的范疇。不過只要PWM這條線走得穩(wěn)到那個(gè)階段也只是水到渠成的事。