9 changed files with 495 additions and 41 deletions
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1,262 @@ |
|||
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; |
|||
} |
|||
} |
Loading…
Reference in new issue