最近有好几个朋友私信我问 ESP32 控制 SG90 舵机的事问题集中在“接上线为什么不动”“代码明明没问题舵机却一直抖”“上电瞬间乱转”这几类。正好我前几天又把这块整套流程跑了一遍从接线到调试踩了不少坑顺手整理成一篇完整避坑指南。这篇东西适合刚入门 ESP32 和 SG90 的玩家也适合已经能用 Arduino 控制舵机、但换到 ESP32 平台就各种不顺手的朋友。先说结论ESP32 控制 SG90 舵机代码难度其实很低真正的坑基本都在硬件供电和 GPIO 选型上。ESP32Servo 这个库把底层 PWM 细节封装得很干净但如果你不知道它背后用的是 LEDC 硬件定时器、不知道舵机信号线对波形的要求遇到奇怪问题就会完全无从下手。这篇文章不光讲怎么接线、怎么写代码还会把每个坑背后的原理讲清楚让你调完这次以后遇到别的舵机、别的板子也能举一反三。1. 项目概述与核心思路拆解1.1 这个项目到底在做什么整个项目的目标是用 ESP32 开发板输出 PWM 信号驱动 SG90 舵机按指定角度偏转。SG90 是目前最常见的 9g 微型舵机广泛用在智能小车、机械臂、云台、小车转向机构上市面上价格十块钱左右刷新率不高、扭矩也不大但胜在便宜、轻巧、够用是新手入门舵机控制的首选。ESP32 是乐鑫推出的双核 Wi-Fi/蓝牙 MCU性能比 Arduino Uno 强不少而且自带多路硬件 PWM也就是 LEDC 外设理论上不需要额外舵机驱动板就能直接带多路舵机。ESP32Servo 这个库在 Arduino 生态里对 ESP32 的 Servo 功能做了封装你只要写servo.write(90)这样的代码就能让舵机转到 90 度看起来非常简单。但简单背后的代价是很多人不知道接线时 SG90 的电源线不能直接吃 ESP32 的 3.3V 输出也不知道随意挑一个 GPIO 可能会踩到“只能输入不能输出 PWM”的雷更不知道上电瞬间舵机为什么会猛抽一下。这篇文章就是把这些暗坑一个个翻出来摊开讲。1.2 为什么选 ESP32Servo 库而不是标准 Servo 库Arduino 平台无论 Uno 还是 Mega标准做法是直接用官方的Servo.h库。但在 ESP32 上官方Servo.h是不直接兼容的它底层依赖 AVR 芯片的 Timer1 做 PWM 输出架构完全不同。ESP32 这边用的最广的就是ESP32Servo这个社区库它由 Kevin Harrington 维护封装的是 ESP32 的 LEDC 硬件 PWM 外设。选它有三个理由第一API 兼容性极好。接线的思路、代码的写法都跟标准 Servo 库一致从 Arduino 迁移过来几乎零学习成本attach()、write()、writeMicroseconds()这些方法名直接沿用网上能找到的大多数示例代码都能直接跑。第二底层用的是硬件定时器而不是软件模拟 PWM。AVR 平台的 Servo 库靠中断模拟输出CPU 占用比较高ESP32Servo 则直接走 LEDC 外设脉冲输出完全由硬件完成不占 CPU同时可以支持最多 16 路舵机对多关节机械臂这种项目特别友好。第三库已经帮你处理了 ESP32 特有的一些细节比如自动分配 LEDC 通道、自动配置 50Hz 频率等。但这里要特别注意所谓“自动处理”不等于所有参数都对频率和脉宽范围这两个值后续还是需要手动确认的这也是很多调试问题的根源。1.3 核心原理解读PWM、50Hz 与脉宽要彻底搞懂舵机调试离不开两个基本概念PWM 频率和脉冲宽度。SG90 这种模拟舵机的控制信号本质上是周期 20ms、频率 20ms 的 PWM 波频率通常是 50Hz。舵机内部有个基准电路它会把输入脉冲的宽度跟内部基准脉冲宽度做比较得出的差值驱动电机正转或反转直到舵机输出轴转到跟脉冲宽度对应的角度为止。SG90 的常见规格是这样的0 度对应脉宽约 0.5ms180 度对应脉宽约 2.4ms有的资料写 2.5ms中间的 90 度对应约 1.45ms。也就是说改变脉宽就能改变目标角度舵机内部自动完成闭环。这里我多说一句“占空比”这个概念。占空比是脉冲高电平时间占整个周期的比例比如周期 20ms、脉宽 1.5ms那占空比就是 1.5/20 7.5%。很多人一开始被占空比绕晕其实舵机只关心绝对脉宽不关心占空比。你在 ESP32Servo 里写的servo.write(90)库内部会先把 90 度换算成对应的脉宽微秒数再配置 LEDC 输出一个宽度为这个脉宽的脉冲。理解这个换算关系后后面调试波形时你就能一眼看出输出是否正常。2. 硬件接线与供电设计第一步错后面全白搭2.1 SG90 三根线的接法SG90 舵机引出的三根线颜色一般是这样红色是电源正极棕色是 GND橙色是信号线。但注意市面上 SG90 真的特别多仿冒品线序颜色可能完全不一样我手里有一只“SG90”的橙色线其实是 GND所以拿到舵机第一件事是拿万用表测电阻、看线序不要迷信颜色。标准接法是把信号线接到 ESP32 的某个 GPIO电源线接到 5VGND 线跟 ESP32 的 GND 共地。看起来简单但这个“5V”是整篇文章最大的坑。SG90 标称工作电压是 4.8V 到 6V也就是说它需要 5V 左右的外部电源。ESP32 开发板上的 3.3V 引脚通常由板载 LDO低压差稳压器输出电流能力非常有限以常见的 ESP32 DevKit 为例也就几百毫安而且 3.3V 本来也不在 SG90 的最佳工作电压范围内。如果你直接把 SG90 接到 ESP32 的 3.3V 引脚最常见的现象就是舵机插上后发出“滋滋”的电流声但一动也不动或者空载的时候能转挂上负载就彻底罢工。原因很简单舵机启动瞬间电流能达到几百毫安堵转时甚至可能超过 1A3.3V 电路根本喂不饱它。建议的做法是准备一个独立的 5V 电源USB 5V 电源、锂电池降压模块、两节锂电池串联都是常见选择舵机红线接 5V 正极棕色线接电源负极同时把电源负极和 ESP32 的 GND 接在一起。共地这一步非常关键ESP32 输出的是相对自己 GND 的电压信号如果两边的参考地不统一信号线的高电平在舵机看来就是乱跳的舵机不转或者在错误位置抖动都属于正常。2.2 GPIO 选型不是所有引脚都能接舵机很多朋友习惯看哪个引脚空着就接哪个这在 ESP32 上很容易翻车。ESP32 一共有几十个 GPIO但 34、35、36、39 这四个引脚是纯输入模式内部没有输出驱动电路手里也没法输出 PWM 信号。如果你把信号线接到这几个脚上舵机当然纹丝不动。另外还有几个引脚是“能输出但不建议用”的。GPIO 1 和 GPIO 3 在开发板上默认接 USB-UART 转换芯片下载程序的时候有信号冲突GPIO 6 到 GPIO 11 连接着板载 SPI Flash正常使用时绝对不能碰GPIO 12 在上电瞬间如果被拉高会影响 Flash 的工作电压导致启动失败GPIO 0 是 BOOT 引脚上电时必须是高电平外部电路接不好的话容易出幺蛾子。我给 SG90 选引脚的时候一般用 GPIO 13、GPIO 15、GPIO 2、GPIO 4这几个脚功能干净、没有特殊约束接舵机最省心。如果要同时带多路舵机建议把引脚的“特殊性”列个表优先避坑。引脚能不能接舵机原因GPIO 13 / 15 / 2 / 4推荐功能干净输出能力充足GPIO 12不推荐上电电平影响 Flash 电压可能导致启动失败GPIO 0需谨慎BOOT 引脚启动时需为高电平GPIO 1 / 3不推荐UART 引脚与下载调试冲突GPIO 6-11严禁使用连接 SPI FlashGPIO 34-39不能使用纯输入无法输出 PWM2.3 完整接线清单与检查表我从项目里抽一个典型接线方案出来ESP32 DevKit V1 控制单路 SG90舵机电源用独立的 5V 电源模块。完整清单如下ESP32 开发板 × 1SG90 舵机 × 15V 电源锂电池 降压模块或 USB 供电 稳压模块 × 1公对公杜邦线 × 3如果做多路则按数量增加面包板 × 1方便搭接接线步骤按这个顺序来比较安全先把 5V 电源正极接到 SG90 的红色电源线电源负极接到 SG90 的棕色 GND 线。然后再把电源负极接到 ESP32 的 GND。最后把 SG90 的橙色信号线接到 ESP32 的 GPIO 13。这个顺序可以避免在接地的过程中产生意外的电流路径。上电前我习惯拿万用表量一遍三个关键点的电压电源正负极之间的电压是不是 5V 左右ESP32 的 3V3 引脚对 GND 是不是 3.3V信号引脚对 GND 在上电前应该是 0V。确认这三点没问题再上电能省掉很多排查时间。另外一个小技巧如果你用的是那种小号 SG90舵机摇臂舵盘先不要拧紧等代码能正常控制舵机转到正确角度后再套上摇臂并锁螺丝。如果你一开始就锁死摇臂调试过程中舵机突然偏转可能会卡到机械结构轻则堵转烧电机重则把舵机塑料齿轮打坏。3. 软件实现从库安装到舵机动起来3.1 安装 ESP32Servo 库软件部分其实非常简单如果你已经能在 Arduino IDE 里正常烧录 ESP32 程序库安装就两步菜单栏选“工具”里的“管理库”在库管理器搜索框输入ESP32Servo找到后点安装。这个库有多个维护分支装的时候注意认准作者是 Kevin Harrington 的那一个。库装好之后代码骨架长这样#include ESP32Servo.h Servo myServo; void setup() { // 允许在代码中修改舵机频率 myServo.setPeriodHertz(50); // 标准舵机频率 50Hz myServo.attach(13, 500, 2400); // GPIO13最小脉宽500us最大脉宽2400us } void loop() { myServo.write(0); delay(1000); myServo.write(90); delay(1000); myServo.write(180); delay(1000); }这段代码就是最基础的“舵机从 0 度转到 180 度再回 0 度”的测试程序。程序烧进去之后舵机应该每秒钟跳一个角度。如果一切正常你就已经完成了最核心的控制链路。但如果你直接照抄上面代码发现舵机不动参考下面几个最常见的坑。3.2 attach 方法里那串数字到底什么意思很多新手会用myServo.attach(13)这种最简写法这在标准 Arduino 库下确实可以但在 ESP32Servo 库下我强烈建议显式传三个参数attach(pin, minPulseWidth, maxPulseWidth)。第三个参数不传的时候库会用默认值 544 到 2400 微秒。这个默认值本身问题不大但对于不同批次、不同厂商的 SG90实际可用的脉宽范围差别是挺大的。有的 SG90 在 500us 到 2400us 范围内就能完整走完 0 到 180 度有的则要 550us 到 2500us甚至有些劣质舵机 500us 就已经转过了头导致通电瞬间顶到限位造成堵转。attach里的 500 对应的是 0 度时的脉宽2400 对应 180 度时的脉宽。ESP32Servo 在你调用write(90)时会在 500 到 2400 之间做线性插值算出 90 度对应的脉宽是 1450 微秒。如果你想要更精确的 90 度可以加大最小脉宽或者缩小最大脉宽让映射更贴合你的实际舵机。这里还牵扯到一个隐藏问题write()这个方法在某些库版本里接受的参数是 0 到 180 的“角度”但舵机实际能转的角度范围取决于硬件限位。你调用write(180)之后如果舵机“咔咔”响很可能是它已经转到机械极限位置了还在被驱动这种情况必须马上断电否则轻则丢步重则烧板。3.3 频率不是随便设的setPeriodHertz 的坑setPeriodHertz(50)这行代码很不起眼但如果不写某些版本的 ESP32Servo 默认频率是 50Hz有些版本却是 60Hz还有改过的版本可能在 50 到 240Hz 之间。SG90 这类模拟舵机设计工作频率就是 50Hz也就是周期 20ms。如果你把频率设成 240Hz 甚至 500Hz虽然脉宽微秒数没变但脉冲高电平只占整个周期的很小比例实际等效的平均电压会让舵机内部电路乱套表现就是舵机在一个角度疯狂抖动、乱跑、或者干脆失去控制。高频 PWM 控制的问题在于舵机内部基准电路寻找的是“脉冲宽度”而非“占空比”高频下脉冲的占空比虽然更精准可控但 SG90 这种廉价模拟舵机的核心元器件响应速度根本跟不上反而会出乱子。所以我的建议是代码第一行就写myServo.setPeriodHertz(50)并且在注释里标明“这是标准舵机频率不要随手乱改”。这个习惯养成之后以后换成 MG995、MG996R 这类大舵机也只是改一下供电频率逻辑不用重新想。3.4 多舵机控制与定时器分配ESP32Servo 库宣称支持最多 16 路舵机但实际使用中这个上限不是所有场景都能达到。ESP32 的 LEDC 定时器资源是有限的这个库会为每路舵机分配一个硬件定时器让你连续控制多路的时候不会互相干扰。不过要注意同一个定时器的分辨率会限制 PWM 的精度ESP32Servo 默认设置是 14 位分辨率也就是 0 到 16383 的占空比值。这样精度很高但同时会占用更多的定时器资源。如果只是一路 SG90问题很小但如果你要同时控制 8 路甚至 16 路我建议你在设计阶段就把所使用的 GPIO 列出来确认它们分别挂在哪个定时器上避免后来发现某几个舵机只能同时动、不能单独动。多路代码跟单路代码几乎一样只是要多定义几个 Servo 对象#include ESP32Servo.h Servo servo1; Servo servo2; void setup() { servo1.setPeriodHertz(50); servo2.setPeriodHertz(50); servo1.attach(13, 500, 2400); servo2.attach(14, 500, 2400); } void loop() { servo1.write(0); servo2.write(180); delay(1000); servo1.write(180); servo2.write(0); delay(1000); }这里还有个常见误区你可能会顺手把舵机信号线接到 GPIO 36、39 这类 ADC 引脚上因为它们裸露在板子边缘很顺手。但 ESP32 的 GPIO 36 到 39 和 34、35 一样是纯输入引脚没有 LEDC 输出能力接上去舵机纹丝不动且没有任何报错信息。这种问题是最难排查的因为你查代码、查供电都没问题最后用示波器一量才发现该引脚根本没有波形输出。4. 调试实战学会看波形比看代码更管用4.1 起步调试串口打印你的控制量程序烧录后舵机不动的第一件事不是拆硬件而是先确认你的代码确实在跑、write 确实被执行了。最简单的方式是加串口打印#include ESP32Servo.h Servo myServo; void setup() { Serial.begin(115200); myServo.setPeriodHertz(50); myServo.attach(13, 500, 2400); } void loop() { for (int angle 0; angle 180; angle 10) { myServo.write(angle); Serial.print(Angle: ); Serial.println(angle); delay(500); } }打开 Arduino IDE 自带的串口监视器波特率选 115200。如果能看到Angle: 0、Angle: 10这样的输出说明代码确实在跑问题一定出在硬件链路或者波形输出上。这一步看似简单但能帮你把排查范围缩小一半避免在代码里瞎猜。这里我特别想推荐一个东西你可以把角度换算后的脉宽微秒数也一起打印出来比如Serial.print(Pulse: ); Serial.println(myServo.readMicroseconds());。readMicroseconds()会返回当前实际写入的脉宽和角度值一对比就能发现映射是否异常。比如你写 90 度它读出来却是 500us那说明库内部的映射逻辑出问题了多半是 attach 传参顺序写反了。4.2 进阶调试用逻辑分析仪抓 PWM 波形串口打印只能证明“代码执行了”不能证明“GPIO 真的输出对了波形”。要想彻底确认 PWM 输出是否正常强烈建议用一个逻辑分析仪哪怕是最便宜的 8 通道 24MHz 采样版比如常见的基于 Cypress FX2 的 24MHz 逻辑分析仪或者十几块钱的 USB 逻辑分析仪把信号线夹住在电脑上用 PulseView 或者对应的上位机看波形。很多人一开始觉得逻辑分析仪是高端工具但其实它就像一个“微电脑示波器”可以采集数字信号的高低电平变化非常适合验证舵机这种低频 PWM 信号。接线很简单逻辑分析仪的 GND 夹 ESP32 的 GND另一个通道夹舵机信号线然后运行代码观察波形。正常情况下你会在屏幕上看到一个周期约 20ms、高电平持续时间约 0.5ms 到 2.4ms 的方波。如果周期远远偏离 20ms比如只有 4ms那说明你setPeriodHertz设错了如果脉冲宽度完全不变比如始终是 1.5ms但代码在不同角度之间切换那说明库映射有问题或者引脚选错了如果波形非常杂乱、有很多毛刺就要怀疑供电不稳定、信号线过长或接触不良。测量脉冲宽度非常关键SG90 的 0 度对应 0.5ms 脉宽180 度对应约 2.4ms 脉宽。你用逻辑分析仪上自带的测量工具一量立刻就能判断舵机不动的原因是“信号没给对”还是“舵机本身坏了”。我遇到过好几个案例代码和接线都正常最后确认是舵机内部减速齿轮卡死这种问题看波形才是最直接的。4.3 调试方向从现象反推问题点调试舵机系统有一个很实用的反推思路把现象分三种然后逐一排查。第一种现象舵机完全没反应也没有电流声。这种情况优先怀疑供电量一下舵机电源两端有没有 5V再量一下信号线在运行代码时有没有 3.3V 左右的高电平。如果信号线有电压但不规律可能是 GPIO 选错了换成推荐引脚再试。第二种现象舵机有电流声但不转或者轻微抖动但无法到位。这种大概率是供电不足或者脉宽范围不对。SG90 空载启动时需要 300mA 甚至更高的峰值电流电流不足时它只能维持一个“想动却动不了”的状态。换独立 5V 电源、把电源线尽量缩短通常能解决。第三种现象舵机能转但角度不对比如让它转 90 度实际只转了 60 度或者转到 180 度时舵机顶死咔咔响。这种是脉宽映射不匹配造成的。你需要用逻辑分析仪量一下它转 90 度时实际输出的脉宽然后根据真实角度微调 attach 里的最小和最大脉宽。4.4 常见问题速查表现象大概率原因排查方法舵机完全不动无任何反应供电没接通 / GPIO 接错万用表量电源换推荐引脚舵机有电流声轻微抖动供电不足用独立 5V 电源检查电压电流舵机上电瞬间猛转一下库在 attach 时默认输出一个脉宽在 attach 后立即 write 到安全角度舵机转到位后咔咔响脉宽范围超过机械限位调小 maxPulseWidth比如从 2400 降到 2200舵机角度偏小或偏大脉宽映射不匹配用逻辑分析仪测实际脉宽后重新标定多路舵机互相干扰定时器或通道冲突确认每个 Servo 对象引脚不同重刷固件上电后程序卡住不跑GPIO 12 被拉低/拉高避开特殊功能引脚4.5 调试工具选型串口助手、逻辑分析仪与波形可视化调试这个项目的时候我用到了三类工具这里一并说清楚各自适合什么场景。第一类是串口调试工具。Arduino IDE 自带的串口监视器基本够用但如果你想在调试时同步看角度变化曲线建议用开源上位机 Vofa 或者 SerialPlot。这类工具可以把串口输出的数字流直接画成波形比如让舵机做正弦摆动你就能看到实际脉宽是不是也走了一条平滑的正弦曲线。如果曲线出现跳变就说明代码写出的角度本身有异常可以快速定位。第二类是逻辑分析仪前面已经详述过了。重点是注意采样率24MHz 采样率对 50Hz 的 PWM 绰绰有余不要追求超高采样率贵且没必要。第三类是普通的万用表。很多人会忽略万用表在舵机调试里的作用但它恰恰是最便宜的“安全检测仪”。上电前量一下电源正负有没有短路上电后量一下舵机电源电压有没有被拉低这两步能避免大量因为误接线导致的烧板问题。5. 避坑清单与实战心得5.1 供电系统舵机多起来时别硬扛如果只是单路 SG90一个 5V 电源模块搞定没问题。但如果你带 4 个、8 个 SG90或者以后升级到 MG996R 这类大舵机供电就完全不是“一个稳压模块”能搞定的了。大舵机堵转电流能飙到 2A 以上这时候必须用电池或者独立的开关电源并且每个舵机最好有独立的 100uf 到 470uf 电解电容去耦。我在实际项目里给 4 路 SG90 供电时用的是一块 2S 锂电池降压到 5V 的模块但从电池到舵机这一段的导线太细的话启动瞬间压降依然很大。解决办法是在舵机电源两端并联一个大电容容量至少 470uF并且把线径换成 AWG22 左右。如果你发现舵机跟 Wi-Fi 同时工作时有干扰多半是电源纹波引起的加电容、缩短线长能改善很多。5.2 初始化角度上电瞬间的“九转”问题很多人遇到过的“舵机上电瞬间直接转到某个位置然后才回到指定角度”的问题原因是 ESP32Servo 的attach()执行时就会立刻输出一个脉宽这个脉宽可能是库内部默认值也可能是上一次断电前残留的 LEDC 寄存器值于是舵机在上电启动的瞬间就被拉到了一个非预期的角度。要解决这个问题最实用的做法是在attach()之后立即调用write()设置一个安全角度void setup() { myServo.setPeriodHertz(50); myServo.attach(13, 500, 2400); myServo.write(90); // 先转到中间角度避免初始乱跳 delay(100); }另外如果舵机连接的是机械臂这类结构上电瞬间的乱转会撞到限位非常容易损坏齿轮或结构件。对于这种场景建议在舵机供电回路上加一个 MOSFET 开关或者延时继电器让 ESP32 先完成 GPIO 初始化和角度赋值再接通舵机的电源。这个“先上逻辑、后上动力”的思路在很多机器人项目里都是标配。5.3 方向与控制死区角度反向和微调标定调试过程中你可能会发现代码里写 90 度舵机却是反方向偏的。这不是坏了是安装方向的问题。比如舵机摇臂朝左你写 0 度它就朝左走但你的机械结构要求 0 度朝右这时候很多人会去改接线其实完全没必要。最简单的方案是对角度做反转映射int mappedAngle map(angle, 0, 180, 180, 0); myServo.write(mappedAngle);这个 180 度映射只是最粗的方向修正。更精细的标定是检测舵机的“死区”也就是“写小角度时不动、写大角度时突然跳一下”的区域。SG90 本身存在约 5 到 10 微秒的脉宽死区比如你连续从 1480us 增加到 1490us舵机可能毫无反应但加到 1500us 时又瞬间跳了一格。这种不连续现象是模拟舵机的物理特性不是代码 bug。如果项目要求非常精确的角度定位建议换数字舵机或者在软件里对脉宽做平滑插值而不是直接写目标角度。5.4 经验总结每次做舵机项目的固定排查顺序踩了几次坑之后我现在每次做舵机项目都按同一个顺序排查能省很多无头苍蝇式的时间。第一步上电前拿万用表量一遍电源。确认舵机电源正负极之间 5V 正常确认 ESP32 的 GND 和舵机电源 GND 已连通。这一步五分钟能规避 80% 的不动问题。第二步烧录程序后看串口输出。确认代码真的在跑、角度在变化。第三步用逻辑分析仪量信号波形。确认脉宽和周期是否符合预期。第四步才用舵机本身做空载测试。把摇臂拆下来用手轻轻拨动舵机输出轴看看是否有阻力、齿轮有没有卡死。如果舵机空载也转不动那就是舵机硬件问题别在代码上浪费时间。这套流程说出来很简单但我见过太多人在第三步之前就反复改代码、换舵机最后发现是电源线虚焊的问题。调试的本质是“每次只改变一个变量”而变量的优先级应该从硬件到软件从电源到信号从信号到机械。5.5 一个小技巧把舵机标定参数集中管理项目到最后如果你要同时控制多个舵机会出现一堆attach(pin, min, max)参数散落在代码里。我现在的习惯是在代码开头单独定义一个配置文件把每个舵机的引脚、最小脉宽、最大脉宽、初始角度全部集中放好// servo_config.h #define SERVO1_PIN 13 #define SERVO1_MIN_US 500 #define SERVO1_MAX_US 2400 #define SERVO1_INIT_ANG 90这样做的好处是换舵机批次后只需要改一处参数不用满代码里找数字。SG90 的个体差异其实挺大的这一批舵机用 500 到 2400 很好下一批就可能要 480 到 2520集中管理后调试效率明显高很多。这个习惯也是我调试系统里最值得保留的一个做法。当你从“控制一个舵机”走向“控制一台机器人”时参数管理迟早会成为瓶颈不如刚开始就留好接口。后面你想给这个项目再加角度平滑过渡、串口遥控、Wi-Fi 网页控制这些配置代码都能直接复用不用推倒重来。
