feat: WP5 조성 trim rate 제한 추가 (CompRate, 듀티 대역폭 placeholder)
trim을 clamp(±5%) 후 RateLimiter로 완만히 이동(rate≤1.0/min placeholder, 현장 calibrate). 과도/역전 시 trim→0 graceful. 49/49. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ public sealed class StreamState
|
|||||||
public DeadTimeBuffer Dead { get; } = new();
|
public DeadTimeBuffer Dead { get; } = new();
|
||||||
public FirstOrderLag Lag { get; } = new();
|
public FirstOrderLag Lag { get; } = new();
|
||||||
public RateLimiter Rate { get; } = new();
|
public RateLimiter Rate { get; } = new();
|
||||||
|
public RateLimiter CompRate { get; } = new(); // WP5: 조성 trim rate 제한(듀티 대역폭)
|
||||||
public double LastRec { get; set; } = double.NaN;
|
public double LastRec { get; set; } = double.NaN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +139,7 @@ public sealed class FeedforwardEngine
|
|||||||
var (frontState, frontTrim) = ApplyFront(cfg, st, ts, temps, transient); // WO-5 프론트 위치
|
var (frontState, frontTrim) = ApplyFront(cfg, st, ts, temps, transient); // WO-5 프론트 위치
|
||||||
var tp = JudgeTempProfile(temps, st, transient); // §10 역전 판정
|
var tp = JudgeTempProfile(temps, st, transient); // §10 역전 판정
|
||||||
var f2 = ApplyFront2Point(st, ts, temps, transient); // WP5 1단계 2-point front
|
var f2 = ApplyFront2Point(st, ts, temps, transient); // WP5 1단계 2-point front
|
||||||
ApplyCompositionTrim(cfg, ff, st, f2, transient, tp.State, ref outs); // WP5 2·3단계 조성base+trim
|
ApplyCompositionTrim(cfg, ff, ts, st, f2, transient, tp.State, ref outs); // WP5 2·3단계 조성base+trim
|
||||||
// 역전/붕괴 시 front 트림 보류(방향 신뢰 불가)
|
// 역전/붕괴 시 front 트림 보류(방향 신뢰 불가)
|
||||||
if (tp.State is "온도역전" or "프로파일붕괴") frontTrim = null;
|
if (tp.State is "온도역전" or "프로파일붕괴") frontTrim = null;
|
||||||
var (mode, modeReason, feedRecSp) = ApplyRecovery(
|
var (mode, modeReason, feedRecSp) = ApplyRecovery(
|
||||||
@@ -340,13 +341,14 @@ public sealed class FeedforwardEngine
|
|||||||
|
|
||||||
// ── WP5 2·3단계: 조성목표 base(분율×feed) + 유계 trim(±5% clamp). LevelDriven 드로우 advisory(쓰기 없음). ──
|
// ── WP5 2·3단계: 조성목표 base(분율×feed) + 유계 trim(±5% clamp). LevelDriven 드로우 advisory(쓰기 없음). ──
|
||||||
// 분율 = sc.TargetCoeff(수동입력 시 supervisor가 치환). trim 게인·부호는 현장 calibrate 필요(데모 검증 불가).
|
// 분율 = sc.TargetCoeff(수동입력 시 supervisor가 치환). trim 게인·부호는 현장 calibrate 필요(데모 검증 불가).
|
||||||
private static void ApplyCompositionTrim(ColumnConfig cfg, double ff, ColumnState st,
|
private static void ApplyCompositionTrim(ColumnConfig cfg, double ff, double ts, ColumnState st,
|
||||||
(string? upState, double? upMetric, string? loState, double? loMetric) f2, bool transient,
|
(string? upState, double? upMetric, string? loState, double? loMetric) f2, bool transient,
|
||||||
string? tempProfileState, ref List<StreamAdvisory> outs)
|
string? tempProfileState, ref List<StreamAdvisory> outs)
|
||||||
{
|
{
|
||||||
const double clampFrac = 0.05; // ±5%(보수, 결정)
|
const double clampFrac = 0.05; // ±5%(보수, 결정)
|
||||||
const double gainPerDeg = 0.01; // 1%/°C placeholder — 현장 calibrate
|
const double gainPerDeg = 0.01; // 1%/°C placeholder — 현장 calibrate
|
||||||
bool block = transient || tempProfileState is "온도역전" or "프로파일붕괴"; // 과도/역전 시 trim 0
|
const double trimRatePerMin = 1.0; // trim rate 제한[/min] placeholder — 듀티 대역폭 현장 calibrate
|
||||||
|
bool block = transient || tempProfileState is "온도역전" or "프로파일붕괴"; // 과도/역전 시 trim→0
|
||||||
double devUp = f2.upMetric.HasValue ? f2.upMetric.Value - st.UpperFrontBase.Value : double.NaN;
|
double devUp = f2.upMetric.HasValue ? f2.upMetric.Value - st.UpperFrontBase.Value : double.NaN;
|
||||||
double devLo = f2.loMetric.HasValue ? f2.loMetric.Value - st.LowerFrontBase.Value : double.NaN;
|
double devLo = f2.loMetric.HasValue ? f2.loMetric.Value - st.LowerFrontBase.Value : double.NaN;
|
||||||
|
|
||||||
@@ -356,21 +358,20 @@ public sealed class FeedforwardEngine
|
|||||||
var sc = cfg.Streams.FirstOrDefault(x => x.Key == a.Key);
|
var sc = cfg.Streams.FirstOrDefault(x => x.Key == a.Key);
|
||||||
if (sc is null || ff <= 1e-6) return a;
|
if (sc is null || ff <= 1e-6) return a;
|
||||||
double baseSp = sc.TargetCoeff * ff; // 분율×feed
|
double baseSp = sc.TargetCoeff * ff; // 분율×feed
|
||||||
|
var stt = st.Stream(a.Key);
|
||||||
|
|
||||||
double dev; string src;
|
double dev; string src;
|
||||||
if (a.Key.Equals("B", StringComparison.OrdinalIgnoreCase)) { dev = devLo; src = "하부front(C−B)"; }
|
if (a.Key.Equals("B", StringComparison.OrdinalIgnoreCase)) { dev = devLo; src = "하부front(C−B)"; }
|
||||||
else if (a.Key.Equals("D", StringComparison.OrdinalIgnoreCase)) { dev = -devUp; src = "상부front(C−D)"; }
|
else if (a.Key.Equals("D", StringComparison.OrdinalIgnoreCase)) { dev = -devUp; src = "상부front(C−D)"; }
|
||||||
else return a with { CompositionBase = baseSp, RecommendedSpComposition = baseSp, TrimSource = "trim 미적용" };
|
else return a with { CompositionBase = baseSp, RecommendedSpComposition = baseSp, TrimSource = "trim 미적용" };
|
||||||
|
|
||||||
if (block || !Num.IsFinite(dev))
|
// 목표 trim(clamp). 과도/역전/무신호면 0으로. 그 다음 rate 제한으로 완만히 이동.
|
||||||
return a with { CompositionBase = baseSp, Trim = 0.0, RecommendedSpComposition = baseSp,
|
double rawTrim = (block || !Num.IsFinite(dev)) ? 0.0 : Num.Clamp(gainPerDeg * dev, -clampFrac, clampFrac) * baseSp;
|
||||||
TrimSource = $"{src} (과도/역전/무신호 — trim 0)" };
|
double trim = stt.CompRate.Step(rawTrim, trimRatePerMin, trimRatePerMin, ts);
|
||||||
|
string note = (block || !Num.IsFinite(dev))
|
||||||
double trim = Num.Clamp(gainPerDeg * dev, -clampFrac, clampFrac) * baseSp;
|
? $"{src} (과도/역전/무신호 — trim→0)"
|
||||||
return a with {
|
: $"{src} dev={dev:F2}°C (±{clampFrac:P0} clamp · rate≤{trimRatePerMin}/min · 게인·부호 현장 calibrate)";
|
||||||
CompositionBase = baseSp, Trim = trim, RecommendedSpComposition = baseSp + trim,
|
return a with { CompositionBase = baseSp, Trim = trim, RecommendedSpComposition = baseSp + trim, TrimSource = note };
|
||||||
TrimSource = $"{src} dev={dev:F2}°C (±{clampFrac:P0} clamp · 게인·부호 현장 calibrate · rate제한 후속)"
|
|
||||||
};
|
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user