盈动线性绝对值编码器(光栅尺)的测试记录

摘要:
18this.波特率=9600;20this.StopBits=StopBits.One;25}26 catch(异常1)27{28throw1;dataBuff=newList<41varcary=this.Read(sbyteAry;i-)43{44if((sbyteAry[i]==0xAA&&

这款盈动的线性绝对值编码器型号为: CAPLIN-A-485

其安装好光栅尺后的外型下面这样的.

盈动线性绝对值编码器(光栅尺)的测试记录第1张

 工作的时候,信号端子和光栅尺是固定的, 滑头由电机带动.

 盈动线性绝对值编码器(光栅尺)的测试记录第2张

编码器数据由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了, 就好像是数据被累计了一样. 这个情况是在使用了一个月后突然出现的. 厂家认为我们是程序有问题. 但显然不是, 因为之前跑了那么久都是好的.  后来我发现断掉设备所有的电源后, 再开又好了. 真是不得其解.

由于测试工程是用于验证另一个项目, 本编码器光栅尺只是其中的一个元件, 所以我就不把原代码发出来了.

原创文章,出处 : http://www.cnblogs.com/hackpig/

免责声明:文章转载自《盈动线性绝对值编码器(光栅尺)的测试记录》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇文件创建及读取的方法统计学术语下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

python socket编程介绍

一、概述 socket 通常被称作“套接字”,用于描述IP地址和端口,是通讯链的句柄,应用程序通过socket向网络发送请求或者回应网络的请求。 socket起源于UNIX,在linux、UNIX中“一切皆文件”,对于文件用 打开、读写、关闭 模式来操作。socket就是该模式的一个实现,是一种特殊的文件。一些socket函数就是对其进行的操作(读IO、写...

MySql数据类型

MySql数据类型 1.整型(xxxint) MySQL数据类型 含义 tinyint(m) 1个字节表示(-128~127) smallint(m) 2个字节表示(-32768~32767) mediumint(m) 3个字节表示(-8388608~8388607) int(m) 4个字节表示(-2147483648~21474836...

二、Metrics指标类型

Prometheus 的客户端库中提供了四种核心的指标类型。但这些类型只是在客户端库(客户端可以根据不同的数据类型调用不同的 API 接口)和在线协议中,实际在 Prometheus server 中并不对指标类型进行区分,而是简单地把这些指标统一视为无类型的时间序列 2.1、Counter (计数器) ​ Counter 类型代表一种样本数据单调递增的指...

mysql通过数据文件恢复数据方法

情况描述:服务器硬盘损坏,服务器换了个新硬盘 ,然后老硬盘插在上面。挂载在这台机器。可以从老硬盘里面拿到数据。只拿到了里面的mysql数据文件夹,把数据文件夹覆盖新的服务器mysql数据文件夹 启动报错!!!报错!!!错!!!。 数据文件如下: 于是觉得不能使用覆盖文件的方式还原数据,得使用科学方法恢复。google之…… 找到解决办法如下: 可以发现数...

RFID 卡片防复制

RFID 卡片防复制 这里的卡指的 MIFARE Classic 系列 或 NTAG 系列的卡。其它的卡没接触过,不了解。 防伪有两层意义: 卡片防复制。比如小区卡被复制,使得出入小区的人员管理更困难了。这是需要防范的事情 卡片数据被破解。比如公交卡或食堂饭卡,如果破解了其中的数据,就可以篡改金额。 复制会产生盗刷问题,需要在第一层解决。而卡内数据加密...

svg 不同比例缩放后 拖拽事件的鼠标位置转换为svg图纸中的位置

说起来可能有点绕,下面尽可能的把我想表达的表述清楚吧 来看下下面这张图 svg元素的宽度和浏览器的宽度是不一样的,事实上,还有一个宽度,那就是svg画布的宽度 为什么会有这么多不一样的宽度呢,直接浏览器的宽高就是svg标签的宽高,同时也设置同样的画布宽高,不就不用转换拖拽事件鼠标的坐标位置了吗? 是这么个道理,一开始我也是这么干的,可是后来发现,不同客户...