STM32 PWM控制舵机:从抖动到丝滑的实战指南

发布时间:2026/10/15 15:39:59
STM32 PWM控制舵机:从抖动到丝滑的实战指南
简介这份资源面向嵌入式开发初学者与需要实现舵机角度控制的工程师围绕STM32的PWM输出展开可用于无人机、机器人、机械臂关节等运动控制场景。包内共889个文件以198个h头文件、173个c源文件为核心配合136个crf、113个o等编译中间文件以及uvprojx、uvoptx工程配置、hex烧录文件、map映射表、bat批处理脚本和少量说明文档压缩包约27.97MB工程结构完整便于直接打开编译与二次修改。内容涉及TIM模块时钟配置、预分频与自动装载值设定、PWM1/PWM2模式选择、输出比较寄存器CCR调节占空比以及HAL库初始化、通道配置与启动输出的完整流程并覆盖1ms至2ms脉冲宽度对应舵机中位与两端角度的换算思路。已有3232人学习下载适合对照工程理解PWM驱动舵机的实现细节快速搭建可运行的控制框架。1. stm32pwm控制舵机从抖动到丝滑差的不只是一个占空比舵机这东西玩过小车和机械臂的都懂——给它一个 PWM 信号它转到指定角度听起来简单到不值一提。但真正上手 STM32 去驱动舵机时很多人会发现明明占空比算对了舵机却在目标角度附近来回抖换一个定时器通道角度又偏了十几度想让它慢速平滑转动结果一顿一顿像卡了壳。这些问题的根源往往不在舵机本身而在于对 STM32 定时器 PWM 输出模式、时钟树配置和舵机时序特性的理解深度不够。这篇笔记就围绕 stm32pwm控制舵机这条主线把从定时器参数计算到多路舵机联动的完整路径拆开讲清楚。适合正在做课程设计、机械臂关节控制、云台转向的嵌入式开发者也适合已经能点灯但还没真正吃透 PWM 输出模式的进阶新手。读完你至少能独立配出一路不抖的舵机控制并知道多路扩展时该往哪个方向调。2. 先搞懂舵机要什么再谈 STM32 给什么2.1 舵机的控制信号到底长什么样常见舵机如 SG90、MG996R 这类模拟舵机的控制协议是周期 20ms 的 PWM 波也就是频率 50Hz。在这个周期内高电平持续时间决定转角0.5ms 对应 0°1.5ms 对应 90°2.5ms 对应 180°。注意这是典型值不同型号舵机会有偏差有些 270° 舵机的脉宽范围是 0.5ms 到 2.5ms 对应 0° 到 270°。关键点在于舵机识别的是脉宽绝对值不是占空比百分比。很多人习惯说“占空比 7.5% 对应 90°”这没错但换一个 PWM 频率同样的占空比脉宽就变了舵机角度也跟着变。所以配置时脑子里要始终盯着“高电平多少微秒”而不是“占空比百分之几”。还有一个容易被忽略的参数死区宽度。舵机内部有死区脉宽变化小于某个阈值时它不动。数字舵机死区小模拟舵机死区大。如果你用 16 位定时器配 50Hz分辨率完全够用死区不是问题但如果为了多路舵机把定时器频率拉得很高再分频分辨率掉下来就可能出现“调了一格没反应再调一格跳两度”的玄学现象。2.2 STM32 的 PWM 输出模式怎么选STM32 的通用定时器和高级定时器都支持 PWM 输出核心是配置 ARR自动重装载值和 CCR捕获比较值。ARR 决定周期CCR 决定高电平时间。PWM 模式 1 和模式 2 的区别只在于有效电平极性模式 1 是 CNT CCR 时输出有效电平模式 2 反过来。对于舵机控制我一般用PWM 模式 1 有效电平为高。这样 CCR 值直接对应高电平计数值算起来直观。如果你用模式 2 或者把极性设成低后面算角度时就得反着推容易把自己绕进去。定时器选型上驱动少量舵机用 TIM2、TIM3、TIM4 这类通用定时器就够了每个定时器 4 个独立通道能控 4 路舵机。如果要控 6 路以上可以考虑级联多个定时器或者用高级定时器 TIM1/TIM8 的互补输出——但互补输出是给电机驱动用的舵机不需要互补用不上。时钟树这块是翻车重灾区。以常见的 72MHz 系统时钟为例APB1 定时器时钟可能是 72MHz 或 36MHz取决于分频系数。配置前一定要在数据手册里确认你用的定时器挂在哪个总线上否则算出来的 ARR 和实际频率对不上舵机要么不动要么乱转。2.3 参数计算从 50Hz 反推 ARR 和 CCR假设定时器时钟为 72MHz要产生 50Hz 的 PWM预分频器 PSC 先分频得到计数频率计数频率 72MHz / (PSC 1)PWM 周期 (ARR 1) / 计数频率要 20ms 周期即 50Hz如果取 PSC 71计数频率 72MHz / 72 1MHz即每计数一次 1μs。那么 ARR 20000 - 1 19999周期就是 20000μs 20ms。这样 CCR 的值直接就是高电平微秒数CCR 500 对应 0.5msCCR 1500 对应 1.5msCCR 2500 对应 2.5ms。非常直观。提示PSC 和 ARR 都是 16 位寄存器最大值 65535。72MHz 下如果 PSC 取 0ARR 要 1440000 才能到 50Hz超了。所以必须分频。下面是初始化代码的核心部分以 TIM3 通道 1 为例// 假设系统时钟 72MHzAPB1 定时器时钟 72MHz void Servo_TIM3_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; // 1. 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2. 配置 GPIO 为复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_6; // TIM3_CH1 在 PA6 GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 3. 定时器基础配置PSC71, ARR19999 - 50Hz TIM_TimeBaseStructure.TIM_Period 19999; TIM_TimeBaseStructure.TIM_Prescaler 71; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); // 4. PWM 模式配置模式1使能输出 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 1500; // 初始 1.5ms90度 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM3, TIM_OCInitStructure); // 5. 使能预装载防止改 CCR 时出现毛刺 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); // 6. 启动定时器 TIM_Cmd(TIM3, ENABLE); }这段代码里几个参数值得展开说。TIM_Prescaler 71意味着实际分频系数是 72因为寄存器值是从 0 开始计的。TIM_Period 19999同理实际周期是 20000 个计数。TIM_Pulse 1500就是 CCR1 的初始值对应 1.5ms 高电平。TIM_OCPreload_Enable这一句很关键——它让 CCR 的修改在下一个周期才生效避免当前周期中途改值导致输出一个畸形脉冲舵机对畸形脉冲很敏感可能直接抖一下。改角度就一句话// 设置舵机角度angle 范围 0~180 void Servo_SetAngle(TIM_TypeDef* TIMx, uint8_t channel, uint8_t angle) { uint16_t ccr; // 线性映射0度-500, 180度-2500 ccr 500 (uint16_t)(angle * 2000 / 180); switch(channel) { case 1: TIM_SetCompare1(TIMx, ccr); break; case 2: TIM_SetCompare2(TIMx, ccr); break; case 3: TIM_SetCompare3(TIMx, ccr); break; case 4: TIM_SetCompare4(TIMx, ccr); break; } }映射公式500 angle * 2000 / 180把 0 到 180 度线性映射到 500 到 2500 微秒。这里用整数运算精度到 1μs 左右对舵机来说完全够。如果你要更精细可以把 CCR 算成浮点再取整但实际舵机机械精度远达不到 1μs 对应的角度分辨率没必要。3. 多路舵机联动定时器通道分配与相位管理3.1 一个定时器控四路通道独立改 CCRTIM2、TIM3、TIM4 各有 4 个独立通道共享同一个 ARR 和 PSC但每个通道有独立的 CCR。这意味着四路舵机的 PWM 频率完全一致都是 50Hz但脉宽可以各自独立设置。这是最经济的多路方案——一个定时器搞定四路不占用额外硬件资源。配置时只需要把四个通道的 GPIO 都配成复用推挽然后在 PWM 初始化里分别调用 TIM_OC1Init 到 TIM_OC4Init。注意每个通道对应的 GPIO 引脚不能搞错STM32 的引脚复用表要查清楚。比如 TIM3_CH1 是 PA6CH2 是 PA7CH3 是 PB0CH4 是 PB1。如果引脚配错通道有输出但引脚没信号示波器一量就露馅。// 四路舵机初始化TIM3 四个通道 void Servo_4CH_Init(void) { // GPIO 配置PA6, PA7, PB0, PB1 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_Init(GPIOB, GPIO_InitStructure); // 定时器基础配置同上PSC71, ARR19999 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period 19999; TIM_TimeBaseStructure.TIM_Prescaler 71; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); // 四个通道统一配置 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OCInitStructure.TIM_Pulse 1500; // 全部先回中位 TIM_OC1Init(TIM3, TIM_OCInitStructure); TIM_OC2Init(TIM3, TIM_OCInitStructure); TIM_OC3Init(TIM3, TIM_OCInitStructure); TIM_OC4Init(TIM3, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); TIM_Cmd(TIM3, ENABLE); }这段代码和单路的区别就在于多调了几次初始化函数以及 GPIO 配置覆盖了四个引脚。逻辑上没有任何新东西但实际接线时要注意四个舵机的电源不能全从 STM32 的 3.3V 取。舵机堵转电流能到 1A 以上STM32 的 LDO 根本扛不住轻则复位重则烧片。正确做法是舵机电源单独走一路 5V和 STM32 共地但不共电源。3.2 多路同时动作时的相位问题四路舵机共享同一个计数器这意味着它们的 PWM 周期是严格对齐的——每个周期都是从 0 计数到 ARR四个通道的高电平都从计数器归零后开始。这本身没问题但如果四路舵机同时从 0° 转到 180°四路电流会同时达到峰值对电源的瞬时电流要求很高。一个实用的技巧是错开启动时刻。不要让四路舵机在同一时刻开始转动而是间隔几十毫秒依次启动。这样峰值电流被分散电源压力小很多。实现上就是在设置角度时加一个短延时// 依次设置四路角度间隔 20ms 错开峰值 void Servo_4CH_SetAngles(uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4) { Servo_SetAngle(TIM3, 1, a1); Delay_ms(20); Servo_SetAngle(TIM3, 2, a2); Delay_ms(20); Servo_SetAngle(TIM3, 3, a3); Delay_ms(20); Servo_SetAngle(TIM3, 4, a4); }这个延时不是随便定的。舵机从收到信号到开始转动大约有 5 到 10ms 的响应延迟20ms 的间隔足够让前一路舵机进入稳定转动状态再启动下一路。如果你做的是机械臂四路同时动反而会让机械臂抖动错开启动还能让动作看起来更自然。3.3 超过四路怎么办定时器级联与外部 PWM 驱动如果需要控 6 路、8 路甚至更多舵机STM32 自带的定时器通道就不够用了。常见做法有三种第一种是用两个定时器比如 TIM3 控 4 路TIM2 再控 4 路总共 8 路。两个定时器各自独立配置PSC 和 ARR 都设成一样的值保证频率一致。这种方案最简单但占用两个定时器资源。第二种是用 PCA9685 这类外部 PWM 驱动芯片。PCA9685 是 I2C 接口的 16 路 PWM 驱动器内置 25MHz 振荡器频率和脉宽都通过寄存器配置不占用 STM32 的定时器资源。STM32 只需要通过 I2C 发指令就行。缺点是多了个芯片布线复杂一点但控多路舵机时非常省心。第三种是软件模拟 PWM。用普通 GPIO 加定时器中断在中断里手动翻转引脚。这种方案不推荐——软件模拟的 PWM 抖动大舵机会发出滋滋声而且占用 CPU 时间。除非你实在没有硬件定时器可用了否则别走这条路。注意无论用哪种方案舵机电源和逻辑电源一定要分开。逻辑电源 3.3V 给 STM32舵机电源 5V 到 6V 单独供。共地是必须的否则 PWM 信号没有参考电平舵机收不到正确信号。4. 避坑与排查舵机抖动、角度偏差、定时器不输出的常见原因4.1 舵机在目标角度附近来回抖动现象舵机转到指定角度后不是稳定停住而是以肉眼可见的频率来回摆动有时还伴随嗡嗡声。原因最常见的原因是 PWM 信号本身不稳定。可能是 CCR 值在程序里被反复改写比如在主循环里不断调用设置角度的函数每次算出来的 CCR 有微小差异舵机就跟着微调。另一个原因是 PWM 频率偏离 50Hz 太多舵机内部的控制环路失稳。还有一种情况是电源纹波太大舵机供电电压波动导致内部比较器误判。解决先确认 CCR 只在需要改变角度时才写不要在循环里反复写同一个值。然后用示波器或逻辑分析仪量 PWM 波形确认周期稳定在 20ms。如果波形没问题检查舵机电源——用万用表交流档量电源纹波超过 100mV 就要加滤波电容。我一般会在舵机电源引脚并一个 470μF 电解电容加一个 0.1μF 陶瓷电容基本能压住大部分纹波。4.2 角度偏差十几度怎么调都不对现象设 90°舵机停在 75° 左右设 180°舵机只转到 160° 就顶住了。原因舵机的脉宽-角度映射不是理想线性的而且不同型号的舵机端点脉宽差异很大。SG90 的 0° 可能对应 500μs但 MG996R 的 0° 可能是 600μs。另外舵机机械限位也会影响——有些舵机标称 180°实际机械行程只有 170° 左右强行给 2.5ms 脉宽它会顶在限位上嗡嗡响。解决不要死守 500 到 2500 这个范围。写一个校准函数手动微调端点的 CCR 值找到实际能稳定到达 0° 和 180° 的脉宽。校准时从 1500 开始每次减 50观察舵机停在哪里直到它刚好到 0° 不再继续转。180° 端同理。把这两个值记下来重新算映射公式。// 校准后的映射min_ccr 和 max_ccr 是实测值 uint16_t Servo_AngleToCCR(uint8_t angle, uint16_t min_ccr, uint16_t max_ccr) { // 防止越界 if (angle 180) angle 180; return min_ccr (uint16_t)((uint32_t)angle * (max_ccr - min_ccr) / 180); }这个函数把校准参数暴露出来换舵机时只改两个宏定义就行不用动逻辑。4.3 定时器配置了但引脚没输出现象代码编译下载都没报错但示波器量引脚一直是低电平舵机完全不动。原因按概率排序第一是 GPIO 复用功能没开——STM32 的引脚默认是普通 IO必须配置成 AF_PP 才能把定时器信号引出来。第二是定时器时钟没使能RCC_APB1PeriphClockCmd 那行漏了或者写错了总线。第三是通道映射搞错比如把 TIM3_CH1 配到了 PA7 上PA7 其实是 CH2。第四是高级定时器 TIM1/TIM8 需要额外使能主输出 MOE否则 PWM 出不来。解决按顺序排查。先用万用表量引脚电压如果是 0V 或 3.3V 固定不变说明 GPIO 没配成复用。检查 RCC 初始化确认定时器时钟和 GPIO 时钟都开了。查数据手册的引脚复用表确认引脚和通道对应关系。如果用的是 TIM1 或 TIM8加一句TIM_CtrlPWMOutputs(TIM1, ENABLE);。4.4 改 CCR 时舵机突然跳一下现象从 90° 切到 120° 时舵机先往反方向抖一下再转过去。原因CCR 预装载没使能。如果没开预装载写 CCR 的值会立即生效。如果当前计数器已经超过新写的 CCR 值这一周期的高电平就提前结束了输出一个异常窄的脉冲。舵机收到这个窄脉冲会误判为极端角度先往那个方向动一下下一周期收到正常信号才转回来。解决使能 CCR 预装载。TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);这一句不能省。开了预装载后CCR 的写入会在下一个更新事件时才生效保证每个周期的脉冲都是完整的。4.5 多路舵机同时动时 STM32 复位现象单路舵机测试正常四路一起动的时候 STM32 随机复位串口输出乱码。原因舵机电源和 STM32 电源共用舵机启动瞬间拉低电压STM32 的 BOR 复位。或者舵机电源线太细压降太大。解决舵机电源必须独立。如果实在要用同一路 5V至少给 STM32 的 3.3V LDO 输入端加一个大电容220μF 以上并且舵机电源线用粗线。更好的做法是舵机走 5V 独立电源STM32 走 USB 或另一路 3.3V两者共地。这样舵机怎么折腾都不会影响 MCU。5. 让舵机转得丝滑S 曲线调速与角度插值5.1 为什么直接改 CCR 会让舵机一顿一顿舵机内部是一个位置闭环系统你给它一个目标脉宽它用最大速度往目标冲。如果目标角度变化很大舵机会全速转动到目标附近再急停。这个急停过程在机械上表现为抖动在视觉上就是一顿。如果你做的是云台或者机械臂这种顿挫感非常明显。要让它丝滑核心思路是把一个大角度变化拆成很多个小角度变化每个小变化之间加短延时让舵机以小步长逼近目标。这就是角度插值。5.2 线性插值与 S 曲线插值的区别最简单的插值是线性插值从当前角度到目标角度每次加固定步长延时固定时间。比如从 0° 到 180°每次加 1°延时 10ms总共 1.8 秒转完。这种方式比直接跳变好很多但启动和停止时还是有一点冲击因为速度是突变的。S 曲线插值让速度按 S 形变化启动时慢慢加速中间匀速接近目标时慢慢减速。这样启停都没有冲击视觉效果最平滑。实现上可以用一个简单的三次函数或者查表法。// S 曲线插值从 start 到 end总步数 steps void Servo_SmoothMove(TIM_TypeDef* TIMx, uint8_t ch, uint8_t start, uint8_t end, uint16_t steps) { float t, s; uint8_t angle; for (uint16_t i 0; i steps; i) { t (float)i / steps; // 归一化时间 0~1 // 三次 S 曲线s 3t^2 - 2t^3 s 3.0f * t * t - 2.0f * t * t * t; angle start (uint8_t)(s * (end - start)); Servo_SetAngle(TIMx, ch, angle); Delay_ms(10); // 每步 10ms } }这个函数里3t² - 2t³是经典的 smoothstep 函数在 t0 和 t1 处导数为零意味着启停速度为零。steps控制总步数配合 10ms 延时steps100 就是 1 秒转完。steps 越大越平滑但响应越慢。实际调试时我一般用 50 到 100 步对应 0.5 到 1 秒的转动时间手感比较自然。5.3 多路 S 曲线联动的资源冲突如果你四路舵机都要做 S 曲线平滑不能在主循环里依次调用 Servo_SmoothMove因为每个调用都会阻塞 1 秒左右四路串行就是 4 秒完全没法用。正确做法是用状态机 定时器中断。把每路舵机的当前角度、目标角度、当前步数存在结构体里用一个 10ms 的定时器中断来推进所有通道的插值。中断里只做计算和写 CCR不延时。typedef struct { uint8_t current; uint8_t target; uint8_t start; uint16_t step; uint16_t total_steps; uint8_t active; } Servo_State; Servo_State servo[4]; // 10ms 定时器中断里调用 void Servo_UpdateHandler(void) { for (int i 0; i 4; i) { if (!servo[i].active) continue; servo[i].step; float t (float)servo[i].step / servo[i].total_steps; float s 3.0f * t * t - 2.0f * t * t * t; servo[i].current servo[i].start (uint8_t)(s * (servo[i].target - servo[i].start)); Servo_SetAngle(TIM3, i 1, servo[i].current); if (servo[i].step servo[i].total_steps) { servo[i].active 0; servo[i].current servo[i].target; } } }这个结构体数组的方案让四路舵机真正并行平滑运动互不阻塞。中断周期 10ms 对应 100Hz 的更新率对舵机来说足够。如果你觉得 10ms 粒度太粗可以降到 5ms但要注意中断里浮点运算的时间开销——STM32F1 没有硬件浮点每次算3t² - 2t³大概几十个周期四路加起来在 10ms 中断里完全来得及但如果你把中断周期降到 1ms 就要小心了。5.4 用查表法替代浮点运算如果 MCU 没有 FPU或者你想进一步省时间可以把 S 曲线预先算成一张表。比如 100 个点的 S 曲线表中断里直接查表取值省掉浮点乘法和除法。// 预计算的 S 曲线表100 个点值域 0~1000 const uint16_t s_curve_table[101] { 0, 0, 0, 1, 2, 4, 7, 11, 16, 22, // ... 中间省略实际使用时用脚本生成完整表 978, 984, 989, 993, 996, 998, 999, 1000, 1000, 1000 }; uint8_t Servo_LookupAngle(uint8_t start, uint8_t end, uint16_t step, uint16_t total) { uint16_t idx (uint32_t)step * 100 / total; if (idx 100) idx 100; uint16_t s s_curve_table[idx]; // 0~1000 return start (uint8_t)((uint32_t)s * (end - start) / 1000); }这张表用 Python 或 Excel 生成一次就行精度到千分之一实际角度误差远小于 1°。查表法把中断里的计算量从几十个周期降到几个周期四路同时跑也毫无压力。5.5 一个容易忽略的细节舵机响应延迟S 曲线插值假设舵机能瞬间跟上每个中间角度但实际上舵机有 5 到 10ms 的响应延迟。如果你的插值步长是 10ms舵机可能还没转到上一个目标下一个目标就来了。结果就是舵机一直在追实际轨迹比理论轨迹滞后。解决办法有两个一是把步长延时加大到 15 到 20ms给舵机留出响应时间二是在插值目标上做超前补偿但这就复杂了。我一般用第一种简单可靠。如果你发现舵机运动轨迹和预期差很多先把延时加大试试别急着上补偿算法。提示S 曲线插值的总时间不要设得太短。舵机最大角速度有限SG90 大概是 0.1 秒每 60°180° 转完至少 0.3 秒。如果你设 0.2 秒转 180°舵机根本跟不上S 曲线也救不了。先查舵机手册里的速度参数再定总时间。这套 S 曲线加状态机的方案我在几个云台和机械臂项目里都用过四路舵机同时平滑运动肉眼看不到任何顿挫。唯一要注意的是中断里不要做耗时操作浮点能换查表就换查表中断时间留够余量。希望帮到你。本文还有配套的精品资源点击获取