一 先定兩段曲線與邊界歸屬
兩段線性換算不是把兩個 IF 接在一起就完成。先把輸入範圍、輸出端點和交界歸屬寫成規格。本篇案例採 x=0..100,分成第一段 x∈[0,50),由 y=0..80;第二段 x∈[50,100],由 y=80..100。第一段含左端不含右端,第二段含兩端,x=50 只有第二段能處理。
第一段斜率為 (80-0)/(50-0)=1.6,所以 y=1.6x。第二段斜率為 (100-80)/(100-50)=0.4,所以 y=80+0.4(x-50)。代入 x=25 得 40,x=50 得 80,x=75 得 90。這三個數字應先寫進離線測試表,再設計 PLC 中的分支。
每次運算先判斷輸入是否有效,再判斷它屬於哪一段;不要讓兩段各自用 x<=50,造成交界被重複處理。若資料超出 0..100,需明確選擇 Reject 或 Clamp,並保存原值。本文採用 OutOfRange 狀態示例,不把超界悄悄送入第一段或第二段。
| 區間 | 端點 | 公式 | 邊界規則 |
|---|---|---|---|
| 第一段 | x=0..50 | y=1.6x | [0,50) |
| 第二段 | x=50..100 | y=80+0.4(x-50) | [50,100] |
| 低於範圍 | x<0 | 不計算 | Reject/ClampLow |
| 高於範圍 | x>100 | 不計算 | Reject/ClampHigh |
兩段端點目前連續,第一段接近 50 時 y 接近 80,第二段在 50 時 y=80。連續不代表斜率相同;x=49.9 與 x=50.1 的輸出變化速度不同,這可能影響控制器對微小輸入變化的反應,必須列為適用限制。
兩段曲線的節點資料也要保留單位與版本。例如 x 是攝氏度、y 是百分比時,x=50 的物理意義必須寫清楚,不能只在程式註解留下數字。若日後把 y 改成工程單位,應建立新 curve_version,舊資料仍依原版本重算,避免同一歷史紀錄被兩套斜率解讀。
二 正算案例與重複 IF 的錯誤
用 本篇測試資料逐筆計算。x=0 走第一段得到 0,x=25 得 40;x=49 得 78.4。x=50 必須走第二段得到 80,x=75 得 90,x=100 得 100。若把 x=50 留在第一段,數值恰好仍為 80,錯誤可能被掩蓋;因此驗收還要檢查 branch_id,確認邊界歸屬不是只看 y。
若程式先寫 IF x<=50 計算第一段,再寫 IF x>=50 計算第二段,x=50 會被寫兩次。本例兩式在交界都得到80,數值暫時相同;若日後端點不同或分支有計數等副作用,執行順序就會影響結果。正確做法是先設定 branch_id=Invalid,再使用互斥的 IF/ELSE IF 或等價的單一路徑,讓每個有效 x 只產生一次輸出。
| x | 預期區段 | 計算 | y | 驗收 |
|---|---|---|---|---|
| 0 | 第一段 | 1.6×0 | 0 | branch=A |
| 25 | 第一段 | 1.6×25 | 40 | branch=A |
| 49 | 第一段 | 1.6×49 | 78.4 | branch=A |
| 50 | 第二段 | 80+0.4×0 | 80 | branch=B |
| 75 | 第二段 | 80+0.4×25 | 90 | branch=B |
| 100 | 第二段 | 80+0.4×50 | 100 | branch=B |
浮點數比較要考慮資料型別。如果 x 來自整數,邊界 50 可以精確比較;若 x 是量測後的浮點值,仍應保留原始值與比較結果,不能用任意四捨五入把 49.999 變成 50 而改變區段。是否要設死區或容差,是規格決定的另一件事。
本例的表格是數學驗收資料,不是某個 PLC 的現成指令。實作時需核對目標語言的浮點常數、型別轉換、比較運算和除法精度,並確認 x=100 的右端是否允許。
測試時除整數點外,還要使用靠近交界的 49.999 與 50.001。前者只能進第一段,後者只能進第二段;若浮點解析度不足,應在規格中定義可接受誤差與輸入量化方式。不能因兩筆 y 看起來接近,就省略 branch_id 的核對。
三 交界跳變先修規格
若第一段終點被規格寫成 y=80,第二段起點卻寫成 y=82,x=50 會出現兩個問題:第一段的最後一點趨近 80,第二段在交界直接給 82;若兩個分支都包含 50,還會有重複歸屬。這不是靠多寫一個 IF 就能修好的程式問題,而是輸入輸出規格本身不連續。
應先與製程負責人決定交界策略:修正第二段起點為 80 以保持連續;或明確接受 2 單位跳變,並在驗收與警報說明中記錄。若選擇平滑過渡,也要重新定義曲線,而不是在程式中偷偷加補償。本文後續沿用連續的 80,跳變版本只作失敗測試。
| 版本 | 第一段 x→y | 第二段 x→y | x=50結果 | 判定 |
|---|---|---|---|---|
| 連續規格 | 0→0、50→80 | 50→80、100→100 | 80 | 可用 |
| 跳變規格 | 0→0、50→80 | 50→82、100→100 | 依歸屬為80或82 | 需求未決,不可交付 |
| 重複IF | 兩段都含50 | 兩段都含50 | 取決於最後寫入 | 程式錯誤 |
| 缺口規格 | 第一段<50 | 第二段>50 | 50無分支 | 漏值 |
驗收不只測 x=25、50、75,還要測 49.999、50、50.001,並記錄 branch_id、y_raw、y_display。若規格接受跳變,三個點的差異應出現在需求文件;若規格要求連續,則 x=50 左右的結果必須符合允差。
來源資料若本來就是校正表,也可能出現不單調或重複 x。先檢查節點排序、唯一性與端點,不能套用分段公式後才發現輸入表不滿足假設。
若現場要求輸出平滑,應把平滑器視為曲線外的另一個功能,分別驗收原始分段值與平滑後值。直接在交界處偷偷加減一個補償,會使反算公式失去對應關係。每次版本變更都要重新列出兩段斜率、端點和允差。
四 反算 多值與非單調限制
正算曲線只有在嚴格單調且區段輸出範圍清楚時,才適合直接反算。此例第一段 y=0..80,第二段 y=80..100,反算時 y∈[0,80) 走第一段 x=y/1.6;y∈[80,100] 走第二段 x=50+(y-80)/0.4。y=80 應選定唯一區段,例如歸給第二段,得到 x=50。
若兩段都能產生相同 y,例如第一段 y=0..80、第二段 y=80..0,反算 y=40 會有兩個 x。這是多值關係,不能用一個 IF 假裝只有一個答案。資料模型應回傳候選數量、候選 x 或拒絕反算,並說明控制用途不能直接採任意一個。
若曲線有平坦區,像 y=80 在 x=50..60 都不變,反算 y=80 會得到一整段 x,而不是單一 x。若輸出曲線非單調,反算不能直接對輸出值做單調二分搜尋;輸入節點仍可按x排序作正常正算插值。先用節點檢查輸入輸出是否嚴格遞增或遞減,再決定可用的反算演算法。
| y輸入 | 反算區間 | x計算 | 結果性質 |
|---|---|---|---|
| 0 | 第一段 | 0/1.6 | x=0 |
| 40 | 第一段 | 40/1.6 | x=25 |
| 80 | 第二段 | 50+(0/0.4) | x=50,邊界唯一 |
| 90 | 第二段 | 50+10/0.4 | x=75 |
| 100 | 第二段 | 50+20/0.4 | x=100 |
反算的驗收要先定端點歸屬,再測 y=80。若把 y=80 先四捨五入或夾限,可能把 79.96 和 80.04 都送到同一段,造成看似穩定但實際錯誤。保留 y_raw、segment_id 和 clamp_state,才能追查分支決策。
反算流程應先檢查 y 是否位於某一段的輸出範圍,再決定區段。若兩段輸出範圍重疊,回傳候選清單與數量;若沒有候選,回傳 OutOfRange。把 y 先夾到 0..100 會掩蓋無效輸入,因此夾限狀態必須和結果分欄保存。
五 驗收 FAQ與來源
本篇驗收表應至少包括 x=0、25、49、50、75、100、-1、101,以及兩段跳變版的 x=50。每筆同時核對 y、segment_id、valid 與邊界狀態。若輸入超界採 Reject,就不得留下看似正常的 y;若採 Clamp,畫面值與狀態必須分開。
提交前再檢查三件事:每個有效 x 只有一個區段、端點規格沒有缺口或重複、反算在單調條件下能回到原 x。任何一項不成立,就先回到規格或資料表修正,不能用重複判斷把問題藏起來。
FAQ1:為什麼 x=50 必須明確指定第二段?答:因為第一段採 [0,50),第二段採 [50,100],這樣每個有效輸入只有唯一歸屬。
FAQ2:若第一段終點 80、第二段起點 82,應如何處理?答:先修正規格或明確接受跳變,不能靠兩個 IF 重複寫值掩蓋缺口。
FAQ3:y=80 反算是否一定只有 x=50?答:在本案例的連續且單調定義下是 x=50;若曲線平坦或非單調,可能是多個 x,必須回傳多值或拒絕。
FAQ4:NumPy 的 interp 可以直接當 PLC 指令嗎?答:不能。它只作一維插值的數學參考;PLC 實作仍需核對目標語言的型別、比較、除法與邊界行為。
參考:NumPy interp 官方文件:要求樣本點遞增,提供一維分段線性插值與左右邊界參數(僅作數學參考,非 PLC API)。
參考:NIST Dataplot Linear Interpolation:線性插值與端點計算概念(僅作數學參考)。