使用 UT395B 实现激光测距-Arduino中文社区 - Powered by Discuz! Archiver

Zoologist 发表于 2019-1-31 12:03

使用 UT395B 实现激光测距

国际单位制的长度单位“米”(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 发送给主机的数据。


通过观察,最终总结出来数据的含义如下:
序号方向数据功能
1主机à设备41 54 4B 30 30 31 23 00 00 00 00 4100 00 00 54 00 00 00 44 00 00 00 00主机要求打开激光瞄准
2设备à主机41 54 4B30 30 31 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00设备 ECHO
3主机à设备41 54 44 30 30 31 23 00 00 00 00 41 00 00 00 54 00 00 00 4B 00 00 0030主机要求获得上一笔距离数据
4设备à主机41 54 44 00 00 12 A0 00 00 00 35 00 00 00 35F5 23 00 00 00 00 00 00 00设备返回上一笔数据
5主机à设备41 54 4B 30 30 31 23 00 00 00 00 4100 00 00 54 00 00 00 44 00 00 00 00主机要求关闭激光瞄准 ,这个和第一条命令是相同的。就是说当设备激光出于关闭状态时,收到这个命令会打开激光;反之是关闭激光
6设备à主机41 54 4B 30 30 31 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000设备 ECHO
7主机à设备41 54 44 30 30 31 23 00 00 00 00 41 00 00 00 54 00 00 00 4B 00 00 0030主机要求获得上一笔数据
8设备à主机41 54 44 00 00 21 D2 00 00 00 1F 00 00 00 1F0A 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关键代码如下:**** Hidden Message *****
调用方法如下:
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#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::PID == UT395B_PID;

      };



      virtual uint8_t GetAddress() {

                return bAddress;

      };



};

UT395.ino#include "UT395.h"

#include <SPI.h>



USB   Usb;

UT395But395(&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={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={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;



       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) {

             unsigned long d;

             d = ((((unsigned long)inBuff)<< 16) & 0xFF0000) +

                               ((inBuff << 8) & 0xFF00) +

                               (inBuff & 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,HEX); Serial.print(" ");}

       //Serial.println("");

            

}

}


有了上面的方法,我们可以在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月

ecstart 发表于 2019-2-1 13:48

顶一下!真的很不错!学习了!:handshake

xspeedm 发表于 2019-9-1 23:51

非常好的资料,感谢作者!

wangyirun 发表于 2019-9-24 21:15

哇,真厉害

fengzaiqingdao 发表于 2019-11-18 16:06

这个比较实用

ty_gdl 发表于 2020-2-7 15:33

非常好的资料,感谢作者!

weekendzz 发表于 2020-2-24 16:37

学习大神作品

yes_wu 发表于 2022-1-12 16:36

谢谢提供!

guoweiyi86 发表于 2022-4-20 16:38

手头有设备,就是不知道怎么用。

Zoologist 发表于 2022-4-20 16:45

guoweiyi86 发表于 2022-4-20 16:38
手头有设备,就是不知道怎么用。

你可以试试,有 windows代码和arduino 代码
页: [1] 2
查看完整版本: 使用 UT395B 实现激光测距