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.
262 lines
8.2 KiB
262 lines
8.2 KiB
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;
|
|
}
|
|
}
|
|
|