探究激光雷达测距模块及数据分析+AD模块

1.产品介绍
SKP 系列激光测距雷达是一种能够测量物体到测距雷达前端距离的设备, 可用于工业测量、 机器人避障、 汽车防撞等场景。 SKP40 拥有长达 40 米的量程, 并且在全距离内都拥有厘米级精度, 全新的 DeTOF 技术让SKP40 在室外强光下稳定工作, 这些特性进一步提升了 SKP40 在各种环境下的适应能力。(上海申稷光电的激光雷达还蛮好的,推荐使用,仅供参考,本次主要以记录使用情况,为以后整理做准备。)

2.关键特性

  • 20/40 米测量距离
  • 3cm 绝对精度( 弱反射率下绝对精度 5cm, 极端弱反射率下绝对精度 7cm)
  • 自适应波特率功能(这个要注意不要设置太小的波特率)
  • 8000Hz 测量频率
  • 接口(本次实验使用的是LVTTL)

3.接线示意图
在这里插入图片描述
SKP40 激光测距雷达的输出接口为 LVTTL 电平的串口, 可以直接连接单片机、 ARM 的串口管脚, 但是不能直接连接 RS232 电平的串口到 PC 机。 LVTTL 电平的高电平为 3.3V, 低电平为 0V。

4.通信协议
SKP 系列激光测距雷达通信协议是构建在串口通信协议基础之上的应用层协议, 协议最大帧长度为 8 字节,使用小端格式
在这里插入图片描述
5.数据解析
5.1获取设备的信息(0x01)
功能描述
上位机需要获取当前设备信息, 发送<设备信息请求>至测距雷达, 测距雷达会响应并返回设备软硬件版本号。
快速指令参考3: 55 01 00 00 00 00 D3 AA
Value 内容描述
Value 内容为空
返回数据
共返回两帧数据:
第一帧: 55 01 AA BB BB BB JY AA
AA: 当前设备型号
BB: 当前固件版本号
JY: 校验位
第二帧: 55 01 AA BB CC CC JY AA
AA: 当前设备输出的数据格式:
00: 字节格式 01: Pixhawk 格式
BB: 当前设备的测量模式:
00: 连续测量模式-开机启动
01: 单次测量模式
02: 连续测量模式-开机不启动
CC: 当前设备设置的测量频率
JY: 校验位

5.2设置波特率(0x03)
功能描述
上位机发送<设置测量频率>至 SKP40, SKP40 根据数据包的 Value 值立即更新测量频率。 参数掉电丢失。
Value 内容描述
3 快速指令参考指的是用户可以直接发送此指令进行控制, 而无需重新手动计算 CRC 校验值。 适用于无 Value
值的指令。
返回数据
无。

5.3设置数据格式(0x04)
功能描述
上位机发送<设置测量模式>至 SKP40, SKP40 立即更新输出数据的格式。 参数掉电丢失。
快速指令参考:
字节格式: 55 04 00 00 00 01 2E AA
Pixhawk 格式: 55 04 00 00 00 02 7D AA
Value 内容描述
在这里插入图片描述
返回数据

5.4启动测量(0x05)
5.5停止测量(0x06
5.7测量数据返回(0x07)
5.8高速测量数据返回(0x0E)(这个要参考厂家的数据手册注意一下)
5.9保存设置(0x08)
5.10获取序列号(0x0A)

我们通过厂家的数据手册学习,我可以有两种方式来使用此激光雷达:
一、在PC端进行数据的采集和分析以及设置
二、在单片机(其他MCU)进行通信设置激光雷达

我们在使用单片机来设置激光雷达时,可以参考数据手册提前设置好,然后保存格式。;也可以将一系列的启动和其他参数通过单片机的初始化设置。
代码如下:

/*以下为步进电机连接引脚 
 * author:yuyu  SCAU
 * dir -->d2
 * en --> d5
 * stp -->d6
 * com -->5v
 * V --> 12v
 * GND-->GND
 * 
 * 以下为AD采样引脚:
 * Vs -->5v
 * GND-->GND
 * Vout-->A0
 * 
 * 以下为蓝牙模块引脚:
 * Vcc-->3.3v/5.0v
 * GND-->GND
 * RX-->TX1
 * TX-->RX1
 */
#define dir_pin 2
#define en_pin 5
#define stp_pin 6

//角度传感器的相关参数
const int PIN_AD = A0; //pin connection
float sensorVolt =0; //unit: mV
float angle; //unit: centigrade

//激光雷达的相关变量机常量
float range = 0;//目标距离
unsigned char uart[8]={0},counter = 0;//存放雷达测量的数据
unsigned char data[3]={0};
unsigned char sign=0;
const unsigned char HEADER=0x55;//数据包帧头
const unsigned char END=0xAA;//终止序列

//将接收的数据整合到一个结构体中,使其存储到一个连续的内存中,较为高效
struct Target_Bits
{
  unsigned char    Target_header:8;        /* 0:7    */
  unsigned char    Target_key:8;           /* 8:15   */
  unsigned char    Target_value:8;          /* 16:23  */
  unsigned char    Target_data1:8;         /* 24:31  */
  unsigned char    Target_data2:8;          /* 32:39  */
  unsigned char    Target_data3:8;         /* 40:47  */
  unsigned char    Target_CRC:8;            /* 48:55  */
  unsigned char    Target_end:8;           /* 56:63  */
};

//联合表示几个变量公用一个内存位置, 在不同的时间保存不同的数据类型和不同长度的变量
typedef union{                    
  unsigned char Byte[8];
  struct  Target_Bits bit;
}TargetData;

void setup()
{  
  Serial.begin(115200);//设置arduino与电脑连接串口的波特率
  Serial1.begin(115200);//设置雷达与arduino连接串口的波特率
           
  //set timer4
    TCCR4A = 0;
    TCCR4B = 0;
    TCNT4  = 0;
    // Mode 10: phase correct PWM with ICR4 as Top (= F_CPU/2/25000)
    // OC4A OC4B OC4C as Non-Inverted PWM output
    ICR4   = (F_CPU/2000)/2;
    TCCR4A =_BV(COM4A1) | _BV(COM4B1) | _BV(COM4C1) | _BV(WGM41);
    TCCR4B = _BV(WGM43) | _BV(CS40);
    
    // Set the PWM pin as output.
    pinMode(2,OUTPUT);
    pinMode(6,  OUTPUT);
    //模拟量输入
    pinMode(PIN_AD,INPUT);
    
}

void loop()
{
    //just set this for step motor
    Pwm_out(6,1000);
    digitalWrite(2,HIGH);

    sensorVolt = analogRead(PIN_AD); //转后的电压值
    angle = sensorVolt*(360/1023.0);  
//    Serial.print("angle: "); //print the result
//    Serial.print(angle);    
//    Serial.println(" 度"); 
    
    if(sign)
   { 
       sign=0;
       if(uart[1]==0x07)
       {
          TargetData TargetData;
          for (uint8_t i = 0; i < 8; i++)
          {
            TargetData.Byte[i] = uart[i];    //  Buf指向有效数据起始
    //       Serial.print(TargetData.Byte[i]);
    //       Serial.print("\n");
          }
          for(int j =0;j<3;++j)
          {
            data[j]=TargetData.Byte[j+3];
           }
         range=readCharToLL(data,3);
         Serial.print(range);
         Serial.print(",");
         Serial.print(angle);    
         Serial.print("\n");
       }                               
    } 
    
}

//激光数据采集
void serialEvent1() {
    while (Serial1.available()) {
    uart[counter]=(unsigned char)Serial1.read();
    if(counter==0&&uart[0]!=HEADER) return;      //第0号数据不是帧头             
    counter++;   
    if(counter==8)             //接收到8个数据
    { 
       counter=0;               //重新赋值,准备下一帧数据的接收 
       sign=1;
    }   
  }
}

/** 
 * 将unsigned char数组转换成long long数值
 * @param str 数组 
 * @param len str数组长度
 * @returns 0 on error
 */
int readCharToLL(unsigned char* str, int len)
{
    if (str == NULL||len <= 0)  //指针的判断
    {
        return 0;
    }
    int i = 0;
    int value = 0;
    for (i=0;i<len;i++)
    {
        value = value*16*16 + (str[i]/16)*16 + str[i]%16;
    }
    return value;
}

//motor driver 
void Pwm_out(int pin ,int value)
{ 
  switch(pin){
    case 5:
        OCR3A = value;
        break;    
    case 6:
        OCR4A = value;
        break;
    case 7:
        OCR4B = value;
        break;
    case 8:
        OCR4C = value;
        break;
    default:
        //no other pin will work
        break;  
  }
}

加入AD模块后的效果:
参考此视频链接

关于arduino的AD模块的知识扩展:
根据官网说明:analogRead()大约100us,换句话说,一秒钟最多只能读一万次(10k),进一步来说,在Arduino的sampling rata 是9600HZ,接近10KZ.这是因为Arduino的ADC的Prescaler(预分频器)被设置为128;

推演:
假设 Arduino 的频率(Clock)是 16MHz, 则 ADC clock = 16MHz / 128 = 125KHz;一次 ADC 转换要踢它13下, 就是要 13 clock (tick), 于是变成 125KHz/13 = 9600Hz。
我们可以参考analogRead()的源代码进行底层的改写与调用:
请参考此链接

我们可以看看 analogRead( ) 这函数的源代码:

int analogRead(uint8_t pin) {
  extern unsigned char analog_reference;
  uint8_t low, high;
  const unsigned char bitADSC = (1 << ADSC);
  if (pin >= 14) pin -= 14; // allow for channel or pin numbers
  // set the analog reference (high two bits of ADMUX) and select the
  // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
  // to 0 (the default).
  ADMUX = (analog_reference << 6) | (pin & 0x07);
  // start the conversion
  ADCSRA |= (1 << ADSC);  //    sbi(ADCSRA, ADSC);
  // ADSC is cleared when the conversion finishes
  while (bit_is_set(ADCSRA, ADSC));  // while(ADCSRA & bitADSC) ;
  // we have to read ADCL first; doing so locks both ADCL
  // and ADCH until ADCH is read.  reading ADCL second would
  // cause the results of each conversion to be discarded,
  // as ADCL and ADCH would be locked when it completed.
  low  = ADCL;
  high = ADCH;
  // combine the two bytes
  return (high << 8) | low;
}

看到了吧, analogRead( ) 里面有个如下的 while Loop:
while (bit_is_set(ADCSRA, ADSC));
这句意思是要一直等到 ADC 转换完成把 ADSC 这 bit 清除为 0;
然后才可以读取 ADCL 与 ADCH, 合成总共 10 bit, 代表 0 ~ 1023 的取样值 !

对于Prescaler是可以改的,只要把 ADC 的 Prescaler 改小, 就可以加快 ADC 转换速度与 analogRead( )速度!

默认情况下,你具有5V的模拟基准。因此,analogRead的精度为5/1023 = 0.0048875。
本节总结:
1)在此小节,解决了上节激光雷达的初始化慢的原因,解决办法:将通信的的波特率设置高一些,还可以在单片机的初始化对激光雷达进行初始化。

2)又将无线蓝牙模块重新拆下进行波特率设置,整个装置的拆与组装也用了较长时间。

3)初步加入AD模块的代码,但是还未进行底层改写,加快采样率。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值