十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

两轮自平衡小车串级 PID 与 PID 神经网络整定实践

两轮自平衡小车串级 PID 与 PID 神经网络整定实践 简介《基于PID神经网络的两轮自平衡小车研究》面向自动化、嵌入式控制与神经网络交叉方向的学习者与研发者聚焦用PID神经网络优化两轮自平衡小车控制器参数这一核心问题。内容给出C语言神经网络辨识程序思路对上一时刻编码器、陀螺仪与加速度计的数据进行处理获得角度、角速度、位置和位置微分四个状态量据此生成下一时刻的电机控制信号反向调权时以符号函数近似代替偏导结合BP算法训练NNI辨识模型与NNC控制器再将优化参数传递给AVR控制器。内容还涉及微控制器与EEPROM之间的参数传送流程并通过实验对比说明优化后小车直立角度波动由约±2.1°降至±1.4°平衡性能提升约33.3%。资源包仅含1个PDF文件约515KB便于离线查阅与检索已有168人学习下载。1. 两轮自平衡小车为什么只靠串级 PID 调不稳车模第一次上电直立环 Kp 给到 20电机立刻高频嗡鸣、车身抖成筛子降到 8车又软塌塌往一侧倒。加上速度环做串级 PID 控制勉强站住了可换到地毯上跑两圈同一组 kp ki kd 又不管用了——做两轮自平衡小车的人大多都撞过这堵墙。根子不在手抖。俯仰通道本质是一阶倒立摆非线性、欠驱动、参数随负载和地面时变而 PID 控制器里的三个系数是固定常数只能在某个工作点附近近似最优。PID 神经网络要解决的就是这件事——把固定增益换成由神经网络在线输出的增益让控制器自己朝着 pid 最优曲线的方向收敛。它适合两类人玩过 STM32 裸机 PID 控制电机转速、看得懂位置式 PID 用离散化差分方程的同学想在毕业设计或数学建模里把控制算法抬一层以及做运动控制的工程师想给 pid 闭环控制找一条不靠反复试凑的整定路径。2. PID 与神经网络的结合方式位置式、增量式 PID 与三种融合形态选型2.1 位置式 PID 用离散化差分方程落到代码里位置式 PID 的形式最直观也最适合讲清楚 kp ki kd 的物理含义输出直接由误差的当前值、累积值、变化率三部分加权而成。落到单片机里就是下面这段注意积分累加要和采样周期 dt 绑定不要把 Ki 当成一个纯粹的比例系数用。/* position_pid.c —— 位置式 PID 的离散化实现带积分限幅与输出限幅 */ typedef struct { float Kp, Ki, Kd; /* 三参数由模拟式的比例带 PB、积分时间 Ti、微分时间 Td 换算 */ float e1; /* e(k-1) */ float integral; /* 积分累加项 Σe(j)·dt */ float out_max; /* 输出限幅最终映射到 PWM 占空比 */ } PosPID; float PosPID_Calc(PosPID *p, float target, float fb, float dt) { float e target - fb; /* 当前误差 */ p-integral e * dt; /* 用 dt 折算采样周期一变积分量纲就变 */ float lim p-out_max / (p-Ki 1e-6f); if (p-integral lim) p-integral lim; /* 积分限幅是抗饱和第一道闸 */ if (p-integral -lim) p-integral -lim; float u p-Kp * e p-Ki * p-integral p-Kd * (e - p-e1) / dt; /* dt 不能为 0调度周期抖动会放大噪声 */ p-e1 e; if (u p-out_max) u p-out_max; /* 输出限幅防止 PWM 长时间满占空比 */ if (u -p-out_max) u -p-out_max; return u; }Kp 决定响应刚度Ki 消静态倾角偏差但引入相位滞后Kd 抑超调却对 MPU6050 的角速度噪声极其敏感。常见做法是舍弃差分得到的微分直接用陀螺仪输出的角速度作为微分项等价于微分先行噪声小一大截。位置式的短板也很清楚积分项每拍都要累加且 Ki 与采样周期强耦合换个主频或改调度周期整定好的参数就得重来。2.2 增量式 PID 算法与两轮自平衡小车的串级 PID 控制分层增量式只输出本拍的增量 Δu由调用方累加。好处是天然带积分限幅能力且算式中 Ki 已经吸收掉了采样周期 T写成代码就三步乘加。/* inc_pid.c —— 增量式 PID输出 Δu(k)由调用者累加得到 u(k) */ float IncPID_Calc(IncPID *p, float target, float fb) { float e target - fb; /* Δu Kp·[e(k)-e(k-1)] Ki·e(k) Kd·[e(k)-2e(k-1)e(k-2)] */ float du p-Kp * (e - p-e1) p-Ki * e p-Kd * (e - 2.0f * p-e1 p-e2); p-e2 p-e1; p-e1 e; return du; }这里的 Ki 实际等于 Kp·T/Ti所以采样周期 T 固定时它才是常数改 MCU 主频或改调度周期必须重算或重新整定这是新手最容易漏的一条。串级 PID 控制的层次划分在自平衡小车上基本固定外环速度或位置10~20 Hz 执行内环直立角度100~200 Hz 执行外环的输出作为内环的目标值。内外环带宽至少要差 3~5 倍否则两环互相打架表现就是车在原地前后抽动、怎么调都不收敛。2.3 PID 神经网络的三种融合形态与选型对照到底把神经网络接在 PID 的哪一环直接决定工作量。三种常见融合形态的差别如下。融合形态网络结构网络输出学习算法适合场景落地难度BP 神经网络整定 PID4-5-3 前馈网络Kp、Ki、Kd误差反向传播仿真验证、参数慢时变中需要对象雅可比单神经元自适应 PID单神经元 3 个权值Δu 增量有监督 Hebb 规则裸机在线实时、算力受限低几十行 C模糊 PID对照组模糊规则表ΔKp、ΔKi、ΔKd无学习过程经验规则可写清低但规则靠人工选型思路很实在如果目标是论文或数学建模层面体现神经网络BP 神经网络整定 PID 最好写曲线也好看如果目标是车真的站在 STM32F103 这种裸机上跑稳单神经元自适应 PID 更现实一次前向加一次权值更新约三十次浮点乘加1 kHz 毫无压力。前馈神经网络和 CNN 在这类问题上并不占优——图像处理用 CNN 是因为像素有局部相关性而控制信号是 4 维短时序标量小网络足够。循环神经网络引入历史状态的思路可行但增量式 PID 的二阶差分本身已经携带两拍历史收益有限反而多了一份状态维护成本。3. 从倒立摆建模到 PID 神经网络两轮自平衡小车的控制结构3.1 状态量选取与倒立摆简化模型两轮自平衡小车俯仰通道线性化后近似成一个带右半平面极点的二阶模型传递函数形如G(s) b / (s² a·s - c)其中 c 为正意味着开环不稳定——不闭环保不住这是选闭环控制的硬理由。建模时用拉格朗日方程推一遍完整形式再在小角度处做泰勒展开线性化是毕业设计里最稳妥的写法。状态量一般取四个俯仰角 θ、俯仰角速度 θ̇、车轮前进速度 v、航向角 ψ。执行器只有两个轮子所以控制输入拆成共模 PWM管直立和速度和差模 PWM管转向。采样周期上直立环取 5 ms、速度环取 50 ms 是很成熟的搭配直立环再往下压到 1 ms 收益有限反而放大陀螺仪噪声。3.2 单神经元自适应 PID 的增量式结构与权值更新单神经元自适应 PID 的输入向量直接取自误差序列x1(k) e(k)对应比例分量x2(k) e(k) − e(k−1)对应一阶差分x3(k) e(k) − 2e(k−1) e(k−2)对应二阶差分微分输出为 Δu(k) K · Σ wi(k)·xi(k)权值按有监督 Hebb 规则更新wi(k1) wi(k) η·e(k)·Δu(k)·xi(k)。K 是神经元比例系数管整体收敛速度η 是学习率管权值跟随误差调整的猛烈程度。/* single_neuron_pid.c —— 单神经元自适应增量式 PID跑在裸机主循环里 */ typedef struct { float w[3]; /* 对应 e、Δe、Δ²e 的三个权值初值可给 {0.30f, 0.10f, 0.10f} */ float e1, e2; /* 前两拍误差 */ float K; /* 神经元比例系数典型 0.05~0.50 */ float eta; /* 学习率 η典型 0.01~0.10 */ float u; /* 累加后的控制量 */ float out_max; /* 输出限幅 */ } SN_PID; float SN_PID_Calc(SN_PID *p, float target, float fb) { float e target - fb; float x[3]; x[0] e; /* 比例分量 */ x[1] e - p-e1; /* 一阶差分 */ x[2] e - 2.0f * p-e1 p-e2; /* 二阶差分等价微分项 */ float du 0.0f; for (int i 0; i 3; i) du p-K * p-w[i] * x[i]; p-u du; /* 增量累加得到最终输出 */ if (p-u p-out_max) p-u p-out_max; if (p-u -p-out_max) p-u -p-out_max; /* 有监督 Hebb 学习误差越大、本拍增量越大权值调整越猛 */ for (int i 0; i 3; i) { p-w[i] p-eta * e * du * x[i]; if (p-w[i] 1.0f) p-w[i] 1.0f; /* 权值限幅防发散 */ if (p-w[i] -1.0f) p-w[i] -1.0f; } p-e2 p-e1; p-e1 e; return p-u; }这里用 Δu 而不是 u 做学习信号是为了量纲一致u 的绝对值会随输出漂移用 u 会让权值整体跟着漂。η 的边界可以拿原始陀螺仪数据实测超过 0.1 后角速度噪声会直接灌进权值车身出现低频呼吸式晃动。权值初值给经验值比全零或随机初值收敛快得多。3.3 BP 神经网络整定 Kp、Ki、Kd 的网络结构与超参输入层 4 个节点e(k)、e(k)−e(k−1)、e(k)−2e(k−1)e(k−2)、偏置 1。隐含层 5 个节点sigmoid 激活。输出层 3 个节点对应 Kp、Ki、Kd经 sigmoid 归一化到 [0,1] 后线性映射回物理区间K k_min (k_max − k_min)·Ok。超参典型取值影响学习率 η0.20~0.30大则收敛快但易振荡动量因子 α0.02~0.10抑制振荡加速穿越平坦区隐含层节点数4~6少则欠拟合多则白烧算力采样周期 T5~20 ms决定 Ki 的换算关系Kp 映射区间[0, 15]由手调 PID 结果圈定Ki 映射区间[0, 0.8]过大直接积分饱和Kd 映射区间[0, 0.6]过大放大陀螺仪噪声真正的难点不在网络本身而在反向传播需要被控对象的雅可比 ∂y/∂u。实车上这个量拿不到解析式只能用符号函数 sign(∂y/∂u) 近似或者用前后两拍的一阶差分估计。估计不准的直接后果是学习率必须压得很小收敛慢这也是 BP-PID 在仿真里漂亮、上实车就拉胯的主要原因。4. MATLAB 仿真跑通 BP 神经网络整定 PID再落到 STM32 裸机4.1 MATLAB 脚本BP 神经网络在线整定 PID 三参数先在 MATLAB 里跑通闭环确认网络能收敛再移植到板子上。下面这段把对象离散化后逐步迭代网络每一步输出一组 Kp、Ki、Kd 驱动增量式 PID。% bp_pid_tune.m —— BP 神经网络在线整定增量式 PID 三参数 clear; clc; T 0.02; % 采样周期 20 ms与实车直立环同频 N 2000; % 总迭代步数 % 被控对象两轮自平衡小车俯仰通道线性化后的不稳定二阶模型 sys tf(1.2, [1 0.8 -12.5]); sysd c2d(sys, T, zoh); % 零阶保持离散化 [Ad, Bd, Cd, ~] ssdata(sysd); xite 0.25; % 学习率 η alfa 0.05; % 动量因子 α wi rand(5, 4); wo rand(3, 5); wi1 wi; wo1 wo; % 动量项缓存 k_min [0 0 0]; % Kp Ki Kd 映射下界 k_max [15 0.8 0.6]; % 映射上界来自手调 PID 的经验区间 x [0; 0]; u 0; u1 0; y 0; y1 0; e1 0; e2 0; I 0; du 0; Y zeros(1, N); U zeros(1, N); for k 1:N r 0.1; % 0.1 rad 俯仰角阶跃模拟被推一下 e r - y; xi [e; e - e1; e - 2*e1 e2; 1]; % 输入层误差、一阶差分、二阶差分、偏置 Ii wi * xi; Oi 1./(1 exp(-Ii)); % 隐含层前向 Ik wo * Oi; Ok 1./(1 exp(-Ik)); % 输出层前向 K k_min (k_max - k_min) .* Ok; % 反归一化得到实际增益 Kp K(1); Ki K(2); Kd K(3); du Kp*(e - e1) Ki*e Kd*(e - 2*e1 e2); % 增量式 PID u u du; u max(min(u, 10), -10); % 输出限幅等效 PWM 上限 x Ad*x Bd*u; y Cd*x; % 对象推进一步 Y(k) y; U(k) u; % ---- 反向传播性能指标 J 0.5·e² ---- dyu sign((y - y1) / (u - u1 1e-6)); % 对象雅可比符号近似 dudK [e - e1; e; e - 2*e1 e2]; % ∂du/∂K3×1 dOk (-e * dyu) * (dudK .* (k_max - k_min)) ... .* (2 ./ (exp(Ik) exp(-Ik)).^2); % 输出层反传信号 dOi (wo * dOk) .* (2 ./ (exp(Ii) exp(-Ii)).^2); wo1 wo; wo wo - xite * (dOk * Oi) alfa * (wo - wo1); wi1 wi; wi wi - xite * (dOi * xi) alfa * (wi - wi1); e2 e1; e1 e; y1 y; u1 u; end plot(T*(1:N), Y, LineWidth, 1.2); grid on; xlabel(时间 / s); ylabel(俯仰角 / rad);几个参数得说清楚。xite取 0.25 是仿真里比较稳的起点实车要往下压到 0.05 以下alfa只用来抑制振荡超过 0.1 会让权值更新带上过强的惯性反而拖慢收敛。k_min和k_max这两个映射区间比网络结构本身更关键——区间圈窄了网络输出永远被截断圈宽了等效于没加约束Kp 在高低两端来回跳。区间怎么来先关掉网络用手调 PID 把车调到能站把那组参数放大 1.5 倍作为上界、缩到 0.2 倍作为下界。4.2 用 matlab pid tuner 与在线整定工具圈定增益初值MATLAB 自带整定工具能省掉大部分试凑。命令行直接拿增益% 用 pidtune 无界面整定拿一组参考增益和稳定裕度 sysd c2d(tf(1.2, [1 0.8 -12.5]), 0.02, zoh); [C, info] pidtune(sysd, PID); fprintf(Kp%.3f Ki%.3f Kd%.3f\n, C.Kp, C.Ki, C.Kd); disp(info.Stable); % 返回 1 表示闭环稳定可作为初筛想要交互式拖带宽滑块就敲pidTuner(sysd, PID)界面里直接把响应时间往左拉看超调怎么变。没有 MATLAB 授权时快马平台的 PID 智能整定网页端这类工具也能用贴一段阶跃响应数据点进去它拟合出一阶惯性加纯滞后模型再给一组 kp ki kd 初值。要提醒的是pid tuner 基于线性模型给出的是参考初值而非实车最优。它的价值在于让你把 3.3 节那张映射区间表填准——区间对了网络在线微调才有意义。工程上更常见的组合是先手调得到可行参数再让网络在这组参数附近做小幅在线修正而不是让网络从零开始瞎找。4.3 STM32 裸机 PID 控制与串口回传波形板子端的分工TIM 中断 5 ms 触发一次 MPU6050 采样I2C DMA直立环在同一个中断里跑完速度环放在 50 ms 的软件计数器里主循环只负责串口发送和按键处理。定时器优先级要保证角度环不被串口中断打断否则采样周期抖动会直接污染微分项。调试阶段最忌讳用printf(%f)发浮点。STM32 上的浮点格式化在中断上下文里可能耗掉几百微秒5 ms 周期里很容易超时表现为车一开串口就抖。改成二进制定长帧用 DMA 发送/* debug_frame.c —— 把三路关键量打包成一帧通过串口发给上位机 */ typedef union { float f; uint8_t b[4]; } FloatBytes; void SendDebugFrame(float target, float actual, float u) { uint8_t buf[16]; FloatBytes t, a, o; t.f target; a.f actual; o.f u; buf[0] 0xAA; buf[1] 0x55; /* 双字节帧头便于对齐 */ for (int i 0; i 4; i) buf[2 i] t.b[i]; for (int i 0; i 4; i) buf[6 i] a.b[i]; for (int i 0; i 4; i) buf[10 i] o.b[i]; uint8_t sum 0; for (int i 0; i 14; i) sum ^ buf[i]; /* 异或校验能挡住大部分错位 */ buf[14] sum; buf[15] 0x0D; HAL_UART_Transmit_DMA(huart1, buf, 16); /* DMA 发送别阻塞主循环 */ }帧格式统一成下表上位机解析逻辑就能写死不用来回改。字节偏移内容类型说明0~10xAA 0x55uint8×2帧头用于重同步2~5目标角度float 小端单位 rad6~9实际角度float 小端单位 rad10~13控制量 ufloat 小端映射前的输出14校验和uint8前 14 字节异或150x0Duint8帧尾上位机侧用 Python 收帧重点是丢包重同步不能写成死循环读满 16 字节否则错位一次后面全乱# plot_pid.py —— 解析二进制帧并绘制角度环曲线 import serial, struct, collections import matplotlib.pyplot as plt ser serial.Serial(COM3, 115200, timeout0.1) buf bytearray() target, actual, ctrl (collections.deque(maxlen500) for _ in range(3)) while True: buf ser.read(64) while len(buf) 16: if buf[0] ! 0xAA or buf[1] ! 0x55: buf.pop(0) # 帧头没对齐丢一个字节重新找 continue frame, buf buf[:16], buf[16:] if frame[15] ! 0x0D: # 帧尾不对说明粘包错位整帧丢弃 continue target.append(struct.unpack(f, frame[2:6])[0]) actual.append(struct.unpack(f, frame[6:10])[0]) ctrl.append(struct.unpack(f, frame[10:14])[0])拿到曲线之后判断标准不是看着顺眼而是看 u(t) 有没有锯齿、角度曲线有没有等幅振荡、稳态段是不是一条水平线。5. PID 神经网络的调参、排错与两轮自平衡小车抗扰验证5.1 整定顺序与参数表顺序不能乱先关掉网络用手调增量式 PID 把车调到能原地站住再用这组参数反推权值初值和映射区间然后打开网络学习率从 0.01 起步慢慢加最后才闭速度环。跳过前两步直接让网络从随机权值开始学大概率看到的是车在原地画圈。参数符号典型初值调整方向对应现象直立环采样周期T5 ms固定不轻易改改小需重算 Ki神经元比例系数K0.1大→响应快易振荡车身高频抖动学习率η0.02大→跟踪快噪声放大低频呼吸式晃动权值初值w1/w2/w30.30/0.10/0.10用手调 PID 反推收敛慢或根本不动Kp 映射区间[k_min,k_max][0, 15]按手调结果圈定网络输出长期贴边界积分抗饱和阈值—0.8·out_max减小更保守起步瞬间车头下沉陀螺仪低通截止fc80~120 Hz降低更平滑但滞后高频嗡鸣是否消失5.2 抖动、积分饱和、超调高频故障的定位顺序现象优先怀疑验证方法处理车身高频嗡鸣Kd 过大或陀螺仪噪声关掉微分项看是否消失加一阶低通或改用角速度做微分静止时低频前后晃学习率偏大、权值发散打印 w1~w3 曲线η 减半权值限幅收窄起步车头下沉再回弹积分饱和观察 u 是否长时间贴限幅积分限幅 积分分离车缓慢朝一个方向漂陀螺仪零偏未校准静置打印 200 帧求均值上电静止取均值做零偏补偿网络调完不如手调映射区间或雅可比符号不对打印 Kp/Ki/Kd 实值手工圈区间先做小幅微调注意网络输出是归一化值时务必在代码里 clamp 后再反归一化。sigmoid 在输入稍大时就会饱和到接近 1反归一化后 Kp 可能直接顶到几十PWM 满占空比电机瞬间堵转。5.3 三组验证实验阶跃、脉冲扰动与上位机波形判读第一组做阶跃给目标角度 0.05 rad记录上升时间、超调量和稳态误差要求超调小于 10%、稳态误差接近零。第二组做脉冲扰动手轻推车身一下看回正时间和振荡次数一般要求两到三个周期内收敛超过四个说明 Kd 或学习率还需要收。第三组是变负载与变地面在车顶加配重、从瓷砖换到地毯观察权值是否在线变化。第三组才是 PID 神经网络相对固定 PID 唯一有说服力的对比实验——前两组手调 PID 也能过。判读时把三组曲线里的 u(t) 叠在同一坐标系看如果变负载那条只是整体平移、形状没变说明网络实际学到的只是一个偏置量并没有真正改变增益这时候该动的是映射区间而不是学习率如果形状变了、振荡次数明显少于固定 PID那才是网络在线整定在起作用。本文还有配套的精品资源点击获取
返回列表