using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DyeingComputer.UserClass { /// /// 加注曲线类型 /// public enum DosingCurveType { Linear, // 线性曲线 Parabolic, // 抛物线曲线(开始快,后来慢) Exponential // 指数曲线(开始慢,后来快) } /// /// 液体加注PID控制器 /// public class LiquidDosingController { private DateTime _startTime; private DateTime _lastUpdateTime; private double _lastLevel; private bool _isDosingActive; // PID控制器参数 private double _proportionalGain; private double _integralGain; private double _derivativeGain; private double _integralTerm; private double _lastError; /// /// 当前比例阀开度 (0-100%) /// public double CurrentValveOpening { get; private set; } /// /// 目标完成时间 (秒) /// public double TargetTime { get; set; } /// /// 加注曲线类型 /// public DosingCurveType CurveType { get; set; } /// /// 容器最大容量 /// public double MaxCapacity { get; set; } /// /// 当前液位 /// public double CurrentLevel { get; private set; } /// /// 已加注体积 /// public double DosedVolume { get; private set; } /// /// 液体加注事件 /// public event Action OnDosingUpdate; /// /// 初始化液体加注控制器 /// /// 目标加注时间(秒) /// 容器最大容量 /// 加注曲线类型 /// 比例增益 /// 积分增益 /// 微分增益 public LiquidDosingController(double targetTime, double maxCapacity, DosingCurveType curveType = DosingCurveType.Linear, double kP = 1.0, double kI = 0.1, double kD = 0.01) { TargetTime = targetTime; MaxCapacity = maxCapacity; CurveType = curveType; _proportionalGain = kP; _integralGain = kI; _derivativeGain = kD; _isDosingActive = false; CurrentValveOpening = 0; } /// /// 开始加注过程 /// /// 初始液位 public void StartDosing(double initialLevel) { if (_isDosingActive) return; _lastLevel = initialLevel; CurrentLevel = initialLevel; DosedVolume = 0; _integralTerm = 0; _lastError = 0; CurrentValveOpening = 0; _startTime = DateTime.Now; _lastUpdateTime = _startTime; _isDosingActive = true; OnDosingUpdate?.Invoke(CurrentValveOpening); } /// /// 停止加注过程 /// public void StopDosing() { _isDosingActive = false; CurrentValveOpening = 0; OnDosingUpdate?.Invoke(0); } /// /// 更新当前液位并计算新的阀门开度 /// /// 当前液位 /// 新的阀门开度 public double UpdateLevel(double currentLevel) { if (!_isDosingActive) return 0; // 获取当前时间 DateTime currentTime = DateTime.Now; // 计算时间间隔 double timeInterval = (currentTime - _lastUpdateTime).TotalSeconds; // 如果时间间隔太小,避免计算过于频繁 if (timeInterval < 0.1) return CurrentValveOpening; // 更新液位 CurrentLevel = currentLevel; // 计算这段时间内的液体减少量 double volumeDosedInInterval = _lastLevel - currentLevel; DosedVolume += volumeDosedInInterval; _lastLevel = currentLevel; // 计算已用时间和剩余时间 double elapsedTime = (currentTime - _startTime).TotalSeconds; double remainingTime = TargetTime - elapsedTime; // 如果已超过目标时间,完全关闭阀门 if (remainingTime <= 0) { StopDosing(); return 0; } // 计算期望的加注速率(基于所选曲线类型) double desiredDosingRate = CalculateDesiredDosingRate(elapsedTime, remainingTime); // 计算实际加注速率(升/秒) double actualDosingRate = volumeDosedInInterval / timeInterval; // 计算误差(期望速率与实际速率之差) double error = desiredDosingRate - actualDosingRate; // PID计算 double proportionalTerm = _proportionalGain * error; _integralTerm += _integralGain * error * timeInterval; // 积分项抗饱和 _integralTerm = Math.Max(Math.Min(_integralTerm, 100), 0); double derivativeTerm = _derivativeGain * (error - _lastError) / timeInterval; _lastError = error; // 计算新的阀门开度 double newValveOpening = CurrentValveOpening + proportionalTerm + _integralTerm + derivativeTerm; // 限制阀门开度在0-100%范围内 newValveOpening = Math.Max(Math.Min(newValveOpening, 100), 0); CurrentValveOpening = newValveOpening; // 更新最后更新时间 _lastUpdateTime = currentTime; // 触发更新事件 OnDosingUpdate?.Invoke(CurrentValveOpening); // 检查是否已完成加注 if (CurrentLevel <= 0) { StopDosing(); } return CurrentValveOpening; } /// /// 根据曲线类型计算期望的加注速率 /// private double CalculateDesiredDosingRate(double elapsedTime, double remainingTime) { double remainingVolume = CurrentLevel; // 确保不会除零 if (remainingTime <= 0) return 0; double progress = elapsedTime / TargetTime; switch (CurveType) { case DosingCurveType.Linear: // 线性:恒定速率 return remainingVolume / remainingTime; case DosingCurveType.Parabolic: // 抛物线:开始快,后来慢 // 使用二次函数减少速率 double parabolicFactor = 1.0 - Math.Pow(progress, 2); return (remainingVolume / remainingTime) * parabolicFactor; case DosingCurveType.Exponential: // 指数:开始慢,后来快 // 使用指数函数增加速率 double exponentialFactor = Math.Exp(progress) / Math.Exp(1); return (remainingVolume / remainingTime) * exponentialFactor; default: return remainingVolume / remainingTime; } } /// /// 设置PID参数 /// public void SetPIDParameters(double kP, double kI, double kD) { _proportionalGain = kP; _integralGain = kI; _derivativeGain = kD; // 重置积分项和误差 _integralTerm = 0; _lastError = 0; } /// /// 检查加注是否正在进行中 /// public bool IsDosingActive => _isDosingActive; } }