编辑
2025-02-24
C# 应用
00
请注意,本文编写于 71 天前,最后修改于 71 天前,其中某些信息可能已经过时。

目录

概述
PID控制器原理
完整代码实现
主窗体代码
3.2 PID控制器类
4. 调试建议
5. 总结

概述

本文将详细介绍如何使用C#实现一个PID控制器的仿真系统,并通过GDI+进行实时动画展示。PID控制器是最常用的控制器之一,通过比例(P)、积分(I)和微分(D)三个环节的组合来实现对系统的控制。

PID控制器原理

PID控制器的输出由以下三部分组成:

  • 比例项(P):与当前误差成比例
  • 积分项(I):误差随时间的积累
  • 微分项(D):误差变化率

数学表达式:

Markup
u(t) = Kp * e(t) + Ki * ∫e(t)dt + Kd * de(t)/dt

其中:

  • u(t) 是控制器输出
  • e(t) 是误差信号
  • Kp 是比例系数
  • Ki 是积分系数
  • Kd 是微分系数

完整代码实现

主窗体代码

image.png

C#
using Timer = System.Windows.Forms.Timer; namespace AppPid { public partial class Form1 : Form { private PIDController pidController; private Timer simulationTimer; private List<PointF> setpointPoints; private List<PointF> actualPoints; private float currentTime = 0; private float setpoint = 50; private float currentValue = 0; public Form1() { InitializeComponent(); // 初始化PID控制器 pidController = new PIDController(0.5f, 0.2f, 0.1f); // 初始化数据点列表 setpointPoints = new List<PointF>(); actualPoints = new List<PointF>(); // 设置定时器 simulationTimer = new Timer(); simulationTimer.Interval = 50; // 50ms simulationTimer.Tick += SimulationTimer_Tick; // 设置双缓冲 this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); // 启动仿真 simulationTimer.Start(); } private void SimulationTimer_Tick(object sender, EventArgs e) { // 计算PID输出 float error = setpoint - currentValue; float output = pidController.Compute(error); // 更新系统状态(简单的一阶系统模拟) currentValue += output * 0.1f; // 添加数据点 setpointPoints.Add(new PointF(currentTime, setpoint)); actualPoints.Add(new PointF(currentTime, currentValue)); // 限制点的数量,保持图表显示最近的数据 if (setpointPoints.Count > 200) { setpointPoints.RemoveAt(0); actualPoints.RemoveAt(0); } currentTime += 0.05f; // 触发重绘 this.Invalidate(); } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); DrawSimulation(e.Graphics); } private void DrawSimulation(Graphics g) { g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 设置坐标系 int margin = 40; int width = this.ClientSize.Width - 2 * margin; int height = this.ClientSize.Height - 2 * margin; // 绘制坐标轴 using (Pen axisPen = new Pen(Color.Black, 2)) { g.DrawLine(axisPen, margin, this.ClientSize.Height - margin, this.ClientSize.Width - margin, this.ClientSize.Height - margin); // X轴 g.DrawLine(axisPen, margin, margin, margin, this.ClientSize.Height - margin); // Y轴 } // 绘制数据曲线 if (setpointPoints.Count > 1) { // 绘制目标值曲线 using (Pen setpointPen = new Pen(Color.Red, 2)) { DrawCurve(g, setpointPoints, setpointPen, margin, width, height); } // 绘制实际值曲线 using (Pen actualPen = new Pen(Color.Blue, 2)) { DrawCurve(g, actualPoints, actualPen, margin, width, height); } } // 绘制图例 DrawLegend(g, margin); } private void DrawCurve(Graphics g, List<PointF> points, Pen pen, int margin, int width, int height) { PointF[] scaledPoints = new PointF[points.Count]; float timeRange = 10.0f; // 显示最近10秒的数据 float valueRange = 100.0f; // 值的范围0-100 for (int i = 0; i < points.Count; i++) { float x = margin + (points[i].X % timeRange) / timeRange * width; float y = margin + height - (points[i].Y / valueRange * height); scaledPoints[i] = new PointF(x, y); } if (scaledPoints.Length > 1) { g.DrawLines(pen, scaledPoints); } } private void DrawLegend(Graphics g, int margin) { using (Font font = new Font("Arial", 10)) { // 目标值图例 g.DrawLine(new Pen(Color.Red, 2), margin + 10, margin + 20, margin + 30, margin + 20); g.DrawString("目标值", font, Brushes.Red, margin + 35, margin + 13); // 实际值图例 g.DrawLine(new Pen(Color.Blue, 2), margin + 10, margin + 40, margin + 30, margin + 40); g.DrawString("实际值", font, Brushes.Blue, margin + 35, margin + 33); } } } }

3.2 PID控制器类

C#
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace AppPid { public class PIDController { private readonly float Kp; // 比例系数 private readonly float Ki; // 积分系数 private readonly float Kd; // 微分系数 private float integralSum; // 积分项累加 private float lastError; // 上一次误差 private bool isFirstCompute; // 是否第一次计算 public PIDController(float kp, float ki, float kd) { Kp = kp; Ki = ki; Kd = kd; Reset(); } public void Reset() { integralSum = 0; lastError = 0; isFirstCompute = true; } public float Compute(float error) { // 计算积分项 integralSum += error; // 计算微分项(第一次计算时,微分项为0) float derivative = isFirstCompute ? 0 : error - lastError; // 计算PID输出 float output = Kp * error + // 比例项 Ki * integralSum + // 积分项 Kd * derivative; // 微分项 // 更新状态 lastError = error; isFirstCompute = false; return output; } } }

PID控制器的实现采用了离散化的方法:

  • 比例项直接使用当前误差值
  • 积分项使用误差累加
  • 微分项使用当前误差与上一次误差的差值

4. 调试建议

  1. PID参数调整:
    • 先调节Kp,使系统有基本的响应
    • 添加Ki,消除稳态误差
    • 最后调节Kd,改善动态特性
  2. 常见问题:
    • 震荡:减小Kp或Kd
    • 响应慢:增大Kp
    • 稳态误差:增大Ki

5. 总结

本文详细介绍了如何使用C#实现PID控制器仿真系统,并通过GDI+进行可视化展示。通过这个实现,我们可以直观地观察PID控制器的工作过程,便于理解和调试PID参数。 这个实现可以作为学习PID控制原理,也可以作为实际项目中PID控制器的参考实现。

本文作者:rick

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!