尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

电位器控制SG90舵机:从PWM原理到代码实现

发布时间:2026/9/24 12:36:10

资讯中心
01
ARTICLE

电位器控制SG90舵机:从PWM原理到代码实现

电位器控制SG90舵机:从PWM原理到代码实现
1. 从一个电位器旋钮说起SG90舵机控制的底层逻辑很多人第一次接触舵机脑子里冒出来的问题都差不多这东西到底怎么转为什么给它一个信号它就能停在某个角度不动我当初也是这么想的直到拿一个SG90和一只10k电位器搭了个最朴素的联动电路才真正把“PWM占空比决定角度”这件事从抽象概念变成了手上能摸到的动作。这篇内容就是围绕“用一只电位器实时控制SG90舵机角度”这个最小系统展开的从原理、选型、接线、代码到调试踩坑全部讲透。不管你是刚拿到第一块开发板的新手还是想给机械臂、云台、小车转向找个快速验证方案的工程师这套东西都能直接抄作业。SG90是市面上最常见的一款微型舵机塑料齿轮、重量约9克、工作电压典型4.8V到6V堵转扭矩大概在1.8kg·cm左右。它的核心不是电机本身而是内部那块小控制板它把外部送进来的PWM信号解算成目标角度再驱动电机转到对应位置并锁住。所谓“控制舵机”本质上就是控制一路PWM的高电平持续时间。SG90的规格书里写得很清楚周期20ms也就是50Hz高电平0.5ms对应0度2.5ms对应180度中间线性对应。电位器在这里扮演的角色就是一个“人肉角度发生器”——你转动旋钮它输出一个变化的电压开发板把这个电压读成数字量再映射成PWM脉宽舵机就跟着动。整条链路是旋钮位置 → 模拟电压 → ADC数字值 → 角度映射 → PWM脉宽 → 舵机角度。理解了这条链路后面所有代码和接线都是它的物理实现。这个项目适合谁如果你正在做机械臂关节、摄像头云台、小车前轮转向、或者只是想给舵机找个不写死角度的交互方式那这套方案就是最直接的起点。它不需要复杂的通信协议不需要上位机一个开发板、一个舵机、一个电位器、几根杜邦线就能跑起来。下面我按“整体设计思路 → 核心细节与实操要点 → 完整实现过程 → 常见问题排查”四块来讲中间会穿插我实际调试时踩过的坑和验证过的参数。2. 整体设计与方案选型为什么是电位器加舵机2.1 为什么用“电位器直读”而不是按键或串口指令给舵机定角度的方法有很多按键加一减一、串口发角度值、上位机滑条、传感器自动跟随。但电位器方案有个不可替代的优势——它把角度控制变成了连续、直观、零延迟的物理动作。你转动旋钮舵机同步转动中间没有菜单、没有指令、没有确认步骤。这种“所见即所得”的交互在调试阶段价值极高你可以快速扫过整个0到180度范围观察舵机有没有卡顿、有没有抖动、有没有在某个角度失步。相比之下按键方案只能离散跳变串口方案还得先打开终端敲命令调试效率差一大截。从电路角度看电位器接成分压电路是最稳妥的。三只引脚两端分别接电源正和地中间抽头接开发板的ADC输入。旋钮转动时抽头对地的电压在0到VCC之间线性变化。这里有个关键点电位器的阻值选择。常见有1k、10k、100k。我实测下来10k最合适。1k的话静态电流偏大长时间接电池供电会白白耗电100k的话ADC输入阻抗和它匹配不好读数容易飘尤其是杜邦线稍长一点噪声明显。10k在功耗和抗噪之间平衡得最好也是绝大多数教程默认的选择。2.2 开发板选型的现实考量标题里没指定开发板但热词里出现了“h3861舵机sg90运行代码”说明不少人是在特定芯片平台上做这件事。不过从通用性出发我更愿意用Arduino UNO或ESP32来演示原因是它们的ADC和PWM资源足够、资料多、上手快。Arduino UNO有6路10位ADC和多路定时器PWM配合Servo库几乎零配置ESP32的ADC是12位分辨率更高PWM通道也更灵活适合需要更高角度精度的场景。如果你用的是其他平台逻辑完全一样只是API名字不同。这里要提醒一个容易忽略的点ADC参考电压。Arduino UNO默认用5V作为参考10位ADC把0到5V分成1024份每份约4.9mV。电位器转满一圈读数从0到1023。ESP32默认参考电压约3.3V12位ADC分成4096份。如果你直接把UNO的代码搬到ESP32上映射范围不改舵机角度就会错乱。所以选型确定后第一件事是确认ADC位数和参考电压这决定了后面映射公式的系数。2.3 供电方案别让舵机把开发板拖垮SG90空载电流大概100mA到200mA堵转时瞬间能冲到700mA甚至更高。很多人图省事直接把舵机的红线接到开发板的5V引脚上结果一上电开发板就复位或者舵机一抖一抖。原因很简单开发板的5V通常来自USB口或板载稳压器能提供的电流有限舵机一堵转就把电压拉低开发板欠压复位。正确做法是舵机单独供电用4节镍氢电池约4.8V或者一个能输出5V/2A的稳压模块然后把舵机的地和开发板的地共地。共地这一步不能省否则PWM信号没有共同参考舵机根本不认。我自己的测试台是这样接的一个5V/3A的开关电源给舵机供电开发板用USB单独供电两者的GND用一根线连起来。这样即使舵机堵转开发板也稳如泰山。如果你非要用一块电池同时供两者那至少保证电池能持续输出1A以上并且在开发板电源入口并一个470uF以上的电解电容做缓冲。3. 核心细节解析PWM、ADC与角度映射的实操要点3.1 SG90的PWM时序到底怎么算SG90的控制信号是周期20ms、高电平0.5ms到2.5ms的方波。换算成占空比0.5ms/20ms等于2.5%2.5ms/20ms等于12.5%。也就是说舵机只认这2.5%到12.5%之间的占空比超出范围不仅不会转到更大角度反而可能触发内部保护或者发出“吱吱”的堵转声。这一点在写代码时必须卡死不能让映射结果溢出。用Arduino的Servo库时你不需要自己算占空比直接servo.write(angle)库内部会把0到180度换算成对应的脉宽。但如果你用的是裸定时器或者某些特定芯片的PWM外设就得自己算计数值。以16位定时器、预分频后计数频率1MHz为例20ms周期对应20000个计数0.5ms对应5002.5ms对应2500。角度a对应的计数值就是500 a * (2000 / 180)约等于500 a * 11.11。这个公式我在多个平台上验证过误差在1度以内够用。注意不同批次的SG90脉宽范围略有差异。有的舵机0.4ms就到0度2.6ms才到180度。如果你发现舵机在0度或180度附近有轻微“顶死”的声音把脉宽范围稍微收窄一点比如0.6ms到2.4ms牺牲一点行程换取安静和寿命。3.2 电位器读数的滤波与死区处理ADC读电位器最头疼的是抖动。你手不动读数也在±2到±5之间跳。如果直接把原始读数映射成角度舵机就会跟着微微抖动发出持续的“嗡嗡”声时间长了齿轮磨损加快。解决办法有两层硬件滤波和软件滤波。硬件上在电位器抽头和地之间并一个0.1uF陶瓷电容能滤掉大部分高频噪声。软件上我用的是滑动平均滤波连续读8次去掉最大和最小剩下6次求平均。这样读数非常稳舵机静止时几乎听不到声音。另一个技巧是设置死区如果本次读数与上次读数的差值小于某个阈值比如5就不更新舵机角度。这样手轻微晃动不会引起舵机动作只有明显转动才响应。int readPotSmooth() { int samples[8]; for (int i 0; i 8; i) { samples[i] analogRead(POT_PIN); delay(2); } // 冒泡排序 for (int i 0; i 7; i) { for (int j i 1; j 8; j) { if (samples[i] samples[j]) { int t samples[i]; samples[i] samples[j]; samples[j] t; } } } int sum 0; for (int i 1; i 7; i) sum samples[i]; return sum / 6; }这段代码看起来笨但实测比简单的analogRead稳太多。8次采样加排序大概耗时20ms左右正好和舵机PWM周期错开不会互相干扰。3.3 角度映射的边界处理映射公式本身很简单angle map(adcValue, 0, 1023, 0, 180)。但实际用的时候电位器很难真正转到0和1023。机械限位、接触电阻、ADC非线性都会让两端读数停在比如30和990。如果你直接用0到1023映射舵机永远转不到0度和180度行程被压缩。我的做法是先实测两端极值再代入映射。具体操作把电位器逆时针拧到底串口打印读数记下最小值顺时针拧到底记下最大值。然后把这两个值填进map函数。这样舵机行程能覆盖完整的0到180度。另外map函数在Arduino里是整数运算如果输入范围小于输出范围会有台阶感。比如输入范围只有200输出要180度每度对应的ADC变化不到1.2舵机会一跳一跳。解决办法是用浮点映射再取整或者换用12位ADC的开发板。ESP32的4096级分辨率就完全没有这个问题转动非常顺滑。4. 完整实现过程从接线到跑通的第一版代码4.1 物料清单与接线表先列一下我这次用的东西都是最普通的件物料规格数量备注开发板Arduino UNO1或其他5V逻辑开发板舵机SG901附带舵盘和螺丝电位器10k线性1B10K三脚杜邦线公对公/公对母若干建议用不同颜色区分电源5V/2A以上1舵机独立供电电容0.1uF1电位器抽头对地接线按下面的对应关系来一根都别接错电位器两端一端接开发板5V另一端接开发板GND。电位器中间抽头接开发板A0。舵机红线接外部5V电源正。舵机棕线接外部5V电源负同时和开发板GND相连。舵机橙线接开发板D9。提示舵机线序不同厂家可能不同常见是红正、棕负、橙信号。如果你拿到的是红正、黑负、黄信号逻辑一样按颜色对应即可。接反不会烧但舵机不动别慌调换过来就行。4.2 第一版代码最小可用系统下面这段代码是我验证过的最简版本去掉了所有花哨功能只保留核心链路。你可以直接复制到Arduino IDE里编译上传。#include Servo.h Servo myServo; const int POT_PIN A0; const int SERVO_PIN 9; int lastAngle -1; void setup() { myServo.attach(SERVO_PIN); Serial.begin(9600); } void loop() { int raw analogRead(POT_PIN); int angle map(raw, 0, 1023, 0, 180); angle constrain(angle, 0, 180); if (abs(angle - lastAngle) 2) { myServo.write(angle); lastAngle angle; Serial.print(ADC: ); Serial.print(raw); Serial.print( Angle: ); Serial.println(angle); } delay(20); }上传后打开串口监视器波特率9600。转动电位器你应该能看到ADC值从0附近变到1023附近角度从0变到180舵机同步转动。如果舵机不动先检查供电和共地再看信号线是不是插在D9上。4.3 加入滤波和死区的进阶版第一版能跑但舵机静止时会有轻微抖动。把前面讲的滤波和死区加进去代码变成这样#include Servo.h Servo myServo; const int POT_PIN A0; const int SERVO_PIN 9; const int DEAD_ZONE 3; int lastAngle -1; int readPotSmooth() { int samples[8]; for (int i 0; i 8; i) { samples[i] analogRead(POT_PIN); delay(2); } for (int i 0; i 7; i) { for (int j i 1; j 8; j) { if (samples[i] samples[j]) { int t samples[i]; samples[i] samples[j]; samples[j] t; } } } int sum 0; for (int i 1; i 7; i) sum samples[i]; return sum / 6; } void setup() { myServo.attach(SERVO_PIN); Serial.begin(9600); } void loop() { int raw readPotSmooth(); int angle map(raw, 0, 1023, 0, 180); angle constrain(angle, 0, 180); if (abs(angle - lastAngle) DEAD_ZONE) { myServo.write(angle); lastAngle angle; Serial.print(ADC: ); Serial.print(raw); Serial.print( Angle: ); Serial.println(angle); } delay(10); }实测下来这版代码舵机静止时几乎无声转动响应也很跟手。DEAD_ZONE设成3度如果你觉得响应不够灵敏可以降到2如果还抖加到5。4.4 实测记录不同电位器位置的ADC与角度对照我在测试台上把电位器从逆时针到底转到顺时针到底每隔一段记录一次得到下面这组数据。你可以拿它对照自己的系统判断线性度是否正常。电位器位置ADC读数映射角度舵机实际角度目测逆时针到底122约0度1/4处26847约45度1/2处52191约90度3/4处774136约135度顺时针到底1015178约180度线性度相当好最大偏差在3度以内。如果你测出来中间某段跳变很大多半是电位器质量不行或者ADC参考不稳换一个电位器或者加电容再试。5. 常见问题与排查技巧实录5.1 舵机不动或只往一个方向转这是新手遇到最多的问题。按下面顺序排查供电是否独立舵机红线有没有接到外部5V如果接在开发板5V上换独立电源。共地是否连接外部电源的GND和开发板GND之间有没有线没有的话信号没有参考舵机不认。信号线是否插对橙色线是不是在D9代码里SERVO_PIN是不是9电位器接线是否反了两端接反不会烧但转动方向会反过来映射角度也跟着反。调换电位器两端即可。舵机是否损坏拿一个已知能用的舵机替换测试或者用myServo.write(90)固定角度测试排除代码问题。5.2 舵机抖动、嗡嗡响、发热抖动基本来自ADC噪声。先加0.1uF电容再加软件滤波最后加死区。如果还抖检查电源纹波用示波器看5V线上有没有大的波动。舵机发热通常是堵转或者脉宽超出范围检查映射结果有没有被constrain限制在0到180之间。另外SG90是塑料齿轮长时间堵转会扫齿调试时尽量避免让舵机顶在机械限位上。5.3 角度范围不够或超出如果舵机只能转90度先确认map的输入范围是不是0到1023。如果你的电位器实际读数只有200到800映射出来就只有35到140度。解决办法是实测极值后修改map参数。如果舵机转到头有“咔咔”声说明脉宽超了把constrain的上限从180降到175试试。5.4 上电瞬间舵机猛转一下这是Servo库上电默认输出90度造成的。如果你希望上电时舵机保持在某个安全位置在setup里先myServo.write(90)等电位器读数稳定后再进入主循环。或者用myServo.attach(SERVO_PIN, 500, 2500)显式指定脉宽范围减少上电冲击。5.5 常见问题速查表现象可能原因解决方法舵机完全不动供电不足、未共地、信号线错独立供电、共地、检查D9舵机抖动ADC噪声、电源纹波加电容、软件滤波、死区角度范围小电位器极值未校准实测极值后改map参数舵机发热堵转、脉宽超范围constrain限制、检查机械限位上电猛转Servo库默认90度setup中先write(90)转动方向相反电位器两端接反调换电位器两端接线实操心得调试舵机时我习惯先用一个固定角度测试比如write(90)确认舵机能正常定位再接入电位器。这样能把“舵机本身的问题”和“控制链路的问题”分开排查效率高很多。另外杜邦线尽量短尤其是信号线长了容易引入干扰导致舵机小幅度抽动。6. 从最小系统到实际项目的扩展思路这套电位器控制SG90的方案本身就是一个完整的闭环交互原型。把它放到实际项目里可以往几个方向扩展。第一个方向是多路舵机联动机械臂有多个关节每个关节配一个电位器代码里用数组管理多个Servo对象和多个ADC引脚逻辑完全一样只是循环里多读几路。第二个方向是记录与回放在电位器控制的同时把角度值按时间戳存到SD卡或EEPROM里之后可以回放动作序列这对做重复性演示很有用。第三个方向是无线遥控把电位器接到一个带无线模块的开发板上把角度值发出去另一端接收后驱动舵机就变成了一个简单的遥控云台。我自己在做小车转向时就是把电位器换成车上的转向传感器逻辑不变只是输入源从手动旋钮变成了传感器电压。所以这套东西的价值不在于它本身多复杂而在于它把“模拟输入到舵机输出”这条链路跑通了后面换任何传感器都是替换输入源的事。如果你正在学嵌入式或者做机器人入门把这个项目吃透比看十篇理论文章都管用。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。