You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					263 lines
				
				8.2 KiB
			
		
		
			
		
	
	
					263 lines
				
				8.2 KiB
			| 
											2 months ago
										 | using System; | ||
|  | using System.Collections.Generic; | ||
|  | using System.Linq; | ||
|  | using System.Text; | ||
|  | using System.Threading.Tasks; | ||
|  | 
 | ||
|  | namespace DyeingComputer.UserClass | ||
|  | { | ||
|  |     /// <summary>
 | ||
|  |     /// 加注曲线类型
 | ||
|  |     /// </summary>
 | ||
|  |     public enum DosingCurveType | ||
|  |     { | ||
|  |         Linear,      // 线性曲线
 | ||
|  |         Parabolic,   // 抛物线曲线(开始快,后来慢)
 | ||
|  |         Exponential  // 指数曲线(开始慢,后来快)
 | ||
|  |     } | ||
|  | 
 | ||
|  |     /// <summary>
 | ||
|  |     /// 液体加注PID控制器
 | ||
|  |     /// </summary>
 | ||
|  |     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; | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 当前比例阀开度 (0-100%)
 | ||
|  |         /// </summary>
 | ||
|  |         public double CurrentValveOpening { get; private set; } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 目标完成时间 (秒)
 | ||
|  |         /// </summary>
 | ||
|  |         public double TargetTime { get; set; } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 加注曲线类型
 | ||
|  |         /// </summary>
 | ||
|  |         public DosingCurveType CurveType { get; set; } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 容器最大容量
 | ||
|  |         /// </summary>
 | ||
|  |         public double MaxCapacity { get; set; } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 当前液位
 | ||
|  |         /// </summary>
 | ||
|  |         public double CurrentLevel { get; private set; } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 已加注体积
 | ||
|  |         /// </summary>
 | ||
|  |         public double DosedVolume { get; private set; } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 液体加注事件
 | ||
|  |         /// </summary>
 | ||
|  |         public event Action<double> OnDosingUpdate; | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 初始化液体加注控制器
 | ||
|  |         /// </summary>
 | ||
|  |         /// <param name="targetTime">目标加注时间(秒)</param>
 | ||
|  |         /// <param name="maxCapacity">容器最大容量</param>
 | ||
|  |         /// <param name="curveType">加注曲线类型</param>
 | ||
|  |         /// <param name="kP">比例增益</param>
 | ||
|  |         /// <param name="kI">积分增益</param>
 | ||
|  |         /// <param name="kD">微分增益</param>
 | ||
|  |         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; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 开始加注过程
 | ||
|  |         /// </summary>
 | ||
|  |         /// <param name="initialLevel">初始液位</param>
 | ||
|  |         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); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 停止加注过程
 | ||
|  |         /// </summary>
 | ||
|  |         public void StopDosing() | ||
|  |         { | ||
|  |             _isDosingActive = false; | ||
|  |             CurrentValveOpening = 0; | ||
|  |             OnDosingUpdate?.Invoke(0); | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 更新当前液位并计算新的阀门开度
 | ||
|  |         /// </summary>
 | ||
|  |         /// <param name="currentLevel">当前液位</param>
 | ||
|  |         /// <returns>新的阀门开度</returns>
 | ||
|  |         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; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 根据曲线类型计算期望的加注速率
 | ||
|  |         /// </summary>
 | ||
|  |         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; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 设置PID参数
 | ||
|  |         /// </summary>
 | ||
|  |         public void SetPIDParameters(double kP, double kI, double kD) | ||
|  |         { | ||
|  |             _proportionalGain = kP; | ||
|  |             _integralGain = kI; | ||
|  |             _derivativeGain = kD; | ||
|  | 
 | ||
|  |             // 重置积分项和误差
 | ||
|  |             _integralTerm = 0; | ||
|  |             _lastError = 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         /// <summary>
 | ||
|  |         /// 检查加注是否正在进行中
 | ||
|  |         /// </summary>
 | ||
|  |         public bool IsDosingActive => _isDosingActive; | ||
|  |     } | ||
|  | } |