# 答 sandal 刀庫應用編碼器溢位 [TOC] 建議先看猜測問題發生原因。 # 情況描述 > 我們是用海德漢Endat (單圈13bit,多圈12bit) 我曲線測試是用TS5700(單23bit 多16bit)去測試,我沒有海德漢馬達,那時是另一位同事測試,沒有留圖。 > >我懷疑是Endat整個加起來25bit,上位跟驅動器拿的是0x6064(32bit),溢位時下次重新開機0x6064就整個錯誤。 > >開啟ABS_MUTI,測試多圈數往負方向移動時,超過0,會從UINT32_MAX開始減少。但重新開機時,圈數變成52010範圍內。這可能導致0x6064出現問題。 > >以上是我的想法。 > 刀庫 不是用PP 是CSP 一般用CSV 控制時,是讀0x6064的值做回授。所以要做到0x6064可以接收到改動。 ## 訊息整理 * abs encoder type * TS5700 tamagawa Tformat * 海德漢 endat 首先是幾個事實 * encoder underflow 時, * actual position 不明原因的跳變 * 在 encoder 沒跳,也沒有actual position 也沒有over, or under flow的時候. * 坤宗也不知。 * abs encoder 的值確實在開機前後如遇期的truncate * 見圖,關機前,關機後。 65831--> 295 (65831-65535=296) * # 找code處 刀庫的應用上更像是 Turn Table ,只是週期不再是pulse per revolution,而是pulse per track cycle。 turn table 相關集中在 `MotionControl` 的 `PositionMonitor28335`, `PositionModeControl28335`, `SetRotaryRange28335`, `SetTurntableStation28335`, 下面一些 `GetCANNPosStatusWord28335`,其它module 可以得到此 module 資料,本module 和cia402應該蠻有關的。 台中是開 profile position (PP mode),做刀庫。 可參考 `MotionProfile.h` 的 `PPCTRLWORD` 。 在整份code 裡,有在 `UpdatePosSet` ,和各個profile type 裡的 entry 。 Profile 會在 `ProfileExecute` 裡執行。而 `ProfileExecute` 會在 `PositionModeControl28335` 被 call。 `ProfileExecute` 應該是控制要執行 PP mode 的那一個(下一個)block。並且有產生每一瞬間的位置,case `PROFILE_STAGE_EXECUTE`:`ExecuteLinear`。 建議controlword 選relative mode,這樣target position 才是增量的。 profile 的implement 是那種在database裡寫死的array `_SetNextTask28335` called by `_SetNextTask` called by `UpdatePosSet` called by `SetPosCtrl` called by `PosFunction` called by `PositionModeControl28335` in `BLOCK_MODE` case. 這個應該就是指PP mode。 `PosFunction` 幾乎全部都是 `SetPosCtrl` ,所以這個function 應該也是流程控制相關的,第一次規劃完後就不再工作。 >注意下方還有 `PROFILE_VELOCITY` (PV mode)。雖然PV mode 應該和position 毫無關係。 `ModifyPosition` 有relative 的 position implement, ```c=477 if(pTask->cw.b.mode == RELATIVE) /* relative position */ { pThis->lPos = pThis->lSetPos; if(pThis->nLast != -1) pThis->lAbsPos = pLast->lAbsPos + pThis->lSetPos; else pThis->lAbsPos = PosCtrl.lPosTarget + pThis->lSetPos; } else if(pTask->cw.b.mode == ABSOLUTE) ``` 可以看出 `lSetPos` 應該是主要的user input。`_SetTargetPos28335` called by `_SetTargetPos` called by `SetPosTargetPos1` in DB 也驗證這點。 大致上說,我覺得上面這段code 是有一點問題的,但不至於頻繁發生。當AbsPos 是 接近 64bit 上下限時,必須要reset,否則溢位。 不太可能常發生的原因在於已經是64bit,且為有sign 另一個可能出現的問題在於,position loop 和 encoder feedback,如果 絕對型 encoder 的feedback 值沒有做reset 或,取其相對值之類的動作,就會可能產生bug,尚未看該部份。 # 猜測問題發生原因 目前還沒辦法確定發生當下在那一環出問題。但是綜合所有資訊,我們認為最可能發生的原因如下。由於問題的描述是,當絕對型編碼器溢位的當下,不會跳任何 error,接下來的工作也是正常的。問題是當關機之後再開,機器發生誤會自己的位置。 這比較可能的現象是,由於 `lAbsPos` 有64bit>> 絕對型編碼器精度(assume 65535(16bit)),當encoder溢位發生時,由於 `lAbsPos` 有64bit,且驅動器設定為relative mode, 所以只需要知道差值,應該是有做相關處理 e.g. 上1tick 是65532 這一個tick 是5,差值大於10000(hyper parameter),所以差值是 5-65532+65535=8。`lAbsPos` 則會記錄為65532+8=65540 假設此時突然斷電。 但是當斷電再開後,不論驅動器是否記錄 65540,因為驅動器不會知道關機狀態下,馬達有沒有移動,所以都要從encoder 重新讀值(絕對值編碼器的意義),並視此值為絕對位置(絕對值編碼器),所以`lAbsPos`會被設為5。 以下再假設刀庫跑一個cycle 是 800 。斷電前真實所在的位置就是65540%800=740。然後斷電後,卻變成5,所以我們觀察者會認為位置跑掉。除非編碼器的溢位值,剛好是刀庫cycle的倍數 e.g. 771。 65540%771=5 (same) ## 標準的解法 我們要分析一下絕對值編碼器和增量型編碼器有何應用上的差異。一般來說,絕對型編碼器會被用在範圍有距離極限的工況,比如CNC的單軸。旋轉的case 則是手臂,此時手臂的旋轉角度也是有極限的比如+-3圈。 相反的是,增量型編碼器常被用在**無限循環**之類的工況,比如單向不間斷的輸送帶、將360 deg/拆成多個工位的旋轉盤。 兩者選用的差異就在無限循環。因為絕對型編碼器是一定有範圍限制的,且範圍越大的,價格也越貴。當絕對型編碼器會溢位時,它的本質就和增量型編碼器一樣了。 所以,以刀庫這個應用來說,其實一般是用增量型編碼器。 增量型編碼器用在任何位置控制上,開電就要homing。所以雖然我們是用絕對型編碼器,但因為我們的工況其實是增量型的工作,所以建議**開機homing**。 > 週期性工作的有週期,但是單位不同,e.g. 鏈狀刀庫可能是800cm ,轉盤則是360 deg ## 其它解法 如果就是希望不做homing ,並且有絕對型編碼器了話,能不能不做homing。 ### 編碼器支援 是否能達成取決於幾個條件 1. 刀庫cycle < encoder overflow value,否則同一個值,對應n個可能的刀庫位置。(1-multi isn't fulfill function definition) 2. 同上面猜測問題點 的尾部,編碼器的溢位值 是 刀庫cycle的倍數,由於刀庫cycle是機械無法改變。只能動編碼器的溢位值。 1. 由於驅動器斷電時永遠不知道實際的機器發生什麼,所以一但上電就要 **無條件相信** 編碼器的值,所以修改encoder 溢位值的工作一定影響在編碼器上。 1. 如果編碼器可以設定溢位值,可以完美達成。 2. 如果編碼器可以設定位置值,不能設定溢位值,則若在驅動器斷電情況下overflow, underflow,仍會出錯。除非能保證不溢位。 * e.g. tamagawa t-format 用rs485, 要有電池才能工作。 3. 絕對型編碼器也不一定能調。 1. 如果絕對型編碼器的做法是純機械式的。就一定無法調整 1. 有部份電子式的理論上可以調整,但重裝電池後就非要homing 不可。e.g. tamagawa #### 部份支援 編碼器支援是很困難的,驅動器的程式才可控。cia402 有 607C home offset。如果在overflow的瞬間,將跳變的差值算出並修改此值。效果同 第 編碼器支援-2-1-2 點。斷電不溢位了話可行。 但是cia402 有建議,只在homing mode 修改607C。 > NOTE The activation of a new value of the object home offset is manufacturer-specific. It is recommended to apply the new value only while the drive is in homing mode. ### 增加規範 cia402 本身對encoder 的類型沒有做很多的規範,特別是大部份都假設是增量型而非絕對型的。 > * 607Ch: Home offset > 對abs encoder來說,一般的流程就是在裝機後,因為不確定encoder的位置(第幾圈),先用一般的homing 模式+sensor 設定儲存607C的值。下次開機以後都不再需要homing 的流程。直接用此607C值和encoder的讀值相加就知道目前座標。 驅動器無法記憶 abs encoder overflow的原因在於,它要無條件相信abs encoder 的input value。如果驅動器可以做判斷,則可以做一些操作。 如果加上幾個假設/條件, * 事先知道encoder 的overflow value(encoder 多少bit) * cia402 無此規範 register * 最後一次存到ROM裡的絕對位置,和下一次開機間的誤差足夠小。 * 隱含假設斷電後,位置的移動量很小。 * 有不同的做法 * best: 能夠在**斷電時但完全斷電前**,保存最後的絕對位置座標(flash)。 * may work: 週期性的存值(e.g.3s),只要差距夠小就沒事。 * 更好的方法可能是足夠密集的確認,但是只有在與前值有足夠大的差距(足以影響判斷時)才寫入(減少浪費效能,延長flash壽命)。 * 刀庫cycle < encoder overflow value * 否則會1對多,非function。 如此一來,就能在開機上電時,先把儲存值與abs encoder值相比較,當兩值相差過大時,先將 儲存值/overflow value=y, ans=y\*overflow value +encoder value,if 儲存值~=ans,視為回復成功。 ## 結論 ~~綜合以上,homing 是最直接有效,保證適用任何的驅動器的方法。~~ ~~另外建議要有 zero signal 確保刀庫繞了一圈,我這邊不太確定sandal 有沒有這個功能,不然就是如果旋轉很多次週期後,自動重做 homing動作。~~ 我們的目的不是用任意的驅動器都可以兼容。而是只要自己的驅動器,而且我們目標就是要賣整套刀庫,而非單純用刀庫。所以不需考慮相容性問題。 另外梁ㄅㄟ他們真正想用的是5軸上的C軸有絕對型,所以一定要達成。 # 修改 ## 52010 ABS_MULTITURN_REV setting. 同理 52007, 52009 都是abs encoder setting. 據說sandal 有這個功能。`52010 ABS_MUTI`。 > 51020 QEP_FPGA_EN > 絕對值encoder 有BISS 協定。 > EnDat2.2 control.c裡 ```c=1786 {{4096}, _DWORD, 52010, STATUSWRITE, 2, 0, 0xFFFFFFFF, NULL, SetEncoderType}, //EncoderRevSet //AUSTON_PARAMETER_ENCODER_ABS_MULTITURN_REV, ``` 從 control.c `EncoderRevSet`, 和 `_SetAbsRevolution` 看起來,`ENCODER_ABS_MULTITURN_REV` 並非是直接擴展絕對值編碼器的數值範圍。而是問abs encoder 機械多圈值的上限為何 `u_wAbsMultiReso`(default 12)就4096。同理,`u_wAbsSingleReso`,就是abs encoder 單圈有多少pulse。 `ENCODER_PERIOD_PER_REV` 還不太知道幹麻,看起來也是incremental 才遇到。 另一個出現的是 control.c `MultiDistanceCheck`。 Montion control 則有 `SetEncRotaryRange`, `MultiControl28335`, `CheckPositionRange`。 `AUSTON_PARAMETER_ENCODER_ABS_MULTITURN_REV` ,使用上就call `DBVALUE(ENCODER_ABS_MULTITURN_REV)` 另有 `52023` for `AUSTON_PARAMETER_ENCODER_GET_ABS_MULTITURN_REV`,讀值。但看起來對 ABSOLUTEENCODER 目前無作用。 看起來和 `database.c` `POSITION_MULTICOUNT`有關。就連到`SetMultiRemainder28335` call `SetMultiCnt` call `_SetMultiPos` (`u_AbsMultiPosition = dwPos`)。 ### 50064 ENCODERMULTI (not in TMDC yet) `AUSTON_ACTUALDATA_ENCODERMULTI` implement at `_AbsEncoder_28377.c` ```c=4440 DWORD _GetAbsMulti377() { return (u_AbsEncoder.dwLoopCount / u_AbsEncoder.Encoder.wEnPolePairs); } ``` `wEnPolePairs` 可能是某些encoder 有特別的週期性,其abs loop count 記錄的是換算之前的值。對於大部份的encoder,應該是都沒有用。此值在DB裡是 `52030`, `ENCODER_ENPOLEPAIRS`。此值目前看到都是1。 但只要就是要追 `dwLoopCount`。所以看 `encoderconf.h`, `_AbsEncoder_28377.c` 就對了. `ReadAbsEncoder`, `void _InitAbsLoop()` ```c=4393 else if (GetABSType() == ENCODER_TAMAGAWA) { u_AbsEncoder.dwLoopCount = u_dwMultiPosition & ((1L << u_wAbsMultiReso) - 1); if (u_bPositionAdd) { if (u_AbsEncoder.dwLoopCount < (1L << (u_wAbsMultiReso - 1))) u_AbsEncoder.dwLoopCount = u_AbsEncoder.dwLoopCount + (1L<<u_wAbsMultiReso); } } ``` `void _SetMultiAbsCom(BOOL bFlag) `其 flag 就是 `POWER_FUNTIONBIT` ```c if (GetABSType() != ENCODER_HIPERFACE) _SetMultiAbsCom((DBVALUE(POWER_FUNTIONBIT).dwData & MULTI_ABS)?1:0); #endif } ``` ```c {{0}, _DWORD, 51020, STATUSWRITE, 2, 0, 0xFFFFFFFF, NULL, SetFunctionBit}, //AUSTON_PARAMETER_POWER_FUNTIONBIT, ``` 這應該是一個register 去決定要不要開各個功能(by different bit)。 ### 0x51020 function bit `MontionControl.c`, `CheckPositionRange()` 可以看出來,`MULTI_ABS`, 可能才是真正的extend abs encoder 功能。 ```c=3477 if ((lMinPos > 0 && lMaxPos < 0) || (lMinPos > lTemp && lMaxPos < lTemp)) { if (!(DBVALUE(POWER_FUNTIONBIT).dwData & MULTI_ABS)) SetDriveError(AUSTONE_POSITION_OVER_RANGE); } ``` 見驅動器datasheet > P51020 驱动器功能选择 Bit0( CAN_enable) : 通讯使能,通过通讯使能驱动器。 Bit8( ABS_Muti) : 修改绝对位置(360 度转盘禁止使用), 用于防止多圈数位于 0 附近, 仅适用于绝对值编码器和位置 模式, 启用后需要重新归零。 Bit11( POS_TOR) : 位置到位后限制扭矩, 用于较大转盘 机械定位后防止扭矩较大, 而导致的机械上的磨损或者伺服 的报警 Bit13( END_SPEED) : 转盘尾段速度激活 > E00122 位置超过 范围 位置控制中位置范围超过编码器多圈范 围, 启用51018中的bit2可以启用该功能 检查50044编码器数据里面实际编 码器多圈数, 若接近0或4096圈(以 马达实际多圈数为准), 可以分离机 械结构速度模式运行一段时间或激 活51020参数中的ABS_Muti功能, 激活改功能后(sick编码器会自清除) 建议驱动器重启后再次归零 不過目前本來就是有開啟的,沒有開的話,encoder overflow 會 報警。 注意和 `_SetMultiAbsCom` 的關係, `_SetMultiAbsCom((DBVALUE(POWER_FUNTIONBIT).dwData & MULTI_ABS)?1:0);` 就是把`MULTI_ABS` 轉到給encoder 用變為`u_bPositionAdd`。換句話說,可以用這個variable 追multi abs功能。 ### 58558 AUSTON_PARAMETER_POSITION_MULTICOUNT 這個值只用在內部的計算,不能設定。應該就是為了事先的 encoder 值處理。總之處理在這裡。 ### abs encoder `_SetMultiAbsCom` 和 `_ReadAbsEncoder` 看起來是用`u_wInitFlag`去dispatch start reading, read success 後就回到 0。 `58514`(`24676`) `POSITION_ACTUAL_VALUE` 是實際位置,也就是最重要的參數。 對應是 MotionControl.c `g_lActLuPosition` `56008`, `AUSTON_PARAMETER_IDENTIFICATION_ENCODER_ACTUALPOSITION` 是 `當前位置`。 from `Encoder.GetPosition()`。 58514 和 56008 的差異是什麼? _GetAbsPosition 用到的 `u_AbsMultiPosition` 就在`58558`,一般都是0。 `50064` `ENCODERMULTI` function bit 打開之後才有開啟。 ### save 要做abs 回復一定要有save 看到資料是存在eeprom,找 `Write_EEPROM` 只有 `WORD SaveDB()` 。這是存整個DB,而且只能在motor switch off 時存(blocking function)。 發現有 driver function `WORD Save_EEPROM(WORD wAddress, int* pnData, WORD wLength)`. database doesn't use this function. directly used in `control.c`, `MotionControl.c` `void Homeing() ` 有 `Save_EEPROM(DBID(POSITION_MULTICOUNT)*2, (int*)(&DBVALUE(POSITION_MULTICOUNT)), 2);` 除此之外,`Save_EEPROM(DBID(` 開頭的沒有什麼東西。 所以就是原source code 不可能有辦法做斷電回復。這也和經驗相符,不管underflow, overflow,一但發生,因為沒有存actual position 或類似的東西 所以一定會fail。 如果要做了話,我這邊要動的東西有。 1. 在main loop裡週期執行saving. e.g.10s(假設所有控制都在ISR裡執行)。存 POSITION_MULTICOUNT 1. encoder initial 時,和儲存的 POSITION_MULTICOUNT 比較判斷。 # encoder.c `encoder.h` from `platform.h` from `_AbsEncoder_28377.h` form `encoderconf.h` `WORD Set_Encoder(ENCODER* pEncoder)` 會bind 1個encoder implement e.g. abs encoder。 然後control.c 會call 這個binding,接下來所有模組要 position value 都用 `DWORD Get_ActPosition()`,包括 MotionControl.c。對於abs encoder,此時此function bind 到 `_AbsEncoder_28377.c`的 `_GetAbsPosition`。 但是有點炸了 讀取動作現在是用 `void EncoderAbsRead()` call `void _SetMultiAbsCom(BOOL bFlag) `。 I think this is because abs encoder require request and response, which different from other encoder. `AUSTON_PARAMETER_ENCODER_FINERESOLUTION`, `ENCODER_FINERESOLUTION`, `wFineResoBit`. # sandal 實作解法 * 發現 `u_AbsPosition` 只有32bit,對於39bit encoder(16+23)永遠會有overflow. * 它實作的方式不是直接用 u_dwPosition 和 u_AbsPosition 換算。 * 而是用 u_AbsEncoder.dwLoopCount 記錄絕對圈數,此圈數用 single turn(lMechAngle)的overflow & underflow 達成,只有initial 時有參考。 * 單圈則是用lMechAngle,所以resolution永遠被壓縮到一定的range。 * 那它如何斷電回復abs encoder 的值? ## scope 一般用CSV 控制時,是讀0x6064的值做回授。所以要做到0x6064可以接收到改動。 0x6064的直實值 u_dwPosiActValue = Get_ActPosition(); 最終就是 `Encoder.GetPosition()` 但是我內部如果知道目前的位置超過32bit max 上限了 e.g. 43E 我要怎樣把這個值傳給上位機?如果用0x6064了話。由其是 CSP target position 只能給 ABS value。看來真的要64bit ,最好的方法就是開額外的register。 如果有週期性了話,最好是直接回到原位置就reset ,這樣32bit也一定夠。不過這就是402 protocol 的弱項。而且control loop 如果沒有對應的處理也是白搭,overflow 時。 > 要注意0x6064 , 402 protocal 裡是Integer32。但是sandal 裡是uint32。 目前是說可以做在上位機了。問題一下就解決。 關於encoder 16bit 被 壓縮到12bit。這不會有問題。 e.g. 65535%4096==4095 16bit overflow 時,12bit 也會剛好overflow。從外面看起來,就是abs multi encoder 變成12bit而已。 只要real abs encoder bit >=12bit 就好,如果是8bit就會莫名在255就overflow。此時建議在驅動器端做expand功能。 --- 我在另一邊證明type3 用週期性最好,也有描述如何做,就看要不一個 pesudo code。 implement問題要額外考慮在於如果6064 overflow問題,因為此假設功能做在上位機,無法reset 6064,如何處理。其實就是把現在的6064當作encoder value,上位機虛擬一個經過60F2, 607B 設定,會reset的6064。 drive 6064本來就會遇到abs encoder overflow。同樣的方法用在上位機6064 處理就好。e.g. value overflow 時真正的count up value多少就加上而已。結合前面的斷電回復處理斷電overflow。