国际单位制的长度单位“米”(meter, metre)起源于法国。1790年由法国科学家组成的特别委员会,建议以通过巴黎的地球子午线全长的四千万分之一作为长度单位──米,之后法国国民议会决定采纳了这个计量制度。现在全球通用的国际长度单位米,就是由此规定而来。1799年根据测量结果制成一根铂质原器——铂杆,以此杆两端之间的距离定为1米,并交法国档案局保管,所以也称为“档案米”。这就是最早的米定义,这支米原器一直保存在巴黎档案局里。但是随后的使用中,人们发现温度、湿度、气压还有放置方式都会对这个原器长度产生影响,这导致使用和矫正很不方便。20世纪50年代,随着同位素光谱光源的发展。发现了宽度很窄的氪-86同位素谱线,加上干涉技术的成功,人们终于找到了一种不易毁坏的自然标准,即以光波波长作为长度单位的自然基准。1960年第十一届国际计量大会对米的定义作了如下更改:“米的长度等于氪-86原子的2P10和5d1能级之间跃迁的辐射在真空中波长的1650763.73倍”。这一自然基准,性能稳定,没有变形问题,容易复现,而且具有很高的复现精度。70年代以来,随着科学技术的进步,对时间和光速的测定都达到了很高的精确度。因此,1983年10月在巴黎召开的第十七届国际计量大会上通过了米的新定义:“米是1/299792458秒的时间间隔内光在真空中行程的长度”。这样,基于光谱线波长的米的定义就被新的米定义所替代了。 随着时代的发展,很多年前高不可攀的激光距离传感器价格不断走低,激光测距正在逐渐取代传统的皮尺走入寻常百姓家。最近笔者公司有一个检测物体距离的需求,经过研究决定使用激光距离传感器来完成这个要求。淘宝上可以找到一些激光测距模块,但是出于准确性可靠性以及成本的考虑最终选择的是带有USB接口的优利德 UT395B,该型号测试距离范围在0.05-70m,精度可以达到1.5mm±(d*十万分之五),完全可以满足使用需要。
和之前的项目一样,这次依然使用USBlyzer 软件进行分析。在USBlyzer的帮助下,抓取设备和UT395B自带软件通讯包,然后逐步分析使用的协议。下图是配套软件的界面,使用Micro USB线连接好设备和PC之后,点击 Read 按钮会打开激光(手册上说这是打开瞄准的功能),对准需要测试的位置之后,再次点击Read即可返回距离值,随后激光被关闭。
在USBlyzer软件中可以看到UT398B 设备的Descriptor,它使用HID协议,自定义通讯格式。接下来需要使用配套软件进步一抓取通讯数据。具体来说,就是每次点击界面上的Read按钮,配套软件会发送一个命令给设备,然后设备执行完命令后返回对应的结果给上位机。UT395B和之前研究过诸如USB接触式温度计周期性发送消息的设备不同,只有按下配套软件的按钮后才会发送数据,这对于观察和记录数据来说非常方便。最终,看到一次完整的通讯有八笔通讯,四笔是发生在第一次按键之后,另外四笔是发生在第二次按键之后。下图是抓包的结果,其中OUTPUTReport是主机发送给 UT395B的数据,INPUT Report 是UT395B 发送给主机的数据。
通过观察,最终总结出来数据的含义如下: 序号 | | | | | | 41 54 4B 30 30 31 23 00 00 00 00 41 00 00 00 54 00 00 00 44 00 00 00 00 | | | | 41 54 4B 30 30 31 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | | | | 41 54 44 30 30 31 23 00 00 00 00 41 00 00 00 54 00 00 00 4B 00 00 00 30 | | | | 41 54 44 00 00 12 A0 00 00 00 35 00 00 00 35 F5 23 00 00 00 00 00 00 00 | | | | 41 54 4B 30 30 31 23 00 00 00 00 41 00 00 00 54 00 00 00 44 00 00 00 00 | 主机要求关闭激光瞄准 ,这个和第一条命令是相同的。就是说当设备激光出于关闭状态时,收到这个命令会打开激光;反之是关闭激光 | | | 41 54 4B 30 30 31 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | | | | 41 54 44 30 30 31 23 00 00 00 00 41 00 00 00 54 00 00 00 4B 00 00 00 30 | | | | 41 54 44 00 00 21 D2 00 00 00 1F 00 00 00 1F 0A 23 00 00 00 00 00 00 00 | |
距离信息是通过上庙表格中3号和7号命令获得的,UT395B收到这个命令后返回距离值。例如:4154 44 00 00 3A E8 00 …… 其中的 00 3A E8 就是距离信息。0x3AE8=15080对应显示在屏幕上的距离是1.508米。经过多次实验,实际取得的数据是小数点后4位,最后一位会进行四舍五入,屏幕只显示小数点后3位的数值。在 Windows 下使用 VS2015编写代码验证上述发现,结果如下:
这里测试得到的58.152米的结果是在夜间测试远处楼房得到的,白天的时候,光线会湮没在日光中而导致测量失败,从官方文档来看,可以通过加装反光板的方式来增加测试距离。人类的太空探索中同样可以使用这种方式来进行测距。历史上美国登月计划中的阿波罗11号,14号和15号在月面上安放的三台激光反射镜。世界各地的天文台都一直使用这些反射镜测量地月距离,精确度达到厘米级别。
阿波罗15号在月面安放的反射镜 来自https://www.nasa.gov/sites/default/files/images/444021main_apollo15_LRRRpart_HI.jpg 关键代码如下:
调用方法如下:
Switch(hUsb);GetData(hUsb);Sleep(400); SendData(hUsb);GetData(hUsb);Sleep(400); Switch(hUsb);GetData(hUsb); Sleep(800); SendData(hUsb); GetData(hUsb); 在 Windows下完成对UT395B 的控制之后,可以开始编写Arduino代码,硬件上使用USB Host Shield 来作为 HOST。
UT395B.h [mw_shl_code=arduino,true]#include "hiduniversal.h"
#include "controllerEnums.h"
#define UT395B_VID 0x0483
#define UT395B_PID 0x5751
class UT395B : public HIDUniversal {
public:
UT395B(USB *p) :
HIDUniversal(p) { };
bool connected() {
return HIDUniversal::isReady() &&
HIDUniversal::VID == UT395B_VID &&
HIDUniversal:ID == UT395B_PID;
};
virtual uint8_t GetAddress() {
return bAddress;
};
};
[/mw_shl_code] UT395.ino [mw_shl_code=arduino,true]#include "UT395.h"
#include <SPI.h>
USB Usb;
UT395B ut395(&Usb);
void setup() {
Serial.begin(115200);
if (Usb.Init() == -1) {
Serial.print(F("\r\nOSC did not start"));
while (1); // Halt
}
Serial.println(F("\r\nUT395B Started"));
pinMode(2,INPUT_PULLUP);
}
void Switch()
{ uint8_t outBuff[24]={0x41,0x54,0x4B,0x30,0x30,0x31,0x23,0x00,
0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x54,
0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x00};
Usb.outTransfer(ut395.GetAddress(), 1, sizeof(outBuff), outBuff);
delay(500);
}
void SendData()
{ uint8_t outBuff[24]={0x41,0x54,0x44,0x30,0x30,0x31,0x23,0x00,
0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x54,
0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x00};
Usb.outTransfer(ut395.GetAddress(), 1, sizeof(outBuff), outBuff);
delay(500);
}
void loop() {
uint8_t r;
byte cnt;
String s="";
Usb.Task();
if ((digitalRead(2)==LOW)&&(ut395.connected())) {
uint8_t inBuff[24];
Switch();
r=Usb.inTransfer(ut395.GetAddress(), 2, sizeof(inBuff), inBuff,10);
SendData();
Usb.inTransfer(ut395.GetAddress(), 2, sizeof(inBuff), inBuff,10);
Switch();
Usb.inTransfer(ut395.GetAddress(), 2, sizeof(inBuff), inBuff,10);
SendData();
Usb.inTransfer(ut395.GetAddress(), 2, sizeof(inBuff), inBuff,10);
if (0x44 == inBuff[0x02]) {
unsigned long d;
d = ((((unsigned long)inBuff[0x04]) << 16) & 0xFF0000) +
((inBuff[0x05] << 8) & 0xFF00) +
(inBuff[0x06] & 0xFF);
//数据是小数点后4位的,我们需要通过对最后一位四舍五入变成3位的
//第一步:四舍五入最后一位
if (d%10>4) {d=d+10;}
d=d / 10;
//得到小数点后3位
cnt=0;
while (cnt<3) {
s=(d % 10)+s;
d=d /10;
cnt++;
}
//加入小数点
s='.'+s;
//加入小数点前面的整数位
s=(d % 10)+s;
d=d /10;
while (d>0) {
s=(d % 10)+s;
d=d /10;
}
Serial.println(s);
}
//这里可以输出最终获得结果原始数据
//for (byte Index=0;Index<sizeof(inBuff);Index++)
// {Serial.print(inBuff[Index],HEX); Serial.print(" ");}
//Serial.println("");
}
}[/mw_shl_code]
有了上面的方法,我们可以在PC和Arduino上随心所欲的设计需要的功能,比如:根据距离和间隔时间获得物体的运行速度等等。
参考文献: 1. 刘荣 圈圈教你玩USB 第1版 北京航空航天大学出版社2009年1月 2. 萧世文 USB 2.0 硬件设计 第2版 清华大学出版社 2006年10月 3. JanAxelson / 陈乃塘 USB 3.1 完全开发手册 第5版 碁峰资讯股份有限公司 2015年10月 4. 陈吕洲 Arduino程序设计基础 第2版北京航空航天大学出版社 2015年2月
|