简介这份资源面向伺服控制、电机驱动及自动化方向的工程师与爱好者提供EP100伺服系统的完整工程资料帮助读者从电路原理到嵌入式代码全面理解伺服系统的设计与实现。压缩包共352个文件约41.9MB涵盖原理图与PCB设计文档、C语言源码与头文件、Keil工程配置、编译中间文件及说明书PDF等其中原理图展示驱动板、控制板与显示板的电源管理、信号处理、电机驱动和反馈电路布局PCB工程文件可用于实际制板C代码则包含PID控制器、电机控制算法、通信协议与故障保护机制等核心逻辑。已有1275人学习下载适合希望掌握硬件设计、软件编程与系统集成全流程的读者也可作为伺服控制、机器人技术等领域的实践参考。1. EP100 伺服系统全套工程包从原理图到 C 代码的完整闭环如果你正在找一套能直接上手拆解的伺服驱动方案EP100 这套资源大概率能满足你。它不是零散的 demo而是把原理图、PCB 和源代码打包在一起的完整工程。我拿到手第一反应是终于不用在某个论坛翻十几页找配套文件了。伺服驱动本身涉及功率变换、电流采样、位置环控制、通信协议单看某一层都容易卡住而这套东西把硬件和软件放在同一个目录里你可以对着原理图去源码里找对应的外设初始化也能反过来从代码里的 PWM 配置去反推功率级的驱动逻辑。适合谁做电机控制的嵌入式工程师、想从方波驱动过渡到 FOC 的开发者以及需要一套参考设计来改自己板子的硬件同学。下面按「能干什么 → 怎么跑 → 怎么改」的顺序拆。2. 硬件与软件架构EP100 的模块划分和信号链路2.1 从原理图看功率级与控制级的边界打开原理图第一件事不是看芯片型号而是找隔离带。EP100 的硬件设计里功率级和控制级分得很清楚强电侧是整流、母线电容、IPM 或分立 MOS 构成的逆变桥弱电侧是主控 MCU、电流采样运放、编码器接口和通信收发器。常见做法是用光耦或数字隔离器把 PWM 信号和故障反馈隔开EP100 的原理图里也能看到类似的隔离器件。你拿到图之后先沿着「MCU PWM 输出 → 隔离 → 栅极驱动 → 逆变桥 → 电机」这条链路走一遍再沿着「电流采样 → 运放调理 → ADC 引脚」走一遍基本就能把功率板和控制板的接口对上。这里有个容易忽略的点母线电压采样和相电流采样的分压比、放大倍数直接决定了源代码里 ADC 换算的系数。如果你只改硬件不改代码或者只改代码不看硬件电流环大概率会震荡或者直接报过流。我一般会先把原理图里的采样电阻值、运放增益记下来再去源码里找对应的宏定义两边对不上就先停下来查。2.2 源代码目录结构与主控外设映射EP100 的源代码通常是按功能分目录的常见结构如下EP100_Servo/ ├── Drivers/ │ ├── MCU/ # 主控外设驱动PWM、ADC、TIM、UART │ └── Board/ # 板级支持GPIO 映射、LED、继电器 ├── Middlewares/ │ ├── FOC/ # 磁场定向控制Clarke、Park、SVPWM │ └── Encoder/ # 编码器解码增量式或绝对值 ├── App/ │ ├── Control/ # 电流环、速度环、位置环 │ ├── Comm/ # 通信协议Modbus、CANopen 或自定义 │ └── Protect/ # 过流、过压、欠压、过热保护 └── Main/ └── main.c # 初始化与主循环这个结构不是固定的但 EP100 的代码基本能按这个思路去拆。你拿到源码后先看Drivers/MCU里的 PWM 初始化确认载波频率和死区时间再看Middlewares/FOC里的 SVPWM 实现确认扇区判断和占空比计算最后看App/Control里的 PID 参数确认电流环和速度环的带宽。这三步走完整个系统的控制链路就清楚了。2.3 关键参数表硬件与代码的对应关系硬件参数原理图位置代码对应常见值PWM 载波频率主控 TIM 配置PWM_FREQ宏10kHz~20kHz死区时间栅极驱动电路DEAD_TIME宏0.5us~2us电流采样电阻逆变桥下桥臂SHUNT_RES宏5mΩ~50mΩ运放增益电流调理电路AMP_GAIN宏5~20 倍编码器线数编码器接口ENCODER_LINES宏2500 线母线电压分压比母线采样电路VDC_DIV宏100:1~200:1这张表建议你拿到资源后自己填一遍填不出来的地方就是你需要重点看的地方。很多调试翻车不是因为算法难而是因为采样系数写错了电流环一跑就飞。3. 编译与烧录把 EP100 源码跑起来的完整步骤3.1 开发环境搭建与工程导入EP100 的源码大概率是基于 Keil MDK 或者 IAR 的工程也有可能是 STM32CubeIDE 或 Makefile 工程。先看根目录有没有.uvprojx、.eww或者Makefile。如果是 Keil 工程直接双击打开检查一下器件型号和下载器配置。常见坑是工程里选的芯片型号和你手上的板子不一致比如工程选的是 STM32F103VET6你板子上是 STM32F103C8T6编译能过但烧录后外设映射全错。我一般会先做三件事第一确认工程里的器件型号和实际板子一致第二确认下载器配置ST-Link 还是 J-Link和复位方式第三确认编译输出的 hex 或 bin 文件大小没有超出 Flash 容量。这三步做完再点编译能省掉很多「编译过了但跑不起来」的玄学问题。# 如果工程是 Makefile 结构先清理再编译 make clean make -j4 # 查看生成的固件大小 arm-none-eabi-size build/EP100_Servo.elf上面这段命令适用于 Makefile 工程。make clean清掉旧的目标文件make -j4用四核并行编译加快速度最后用arm-none-eabi-size看 text、data、bss 段的大小。如果 text 段接近 Flash 容量上限就要考虑裁剪功能或者换更大 Flash 的芯片。3.2 烧录与上电前的硬件检查烧录之前先别急着插电机。我习惯先用万用表量一下母线电容两端的电阻确认没有短路再量一下 IPM 或 MOS 的栅极和源极之间有没有击穿最后确认编码器接口的电源和地没有接反。这些检查花不了两分钟但能避免上电瞬间炸管。烧录时如果用的是 ST-Link注意接线SWDIO、SWCLK、GND、3.3V 四根线就够了但有些板子需要把 BOOT0 拉低才能从 Flash 启动。烧录完成后先不接电机上电看主控能不能正常跑起来——LED 闪烁、串口有输出、示波器能看到 PWM 波形这三样至少要有一样。// main.c 里常见的初始化顺序 int main(void) { HAL_Init(); // HAL 库初始化 SystemClock_Config(); // 时钟树配置确认主频 MX_GPIO_Init(); // GPIO 初始化 MX_ADC_Init(); // ADC 初始化校准 MX_TIM_PWM_Init(); // PWM 初始化设置载波和死区 MX_ENCODER_Init(); // 编码器接口初始化 MX_UART_Init(); // 通信初始化 FOC_Init(); // FOC 参数初始化 while (1) { FOC_Loop(); // 主控制循环 Comm_Handler(); // 通信处理 Protect_Check(); // 保护检测 } }这段初始化顺序是有讲究的时钟配置必须在最前面否则后面外设的时钟频率全错ADC 校准要在 PWM 启动之前做否则开关噪声会干扰校准结果FOC 初始化要在所有外设就绪之后因为它依赖 ADC 和编码器的数据。如果你把FOC_Init()放到MX_ADC_Init()前面大概率会读到全零的电流值。3.3 首次上电与电机参数识别第一次接电机建议先用手转一下电机轴看编码器读数有没有变化。如果编码器读数不变先查编码器接线和供电如果读数跳变查一下编码器线数和代码里的ENCODER_LINES是否一致。确认编码器正常后再给一个很小的电流指令看电机能不能轻微转动。这一步是为了验证电流环的方向和增益如果电机抖动或者飞车立刻断电检查相序和电流采样极性。常见做法是先做电阻和电感辨识再做编码器零位校准。EP100 的代码里如果有Motor_Identify()之类的函数直接调用如果没有就手动给 d 轴电流、q 轴置零让电机锁定在一个位置然后读编码器值作为零位。这个过程可能需要反复几次直到零位稳定。4. 核心代码拆解FOC 电流环与 SVPWM 实现4.1 Clarke 与 Park 变换的代码落地FOC 的核心是把三相电流变成两个直流量d 轴和 q 轴。EP100 的代码里通常会有Clarke()和Park()两个函数。Clarke 变换把 ia、ib、ic 变成 iα、iβPark 变换再根据电角度把 iα、iβ 变成 id、iq。// Clarke 变换三相转两相 void Clarke(float ia, float ib, float ic, float *ialpha, float *ibeta) { // 假设 ia ib ic 0可以简化计算 *ialpha ia; *ibeta (ia 2.0f * ib) * ONE_BY_SQRT3; // 1/√3 } // Park 变换静止坐标系转旋转坐标系 void Park(float ialpha, float ibeta, float theta, float *id, float *iq) { float sin_t sinf(theta); float cos_t cosf(theta); *id ialpha * cos_t ibeta * sin_t; *iq -ialpha * sin_t ibeta * cos_t; }ONE_BY_SQRT3是 1/√3 的宏定义约等于 0.57735。Clarke 变换里用到了三相电流和为零的假设如果采样有偏差这个假设不成立iα 和 iβ 会有误差。Park 变换里的theta是电角度不是机械角度电角度 机械角度 × 极对数。如果你把机械角度直接传进去电机转起来会一顿一顿的。4.2 SVPWM 扇区判断与占空比计算SVPWM 的目标是根据 ud、uq 算出三个半桥的占空比。EP100 的代码里一般会先算合成电压矢量的幅值和角度再判断扇区最后算作用时间。// SVPWM 扇区判断与占空比计算 void SVPWM(float ud, float uq, float theta, float *duty_a, float *duty_b, float *duty_c) { // 反 Park 变换得到 alpha-beta 电压 float ualpha ud * cosf(theta) - uq * sinf(theta); float ubeta ud * sinf(theta) uq * cosf(theta); // 扇区判断 int sector 0; if (ubeta 0) sector | 0x01; if ((ubeta * ONE_BY_SQRT3 ualpha * 0.5f) 0) sector | 0x02; if ((-ubeta * ONE_BY_SQRT3 ualpha * 0.5f) 0) sector | 0x04; // 根据扇区计算基本矢量作用时间 float t1, t2; switch (sector) { case 1: t1 ubeta; t2 ualpha * 0.5f - ubeta * ONE_BY_SQRT3; break; case 2: t1 ualpha * 0.5f ubeta * ONE_BY_SQRT3; t2 -ubeta; break; // ... 其他扇区 } // 计算占空比注意死区补偿 *duty_a (t1 t2) * 0.5f; *duty_b (*duty_a) - t1; *duty_c (*duty_b) - t2; }扇区判断用的是三个条件每个条件对应一个二进制位组合起来就是 1 到 6 六个扇区。t1和t2是两个相邻基本矢量的作用时间算完之后再分配给三个半桥。这里有个细节占空比计算完之后要做限幅防止超过 PWM 周期。另外死区补偿也很关键死区时间设得太大低速时电流波形会畸变设得太小上下桥臂可能直通。4.3 电流环 PID 的参数整定与限幅电流环是 FOC 里响应最快的环通常放在 PWM 中断里执行频率和载波频率一致。EP100 的代码里电流环 PID 一般长这样// 电流环 PID 控制器 typedef struct { float kp; float ki; float integral; float out_max; float out_min; } PID_Handle; float PID_Calc(PID_Handle *pid, float error) { // 比例项 float p_term pid-kp * error; // 积分项带限幅 pid-integral pid-ki * error; if (pid-integral pid-out_max) pid-integral pid-out_max; if (pid-integral pid-out_min) pid-integral pid-out_min; // 输出限幅 float output p_term pid-integral; if (output pid-out_max) output pid-out_max; if (output pid-out_min) output pid-out_min; return output; }kp决定响应速度ki决定消除静差的速度。整定时先把ki置零慢慢加kp直到电流波形开始震荡然后退回一点再加ki直到静差消除但超调不大。out_max和out_min是输出限幅一般对应最大电压矢量幅值。积分限幅和输出限幅都要做否则积分饱和后恢复很慢电机响应会拖泥带水。5. 避坑与排查EP100 调试中最容易翻车的五个点5.1 上电就过流保护现象一上电还没给指令过流保护就触发或者母线电流直接冲上去。原因最常见的是电流采样极性反了或者 ADC 偏移没校准。也有可能是 PWM 死区太小上下桥臂直通。解决先不接电机用示波器看三相下桥臂的电流采样波形确认静态时是零附近。如果偏了重新校准 ADC 偏移。然后量一下 PWM 死区时间确保大于栅极驱动的最小死区要求。最后检查相序如果电机线接反了电流环会正反馈。5.2 电机抖动不转或者飞车现象给指令后电机抖动或者直接飞车不受控。原因编码器方向反了或者电角度零位不对。也有可能是极对数设错。解决先用手转电机看编码器读数是增加还是减少和代码里的方向定义对比。如果反了在代码里取反或者调换编码器 A/B 相。然后做零位校准给 d 轴电流锁定电机读编码器值作为零位。极对数在电机铭牌上有没有的话用示波器看反电动势波形数一个电周期里的机械周期数。5.3 电流波形畸变严重现象示波器看相电流波形不是正弦有明显的平顶或者毛刺。原因死区时间太大或者电流采样时刻不对。也有可能是 SVPWM 的扇区判断有误。解决先减小死区时间看波形有没有改善。然后检查 ADC 采样时刻常见做法是在 PWM 中心对齐模式下在计数器为零的时候触发 ADC 采样这时候开关噪声最小。如果扇区判断错了电流波形会周期性畸变检查扇区判断的三个条件有没有写反。5.4 通信丢包或者响应慢现象上位机发指令驱动器偶尔不响应或者响应延迟很大。原因通信中断优先级太低被 PWM 中断或者电流环中断打断。也有可能是串口波特率不匹配。解决把通信中断优先级设到比 PWM 中断低但比主循环高。如果用的是 DMA 收发检查 DMA 配置有没有冲突。波特率不匹配的话用示波器量一下串口波形算一下实际波特率。5.5 编译通过但烧录后没反应现象编译零错误零警告烧录也成功但板子没反应。原因启动文件选错了或者时钟配置不对。也有可能是 Flash 等待周期没设对。解决先确认启动文件是startup_stm32f103xe.s还是对应你芯片型号的。然后检查SystemClock_Config()里的晶振频率和实际板子上的晶振一致。如果主频跑得高Flash 等待周期要相应增加否则取指会出错。6. 二次开发从 EP100 改出你自己的伺服方案6.1 硬件改造替换功率级和采样电路EP100 的功率级如果是 IPM你想换成分立 MOS原理图上的栅极驱动电路要重画。常见做法是用半桥驱动芯片比如 IR2104 或者 DRV8301注意自举电容和死区电阻的取值。电流采样如果从单电阻改成三电阻代码里的采样时刻和重构算法都要改。三电阻采样可以在 PWM 的任意时刻采但单电阻只能在特定时刻采重构算法复杂得多。我一般会先把采样电路改好用示波器确认三路电流波形干净再去改代码。硬件没调好就改代码等于在沙子上盖楼。6.2 软件改造增加新的通信协议EP100 如果用的是自定义串口协议你想改成 Modbus RTU 或者 CANopen主要改App/Comm目录。Modbus RTU 的帧格式是地址 功能码 数据 CRCCANopen 是 COB-ID 数据。改的时候注意中断优先级和缓冲区大小通信数据量大的话DMA 加环形缓冲区是标配。// Modbus RTU 解析示例 typedef struct { uint8_t addr; uint8_t func; uint16_t reg; uint16_t len; uint8_t data[64]; } Modbus_Frame; void Modbus_Parse(uint8_t *buf, uint16_t len) { Modbus_Frame frame; frame.addr buf[0]; frame.func buf[1]; frame.reg (buf[2] 8) | buf[3]; frame.len (buf[4] 8) | buf[5]; // CRC 校验 uint16_t crc Modbus_CRC16(buf, len - 2); if (crc ! ((buf[len-2] 8) | buf[len-1])) return; // 根据功能码处理 switch (frame.func) { case 0x03: Modbus_ReadReg(frame.reg, frame.len); break; case 0x06: Modbus_WriteReg(frame.reg, frame.data); break; default: break; } }Modbus_CRC16是 CRC 校验函数Modbus RTU 用的是 CRC-16/IBM多项式是 0xA001。功能码 0x03 是读保持寄存器0x06 是写单个寄存器。解析的时候先校验地址和 CRC再处理功能码。如果通信数据量大建议用 DMA 接收然后在空闲中断里解析避免在串口中断里做耗时操作。6.3 控制算法升级从 PI 到自抗扰或者模型预测EP100 的电流环和速度环大概率是 PI 控制。如果你想升级到自抗扰控制ADRC或者模型预测控制MPC主要改App/Control里的控制器实现。ADRC 的核心是扩张状态观测器把内部扰动和外部扰动一起估计出来补偿掉。MPC 则是用电机模型预测下一时刻的电流选一个最优电压矢量。我试过在 EP100 的框架上把速度环换成 ADRC低速时的抗扰动能力确实比 PI 好但参数整定比 PI 麻烦得多。如果你只是想快速出活PI 加前馈够用了如果想发论文或者做高端应用再考虑 ADRC 或 MPC。6.4 验证方法用示波器和上位机看波形改完之后怎么验证我习惯用三样东西示波器看相电流和 PWM 波形上位机看转速和电流的阶跃响应温度枪看功率级的温升。相电流波形要正弦、平滑、无毛刺阶跃响应要超调小、恢复快温升在额定负载下不超过器件规格的 80%。从那以后我每次改完控制参数都强制走一遍「空载 → 轻载 → 额定负载」的阶梯测试确认每个负载点都稳定再往下走。希望帮到你。本文还有配套的精品资源点击获取