程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 盈動線性絕對值編碼器(光柵尺)的測試記錄,盈動編碼器

盈動線性絕對值編碼器(光柵尺)的測試記錄,盈動編碼器

編輯:C#入門知識

盈動線性絕對值編碼器(光柵尺)的測試記錄,盈動編碼器


這款盈動的線性絕對值編碼器型號為: CAPLIN-A-485

其安裝好光柵尺後的外型下面這樣的.

 工作的時候,信號端子和光柵尺是固定的, 滑頭由電機帶動.

 

編碼器數據由485口主動拋出, IPC可以在適當時機讀取.

 

這款產品的通訊格式如下:

 

默認的波特率為9600,每桢起始位為0,數據位8位,停止位1位,沒有校驗和。
一串完整的數據以“55 AA”或者“AA 55”開頭(每傳輸一次絕
對位置交替一次),跟著4桢為絕對值位置,例如:
“55 AA + XX XX XX XX”->”AA 55+XX XX XX XX”->”55 AA+ XX XX
XX XX”->”AA 55+XX XX XX XX”……..
注:XX為絕對位置數據

 

傳輸上來的 32 位數據
(1) 低 20 位為 4mm 細分位置, 滿量程對應 4mm 長度,最小精
度物理長度是(4mm/4096)。
(2)高 12 位為 4mm 累加位置,其最低位對應的物理長度為4mm.
如下:
‘’ AAAA-AAAA-AAAA’’+’’XXXX-XXXX-XXXX-NNNN-NNNN’’

解析可以直接用’’ AAAA-AAAA-AAAA-XXXX-XXXX-XXXX’高24 位化為十進制,如上個例子化為十進制得 232315,則所對應的絕對位置為(232315/4096)*4mm=226.870mm。

 

筆者通過串口助手采集了一段數據,如下:

AA 55 00 05 19 E3 55 AA 00 05 19 E3 AA 55 00 05 19 E3 55 AA 00 05 19 E3 AA 55 00 05 19 E3 55 AA 00 05 19 E3 AA 55 00 05 19 E3 55 AA 00 05 19 E3 AA 55 

標紅字符之間的就是我們要得到的數據.

 

筆者寫了一個光柵尺的類,  你只需要適時讀取這個類的Curposition屬性即可得到當前的光柵尺讀數.

 1   class GratingRuler:SerialPort
 2     {
 3         private Timer t1;
 4         private double curPosition;
 5         public double CurPosition 
 6         {
 7             get 
 8             {
 9                 return curPosition;
10             }
11         }
12         private IList<byte> readBuff;
13         public GratingRuler()
14         {
15             try
16             {
17                 this.PortName = "COM4";
18                 this.BaudRate = 9600;
19                 this.DataBits = 8;
20                 this.StopBits = StopBits.One;
21                 this.Parity = Parity.None;
22                 readBuff = new List<byte>();
23                 this.ReadBufferSize = 1024;
24                 this.Open();
25             }
26             catch (Exception e1)
27             {
28                 throw e1;
29             }
30 
31             t1 = new Timer(
32                 new TimerCallback( readRulerValue),
33                 null,0,100);
34         }
35 
36         private void readRulerValue(object a)
37         {
38             double res = 0.0f;
39             List<byte> dataBuff = new List<byte>();
40             byte[] sbyteAry = new byte[1024];
41             var cary = this.Read(sbyteAry, 0, this.ReadBufferSize);
42             for (int i = sbyteAry.Length - 1; i > 0; i--)
43             {
44                 if ((sbyteAry[i] == 0xAA && sbyteAry[i - 1] == 0x55) ||
45                     (sbyteAry[i] == 0x55 && sbyteAry[i - 1] == 0xAA))
46                 {
47                     dataBuff.Add(sbyteAry[i - 2]);
48                     dataBuff.Add(sbyteAry[i - 3]);
49                     dataBuff.Add(sbyteAry[i - 4]);
50                     dataBuff.Add(sbyteAry[i - 5]);
51                     res = getvalue(dataBuff);
52                     this.DiscardOutBuffer();
53                     dataBuff.Clear();
54                     break;
55                 }
56             }
57             curPosition = res;
58         }
59 
60         private double getvalue(List<byte> dataBuff)
61         {
62             Int32 v= BitConverter.ToInt32(dataBuff.ToArray(), 0);
63             string s = Convert.ToString(v, 2);
64             s = s.PadLeft(32, '0');
65             double g12 = Convert.ToInt32(s.Substring(32 - 12, 12), 2);
66             double d20 = Convert.ToInt32(s.Substring(8, 12), 2);
67             double res1 = g12 * 4 + (d20 / 4069) * 4;
68             var ss1 = s.Substring(32 - 24, 24);
69             double res2= ((double)(Convert.ToInt32(s.Substring(32-24, 24), 2)) / 4096) * 4;
70             return res2;
71         }
72 
73 
74     }

注意下面的代碼中, 提取了0xAA,0x55或者0x55,0xAA之間的數據, 還需要按計算機內存字節順序(高位在後,低位在前) 排列一下, 才可以解釋為正確正確的32位數字.

如下面代碼中按5,4,3,2的順序重排列了一次.

 if ((sbyteAry[i] == 0xAA && sbyteAry[i - 1] == 0x55) ||
                    (sbyteAry[i] == 0x55 && sbyteAry[i - 1] == 0xAA))
                {
                    dataBuff.Add(sbyteAry[i - 2]);
                    dataBuff.Add(sbyteAry[i - 3]);
                    dataBuff.Add(sbyteAry[i - 4]);
                    dataBuff.Add(sbyteAry[i - 5]);
                    res = getvalue(dataBuff);
                    this.DiscardOutBuffer();
                    dataBuff.Clear();
                    break;
                }

下面的代碼把這4個字節的內容拼成32位數,並且按照此款編碼器的通訊格式解析出最終的位置值, 單位mm

private double getvalue(List<byte> dataBuff)
        {
            Int32 v= BitConverter.ToInt32(dataBuff.ToArray(), 0);
            string s = Convert.ToString(v, 2);
            s = s.PadLeft(32, '0');
            double g12 = Convert.ToInt32(s.Substring(32 - 12, 12), 2);
            double d20 = Convert.ToInt32(s.Substring(8, 12), 2);
            double res1 = g12 * 4 + (d20 / 4069) * 4;
            var ss1 = s.Substring(32 - 24, 24);
            double res2= ((double)(Convert.ToInt32(s.Substring(32-24, 24), 2)) / 4096) * 4;
            return res2;
        }

上面筆者用串口助手抓取的數據為:

00 05 19 E3

按上面的代碼, 解析出來位置值為: 326.4716796mm

朋友們可以把這個結果數據代入代碼中驗證一下, 就能很快明白了.

 

這個類中,有幾點經驗需要記錄一下:

1. 不要在DataReceived事件中取得串口讀數, 因為編碼器會不斷丟數據過來, 這個事件會不斷觸發.

    正確的做法是在需要的時候, 才去讀一下當前串口緩沖區中的數據, 然後立刻丟棄緩沖區中的數據.

2. 不要把"取當前編碼器的值" 這個功能設計成一個包含串口讀取動作的函數,

     因為調用者可能會在多個線程中同時調這個功能, 或者在循環中高速調用這個功能. 這樣會造成串口無法響應這種並行處理或者高速的請求.

    正確的做法是在類中以私有方法按一定速度以單線程讀取當前的串口數據. 對外部公開一個屬性, 供調用者讀取當前編碼器的值. 在本類中, 是在一個內部獨立的線程中以一定速度持續讀取串口數據的, 對外則公開一個屬性curPosition

 

對這款產品的使用感覺

1. 滑動讀頭距離光柵尺的距離, 以及平行關系. 還有光柵尺貼的時候的平整性, 都會影響讀數的精准度.  可以說是安裝的時候要小心翼翼, 是個細活.

2. 目前見到最好的精度是在小數點後第3位跳動, 也就是um級別. 但是仍然發現在同一位置,有時候有可能跳出一個很小的值來, 然後又恢復正常. 感覺如果是高速運動需要時實反饋時, 這東西有些讓人擔心. 如果在運動後,靜止下來測量走了多遠, 還是可以的.

3. 如果你設備冷啟動上電後, 有時候你不動一下滑頭, 讀出來的數值為0, 動一下後, 編碼器又"活"過來了, 開始持續有數據發出來.

4. 見過的一個最嚴重的問題是: 有一天突然發現所有數值被加上了9000, 即昨天還在531的位置, 今天變成了9531了, 就好像是數據被累計了一樣. 這個情況是在使用了一個月後突然出現的. 廠家認為我們是程序有問題. 但顯然不是, 因為之前跑了那麼久都是好的.  後來我發現斷掉設備所有的電源後, 再開又好了. 真是不得其解.

 

 

由於測試工程是用於驗證另一個項目, 本編碼器光柵尺只是其中的一個元件, 所以我就不把原代碼發出來了.

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved