本文将详细介绍如何使用C#实现一个PID控制器的仿真系统,并通过GDI+进行实时动画展示。PID控制器是最常用的控制器之一,通过比例(P)、积分(I)和微分(D)三个环节的组合来实现对系统的控制。
PID控制器的输出由以下三部分组成:
数学表达式:
Markupu(t) = Kp * e(t) + Ki * ∫e(t)dt + Kd * de(t)/dt
其中:
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);
}
}
}
}
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控制器的实现采用了离散化的方法:
本文详细介绍了如何使用C#实现PID控制器仿真系统,并通过GDI+进行可视化展示。通过这个实现,我们可以直观地观察PID控制器的工作过程,便于理解和调试PID参数。 这个实现可以作为学习PID控制原理,也可以作为实际项目中PID控制器的参考实现。
本文作者:rick
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!