TOF激光测距传感器-25米

简介

TOF激光测距传感器是一款基于dTOF(直接飞行时间)技术的新一代激光测距传感器。在硬件设计与算法层面进行了全面升级,测距盲区小于5cm,最大量程可达25m(具体视环境反射率而定),测距精度在±3cm以内(典型值),尤其在动态环境下表现稳定,抗环境光干扰能力高达100K LUX,适用于室内外强光环境。支持UART、I2C、I/O等多种通信接口,满足不同开发架构需求。

TOF激光测距传感器-25米以小体积(仅7.5g重量)和高性价比为核心亮点,专为需要大规模集成激光测距功能的场景设计,例如消费级电子产品、机器人及无人机等。其紧凑的设计使其能够轻松嵌入空间受限的设备中,同时保持高性能测距能力。

应用场景

  1. 机器人避障与导航

    通过多个传感器覆盖机器人探测区域,结合高帧率(可达100Hz)数据输出,实现动态避障与路径规划

  2. 无人机定高与地形跟随

安装于无人机底部,实时测量地面距离,辅助精准定高与降落,尤其在非平整地面场景中表现优异。

  1. 工业检测与自动化

    用于料位检测(如仓储料仓高度监测)、液压堆料形态分析,或生产线上的物体计数与定位

  2. 智慧交通与车流监控

    部署于道路龙门架或停车场,统计车流量、车速及车位状态,支持智能交通系统建设。

  3. 消费电子与智能家居

    集成于智能门锁、健身设备或自动门,实现手势识别、运动监测等交互功能。

产品参数

  • 刷新频率:100Hz
  • 经典测距范围:0.05~25m
  • 经典测距精度:±3cm
  • 波长:905nm
  • 抗环境光:100K LUX照度
  • 视场角(FOV):1~2°
  • 供电电压:4.3V~5.2V
  • 功耗:250mW
  • 通讯接口:UART/IIC/IO
  • IIC默认从机地址:0x80
  • 工作温度:-10℃~60℃
  • 尺寸:22.7 mm *28.0 mm *13.6mm
  • 产品重量:7.5g

尺寸参数

使用教程

准备

  • 硬件
    • TOF激光测距传感器-7.8米
    • ESP32系列开发板
  • 软件

串口输出

UART模式具有两种输出方式:主动输出 Active Output、查询输出 Query Output,两种输出方式可以通过在上位机软件上修改数据输出方式进行切换。通过USB转TTL模块(线序和供电电压参考数据手册)连接 TOFSense-F/F2 系列产品到上位机 软件,识别成功后点击进入设置页面,配置完参数后需要点击写入参数按钮来保存参数,写入参数成功后可以读取一次参数来确认参数是否写入成功。

主动输出

UART 主动输出模式仅可在单模块时使用。

接口类型设置为 UART,数据输出方式设置为 ACTIVE,UART 主动输出模式配置如图所示。该模式下模块默认以50Hz 的频率主动输出测量信息,输出格式遵循 NLink_TOFSense_Frame0 协议。

接线图

示例代码

HardwareSerial mySerial(1);

typedef struct {
  unsigned char id;               //TOF模块的id
  unsigned long system_time;      //TOF模块上电后经过的时间,单位:ms
  float dis;                      //TOF模块输出的距离,单位:m
  unsigned char dis_status;       //TOF模块输出的距离状态指示
  unsigned int signal_strength;   //TOF模块输出的信号强度
  unsigned char range_precision;  //TOF模块输出的重复测距精度参考值,TOFSense-F系列有效,单位:cm
} tof_parameter;                  //解码后的TOF数据结构体

/**
  @brief  在串口读取数据
  @param  buf 存放读取到的数据
  @param  len 读取的字节长度
  @return  返回值是实际读取到的字节数
*/
size_t readN(uint8_t *buf, size_t len);

/**
  @brief  读取完整数据包
  @param  buf 存放读取到的数据
  @return  返回值为真表示成功,返回值为假表示失败
*/
bool recdData(tof_parameter *buf);

void setup() {
  Serial.begin(115200);
  mySerial.begin(921600, SERIAL_8N1, 4, 5);  //RX,TX
}

void loop() {
  tof_parameter tof0;  //定义一个存放解码后数据的结构体
  recdData(&tof0);
  // 通过串口打印数据
  Serial.print("id:");
  Serial.println(tof0.id);
  Serial.print("system_time:");
  Serial.println(tof0.system_time);
  Serial.print("dis:");
  Serial.println(tof0.dis);
  Serial.print("dis_status:");
  Serial.println(tof0.dis_status);
  Serial.print("signal_strength:");
  Serial.println(tof0.signal_strength);
  Serial.print("range_precision:");
  Serial.println(tof0.range_precision);
  Serial.println("");

  delay(1000);
}

size_t readN(uint8_t *buf, size_t len) {
  size_t offset = 0, left = len;
  int16_t Tineout = 1500;
  uint8_t *buffer = buf;
  long curr = millis();
  while (left) {
    if (mySerial.available()) {
      buffer[offset] = mySerial.read();
      offset++;
      left--;
    }
    if (millis() - curr > Tineout) {
      break;
    }
  }
  return offset;
}

bool recdData(tof_parameter *buf) {
  uint8_t rx_buf[16];  //串口接收数组
  int16_t Tineout = 5000;
  uint8_t ch;
  bool ret = false;
  uint8_t Sum;
  while (mySerial.available() > 0) {
    mySerial.read();
  }
  long timeStart = millis();

  while (!ret) {
    if (millis() - timeStart > Tineout) {
      break;
    }
    if (readN(&ch, 1) == 1) {
      if (ch == 0x57) {
        rx_buf[0] = ch;
        if (readN(&ch, 1) == 1) {
          if (ch == 0x00) {
            rx_buf[1] = ch;
            if (readN(&rx_buf[2], 14) == 14) {
              Sum = 0;
              for (int i = 0; i < 15; i++)
                Sum += rx_buf[i];
              if (Sum == rx_buf[15]) {
                buf->id = rx_buf[3];                                                                                                                                                   //取TOF模块的id
                buf->system_time = (unsigned long)(((unsigned long)rx_buf[7]) << 24 | ((unsigned long)rx_buf[6]) << 16 | ((unsigned long)rx_buf[5]) << 8 | (unsigned long)rx_buf[4]);  //取TOF模块上电后经过的时间
                buf->dis = ((float)(((long)(((unsigned long)rx_buf[10] << 24) | ((unsigned long)rx_buf[9] << 16) | ((unsigned long)rx_buf[8] << 8))) / 256)) / 1000.0;                 //取TOF模块输出的距离
                buf->dis_status = rx_buf[11];                                                                                                                                          //取TOF模块输出的距离状态指示
                buf->signal_strength = (unsigned int)(((unsigned int)rx_buf[13] << 8) | (unsigned int)rx_buf[12]);                                                                     //取TOF模块输出的信号强度
                buf->range_precision = rx_buf[14];                                                                                                                                     //取TOF模块输出的重复测距精度参考值
                ret = true;
              }
            }
          }
        }
      }
    }
  }
  return ret;
}

输出结果

查询输出

UART 查询输出模式可在单模块使用。

接口类型设置为UART,数据输出方式设置为INQUIRE,UART查询输出模式配置如图所示,写入参数后将不会再主动上报数据。该模式下需要通过控制器向期望查询模块发送包含该模块ID的查询指令,模块即可输出一帧测量信息。查询帧格式遵循。NLink_TOFSense_Read_Frame0 协议,输出帧格式遵循 NLink_TOFSense_Frame0 协议。

接线图

示例代码

HardwareSerial mySerial(1);

typedef struct {
  unsigned char id;               //TOF模块的id
  unsigned long system_time;      //TOF模块上电后经过的时间,单位:ms
  float dis;                      //TOF模块输出的距离,单位:m
  unsigned char dis_status;       //TOF模块输出的距离状态指示
  unsigned int signal_strength;   //TOF模块输出的信号强度
  unsigned char range_precision;  //TOF模块输出的重复测距精度参考值,TOFSense-F系列有效,单位:cm
} tof_parameter;                  //解码后的TOF数据结构体

/**
  @brief  在串口读取数据
  @param  buf 存放读取到的数据
  @param  len 读取的字节长度
  @return  返回值是实际读取到的字节数
*/
size_t readN(uint8_t *buf, size_t len);

/**
  @brief  读取完整数据包
  @param  buf 存放读取到的数据
  @param  id 模块ID
  @return  返回值为真表示成功,返回值为假表示失败
*/
bool recdData(tof_parameter *buf, uint8_t id = 0);

void setup() {
  Serial.begin(115200);
  mySerial.begin(921600, SERIAL_8N1, 4, 5);  //RX,TX
}

void loop() {
  tof_parameter tof0;  //定义一个存放解码后数据的结构体
  recdData(&tof0, 0);
  // 通过串口打印数据
  Serial.print("id:");
  Serial.println(tof0.id);
  Serial.print("system_time:");
  Serial.println(tof0.system_time);
  Serial.print("dis:");
  Serial.println(tof0.dis);
  Serial.print("dis_status:");
  Serial.println(tof0.dis_status);
  Serial.print("signal_strength:");
  Serial.println(tof0.signal_strength);
  Serial.print("range_precision:");
  Serial.println(tof0.range_precision);
  Serial.println("");

  delay(1000);
}

size_t readN(uint8_t *buf, size_t len) {
  size_t offset = 0, left = len;
  int16_t Tineout = 1500;
  uint8_t *buffer = buf;
  long curr = millis();
  while (left) {
    if (mySerial.available()) {
      buffer[offset] = mySerial.read();
      offset++;
      left--;
    }
    if (millis() - curr > Tineout) {
      break;
    }
  }
  return offset;
}

bool recdData(tof_parameter *buf, uint8_t id) {
  uint8_t rx_buf[16];  //串口接收数组
  int16_t Tineout = 5000;
  uint8_t ch;
  bool ret = false;
  uint8_t Sum;
  uint8_t cmdBuf[8] = { 0x57, 0x10, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00 };
  cmdBuf[4] = id;
  cmdBuf[7] = 0;
  for (int i = 0; i < 7; i++)
    cmdBuf[7] += cmdBuf[i];
  long timeStart = millis();
  long timeStart1 = 0;

  while (!ret) {
    if (millis() - timeStart > Tineout) {
      break;
    }

    if ((millis() - timeStart1) > 1000) {
      while (mySerial.available() > 0) {
        mySerial.read();
      }
      mySerial.write(cmdBuf, 8);
      timeStart1 = millis();
    }

    if (readN(&ch, 1) == 1) {
      if (ch == 0x57) {
        rx_buf[0] = ch;
        if (readN(&ch, 1) == 1) {
          if (ch == 0x00) {
            rx_buf[1] = ch;
            if (readN(&rx_buf[2], 14) == 14) {
              Sum = 0;
              for (int i = 0; i < 15; i++)
                Sum += rx_buf[i];
              if (Sum == rx_buf[15]) {
                buf->id = rx_buf[3];                                                                                                                                                   //取TOF模块的id
                buf->system_time = (unsigned long)(((unsigned long)rx_buf[7]) << 24 | ((unsigned long)rx_buf[6]) << 16 | ((unsigned long)rx_buf[5]) << 8 | (unsigned long)rx_buf[4]);  //取TOF模块上电后经过的时间
                buf->dis = ((float)(((long)(((unsigned long)rx_buf[10] << 24) | ((unsigned long)rx_buf[9] << 16) | ((unsigned long)rx_buf[8] << 8))) / 256)) / 1000.0;                 //取TOF模块输出的距离
                buf->dis_status = rx_buf[11];                                                                                                                                          //取TOF模块输出的距离状态指示
                buf->signal_strength = (unsigned int)(((unsigned int)rx_buf[13] << 8) | (unsigned int)rx_buf[12]);                                                                     //取TOF模块输出的信号强度
                buf->range_precision = rx_buf[14];                                                                                                                                     //取TOF模块输出的重复测距精度参考值
                ret = true;
              }
            }
          }
        }
      }
    }
  }
  return ret;
}

输出结果

IIC 输出

IIC 模式可在单模块与级联时使用。IIC 通信模式下通过控制器按照 IIC 通信时序向指定从机地址的期望查询模块发送读取帧,即可获得模块的距离等相关信息。此外,也可以通过IIC通信来更改模块的输出方式等各项参数。读取帧和写入帧格式遵循协议 NLink_TOFSense_IIC_Frame0。

模块处于 UART 模式时(注意上位机无法识别处于IIC模式下的模块),通过USB转TTL模块(线序和供电电压参考数据手册)连接TOF激光测距传感器到上位机 软件,识别成功后点击进入设置页面,IIC输出模式配置如图,可以通过设置模块的ID 来改变该模块的IIC从机地址(7 位从机地址为0x08+模块ID,ID设置范围为0~111),配置完参数后需要点击写入参数按钮来保存参数。注:切换到IIC模式后,可以参考FAQ章节中的方式更改回UART模式。

[!WARNING]

在修改配置之前请记住你配置前的波特率(Baudeate),以便后面切换回UART模式。

接线图

Arduino示例代码

#include <Wire.h>  //I2C library

#define deviceaddress 0x08

typedef struct {
  uint8_t id;                //ID
  uint8_t interface_mode;    //通信接口模式,0-UART,1-CAN,2-I/O,3-IIC
  uint32_t uart_baudrate;    //UART波特率
  uint32_t system_time;      //系统时间
  float dis;                 //距离
  uint16_t dis_status;       //距离状态指示
  uint16_t signal_strength;  //信号强度
  uint8_t range_precision;   //测距精度
} tof_parameter;             //TOFSense-F输出的参数结构体

tof_parameter tof0;  //定义一个存放解码后数据的结构体

uint8_t DATA[4] = { 0 };

uint8_t bbt[4] = { 0x00, 0xc2, 0x01, 0x00 };

void i2c_writeN(uint8_t registerAddress, uint8_t *buf, size_t len) {

  Wire.beginTransmission(deviceaddress);
  Wire.write(registerAddress);  // MSB
  Wire.write(buf, len);
  Wire.endTransmission();
  // delay(4);
}

int16_t i2c_readN(uint8_t registerAddress, uint8_t *buf, size_t len) {
  uint8_t i = 0;
  Wire.beginTransmission(deviceaddress);
  Wire.write(registerAddress);
  if (Wire.endTransmission(false) != 0) {
    return -1;
  }

  Wire.requestFrom(deviceaddress, len);
  delay(100);
  while (Wire.available()) {
    buf[i++] = Wire.read();
  }
  return i;
}

bool recdData() {
  uint8_t pdata[48];  //读取缓存数组
  // Serial.println(i2c_readN(0x00, iic_read_buff, 48));
  if (i2c_readN(0x00, pdata, 32) == 32) {
    if (i2c_readN(0x20, &pdata[32], 16) == 16) {
      tof0.interface_mode = pdata[0x0c] & 0x07;
      tof0.id = pdata[0x0d];
      tof0.uart_baudrate = (uint32_t)(((uint32_t)pdata[0x10]) | ((uint32_t)pdata[0x11] << 8) | ((uint32_t)pdata[0x12] << 16) | ((uint32_t)pdata[0x13] << 24));
      tof0.system_time = (uint32_t)(((uint32_t)pdata[0x20]) | ((uint32_t)pdata[0x21] << 8) | ((uint32_t)pdata[0x22] << 16) | ((uint32_t)pdata[0x23] << 24));
      tof0.dis = (float)(((uint32_t)pdata[0x24]) | ((uint32_t)pdata[0x25] << 8) | ((uint32_t)pdata[0x26] << 16) | ((uint32_t)pdata[0x27] << 24)) / 1000;
      tof0.dis_status = (uint16_t)(((uint16_t)pdata[0x28]) | ((uint16_t)pdata[0x29] << 8));
      tof0.signal_strength = (uint16_t)(((uint16_t)pdata[0x2a]) | ((uint16_t)pdata[0x2ab] << 8));
      tof0.range_precision = pdata[0x2c];
    } else
      return false;
  } else
    return false;
  return true;
}

void setup() {
  Wire.begin(5, 4);  // initialise the connection
  Serial.begin(115200);
  // i2c_writeN(0x10, bbt, 4);
}

void loop() {
  recdData();

  //通过串口打印数据
  Serial.print("id:");
  Serial.println(tof0.id);
  Serial.print("interface_mode:");
  Serial.println(tof0.interface_mode);
  Serial.print("uart_baudrate:");
  Serial.println(tof0.uart_baudrate);
  Serial.print("system_time:");
  Serial.println(tof0.system_time);
  Serial.print("dis:");
  Serial.println(tof0.dis);
  Serial.print("dis_status:");
  Serial.println(tof0.dis_status);
  Serial.print("signal_strength:");
  Serial.println(tof0.signal_strength);
  Serial.print("range_precision:");
  Serial.println(tof0.range_precision);
  Serial.println("");

  delay(1000);  //每1000ms查询一次
}

输出结果

I/O 输出

I/O输出模式下,仅可在单模块时使用,模块不能输出测距值,TX/CAN_L与RX/CAN_H输出互补电平,两根信号线的电平状态是相反的,高电平为3.3V,低电平为0V,另外需要注意输出电流较小,驱动其它设备时需要注意是否能驱动,若不能直接驱动则可以使用继电器等方式来进行驱动。

模块处于UART模式时(注意上位机无法识别处于I/O模式下的模块),通过USB转TTL模块(线序和供电电压参考数据手册)连接TOFSense系列产品到上位机软件,识别成功后点击进入设置页面,配置完参数后需要点击写入参数按钮来保存参数。注:切换到I/O模式后,如果需要更改Band_Start、Bandwidth 等参数,可以参考FAQ章节中的方式来更改回 UART 模式然后进行配置。

[!WARNING]

在修改配置之前请记住你配置前的波特率(Baudeate),以便后面切换回UART模式。

单阈值

接口类型设置为 IO,Band Start 设置为 1000,Band Width 设置为 0,IO 单阈值配置如图4所示,写入参数模块重新启动后将不再输出测距值而是输出高低电平。

按照上述设置后,此时阈值=Band Start=1000(mm),该模式下当测距值<1m 时RX 为高电平,TX 为低电平,当测距值>1m 时 RX 为低电平,TX 为高电平。

例:当测距值为 0.3 米时,RX为高电平,TX为低电平,测距值变大到1.2 米,RX为低电平,TX为高电平。

双阈值

接口类型设置为 IO,滞环起点 Band Start 设置为 1000,滞环宽度 Band Width 设置为500,IO 双阈值配置如图 5 所示,写入参数模块重新启动后将不再输出测距值而是输出高低电平。

按照上述设置后该模式下距离值通过滞环比较转换为高低电平输出。在距离从小变大超过高阈值的时候或者从大到小低于低阈值的时候让 I/O 口电平反转。

例如,基于上述设置低阈值为 1 米,高阈值为 1.5 米。(低阈值=滞环起点Band Start,高阈值=滞环起点 Band Start+滞环宽度 Band Width)

当测距值为 0.3 米时,RX 为高电平,TX 为低电平,

当测距值变大到 1.2 米,RX 为高电平,TX 为低电平,

当测距值持续变大超过 1.5 米时,电平反转,RX 为低电平,TX 为高电平。

当测距值从 1.5 米多降到 1.2 米,RX 为低电平,TX 为高电平,

当测距值持续降到低于 1 米时,电平反转,RX 为高电平,TX 为低电平。

滞环比较示意图如图所示

常见问题

切换到 IIC 或 I/O 模式后,为什么上位机软件识别不了模块?如何在不同通讯模式间进行切换?

目前上位机软件只支持识别 UART 模式下的模块。在 UART 模式下时,可以通过上位机识别成功后进入设置页面将模块配置为IIC或I/O通讯模式;在IIC通讯模式下,可以按照IIC通信协议通过IIC通信向模块发送指令来切换回UART或I/O模式;此外,在没有IIC测试环境或者切换到 I/O 模式后可以通过以下方式来切换回UART 模式:

  1. 用户需要准备一个支持921600波特率的USB转TTL模块(推荐CP2102)并且安装对应的驱动程序,将USB转TTL模块的TX、RX、5V 三根线连接到TOF模块的对应引脚,GND引脚暂时先不连接,然后把USB转TTL模块插入电脑。
  2. 打开上位机软件,点击图标进入串口调试助手,把波特率改为你之前设置的波特率(若未修改过波特率为921600),选择USB转TTL模块对应的COM口然后点击连接按钮连接COM口(大部分情况下会自动连接),在单项发送的文本框内输入54 20 00 ff 00 ff ff ff ff 00 ff ff 00 10 0e ff ff ff ff ff ff ff ff ff ff ff 00 ff ff ff ff 7c,在右下角的定时发送栏将发送间隔改为 20ms,然后勾选定时发送。
  3. 此时将USB转TTL模块的GND接到TOF模块的GND引脚,模块会切换到UART模式并开始输出数据,此时取消勾选定时发送按钮,然后拔掉USB转TTL模块重新给模块上电后点击主页面的识别按钮即可识别模块。
  4. 如果切换失败,将USB转TTL模块拔掉再重复一下整个步骤,切勿在发送命令的情况下多次插拔GND引脚。如果能够正常识别模块但是串口输出数据异常,可以在设置页面中手动更改到UART模式。

DFshopping_car1.png DFRobot商城购买链接