與Arduino PWM控制的DIY實(shí)現(xiàn)與發(fā)布全記錄)
最近在整理舵機(jī)控制相關(guān)的內(nèi)容時我做了一個挺實(shí)用的小樣品三線直線舵機(jī)成品演示模塊配合一個小尺寸圓柱舵機(jī)用來演示推桿伸縮和輔助旋轉(zhuǎn)動作。這個項(xiàng)目本身不難真正花時間的反而是選型、接線、固定結(jié)構(gòu)、PWM參數(shù)調(diào)試和反復(fù)做穩(wěn)定性測試。目前樣機(jī)已經(jīng)準(zhǔn)備好發(fā)布到閑魚后續(xù)如果有人咨詢或提出新的使用需求再繼續(xù)迭代改進(jìn)。這篇文章會把從開發(fā)到發(fā)布前準(zhǔn)備的完整過程記錄下來包含三線直線舵機(jī)的原理、圓柱小舵機(jī)的接線方式、Arduino 控制舵機(jī)的 PWM 代碼示例、常見問題排查以及二手平臺發(fā)布樣品前需要準(zhǔn)備的參數(shù)表、拍攝素材和溝通流程。如果你正在做舵機(jī) DIY、想快速實(shí)現(xiàn)直線推拉動作或者打算把自己做的小硬件樣品放到閑魚試水這篇文章都值得收藏。1. 三線直線舵機(jī)項(xiàng)目背景與核心概念1.1 直線舵機(jī)解決什么問題常見的舵機(jī)輸出的是旋轉(zhuǎn)運(yùn)動。比如 SG90 這類微型舵機(jī)通過改變 PWM 脈寬可以讓舵盤轉(zhuǎn)到指定角度適合做云臺、機(jī)械臂關(guān)節(jié)、小車轉(zhuǎn)向這類動作。但在很多自動化演示項(xiàng)目里真正需要的往往是“伸縮、推拉、頂起、夾緊”這類直線運(yùn)動。比如模擬氣缸推動小車、用小推桿頂起一個開關(guān)、做一個簡易的直線滑臺這些動作如果只用普通旋轉(zhuǎn)舵機(jī)就還要額外設(shè)計(jì)一套齒輪齒條或連桿機(jī)構(gòu)制作成本高而且容易卡頓。三線直線舵機(jī)在輸出端直接就是一根推桿外殼內(nèi)部通過齒輪減速和絲桿結(jié)構(gòu)把電機(jī)旋轉(zhuǎn)轉(zhuǎn)換成推桿的直線伸縮。這樣外部只需要把它固定好不需要自己設(shè)計(jì)傳動機(jī)構(gòu)就能得到穩(wěn)定的直線運(yùn)動非常適合用在實(shí)驗(yàn)教具、小型自動化測試臺、模型改裝和舵機(jī)控制演示中。1.2 三線接口的直觀理解我們經(jīng)常聽到“三線舵機(jī)”這個說法指的是舵機(jī)正面只有三根線電源正極通常是紅色。電源負(fù)極/地線常見棕色或黑色。信號線常見橙色、黃色或白色。這三根線分別接到控制板的 5V、GND 和一個數(shù)字 IO 口即可。信號線上傳輸?shù)氖?PWM 方波通過高電平脈沖寬度來控制舵機(jī)位置。這里要注意三線舵機(jī)和總線舵機(jī)是兩個不同的方向。三線舵機(jī)接線簡單、兼容 Arduino 和各類舵機(jī)測試儀適合單舵機(jī)或少量舵機(jī)控制總線舵機(jī)則需要用串口或 RS485 等協(xié)議進(jìn)行通信連線更復(fù)雜但優(yōu)勢是可以級聯(lián)、回讀角度常見于多自由度機(jī)械臂、仿生機(jī)器人。這個項(xiàng)目選擇三線方案就是看中了它的易用性和低成本方便做成成品演示模塊讓別人也能快速上手。1.3 樣品的功能定位這個樣品把三線直線舵機(jī)和圓柱小舵機(jī)組合在同一個支架上。直線舵機(jī)負(fù)責(zé)做推桿伸縮動作圓柱小舵機(jī)負(fù)責(zé)輔助演示一個旋轉(zhuǎn)或按壓動作。兩者之間通過控制代碼聯(lián)動可以演示出“先伸出推桿再旋轉(zhuǎn)小舵機(jī)夾取/按壓”的效果看起來更像一個自動化小工位。作為準(zhǔn)備發(fā)布的樣品我把它定位成一個“可直接運(yùn)行的舵機(jī)控制演示模塊”。買家拿到手后不需要自己搭電路只需要接上 5V 電源和 Arduino 控制信號就能看到直線舵機(jī)和小舵機(jī)按順序動作。后續(xù)根據(jù)實(shí)際咨詢我還會考慮調(diào)整尺寸、行程、驅(qū)動方式和接口類型。2. 硬件選型與接線說明2.1 物料清單與選型思路這個項(xiàng)目用到的物料不多核心部件是三線直線舵機(jī)和圓柱小舵機(jī)。下面是本次樣品的主要物料清單物料規(guī)格說明作用三線直線舵機(jī)支持標(biāo)準(zhǔn)舵機(jī) PWM 控制輸出直線推桿完成伸縮、推拉動作圓柱小舵機(jī)小型三線舵機(jī)例如 SG90 等 9g 級別輔助演示旋轉(zhuǎn)/按壓動作Arduino 開發(fā)板Arduino Uno 或兼容板產(chǎn)生 PWM 控制信號電源模塊5V/2A 及以上或根據(jù)舵機(jī)規(guī)格調(diào)整給舵機(jī)供電杜邦線母對母、公對母若干信號線與電源連接固定支架亞克力板、3D 打印件或鋁合金型材固定舵機(jī)和保持結(jié)構(gòu)穩(wěn)定選型時要優(yōu)先確認(rèn)三件事工作電壓是多少、推桿行程有多長、輸出推力能否滿足演示負(fù)載。樣品演示通常負(fù)載很小普通小尺寸直線舵機(jī)就可以勝任。但如果后續(xù)有買家想用它推動較重物體就一定要先測量實(shí)際電流必要時換成更大推力的金屬齒輪舵機(jī)并把供電方案一起改掉。2.2 三線接線與供電注意事項(xiàng)接線看起來很簡單但供電和共地問題很容易被新手忽略。標(biāo)準(zhǔn)接線方式如下舵機(jī)線色對應(yīng)接口說明紅色5V 正極按舵機(jī)工作電壓接電源正極棕色/黑色GND接電源負(fù)極同時與控制板 GND 共地橙色/黃色/白色數(shù)字 IO 引腳接 Arduino 的 PWM 引腳信號線我接在 Arduino Uno 的 9 腳。之所以強(qiáng)調(diào)共地是因?yàn)槎鏅C(jī)返回的 PWM 信號需要統(tǒng)一參考地電位。如果舵機(jī)供電和控制板供電地線沒有連在一起信號電平無法形成回路舵機(jī)可能亂動、抖動甚至不動作。關(guān)于供電有一個重要原則幾乎不要直接用 Arduino 板載 5V 引腳給多舵機(jī)供電。Arduino 的穩(wěn)壓芯片輸出電流有限而舵機(jī)啟動和堵轉(zhuǎn)瞬間電流會明顯上升電壓一旦跌落輕則舵機(jī)無力重則導(dǎo)致控制板復(fù)位。正確做法是使用外部 5V 電源給舵機(jī)供電再把外部電源負(fù)極和 Arduino 的 GND 相連。2.3 圓柱小舵機(jī)與直線舵機(jī)如何搭配圓柱小舵機(jī)在我這個樣品里屬于動作輔助角色。常見的圓柱小舵機(jī)和 SG90 這類九克舵機(jī)接口一致工作電壓、信號標(biāo)準(zhǔn)也都兼容只是外形結(jié)構(gòu)不同。設(shè)計(jì)時我會先確定小舵機(jī)要完成什么動作如果想讓它帶動一個夾爪就安裝舵機(jī)臂如果想讓它按壓一個微動開關(guān)就在舵盤上固定一個凸輪或撥片。由于整機(jī)是一個演示樣板圓柱小舵機(jī)直接通過舵機(jī)臂和直線舵機(jī)的推桿端保持一定配合關(guān)系。直線舵機(jī)向前伸出時圓柱小舵機(jī)旋轉(zhuǎn)到某一角度直線舵機(jī)縮回時圓柱小舵機(jī)再回到初始位置。這個聯(lián)動在代碼里只是幾個狀態(tài)切換但結(jié)構(gòu)設(shè)計(jì)上要提前留好舵機(jī)運(yùn)動空間避免機(jī)械干涉造成堵轉(zhuǎn)。3. 結(jié)構(gòu)設(shè)計(jì)與裝配要點(diǎn)3.1 推桿軸向受力與固定方式直線舵機(jī)最怕的不是推力不夠而是推桿受到比較大的徑向力。所謂徑向力就是垂直于推桿運(yùn)動方向的力。比如推桿伸出后如果頂部掛著一個偏重的物體就會產(chǎn)生一個彎矩作用在舵機(jī)內(nèi)部絲桿和銅套上長期運(yùn)行容易加速磨損甚至卡死。固定直線舵機(jī)時我優(yōu)先考慮讓推桿運(yùn)動方向和負(fù)載推力方向完全重合。如果現(xiàn)場條件不允許一定要在推桿末端加裝直線軸承或?qū)驐U把徑向力轉(zhuǎn)移到外部導(dǎo)軌上。對于演示樣品來說推桿末端接的小零件要盡量輕、盡量短減少側(cè)向力矩。安裝底座同樣重要。直線舵機(jī)外殼上如果有螺絲孔建議用螺絲固定沒有螺絲孔時可以用兩塊亞克力做成夾持結(jié)構(gòu)把舵機(jī)夾在中間。演示樣機(jī)不要只靠熱熔膠固定因?yàn)闊崛勰z在持續(xù)往復(fù)運(yùn)動下容易產(chǎn)生蠕變導(dǎo)致舵機(jī)位置偏移影響演示穩(wěn)定性。3.2 微型圓柱舵機(jī)的固定方法圓柱小舵機(jī)外殼通常比較光滑直接膠粘后一旦線接錯或者舵機(jī)損壞更換會很麻煩。我的做法是先制作一個簡單卡座用 3mm 厚亞克力板切成 U 形件把小舵機(jī)卡進(jìn)槽內(nèi)再用螺絲或者扎帶固定。如果手上沒有亞克力加工條件也可以用 3D 打印做一個專用外殼尺寸用卡尺測量后留 0.2mm 左右間隙即可。小舵機(jī)在實(shí)時動作時扭矩不大卡座只要不松曠工作穩(wěn)定就夠用了。注意舵機(jī)臂安裝要留出螺絲刀操作空間不要等裝好了才發(fā)現(xiàn)無法調(diào)整舵機(jī)臂角度。3.3 行程、限位與整機(jī)裝配順序裝配前先手動測試直線舵機(jī)的實(shí)際行程方向再確定演示程序里的“伸出”和“收回”脈寬。為了避免推桿頂?shù)綑C(jī)械限位后長時間堵轉(zhuǎn)程序中一定要設(shè)置足夠的到位停頓時間而不是讓舵機(jī)持續(xù)發(fā)力。穩(wěn)妥的裝配步驟如下先把直線舵機(jī)固定在底座上確認(rèn)推桿運(yùn)動方向沒有被遮擋。安裝圓柱小舵機(jī)卡座接好舵機(jī)臂預(yù)留動作空間。布置電源線和信號線盡量把線束固定在結(jié)構(gòu)件上避免運(yùn)動過程中勾纏。用獨(dú)立穩(wěn)壓電源上電先用程序發(fā)送停止脈寬確認(rèn)舵機(jī)沒有異常抖動。再測試伸出、收回脈寬記錄臨界點(diǎn)和行程末端位置。最后做 10 次以上的連續(xù)往復(fù)動作觀察連接螺絲是否松動。整機(jī)裝配順序直接影響排查效率。如果先接線把所有舵機(jī)都固定到位發(fā)現(xiàn)問題后再拆會浪費(fèi)很多時間。建議每個部件固定后都做一次簡單通電測試確認(rèn)沒問題再往下裝。4. 舵機(jī) PWM 控制原理與代碼實(shí)現(xiàn)4.1 舵機(jī) PWM 脈寬與位置映射大多數(shù)小型舵機(jī)通過信號線上的高電平脈沖寬度控制位置。這種 PWM 的標(biāo)準(zhǔn)周期約為 20ms也就是 50Hz其中高電平持續(xù)時間通常落在 500us 到 2500us 之間。脈沖寬度越寬舵機(jī)輸出位置越靠近一側(cè)脈沖寬度越窄越靠近另一側(cè)。以 SG90 舵機(jī)來說不同廠家的脈寬范圍標(biāo)注并不完全一致有的舵機(jī)是 500us 到 2400us有的是 1000us 到 2000us。所以我更推薦在程序里使用writeMicroseconds()直接設(shè)置脈寬實(shí)測過程中觀察舵機(jī)兩端位置再固化成最終參數(shù)。對于三線直線舵機(jī)如果它內(nèi)部支持標(biāo)準(zhǔn)舵機(jī) PWM 協(xié)議那么控制思路和普通舵機(jī)基本一致只是輸出的不再是角度而是推桿伸出量。有的直線舵機(jī)接收到不同的脈寬會直接對應(yīng)到“伸、停、縮”三種狀態(tài)有的則可以像標(biāo)準(zhǔn)舵機(jī)一樣把脈寬映射到連續(xù)行程。拿到一個不確定的舵機(jī)時第一步是先把脈寬調(diào)到中間值然后再逐步微調(diào)兩端不要一來就發(fā)滿量程脈寬。在應(yīng)用層面真正實(shí)用的舵機(jī)控制算法其實(shí)包含幾部分生成 50Hz 基準(zhǔn)脈沖、對目標(biāo)脈寬做漸變插值、限制單位時間內(nèi)的脈寬變化量以及避免舵機(jī)在行程末端持續(xù)堵轉(zhuǎn)。演示程序只需要覆蓋前三點(diǎn)就能實(shí)現(xiàn)比較平滑的直線往復(fù)運(yùn)動。4.2 直線舵機(jī)往復(fù)伸縮測試程序下面先看一個最精簡的直線舵機(jī)自動往復(fù)測試程序。這個程序無需串口燒錄后接上電源直線舵機(jī)會自動按“伸出-停止-收回-停止”的順序循環(huán)運(yùn)行。/* * 文件路徑line_servo_test/line_servo_test.ino * 功能三線直線舵機(jī)自動往復(fù)伸縮測試 */ #include Servo.h Servo linearServo; const int signalPin 9; // 以下三個脈寬需要根據(jù)實(shí)際舵機(jī)標(biāo)定 const int extendPulse 1800; // 伸出位置 const int stopPulse 1500; // 停止/中位位置 const int retractPulse 1200; // 收回位置 const unsigned long holdTime 3000; // 每個動作保持 3 秒 void setup() { Serial.begin(9600); // attach 的第二個和第三個參數(shù)限定了脈寬范圍 linearServo.attach(signalPin, 1000, 2000); // 上電先把舵機(jī)停在中間避免突然跳到極端位置 linearServo.writeMicroseconds(stopPulse); delay(1000); } void loop() { Serial.println(伸出); linearServo.writeMicroseconds(extendPulse); delay(holdTime); Serial.println(停止); linearServo.writeMicroseconds(stopPulse); delay(1000); Serial.println(收回); linearServo.writeMicroseconds(retractPulse); delay(holdTime); Serial.println(停止); linearServo.writeMicroseconds(stopPulse); delay(1000); }代碼里使用attach(signalPin, 1000, 2000)限定了脈寬的最小值和最大值。這樣即使程序里出現(xiàn)越界數(shù)值Servo庫也能把輸出限制在安全范圍。實(shí)際運(yùn)行時如果發(fā)現(xiàn)舵機(jī)沒有動作或者運(yùn)動方向反了只需要交換extendPulse和retractPulse的值或者根據(jù)舵機(jī)標(biāo)注調(diào)整脈寬區(qū)間即可。4.3 直線舵機(jī)與圓柱小舵機(jī)聯(lián)動示例接下來是本次樣品使用的綜合控制程序。我在里面加入了圓柱小舵機(jī)并設(shè)計(jì)了一個按鍵自動演示模式和串口手動控制模式。硬件接線如下Arduino 引腳外部模塊連接說明D9直線舵機(jī)信號線PWM 信號D10圓柱小舵機(jī)信號線PWM 信號D8按鍵一端按鍵另一端接 GND5V舵機(jī) VCC推薦外部電源小電流測試時可臨時用板載 5VGND舵機(jī) GND和控制板共地/* * 文件路徑servo_demo_combined/servo_demo_combined.ino * 功能三線直線舵機(jī) 圓柱小舵機(jī)聯(lián)動演示 * 串口指令e直線伸出 s直線停止 r直線收回 m全部回中 * 按鍵功能按一下進(jìn)入自動演示再按一下退出 */ #include Servo.h Servo linearServo; // 三線直線舵機(jī) Servo smallServo; // 圓柱小舵機(jī) const int linearPin 9; const int smallPin 10; const int keyPin 8; const int minPulse 1000; const int maxPulse 2000; int currentPulse 1500; bool autoMode false; int autoDir 1; unsigned long lastMoveTime 0; int smallAngle 0; void setup() { Serial.begin(9600); pinMode(keyPin, INPUT_PULLUP); linearServo.attach(linearPin, minPulse, maxPulse); smallServo.attach(smallPin); // 上電保持中位 linearServo.writeMicroseconds(1500); smallServo.write(0); } void loop() { // 按鍵切換模式 if (digitalRead(keyPin) LOW) { autoMode !autoMode; delay(200); // 簡單防抖 Serial.print(模式切換); Serial.println(autoMode ? 自動演示 : 串口控制); } if (autoMode) { // 自動演示模式脈寬每 30ms 增加 3us實(shí)現(xiàn)平滑伸縮 if (millis() - lastMoveTime 30) { currentPulse autoDir * 3; if (currentPulse maxPulse - 100) { currentPulse maxPulse - 100; autoDir -1; } else if (currentPulse minPulse 100) { currentPulse minPulse 100; autoDir 1; } linearServo.writeMicroseconds(currentPulse); // 圓柱小舵機(jī)隨推桿位置聯(lián)動實(shí)際角度需要根據(jù)結(jié)構(gòu)調(diào)整 smallAngle map(currentPulse, minPulse 100, maxPulse - 100, 0, 90); smallServo.write(smallAngle); lastMoveTime millis(); } } else { // 串口手動控制模式 if (Serial.available() 0) { char cmd Serial.read(); switch (cmd) { case e: currentPulse maxPulse - 100; linearServo.writeMicroseconds(currentPulse); Serial.println(直線舵機(jī)伸出); break; case s: currentPulse 1500; linearServo.writeMicroseconds(currentPulse); Serial.println(直線舵機(jī)停止保持); break; case r: currentPulse minPulse 100; linearServo.writeMicroseconds(currentPulse); Serial.println(直線舵機(jī)收回); break; case m: linearServo.writeMicroseconds(1500); smallServo.write(90); Serial.println(全部舵機(jī)回到中位); break; default: break; } } } }這段代碼把脈寬漸變過程寫在了自動演示模式中。currentPulse每一步只改變 3us舵機(jī)運(yùn)動會比較平滑不會出現(xiàn)直接從一端跳到另一端的沖擊感。這種思路在舵機(jī)控制算法里屬于最簡單的速度規(guī)劃實(shí)際機(jī)械臂或仿生機(jī)器人中還會加入加速度限制、梯形加減速、目標(biāo)位置閉環(huán)等更多內(nèi)容但基礎(chǔ)邏輯是相同的。很多新手會直接把smallServo.write(90)看成固定的 90 度其實(shí)write的參數(shù)是角度還是脈寬取決于舵機(jī)庫版本和使用方式。Servo庫默認(rèn)write(angle)是角度范圍通常為 0 到 180。實(shí)際小舵機(jī)安裝在樣品上后0 度和 90 度對應(yīng)什么姿態(tài)需要根據(jù)機(jī)械位置現(xiàn)場調(diào)整不能只憑直覺寫死。4.4 串口調(diào)試與邊界處理樣品演示過程中我時刻關(guān)注兩個邊界條件。第一個是脈寬邊界。不同的直線舵機(jī)對同一脈寬響應(yīng)可能不同所以代碼里minPulse和maxPulse并不是越大越好。脈寬設(shè)置超過舵機(jī)內(nèi)部量程時舵機(jī)不會繼續(xù)增加行程而是會進(jìn)入堵轉(zhuǎn)狀態(tài)時間一長舵機(jī)發(fā)熱明顯齒輪磨損加快。穩(wěn)妥做法是通過串口一步步微調(diào)脈寬觀察推桿是否已經(jīng)到位再決定是否繼續(xù)增加。第二個是模式切換時機(jī)。按鍵控制的autoMode切換邏輯雖然簡單但如果舵機(jī)正在自動運(yùn)動過程中突然切換到串口控制當(dāng)前脈寬和代碼里的目標(biāo)脈寬不一致舵機(jī)可能會出現(xiàn)一次明顯跳動。嚴(yán)重情況下會造成推桿撞擊限位。這個樣品里我通過把currentPulse變量作為全局狀態(tài)來維護(hù)讓手動模式指定位置時也基于這個變量繼續(xù)更新可以在一定程度上減少突變。串口調(diào)試時每次發(fā)送指令后觀察舵機(jī)實(shí)際響應(yīng)比一次燒錄大量新代碼更高效。先確認(rèn)停止脈寬再確認(rèn)兩端安全位置最后再寫入完整演示程序這個順序能明顯縮短調(diào)試時間。5. 成品演示與高頻故障排查5.1 演示流程設(shè)計(jì)一個用于發(fā)布到閑魚展示的樣品演示過程不能太隨意。我把流程設(shè)計(jì)成固定腳本方便錄視頻和拍照。整個演示腳本分為三步上電自檢。接通電源后直線舵機(jī)先回到中位圓柱小舵機(jī)回零。串口指令演示。通過串口依次發(fā)送e、s、r逐個驗(yàn)證直線舵機(jī)的伸出、停止、收回。自動聯(lián)動演示。按下按鍵進(jìn)入自動模式直線舵機(jī)和圓柱小舵機(jī)連續(xù)往復(fù)運(yùn)動約 30 秒展示穩(wěn)定性和聯(lián)動效果。演示時可以在推桿旁邊放一把直尺拍攝特寫鏡頭讓買家直觀看到行程長度。另外要拍一段側(cè)面視角展示直線舵機(jī)安裝方式和內(nèi)部結(jié)構(gòu)避免圖片“美化過度”導(dǎo)致買家收貨后有落差。5.2 穩(wěn)定性觀察項(xiàng)樣品準(zhǔn)備發(fā)布前我建議至少連續(xù)運(yùn)行 20 次以上并在測試過程中