工作流程:感測器資料映射¶
本工作流程說明如何將外部感測器資料(動力計、智慧刀把、加速規)映射至仿真刀具路徑,使仿真步能索引到實際量測資料。
flowchart TD
Prepare["準備感測器 CSV 資料"]
Configure["設定時間映射"]
Simulate["執行仿真"]
Map["將資料映射至仿真步"]
View["檢視映射結果"]
Synth["沒有控制器日誌?<br>由仿真時間軸<br>合成控制器表"]
Prepare --> Configure --> Simulate --> Map --> View
Simulate -.-> Synth -.-> Map
概覽¶
資料映射將外部感測器量測與仿真加工步建立關聯。映射後,每個步皆可參照真實的力、力矩、加速度資料,用於:
- 檢視加工狀態
- 訓練銑削係數(見 銑削力參數訓練)
- 校正銑削係數
- 比較仿真與量測力
依資料量與用途,映射可分為 一對一(每步映射至一筆資料)或 一對多(每步映射至多筆來自高取樣率感測器的資料)。
映射本身只是幾個 SessionShell 呼叫。在網頁應用程式中,兩個檔案映射各自是一種任務命令,其餘的呼叫放進「腳本」命令——見從網頁應用程式;控制器沒有寫下日誌時,由仿真自己的時間軸代替它——見沒有控制器日誌時。欄位帶著廠商自己標題的檔案,照原樣就能映射,只要在命令上指名那些標題;只有單位與讀取器不同時,才需要轉換出一份副本——見廠商提供的檔案。
1. 準備感測器 CSV 資料¶
感測器資料格式¶
感測器檔是序列映射器讀取的檔案——策略 C、策略 D,以及兩層串聯的第二層都讀它。它需要一行標頭,包含一個時間欄位與它所帶的通道:
| 通道 | 接受的標頭 | 單位 |
|---|---|---|
| 時間 | ActualTime、ActualDateTime、TimeTag 或 Time——依上列順序,檔案裡有的第一個就是被讀取的那一個 |
見下文 |
| 力(動力計) | Fx、Fy、Fz,或 Workpiece.Fx、Workpiece.Fy、Workpiece.Fz |
N |
| 力矩(智慧刀把) | Mx、My、Mz,或 Holder.Mx、Holder.My、Holder.Mz |
N·m |
| 加速度 | Ax、Ay、Az |
g——以 g 為單位讀取;產品中沒有任何步屬性或圖表使用這個通道 |
時間儲存格有三種形式。單純的數字是秒。<時>:<分>:<秒>.<小數秒> 是時間碼——記錄器以這種方式寫出它的時鐘時,就是一天中的時刻。帶有日期的儲存格是絕對時刻,透過專案的映射錨點重新基準化:錨點只設定一次,設為專案中任何一次轉換所遇到的第一個絕對時刻當天的 00:00,因此同一天的錄製資料也會變成一天中的時刻。錨點隨專案儲存,而且不會被 ClearTimeMappingData() 清除;在某一天設定了錨點、現在要映射另一天錄製資料的專案,需要在執行前於腳本中寫 MappingAnchorDateTime = null;,或指定一個明確的值。
不論機器的語言設定為何,數字一律以句點作為小數點讀取——與產品寫出數字的方式相同;匯出 11,5 這種寫法的記錄器,檔案需要先轉換。
檔案沒有帶的通道讀成 NaN。其他任何欄位都會被這個讀取器忽略——它只保留時間、力、力矩與加速度,其餘一概不留。要以自訂欄位送到步上的欄位,應放在下方的控制器檔(其額外欄位會嵌入步中),或放在直接播放的 CSV 裡(策略 B)。
控制器資料格式(兩層映射)¶
控制器 CSV 須含 FileNo、LineNo,以及三個時間欄位其中之一:
FileNo,LineNo,ActualTimecode,ActualDateTime,MC.X,MC.Y,MC.Z,...
1,6,00:00:00.030,2026-03-16 15:57:45.559000,0,0,0.37,...
| 時間欄位 | 內容 |
|---|---|
ActualTimecode |
由 WriteStepFiles (API) 寫出、相對於本次運行的時間碼。ActualDateTime(控制器的絕對時刻)伴隨其旁,為選用。 |
ActualTime |
舊有的單一欄位,目前仍會讀取。若儲存格是單純的時間碼,即直接採用;若是絕對日期時間,則透過映射錨點的轉換器重新基準化。 |
EndTimecode |
仿真的結束時間碼,在上述兩者都無法解析時作為後備。該次更名之前寫出的檔案帶的是 AccumulatedTime,同樣被接受。 |
標頭列若三者皆無,會被指名拒絕,而不是映射成空無一物。本產品匯出的檔案帶的是前面那一對,而非舊有的單一欄位 —— 這也是為什麼步檔案不必編輯標頭就能回放並映射。
FileNo 與 LineNo 都從 1 起算。FileNo 依播放進入檔案的順序編號:主程式是 1,它呼叫的第一個副程式是 2,而每一次呼叫都自成一個檔案——被呼叫兩次的副程式,第二次呼叫時會取得新的編號。計數器在每次重置時重新起算,所以每次執行的編號都相同。LineNo 是該檔案內的行號。
多列可以帶相同的 (FileNo, LineNo)。此時該行各步的時間,由第一列的時間線性插值到最後一列的時間,頭尾兩步保留列本身的時間;一行只有一列時,該行的每一步都標上那一個時間。其他每個欄位——機械座標 MC.X … MC.C 除外——都會以自訂欄位嵌入該行的每一步,並以同樣方式插值。腳本呼叫 RegisterStepVariable("<column>", …),就能把這樣的欄位變成圖表可以顯示的步屬性。
欄位標題可以設定¶
上面兩張表中的標頭是預設值,不是對檔案的要求。每個映射呼叫都接受一份設定,指名檔案自己的標題,讀取器查找的每個欄位各一個——控制器檔用 MapSingleCsvConfig (API),感測器檔用 MapSeriesCsvConfig (API):
MapSingleByCsvFile("Mapping/controller.csv", new MapSingleCsvConfig {
FileNoTag = "ProgramNo", LineNoTag = "BlockNo", ActualTimeTag = "Timestamp" });
MapSeriesByCsvFile("Mapping/sensor.csv",
new MapSeriesCsvConfig { TimeTag = "Clock", MzTag = "Tq" });
只有不同的欄位需要指名;沒動的標籤保留預設值,清空的標籤也會回到預設值。
| 檔案 | 標籤 | 預設標題 |
|---|---|---|
| 控制器 | FileNoTag、LineNoTag |
FileNo、LineNo |
| 控制器 | ActualTimecodeTag、ActualDateTimeTag |
ActualTimecode、ActualDateTime |
| 控制器 | ActualTimeTag |
ActualTime |
| 控制器 | SimTimeTag |
EndTimecode——只有在這個標籤保持此預設值時,才會一併接受 AccumulatedTime |
| 控制器 | MachineCoordinatePrefix |
MC.——機械座標欄位是這個前綴加上 X、Y、Z、A、B、C |
| 感測器 | TimeTag |
ActualTime,其次 ActualDateTime、TimeTag、Time |
| 感測器 | FxTag、FyTag、FzTag |
Fx … 或 Workpiece.Fx … |
| 感測器 | MxTag、MyTag、MzTag |
Mx … 或 Holder.Mx … |
| 感測器 | AxTag、AyTag、AzTag |
Ax、Ay、Az |
兩種檔案有兩條規則不同。在控制器檔中,標籤單純取代標題,三個時間來源保持原本的順序——只有一個時間欄位的日誌,就把它指名在 ActualTimeTag 來映射,其儲存格可以是時間碼,也可以是帶日期的時刻。在感測器檔中,保持預設值的標籤保留上表該列接受的每一個標頭,該通道也仍是選用的;改過的標籤則會被精確查找,檔案必須帶有它:標頭列裡沒有它,就會被拒絕並指出該標題,因此打錯的標題不會被當成錄製資料其實沒有的通道。
設定中指名的控制器欄位會被取用;其他每個欄位仍以自己的標題嵌入步中,所以改標籤永遠不會改掉自訂欄位的名稱。
2. 設定時間映射¶
策略 A:一對一映射(MapSingleByCsvFile)¶
MapSingleByCsvFile 讀取上述控制器格式的檔案,並使用時間插值將每一列資料映射至一個仿真步。
策略 B:以 PlayCsvFile 一對一映射¶
PlayCsvFile 可直接由 CSV 驅動仿真,每一列成為一個步。CSV 中的自訂欄位會自動出現在每一步上。
策略 C:一對多全域映射(MapSeriesByCsvFile)¶
對高取樣率資料,先以一對一映射建立 ActualTime,再映射時間序列:
PlayNcFile("NC/file1.nc");
MapSingleByCsvFile("Data/controller.csv"); // 建立 ActualTime
MapSeriesByCsvFile("Data/sensor.csv"); // 映射高取樣率資料
策略 D:一對多局部映射(以錨點為基準)¶
用於把錄製資料映射至 NC 路徑的特定段落,段落的兩端以錨點定在程式本身之中。
步驟 1 — 為錄製資料的時間窗命名。 每個鍵把一個檔案與它的一段時間窗配成一對;這段時間窗會被線性地拉伸到錨點所選取的步上,§4 的提示所描述的漂移就是這樣修正的。
ClearTimeMappingData();
AddTimeDataByFile("lineA", "Mapping/sensor1.csv", "18:25:51.7100", "18:26:12.9910");
AddTimeDataByFile("lineB", "Mapping/sensor1.csv", "18:26:30.5750", "18:27:12.2880");
時間窗的兩端可以是秒、時間碼或帶日期的時刻。表中已有的鍵不會被取代——該呼叫會傳回 false。
步驟 2 — 在 NC 程式中以錨點定出段落。 錨點是寫在程式自己註解裡的 C# 呼叫。執行器的註解語法先把註解從單節取出——Fanuc 家族控制器(Fanuc、Mazak、Syntec)用括號,Siemens 用 ; 到行尾,Heidenhain 用 //、; 或括號——接著腳本語法用兩個標記把腳本從註解文字取出:@@ 是在該單節運動之前執行的腳本,@@^ 是在之後執行的。這兩個標記是該語法的設定,而不是固定字元——見 NC 碼中的腳本命令。
G01 X13. F20 (@@LineSelection("lineA", FirstTouch, ShiftTime_s(2), LineEnd, ShiftDistance_mm(-1));)
G01 X25. F10 (@@LineSelection("lineB", FirstTouch, null, LastTouch, null);)
段落跨越多行時,以 BeginSelection 在第一行的運動之前開啟它,以 EndSelection 在最後一行的運動之後關閉它:
G01 Z-0.2 F1500 (@@BeginSelection("region1", FirstTouch, null);)
...
G01 X10. Y10. (@@^EndSelection("region1", LineEnd, null);)
括號註解從該行第一個 ( 延伸到最後一個 ),所以呼叫裡的括號不成問題——但要讓錨點成為該行唯一的括號文字,因為同一行的第二則註解會被吞進腳本裡。在 Siemens 上,同一個錨點寫在 ; 之後:
呼叫裡的名稱——FirstTouch、LineEnd、ShiftTime_s、null——都是 SessionShell 的。內嵌腳本與腳本命令編譯時用同一組參考與命名空間引用 (using),所以腳本命令能呼叫的,錨點也都能呼叫。有五條規則決定錨點是否真的做了它看起來在做的事:
FirstTouch從開始錨點起順向搜尋。 首次接觸的搜尋從錨點所在那一行開始的步起,順著程式往下找到結束錨點,所以開始錨點可以放在段落的第一條進給行上,而不必放在真正碰到胚料的那一行。LastTouch則以同樣方式從結束錨點起逆向往回找。搜尋找不到任何接觸步的段落什麼也不映射,而且沒有任何訊息說明。- 一個鍵在每次播放中只開啟、關閉一次。 對已經關閉的鍵再下一次
EndSelection,或對應的BeginSelection從未執行過的EndSelection,會在該行結束時丟出例外;本次執行會在那一步回報這個例外,該段落不會被映射。會把同一段落播放兩次的程式需要兩個鍵。 - 重置會清除錨點。 持有這些選取範圍的宿主會在工作階段重置時被清空,每一步的映射樣本也一起清空;步驟 1 的鍵 → 時間窗對照表與映射錨點日期則不會,這就是為什麼腳本以
ClearTimeMappingData()開頭。 - 錨點本身不需要任何授權功能——但 3.2.7 到 3.2.31 的套件只在有
NcComposition時才執行它們:沒有這個授權功能時,第一個內嵌腳本會回報一次Script--NotLicensed,其後的每一個都被靜默跳過,於是錨點什麼也沒選取,什麼也沒映射。之後的套件無條件執行它們,一如 3.1。 - 編譯不過的錨點會在它所屬的步回報
Script-Compile--Error;任務的編譯檢查只涵蓋腳本命令,不涵蓋程式中的註解。
錨點旗標:
| 旗標 | 說明 |
|---|---|
LineBegin |
該行的運動起始點 |
LineEnd |
該行的運動結束點 |
FirstTouch |
第一個接觸工件的點 |
LastTouch |
最後一個接觸工件的點 |
偏移選項:
| 偏移 | 說明 |
|---|---|
null |
無偏移 |
ShiftTime_s(<秒>) |
時間偏移(正值為順向,往較晚的時刻) |
ShiftDistance_mm(<毫米>) |
距離偏移(正值為順向,沿路徑前進) |
Warning
;@… 是前一代執行器的標記。在 Siemens 上,或在 Fanuc 家族控制器的括號內,帶著它的註解只是普通註解:錨點什麼也不做,也沒有任何訊息說明;在 Fanuc 家族控制器上裸寫時,它是殘留文字,建置會發出 UnparsedText--Remaining 警告。
有兩種回報表示所用的建置早於錨點能在 SoftNc 執行器上運作之時。帶錨點的步回報 Script-Run--Exception("BeginSelection has to be used in Command Source File"),是執行器還沒有發布選取呼叫所依賴的行邊界:錨點已被解析並執行,卻什麼也沒映射。帶錨點的行回報 Syntax-Build--Exception、而執行變成一連串快速移動,則是更舊的版本:帶標記的單節失去了運動字,被當成先前模態的快速移動播放。在這兩種建置上,都改用兩層串聯來映射——真實的控制器日誌,或合成的控制器表(見沒有控制器日誌時)。
選取結束時自動映射¶
EnableMapOnSelectionEnd 控制選取結束時是否自動映射(預設:true):
清除映射資料¶
策略 D 的鍵 → 時間窗對照表與映射錨點隨專案儲存,重置後仍然保留;每一步的映射樣本與程式中的錨點則不會——每次執行都會重建它們。加入之前先清除對照表,重新執行時才不會發現自己的鍵已經被佔用:
錨點日期不在它清除的範圍內——見感測器資料格式。
3. 執行仿真¶
為何要解譯 NC 而非直接播放 CSV?
系統 NC 解譯器產生的仿真路徑比直接控制器 CSV 回放更準確;後者取樣解析度有限,會扭曲刀具路徑。
4. 映射資料¶
仿真後,套用步驟 2 選定的映射策略。兩層串聯方式:
// 第一層:控制器資料 → 仿真步(透過 FileNo/LineNo → ActualTime)
MapSingleByCsvFile("Data/controller.csv");
// 第二層:感測器資料 → 仿真步(透過 ActualTime → 感測器讀值)
MapSeriesByCsvFile("Data/sensor.csv");
串聯之所以可行:
- 仿真步與控制器資料共享
FileNo/LineNo錨點 - 控制器資料與感測器資料共享
ActualTime錨點 - 串聯後,仿真步即可索引感測器資料
第二個呼叫逐一走過第一個呼叫標上時間的步,把每一步與它結束之後一個週期內的樣本配對——預設的 SpindleCycle 取樣模式下是主軸轉一圈,FluteCycle 下是一個刀刃的弧段。時間窗內沒有任何樣本的步——在錄製範圍之外,或落在資料中斷處——保持未映射,並計入只發出一次的 Map-ShotGap--StepsSkipped 警告。
Tip
由於工具機加減速行為,仿真時間與實際時間在較長時段會發散。以錨點為基準的線性投影可修正此漂移。
5. 檢視映射結果¶
映射後,每一步都帶著它的樣本。在網頁應用程式中,它們出現在三個地方——所選步的感測器週期圖、由樣本推導的步屬性,以及本次執行的訊息——詳見從網頁應用程式;映射得到的步屬性若是「視覺指標時序圖」所檢視的鍵,也會在 3D 場景中為工件著色。
從腳本匯出映射結果:
完整兩層映射範例¶
// 配置解析度
MachiningResolution_mm = 0.125;
EnablePhysics = true;
// 清除既有映射資料
ClearTimeMappingData();
// 用 NC 解譯器產生準確路徑的仿真
PlayNcFile("NC/machining.nc");
// 映射控制器資料(含 FileNo、LineNo、ActualTime)
MapSingleByCsvFile("Data/controller.csv");
// 映射高取樣率感測器資料(含 ActualTime 與力/力矩)
MapSeriesByCsvFile("Data/sensor.csv");
// 匯出結果
WriteStepFiles("Output/[NcName].step.csv");
從網頁應用程式¶
兩個檔案映射各自是一種任務命令:「CSV 一對一映射」(Map Single By CSV File)與 「CSV 一對多映射」(Map Series By CSV File),位於「新增命令」的「映射」類別中(見其他命令)。每一個都帶著它的檔案與該檔案的欄位標題,感測器那一個另帶「週期取樣模式」。上面其他的呼叫放進「腳本」命令,和任何任務腳本一樣,針對執行中的工作階段求值(見其他命令)。檔案放在專案資料夾中,經由檔案路由上傳(見檔案與具名根目錄(英文,未鏡像))。
任務的形狀是在「程式檔案」之後、同一個「清單」裡放兩個映射命令:
▸ Mapping
腳本 [clear] ClearTimeMappingData();
程式檔案 NC/machining.nc
CSV 一對一映射 Mapping/controller.csv
CSV 一對多映射 Mapping/sensor.csv
SessionShell 在整次執行中是同一個物件,每個命令都依序針對它求值,所以這份清單等同於把完整範例寫進一個「腳本」命令、中間夾著 PlayNcFile。順序也以同樣的方式影響結果:控制器檔給各步它們的實際時間,感測器檔再透過這些時間配對。策略 D 則是在「程式檔案」之前放一個帶 AddTimeDataByFile 呼叫的「腳本」,之後不放映射命令,由程式中的錨點觸發映射。
透過 HTTP 時,任務是一次一個項目建構起來的(專案建構 §3):
| 步驟 | 請求 |
|---|---|
| 加入映射項目 | POST /api/Mission/list-command/entries/{listPath},本體為 {"CommandType": "mapsinglebycsvfile"} 或 "mapseriesbycsvfile",位置的指定方式與下面的「腳本」項目相同。 |
| 設定它的檔案與欄位標題 | GET /api/Mission/commands/{path}/fields 列出各欄位——relFile,接著每個欄位標題各一個(fileNoTag、actualTimeTag、… / timeTag、mxTag、…),感測器命令上還有 cycleSamplingMode 及其 options。PUT /api/Mission/commands/{path}/fields/{key} 接受裸的 JSON 值:"Mapping/controller.csv"、"Timestamp"、"FluteCycle"。不在選項清單中的值會被拒絕。 |
| 加入「腳本」項目 | POST /api/Mission/list-command/entries/{listPath},本體為 {"CommandType": "script", "IsEnabled": true, "InsertIndex": n}——n 是在清單中的位置,0 為第一個;省略時項目放在最後。回應中的 path 從此用來指定這個項目;GET /api/Mission/list-command/entries 列出整個任務及每一個 path。 |
| 寫入腳本 | PUT /api/Mission/commands/{path}/script,本體為 {"ScriptText": "…", "Title": "…"}。回應帶有 contentHash;之後的 PUT 可以把它以 ExpectedContentHash 送回,若腳本在這期間被別處編輯過,該 PUT 就會以 409 content_hash_mismatch 被拒絕。 |
| 檢查能否編譯 | POST /api/Mission/compile-check 編譯每一個已啟用的「腳本」命令,回應 isValid 與一個 scripts 陣列,每個腳本列出其 path、title、hasErrors 與 diagnostics(id、severity、message、line、column);POST /api/Mission/commands/{path}/compile-check 只檢查一個。有已啟用的腳本帶錯誤時,啟動會被拒絕,並回應 code: "script_compile_error"——見透過 HTTP API 進行重播驗收 §2。 |
| 儲存 | POST /api/Project/save——腳本文字與標題會寫入專案檔。 |
結果出現在哪裡:
- 所選步的感測器週期圖。 選取一個步——在時序圖或刀具路徑上點一下,或以
POST /api/execution/cl-strip/select-step送出{"stepIndex": n}——「步資訊」欄的「感測器主軸力矩週期」就會畫出該步在一個週期時間窗內、隨時間變化的Mx、My、Mz樣本,「感測器切削力週期」則畫出它的Fx、Fy、Fz。這兩張圖都從「偏好設定」選單開啟,預設為關閉(檢視一個步)。透過 HTTP,同一組序列是GET /api/execution/cycle-line/sensor-spindle-moment,回應{hasData, ts, xs, ys, zs},力則是GET /api/execution/cycle-line/dynamometer-force;hasData為 false 表示該步沒有樣本。 - 由樣本推導的步屬性。
AvgAbsTorqueByMapping_Nm是該步樣本Mz絕對值的平均,AvgMomentXyByMapping_Nm是彎矩大小的平均;AvgAbsTorqueErrorRatioWithMapping與AvgMomentXyErrorRatioWithMapping是(仿真 − 量測)/量測,量測值低於 0.1 N·m 時為空。可在「步屬性」的選擇器中挑選它們,或在「視覺指標時序圖」的屬性選擇器中選一個,沿著執行畫出來(觀看執行);透過 HTTP,GET /api/execution/step-properties列出這些鍵,而重播驗收 §4 的時序圖請求可以傳回其中任一個的序列。這四個是物理鍵:只有在該次執行啟用物理時(任務的「啟用物理模擬」設定),它們才會被列出與畫出。 - 訊息,在
GET /api/Execution/messages的shell清單中。CsvEmbed--Begin與CsvEmbed--Done包夾控制器那一層。Map-ShotGap--StepsSkipped是一則警告,計數一個週期時間窗內沒有任何樣本的步:錄製範圍之外的步——錄製資料沒有涵蓋整支程式時必然會有——加上錄製範圍內的任何資料中斷。TimeShotFile--NotFound表示找不到感測器檔,TimeShotFile--ReadFailed表示感測器檔的標頭列缺少時間欄位或命令所指名的欄位標題,訊息中會寫出缺少的標題。控制器檔無法讀取,或缺少檔案編號、行號或時間欄位時,沒有自己專屬的訊息;它會讓呼叫映射的那一方出錯。從映射命令呼叫時是Map-SingleCsv--Fault(感測器檔則是在它的TimeShotFile--ReadFailed之後出現Map-SeriesCsv--Fault),從腳本呼叫時是ScriptCommand-Run--Fault——每一則都會指出缺少的標頭,而且都讓任務繼續進行到下一個項目。
沒有控制器日誌時¶
串聯的第一層是為了給每一步一個真實時鐘的時間。控制器沒有寫下日誌時,由仿真自己的時間軸代替它:每一步本來就帶有 FileNo、LineNo 與它仿真的結束時間,控制器表唯一需要補上的,是仿真時鐘與錄製資料時鐘之間的偏移量。
從執行取得時間軸。 「執行後作業」的「步驟檔案輸出」或 WriteStepFiles 寫出的步檔案就帶有這些欄位——FileNo、LineNo、EndTimecode,以及在未映射的執行中寫成 null 的 ActualTimecode——控制器讀取器照原樣讀取它,實際時間儲存格無法解析時就退回使用 EndTimecode。含副程式的工作,要從下面的時序圖請求取得時間軸,而不是從步檔案:每當播放回到某支程式時,步檔案寫出器都會重新開啟該程式的檔案,所以呼叫了副程式的主程式只會留下返回之後的那些列。透過 HTTP,
GET /api/execution/strip-chart?aspect=Individual&inspectingKey=FileNo&xValueCategory=IndexByTime&widthHint=0&dispBegin=0&dispEnd=<count>
會在 xs 中傳回每一步的結束時間,在 items[0].max 中傳回它的 FileNo;同一個請求改用 inspectingKey=LineNo 則得到行號。
加上偏移量。 在每一列把 ActualTimecode 寫成 EndTimecode + offset,格式為時間碼(hh:mm:ss.fff),並保留 FileNo 與 LineNo。偏移量是仿真時間零點時錄製資料時鐘的讀數:記錄器寫出的是時鐘時,它是一天中的時刻;記錄器從自己啟動時開始計數時,它是秒數。
或者平移錄製資料,而不是改寫控制器表。 錄製資料的時間欄位帶的是絕對日期時間時,根本不必寫任何表。步檔案保持原樣——控制器讀取器會給每一步它仿真的 EndTimecode——並在映射之前,把映射錨點設為仿真時間零點時錄製資料時鐘的讀數;感測器讀取器會把每個絕對樣本時間換算成它與錨點的距離,這個距離便落在仿真時鐘上:
MappingAnchorDateTime = new DateTime(2025, 3, 4, 10, 0, 3, 750); // 仿真零點時的錄製資料時鐘
MapSingleByCsvFile("Output/finishing.nc.step.csv"); // 程式自己的步檔案
MapSeriesByCsvFile("Mapping/sensor.csv");
這麼做有兩個條件。步檔案必須來自一次 ActualTimecode 欄位為 null、或已經是仿真時間碼的播放:控制器讀取器在該欄位可以解析時會優先採用它,而在一次標上牆上時鐘時間的映射之後寫出的步檔案,會給每一步一個落在錄製範圍之外的時間——映射於是什麼也沒涵蓋,而且只透過 Map-ShotGap--StepsSkipped 說明。另外,新任務的第一次播放還沒有步檔案,因為「執行後作業」是在映射腳本之後才寫出它:先把映射腳本關掉播放一次。只播放一次的副程式會保有一份自己完整的步檔案,要指名的就是那一份;被呼叫多次的副程式只保留最後一次呼叫。
用相關性找偏移量,不要只看一個峰值。 把一條仿真序列與一條量測序列放到同一個時間格網上——仿真扭矩是時序圖或步檔案中以 IndexByTime 排列的 AvgAbsTorque_Nm,量測的是 Mz——把兩者各自化為包絡(每個格子內的最大絕對值),然後對兩份完整記錄做互相關;最大值處的延遲 (lag) 就是偏移量。只看一個峰值不夠:精修路徑每隔幾秒就重複一次,一個峰值和每一次重複都一樣吻合,所以峰對峰的對齊可能早或晚整整一趟。接著對記錄的前半段與後半段分別做同樣的相關。兩個延遲必須一致;延遲沿著記錄逐漸增大,表示機台的實際時序與仿真時序在發散(加減速、暫停),一個偏移量無法映射整支程式:此時要為記錄的每一段各擬合一個偏移量,並用各自的偏移量寫出該段的列,或改用真實的日誌。
中途停止的錄製資料映射到它結束為止:其結束之後的步,時間窗內沒有樣本,會以 Map-ShotGap--StepsSkipped 一次回報,並附上數量。這個數量就是檢查——它應該等於錄製結束之後的步數,不能更多。
在信任檔案之前先問廠商:時間基準——樣本由哪個時鐘標記、取樣率多少,是控制器的時鐘還是感測器自己的;校正——每個通道從檔案數字換算成 N·m 的係數,以及哪個通道是繞刀軸的扭矩;以及彎矩通道的參考點——它們所參照的刀軸上高度,刀具的觀測點必須設在這個高度(智慧刀把)。
廠商提供的檔案¶
感測器檔很少帶著 §1 的標頭送來,而它也不必如此:映射命令(或呼叫的設定,見 §1 欄位標題可以設定)會指名廠商自己的標題,所以檔案照原樣就能映射。真正要做的,是判斷廠商的哪些欄位可以名副其實地指名為讀取器所賦予的通道。
- 時間。 把廠商的時間欄位指名為「時間欄位」(
TimeTag);其儲存格可以是秒、hh:mm:ss.fff或帶日期的時刻,如 §1 所述。保留廠商記錄時所用的時鐘——偏移量由控制器表吸收。 - 扭矩。 繞刀軸的軸向扭矩是「Z 向力矩欄位」(
MzTag),單位 N·m。它就是上述扭矩步屬性拿來比較的通道。 - 彎矩。 「X 向力矩欄位」/「Y 向力矩欄位」是以觀測點為參考的彎矩,單位 N·m。只有在廠商說明了它們的單位與所參照的點時,才把廠商的彎矩通道指名到這裡:參考高度未知的彎矩,是一個沒有任何東西會回報的倍率誤差,而原始應變讀數也不是力矩。否則就讓這兩個標籤保持預設值——檔案沒帶的通道讀成 NaN,扭矩照樣會映射。
- 其他所有欄位——樣本計數器、狀態旗標、廠商自己計算的欄位——不論叫什麼名字,都會被這個讀取器捨棄。
- 單位不是標題。 指名一個欄位並不會轉換它:以 N·mm 為單位的扭矩,或以 kN 為單位的力,仍然必須在檔案的轉換副本中重新縮放。把廠商的原始檔保留在它旁邊。
- 從報告推得的增益是最後手段。 檔案帶的是原始計數,而手邊唯一的校正是一份報告中的極值(「峰值扭矩 12 N·m」)時,用報告的極值除以原始資料的極值,會得到一個只能重現那一個數字的增益。只有在廠商無法提供校正時才使用它,並把它當作一項假設記在專案的 README 中,連同它所依據的報告與原始極值,以便校正資料送到時換掉它(專案建構 §7)。
一份四列的廠商檔——Time 以秒為單位,兩個未說明參考點的彎矩通道,一個以 N·m 為單位的扭矩:
Time,BendX,BendY,Tq
0.000,-0.031,0.108,0.412
0.001,0.044,-0.045,0.398
0.002,0.012,0.061,0.421
0.003,-0.027,0.019,0.405
只要指名一個標題就能照原樣映射——Time 本來就是接受的時間標頭,而彎矩通道在參考點確知之前保持不指名:
在「CSV 一對多映射」命令上,就是把「Z 向力矩欄位」設為 Tq。
延伸閱讀¶
- 銑削力參數訓練 — 使用映射資料訓練係數
- 基礎仿真 — 基礎仿真設置
- 專案建構 — 透過 HTTP 建構任務,以及記錄假設
- 透過 HTTP API 進行重播驗收 — 時序圖請求與訊息清單
- 透過 HTTP 驅動網頁服務 — 登入、回應封套、檔案根目錄(英文,未鏡像)
- 其他命令 — 兩個映射命令,以及其餘呼叫所放進的「腳本」命令
- 檢視一個步 — 所選步的感測器週期圖
- 觀看執行 — 沿著執行畫出映射步屬性的視覺指標時序圖
- 智慧刀把 — 彎矩通道所參照的觀測點
- NC 碼中的腳本命令 — 撰寫錨點所用的
@@/@@^標記 - 步 (Step) — 步的資料模型
- SessionShell — SessionShell 快速參考
- 串聯映射範例 — 映射示範範例專案
- 動力計訓練範例 — 動力計訓練範例專案