C#实现PID

最近研究了一下PID,用C#实现了一下,搞的差不多明白了,和大家分享一下,感谢大家的关注。
项目源码见文末。

下图是测试效果:
C#实现PID_第1张图片
蓝色是目标值,红色是实际值,黄色是输入值,灰色是逸出值。
下面说一下测试过程,首先模拟一个被调节的设备。大家先可以假设这是一个水罐,我们通过Input口的注水和抽水调节液位Level,罐子本身有个Output口,排出水来干扰我们的控制。

  public class DeviceModel : BindableBase
    {
        public DeviceModel()
        {
            Task.Run(Running);
        }
        private int _timeSpinner = 500;
        public int TimeSpinner
        {
            get { return _timeSpinner; }
            set
            {
                if (value > 10)
                    SetProperty(ref _timeSpinner, value);
            }
        }

        private double _input;
        public double Input
        {
            get { return _input; }
            set { SetProperty(ref _input, Math.Round(value, 3)); }
        }
        private double _output;
        public double Output
        {
            get { return _output; }
            set { SetProperty(ref _output, Math.Round(value, 3)); }
        }
        private double _level;
        public double Level
        {
            get { return _level; }
            set { SetProperty(ref _level, Math.Round(value, 3)); }
        }
        public double MaxLevel { get; set; } = double.MaxValue;
        public double MixLevel { get; set; } = 0d;
        private void Running()
        {
            while (true)
            {
                Thread.Sleep(_timeSpinner);
                if (Level < MaxLevel)
                    Level += Input * 3;
                if (Level > Output)
                    Level -= Output;
                else
                    Level = 0;
            }
        }
    }

然后就是PID控制的水泵

  public class PIDModel : BindableBase
    {
        private double _err_last; //上一个偏差值    
        private double _err_last_last; //上上一个偏差值    

        private double _target;
        public double Target
        {
            get { return _target; }
            set
            {
                _target = _kp * _err;
                SetProperty(ref _target, Math.Round(value, 3));
            }
        }

        private double _kp = 0.34;
        public double Kp
        {
            get { return _kp; }
            set {  SetProperty(ref _kp, Math.Round(value, 3)); }
        }
        private double _ki;
        public double Ki
        {
            get { return _ki; }
            set {  SetProperty(ref _ki, Math.Round(value, 3)); }
        }
        private double _err;
        private double _kd;
        public double Kd
        {
            get { return _kd; }
            set {  SetProperty(ref _kd, Math.Round(value, 3)); }
        }

        private double _outputUpLimit = 10000;
        public double OutputUpLimit
        {
            get { return _outputUpLimit; }
            set { SetProperty(ref _outputUpLimit, value); }
        }
        private double _outputDownLimit=-10000;
        public double OutputDownLimit
        {
            get { return _outputDownLimit; }
            set { SetProperty(ref _outputDownLimit, value); }
        }

        public double Input { get; set; }
        public double Output { get; set; }
        
        public void Adjust()
        {
            _err = Target - Input;
            //增量PID算法公式
            Output += Kp * (_err - _err_last + Ki * _err + Kd * (_err - 2 * _err_last + _err_last_last));
            _err_last_last = _err_last;
            _err_last = _err;
            if (Output > _outputUpLimit) Output = _outputUpLimit;
            if (Output < _outputDownLimit) Output = _outputDownLimit;
        }
    }

然后开动起来

    public class DeviceCotrol
    {
        public Queue<double> PIDTargetList = new Queue<double>();
        public Queue<double> DeviceLevelList = new Queue<double>();
        public Queue<double> PIDOutputList = new Queue<double>();
        public Queue<double> DeviceOutputList = new Queue<double>();
        public DeviceCotrol(IContainerExtension container)
        {
            var PID = container.Resolve<PIDModel>();
            var Device = container.Resolve<DeviceModel>();

            Task.Run(() =>

            {
                double[] n = new double[500];
                PIDTargetList = new Queue<double>(n);
                DeviceLevelList = new Queue<double>(n);
                PIDOutputList = new Queue<double>(n);
                DeviceOutputList = new Queue<double>(n);
                while (true)
                {
                    Thread.Sleep(100);

                    Device.Input = PID.Output;

                    PID.Input = Device.Level;

                    PID.Adjust();

                    PIDTargetList.Enqueue(PID.Target);
                    if (PIDTargetList.Count > 500)
                    {
                        PIDTargetList.Dequeue();
                    }
                    //
                    DeviceLevelList.Enqueue(Device.Level);
                    if (DeviceLevelList.Count > 500)
                    {
                        DeviceLevelList.Dequeue();
                    }
                    //
                    PIDOutputList.Enqueue(PID.Output);
                    if (PIDOutputList.Count > 500)
                    {
                        PIDOutputList.Dequeue();
                    }
                    DeviceOutputList.Enqueue(Device.Output);
                    if (DeviceOutputList.Count > 500)
                    {
                        DeviceOutputList.Dequeue();
                    }
                }
            });

        }

    }

其它就是一些界面的实现了,就不贴了,可以到我们码云下载我的开源项目。
https://gitee.com/tfarcraw/wpfnotes.git
是我平时学习wpf 的一个练手小项目集,这个示例在PrismMain项目中

你可能感兴趣的:(Csharp,PLC)