
电赛控制组这个方向四年带下来不少学弟学妹我的第一句话永远是别急着焊板子、写代码先花一个晚上把这条路线的全貌搞清楚。控制组的题目看起来五花八门——风力摆、板球系统、电磁循迹、送药小车、倒立摆……但本质上都是在做同一件事让一个物理系统按照你期望的方式运动起来。而做到这件事的能力栈拆开看其实非常固定一路是电路板硬件负责供电、感知、驱动另一路是控制算法尤其是PID算法负责根据传感器的数据做决策、给执行机构下指令。两路缺一不可而且每一路都能让一个入门者踩到想摔板子的坑。这篇东西不是教科书是我从大一被电烙铁烫出水泡到后来能独立带队做完整控制题的一条成长路径记录。适合谁看准备参加电赛控制组的大一大二学生、想系统入坑嵌入式控制的自学者以及那些能调通硬件但一提到PID就犯怵的同学。我把从电路板到PID的关键节点、选型逻辑、代码结构和排障方法都摊开来讲希望能帮你在比赛前少走几个月弯路。1. 控制组的完整学习路线先建框架再填细节1.1 控制组到底在做什么先明确一个事情控制组不等于纯软件组也不等于纯硬件组。电赛的控制类题目通常给你一个刚性物理平台——可能是一个摆杆、一块圆盘、一辆小车、一个双轴云台——然后要求你在规定时间内让它做到规定动作。比如风力摆要按设定轨迹摆动板球系统要控制小球停在指定位置送药小车要循迹、识别药房、停车开门。这些题目听上去各不一样但底层能力的交集极其稳定传感器感知 主控决策 执行机构驱动。换句话说你需要一套完整的硬件平台——MPU6050或者编码器告诉你现在系统处于什么状态STM32主控根据状态偏差计算下一步怎么修正电机或者舵机在修正指令下动作。这套闭环就是控制组所有题目的最小单位。所以我的建议非常明确不管今年题目是什么你训练的方向都应该是“把一套通用闭环做得又快又稳”。具体来说就是掌握一块熟悉的驱动板设计、几个常用传感器的数据读取、一路稳定的电机驱动以及一套用得滚瓜烂熟的PID代码框架。有了这套底子任何题目都是往这个框架里填参数和调结构而已。1.2 从零到实战的六个阶段根据我自己的经历也根据我带过的队员的反馈这条路分成六个阶段最合理每个阶段大约两到三周赛前至少留出三到四周做综合调试。第一个阶段是工具和软件。安装立创EDA或者Altium Designer搭好Keil或者STM32CubeIDE的开发环境了解串口、下载器、示波器、万用表的基本用法。这个阶段不求深入但必须把工具链跑通不然后面全是障碍。第二个阶段是最小系统到自制底板。先在一块现成的最小系统板上跑通点灯和串口打印然后把LED、按键、OLED、编码器接口这些外设集中画到一块自己的底板上焊接并调试板子。这一步的核心是真正理解电路板的原理图、PCB布局和电源设计而不是只会在面包板上插线。第三个阶段是传感器和电机驱动。接上编码器电机用定时器读取转速接上MPU6050读取角度和角速度用TB6612或者DRV8833驱动电机实现正反转和PWM调速。信号从传感器到主控这条路是控制组最容易被小问题卡住的地方。第四个阶段是PID算法跑通。先在小车上做一个速度环或者位置环用串口或者OLED把目标值、实际值、输出量三路数据实时打印出来观察PID的收敛和振荡过程。这个阶段要求你完全理解位置式PID和增量式PID的代码实现而不是只会抄库函数。第五个阶段是综合项目整合。把一个完整的控制题当成训练目标比如把板球系统或风力摆做出来把姿态解算、PID控制、显示、按键操作全部整合到一个工程里。这一步的核心是学会模块化编程和“系统联调”的节奏。第六个阶段是赛前冲刺和限时演练。按照电赛四天三夜的节奏给自己限定时间完成一个模拟题目训练快速出方案、快速搭硬件、快速调参的能力同时整理一套自己的模块代码库。这六个阶段看起来多但如果你每天能坚持两三个小时三个多月基本可以走完。关键是不要跳步——尤其是第三个阶段很多人急于写PID结果编码器数据都是错的后面全是在幻觉上做控制怎么调都不可能稳。2. 电路板篇从EDA原理图到能跑起来的板子2.1 EDA软件选型别纠结先能用起来很多同学一上来就问“电赛控制组用AD还是立创EDA”我的回答是看你的目标。如果将来想走硬件工程师路线Altium Designer在工程化、团队协作和信号完整性分析上确实更专业但它的学习曲线也更陡如果只是想快速把比赛需要的板子画出来立创EDA几乎零门槛元件库全和嘉立创打样配合起来甚至能做到“今天画完明天打板后天到手”。我个人的建议大一大二学生直接从立创EDA入门先把原理图和PCB的整个流程跑通等你画过两三块板子之后再决定要不要切换到AD。很多控制组的板子复杂度并不高——主要是主控、电源、几个接口插座、电机驱动、传感器接口立创EDA完全能胜任。另外一个容易被忽略的点EDA画的板子最终要焊。所以原理图设计时要多想一步——这个元件的封装能不能手工焊接0402的电阻虽然体积小但手工焊很容易连锡控制组打样一般选0805或者0603。芯片尽量选有引脚的而不是QFN底部焊盘除非你有热风枪和熟练的拆焊技术。2.2 原理图与PCB设计的几个关键点原理图设计里最重要的不是把芯片引脚连上就完事而是把电源树梳理清楚。一个典型的控制组系统是这样的锂电池提供7.4V或者11.1V经过DCDC降压到5V给电机驱动模块、传感器等供电再经过LDO降到3.3V给MCU供电。每一路电源都要考虑负载电流7.4V直接给电机端的话电机堵转瞬间可能抽走一两安培如果你把MCU和电机挂在同一路电源上主板分分钟死机重启。我在实际设计中常用的是“电源分区”的原则电机电源、逻辑电源、传感器电源在PCB上分区域走线每个区域的地在单点汇合。大电流走线加宽至少要满足1A对应1mm宽的经验值铜箔1oz的情况下并且尽量用铺铜而不是细线。去耦电容是另一个新手容易翻车的地方。每个芯片的电源引脚旁边要放0.1uF陶瓷电容MCU主电源再并联一个10uF或100uF的电解电容。很多同学板子画好了程序也烧进去了但系统一跑就复位排查到最后发现是去耦电容没放电机一启动瞬间电流波动直接把MCU电压拉到低阈值以下。PCB布局上我的习惯是把电机驱动放在板子边缘远离主控和晶振编码器、传感器接口放在另一侧走线尽量短电源入口放保险丝和防反接二极管这个习惯能救你无数次——比赛现场接错电源正负极太常见了。2.3 焊接、拆件与硬件维修动手能力的底层板子画好打样回来接下来就是焊接和调试。这一步也是很多人第一次真正碰电路板操作习惯直接决定后面几天的体验。我的建议是工具不要省但也不用买一堆贵的。电烙铁用恒温的温度调到350度左右助焊剂用笔式的吸锡器准备一个再加上一卷吸锡带。这里顺便回应一个很多人搜索过的问题“拆电路板引脚的针头叫什么工具”其实分几种情况如果你想把通孔引脚上的锡吸走用的是带活塞的吸锡器或者更精细一点的吸锡带两者配合加热后的电烙铁把焊锡吸走就能松开引脚如果是拆多脚芯片光靠吸锡器效率太低通常要上热风焊台先加热整个区域再快速取下。拆板是个熟能生巧的活但每次动手前记住一件事先断电板上大电容要放电不然一颗1000uF电容残余的电荷能让你触电也能打坏芯片。焊接完板子不要急着接电机先做通电前检查。万用表打在蜂鸣档测电源正负极是否短路再测各电源输出点的电压是否正常最后再看每个模块的使能引脚电平是否符合预期。这一套下来虽然花不了几分钟但能过滤掉大部分低级错误。板子通电后如果发现问题排查思路也很固定先用万用表对地量电压确定是哪一路断了再用示波器看信号波形确定是电源纹波、信号干扰还是时序问题最后用“分段断开法”——把负载逐个断开看现象是否消失缩小故障范围。这个电路板维修的套路网上教程很多但核心就三句话先电源后信号、先静态后动态、先整体后局部。只要你有条理地排查绝大多数板子问题都能在半小时内定位。最怕的就是“怀疑是这里就乱拆乱换”那只会把一个小问题变成一场灾难。3. 从电路板到能动的机械主控、传感器与电机驱动3.1 主控选型与外设规划说句实在话电赛控制组99%的主控是STM32系列。最常见的配置是STM32F103C8T6或者F407系列前者便宜好用、资料多、足够应付大多数控制题后者算力更强适合需要跑复杂算法或图像处理的情况。我的建议是如果之前没接触过从STM32F103C8T6开始淘宝一块最小系统板不到十块钱烧录一个LED点灯程序你就基本入门了。外设规划方面控制组必须熟练使用的有GPIO按键、LED、使能信号、定时器PWM输出、编码器模式、输入捕获、ADC电位器、电流采样、USART串口调试、I2C/SPIMPU6050、OLED等传感器和显示设备。其中定时器是重点中的重点——因为电机控制需要PWM编码器反馈需要编码器模式而这两者往往同时被一个定时器资源占用规划不好就会出现引脚冲突。我见过不少同学写程序写到后来发现引脚不够用、定时器冲突最后只能飞线改板。所以画板之前就一定把主控的引脚功能分配表列出来把PWM通道、编码器通道、I2C、串口这些功能提前分配好避免设计到一半才返工。这也是第二章说的“原理图多想一步”的具体体现。3.2 编码器、陀螺仪与信号处理传感器是闭环控制的眼睛如果你对“系统的当前状态”都不知道PID调得再勤奋也没用。控制组最常用的传感器就几类光电编码器测电机转速、MPU6050测角度和角速度、灰度传感器或电磁传感器循迹用、超声波或激光测距模块测位置以及一些电流、电压采样。编码器这块我强调一点不是所有编码器接上去都能直接用。常见的有AB相正交编码器输出两路相位差90度的方波STM32的定时器自带编码器模式只需要配置一下就能在硬件上完成计数不需要外部中断。但编码器如果是开集输出必须加上拉电阻不然电平在临界状态来回跳读出的速度会像抽风一样不稳定。这个坑我当年踩了整整一个晚上才查出来。拿直流减速电机来说读取编码器数据的最终目的是算转速。常用的方法是开启定时器编码器模式在固定时间窗口比如每10ms或20ms读取计数差值然后除以时间得到速度。采样周期不能太短太短会导致低速时一个窗口都采不到几个脉冲速度测量量化误差太大也不能太长太长会让控制响应变慢。一般20ms是一个比较平衡的默认值。MPU6050那边的核心是姿态角的获取。大多数资料会推荐直接用DMP库读出四元数转换成欧拉角确实省事但我的建议是至少理解一下为什么加速度计可以测倾角利用重力分量为什么陀螺仪需要积分但会漂移为什么两者要互补或融合。控制组里常见的处理方式是互补滤波或者直接用DMP在这个基础上再做PID会稳很多。3.3 电机驱动执行机构的最后一环传感器负责“感知”主控负责“决策”电机驱动负责“执行”。控制组的执行机构以直流减速电机、步进电机、舵机、无刷电机云台用为主其中最常出现的是直流减速电机。直流电机的驱动电路核心是H桥。全桥四个开关管控制电机两端的电压极性和占空比就能实现正反转和调速。市面上的驱动模块非常成熟TB6612、DRV8833都是很常用的选择前者驱动电流大一点用在小型车模上很稳后者体积更小。不建议新手第一版就自己搭分立MOSFET的H桥电机驱动这个大电流回路里有太多坑——MOSFET驱动电压不够、续流二极管压降太大、死区时间没处理好随便一个都能把板子烧了。先买模块跑通系统再回头研究原理是控制组更务实的路径。PWM频率的选择也有讲究。我实测下来直流减速电机用10kHz到20kHz比较合适。频率太低电机会有可听见的啸叫和明显的转矩脉动频率太高MOSFET的开关损耗变大驱动模块发热严重。调整PWM占空比就相当于调整电机的平均电压占空比从0到100%之间电机转速会呈现出近似线性的关系这为PID控制器的输出提供了直接换算的物理量——这句话很关键控制器的输出最终就是PWM占空比。电机电源和逻辑电源的处理我前面强调过这里再补一个细节电机两端要并联续流二极管或者使用驱动模块自带的续流保护不然电机在换向瞬间产生的反电动势会通过地线干扰单片机严重时直接烧掉IO口。这是硬件层面最容易忽略却最致命的一个地方。4. PID算法从公式到能跑的程序4.1 PID控制的基本逻辑说到PID很多新手第一反应是“公式好复杂”。其实控制组需要掌握的PID逻辑上非常简单甚至可以用一个生活场景说清楚你在浴室调水温手忙脚乱地左右拧开关。水温低了你就往热的方向拧——这是比例项误差越大拧得越狠但是光拧比例容易过头水温忽冷忽热这时候你看到水温上升的趋势提前回一点——这是微分项误差变化越快修正越温和如果水温始终比目标低那么一点点你就要持续多拧一点角度让它慢慢补上——这是积分项累计误差久了就加大输出。套到控制系统上目标值就是“期望转速”或者说“期望角度”实际值就是编码器或MPU6050读回来的数值误差e就是两者的差。PID控制器根据误差的大小、变化趋势和累计效果计算出控制输出驱动电机或舵机去修正。就这么简单。位置式PID和增量式PID的区别很多人一开始搞不清楚其实一句话就能说透位置式PID直接输出“控制量的大小”比如PWM占空比是百分之多少增量式PID输出的是“控制量的变化量”也就是说每次它告诉你“在当前占空比基础上再加/减多少”。两种算法在本质上同源但应用场景和代码形态差别明显。4.2 位置式PID的代码实现先看最经典的位置式PID公式u(k) Kp * e(k) Ki * Σe(i) Kd * (e(k) - e(k-1))其中e(k)是当前误差Σe(i)是误差累计e(k)-e(k-1)是误差变化率。用C语言实现时通常会定义一个结构体来保存所有状态参数方便管理多个控制回路。typedef struct { float Kp; float Ki; float Kd; float target; // 目标值 float actual; // 实际值 float error; // 当前误差 float last_error; // 上一次误差 float integral; // 误差累计 float output; // 控制器输出 float out_max; // 输出限幅 float int_max; // 积分限幅 } PID_t; float PID_Calc(PID_t *pid, float target, float actual) { pid-target target; pid-actual actual; pid-error target - actual; // 积分项累加时做限幅防止积分饱和 pid-integral pid-error; if (pid-integral pid-int_max) pid-integral pid-int_max; if (pid-integral -pid-int_max) pid-integral -pid-int_max; // 微分项 float diff pid-error - pid-last_error; // 输出计算 pid-output pid-Kp * pid-error pid-Ki * pid-integral pid-Kd * diff; // 输出限幅 if (pid-output pid-out_max) pid-output pid-out_max; if (pid-output -pid-out_max) pid-output -pid-out_max; pid-last_error pid-error; return pid-output; }这个函数每一次周期性调用比如每10ms一次就更新一次输出。位置式PID的优点是直观、容易理解调试时波形好观察缺点是如果你在运行过程中有一条很大的历史误差累积在integral里突然改变目标值时积分项可能会让输出一下子饱和系统反应迟钝这就是所谓的积分饱和问题。所以我在这里加了int_max限幅这是非常必要的不能省。4.3 增量式PID为什么控制组常用它增量式PID在控制组里出镜率极高尤其在电机速度环和位置环里。它的核心思路是不直接计算控制量u(k)而是计算控制量的增量Δu(k)。推导过程很简单写出位置式PID在k时刻和k-1时刻的表达式两者相减整理后得到Δu(k) Kp * (e(k) - e(k-1)) Ki * e(k) Kd * (e(k) - 2*e(k-1) e(k-2))实际输出是u(k) u(k-1) Δu(k)写代码时不需要累积所有历史误差只需要保存前两次误差所以计算量小并且由于输出的是增量即使计算过程中出现一点小偏差也只影响本次的微小变化不容易让执行机构发生剧烈突变。这一点在电动小车、云台这些执行器上非常友好。typedef struct { float Kp; float Ki; float Kd; float error; float last_error; float last_last_error; float delta_out; float output; float out_max; } IncPID_t; float IncPID_Calc(IncPID_t *pid, float target, float actual) { pid-error target - actual; // 计算增量 pid-delta_out pid-Kp * (pid-error - pid-last_error) pid-Ki * pid-error pid-Kd * (pid-error - 2.0f * pid-last_error pid-last_last_error); // 累计得到当前输出 pid-output pid-delta_out; // 输出限幅 if (pid-output pid-out_max) pid-output pid-out_max; if (pid-output -pid-out_max) pid-output -pid-out_max; // 更新历史误差 pid-last_last_error pid-last_error; pid-last_error pid-error; return pid-output; }增量式PID最大的好处很多人总结是“无积分饱和、抗突变”但我个人体会最深的是它比较安全。比赛现场你难免要边跑边调参如果参数试到一个偏大的值位置式PID可能一下子就输出满幅小车直接窜出去撞墙增量式PID即使参数不理想输出也是逐渐变化的你有更多时间反应过来把目标值归零。新手第一次做车模控制我强烈建议从增量式入手。4.4 串级PID进阶必备的一个结构单环PID能解决不少问题但遇到平衡车、云台、风力摆这类系统单环往往不够。原因很简单这些系统不仅有位置/角度的偏差还有位置变化的速度/角速度而单纯对角度环做PID会导致响应慢、超调大、容易振荡。串级PID的结构是外环PID的输出作为内环PID的目标值。以云台控制为例外环是角度环输入目标角度和当前角度输出一个“期望角速度”内环是角速度环输入期望角速度和当前角速度输出PWM控制电机。这样做的好处是内环能更快地抑制扰动相当于给整个系统增加了一个阻尼层外环只需要负责缓慢地修正角度偏差。串级PID的代码本质上就是调用两次PID函数先算外环把输出赋给内环的target再算内环。很多同学一听“串级”就觉得高级其实代码量并不比单环多多少关键在调参顺序先调内环再调外环。内环参数不合适外环怎么调都白搭。我这里建议各位在训练阶段就把“单环速度环”“单环位置环”“串级位置角度环”这三个结构都实现一遍并理解它们各自的适用场景。比赛题目无论怎么变底层需要的基本就是这些结构之一。5. 调参与排障控制组真正的分水岭5.1 PID参数整定从试凑到有方法很多同学写完PID代码后激动地接上电机结果发现转速要么上不去要么来回振荡要么稳是稳了但老是差那么一点。这时候就到了控制组真正的分水岭——调参。调参最怕的是没有方法乱调。我推荐一套比较稳的流程适合绝大多数控制组题目第一步把Ki和Kd全部设为0只留Kp。从很小的值开始比如0.1或0.5逐步增大观察系统响应。你会发现Kp太小的时候响应慢、到位后还有静差Kp大到一定程度系统开始振荡此时记住这个临界振荡点。第二步加入Kd。微分项的作用是抑制振荡它的本质是“看误差的变化趋势提前刹车”。Kd从0开始慢慢调大振荡会逐渐减弱系统响应会变快。但Kd不能太大太大时系统对噪声非常敏感——编码器或者陀螺仪数据稍微抖一下输出就会剧烈跳动。第三步加入Ki。如果你发现系统稳定后和目标值之间有一条淡淡的“距离”——也就是静差就说明需要积分项来补。Ki从很小的值开始加比如0.01观察它能不能把静差吃掉。Ki太大则会让系统产生低频振荡甚至积分饱和导致超调。整个调试过程一定要配合实时数据观测。我常用的方法是用串口把target、actual、output三路数据用类似“目标值,实际值,输出”的格式打印出来再用串口绘图软件或者直接用串口助手画曲线。眼睛看数据波形永远好过盯着电机傻转。现在很多开发工具都带波形显示插件测试起来比早年方便太多了。5.2 常见问题排查速查表控制组的坑80%是下面这几类。我整理了一张速查表都是真实debug过的体会现象可能原因排查方向电机不转驱动芯片未使能 / PWM通道配置错查GPIO使能和PWM输出波形电机只能正转不能反转H桥方向引脚电平固定查方向引脚的逻辑和接线电机抖动、有啸叫PWM频率太低 / 供电电流不足调高PWM频率检查电源容量转速偶尔跳变编码器信号受干扰 / 缺上拉加上拉电阻编码器线远离电机电源线读数漂移得厉害参考地不一致 / 共模干扰传感器和主控共地模拟信号屏蔽系统一开电机就复位MCU供电电压跌落检查电源分区和去耦电容PID输出振荡不收敛Kp太大 / Kd太小 / 采样周期不合理先降Kp再调Kd检查采样周期有静差消除不掉Ki没加 / 输出限幅把小误差吞掉加Ki放宽输出限幅范围增量式PID输出累计到极限out_max太小 / 目标值超物理极限检查限幅和处理目标值的合理性这张表是通用的具体到每个系统还会衍生出很多细节问题。但我建议遇到问题先别急着搜网上的案例而是按照我之前说的排查思路走一遍先电源后信号、先静态后动态、先整体后局部。大部分问题自己定位出来比看一百个教程都记得牢。5.3 比赛现场的几个保命技巧最后分享几个比赛现场的经验这些都不是写在官方指南里的东西。第一程序里一定要做参数和方案的快速切换。我把PID参数放在一个参数结构体数组里通过板上的拨码开关选择第几组参数。现场你会发现理论最优的参数在实际场地里往往不是最优多准备几组参数用于不同场景比临时改代码重新编译高效太多。第二备份和版本管理一定要做。比赛四天三夜前后改动几十个版本很正常我习惯把“能跑的版本”和“正在改的版本”分开存档每次改动前先复制一个备份。没有版本控制的电赛崩一次心态就全完了。第三调试接口一定全部预留出来。串口下载口、SWD调试口、所有传感器的引脚测试点都要在PCB上留出来。现场最绝望的时刻就是你想看某路信号波形结果发现引脚没引出只能拿刀片刮铜层焊飞线。第四现场排障要冷静按顺序来。电赛现场的紧张气氛会让人变得特别想“赌一把”某个原因。我吃过亏——有一次为了多睡一会儿没检查电源电压就直接换了一个电机结果换上去连烧了两个芯片最后才发现是DCDC输出纹波异常。越是时间紧越要按步骤排查这反而最快。写了这么多最后说点掏心窝的话。控制组的整个学习路径从电路板到PID算法看起来内容很多但真正难的不是某一个点而是把整个闭环串起来之后的一遍遍调试。电路板是基础PID是灵魂可把两者结合好的唯一路径就是你亲手做过、烧过、修过、调过——这些过程一个都省不了。我个人带队的最后一个体会是控制组的成长速度几乎和你的调试笔记厚度成正比。每次改参数、换硬件、调结构都记录一下现象和结果一个月后回头看你会发现自己的直觉准了很多很多以前要折腾半天的坑扫一眼就知道问题出在哪。比赛之前的那段集训时光虽然辛苦但那种亲眼看着一个系统从“完全不听指挥”到“指哪打哪”的成就感是很多人后来都一直怀念的。希望这篇东西能帮你少踩几个坑早一点体会到那种手感。