← 返回文章库

机器人避障实战:超声波/红外/ToF/激光雷达四种测距方案怎么选

最后更新 2026-06-20
⏱ 约 18 分钟 🟡 涉接线/强电
你将学到
  • 说清避障的本质是「测前方距离 + 据距离改方向」,而不是某个神奇模块
  • 横向对比四种测距方案的量程/精度/价格/抗干扰,知道什么场景该选哪个
  • 写出能跑的 HC-SR04 测距 + 差速底盘避障状态机,并用舵机云台扫描选更空的方向

让小车自己别撞墙

你已经在 差速底盘那一篇 里让两个轮子能前进、后退、原地转了。现在的问题很现实:它只会闷头往前冲,撞到墙也不知道停。

避障没那么玄。它就两步:测出前方有多远的障碍,然后根据这个距离决定方向——够空就继续走,太近就停下来转个弯。难点全在第一步,也就是「测距」。一个机器人聪明不聪明,很大程度上取决于它的眼睛——测距传感器——靠不靠谱。

这篇我们把主流的四种测距方案摊开比一遍,然后用最便宜的超声波 HC-SR04 写一套真能跑的避障逻辑。HC-SR04 的接线和原理在 /sensor/hc-sr04/ 已经讲透了,这里不重复,直接拿来用。

四种测距方案,各有各的脾气

机器人测距常见的就这四类。它们测的物理量不一样,擅长的场景也完全不同。

① 超声波 HC-SR04——便宜大碗,但有脾气

发一束 40kHz 的声波,撞到障碍弹回来,数往返时间算距离。量程 2cm~400cm,几块钱一个,新手入门首选。

但它有三个坑你必须知道:

  • 盲区:太近(< 2cm)测不准,因为回波还没发完就回来了。
  • 波束宽:它发出去的不是一条线,而是一个约 15° 的锥形。意思是它「看到」的是前方一大片区域里最近的那个点,分不清是正前方还是斜前方。想精确定位障碍在哪,它做不到。
  • 怕软、怕斜:海绵、窗帘这类软物会把声音吸掉,回波太弱测不到;倾斜的桌面会把声波反射到别处去,也收不到回波——于是小车以为前方是空的,直接撞上去。

② 红外测距——近距凑合,看脸色

红外发射管打一束光,接收管根据反射光的强弱或角度判断距离。便宜,响应快,但严重受环境光和物体颜色影响:大太阳底下容易失灵,黑色物体反射弱、测出来比实际远。我的建议是,红外用来做「有没有东西」的近距开关(比如防跌落、循迹)还行,用来做精确测距别指望。循迹的玩法见 循迹小车那篇

③ ToF 激光 VL53L0X——小巧精准,我的首选

ToF 是 Time of Flight,飞行时间。它发一束红外激光,直接测光往返的时间(光速换算)。ST 的 VL53L0X 把这套东西塞进了一颗指甲盖大的芯片里,I2C 通信,量程几 mm 到 2m,精度毫米级,而且几乎不受物体颜色和环境光干扰(具体参数见文末 sources 里 ST 的官方页)。

它比超声波贵几倍,但波束窄、不怕软物、体积小。如果你的小车要在桌面上精细地躲杯子、躲书本,我会直接上 VL53L0X 而不是超声波。 唯一要注意的是它走 I2C,需要懂一点 I2C 地址的概念(模拟量和数字量、总线通信的基础可以回看 /principle/digital-analog/)。

④ 激光雷达 LiDAR——360° 扫描,贵,给 SLAM 用

LiDAR 本质是一个会转的 ToF——它一边发激光一边旋转,转一圈就得到周围 360° 一整圈的距离点云。有了点云,机器人才能「画」出周围环境的地图,这就是建图与定位(SLAM)的基础。

但它贵(便宜的几百,好的上千),功耗也高,新手做避障用不上。我把它放这儿,是为了让你知道:当你想让机器人不只是「躲」而是「认识房间、自己规划路线」时,LiDAR 才登场。那是 机器人接入 ROS 之后的世界,这篇先按下不表。

一张表看懂怎么选

方案 量程 精度 价格 抗干扰 适用场景
超声波 HC-SR04 2~400cm 厘米级 极低(几元) 怕软物/斜面,波束宽 入门避障、防撞,对精度要求不高
红外测距 几~80cm 怕环境光/颜色 近距开关、防跌落、循迹
ToF VL53L0X 几mm~2m 毫米级 中(几十元) 强,不怕颜色 桌面精细避障、小型机器人首选
激光雷达 LiDAR 数十cm~数十m 厘米级 高(数百起) SLAM 建图、自主导航

一句话取舍:学避障从 HC-SR04 起步,要做得靠谱升级 VL53L0X,要做导航再上 LiDAR。

完整代码:从测距到自己绕开

下面这套代码跑在 ESP32(或 Arduino,引脚号改一下即可)上。接线以 /sensor/hc-sr04/ 那篇为准,这里只写引脚定义,不重复画接线图。GPIO 的用法可参考文末 sources 里乐鑫的官方 API 文档。

第一步:HC-SR04 测一个距离出来

原理就是「给 Trig 一个 10μs 高脉冲触发,然后量 Echo 高电平持续了多久」。声速约 340m/s,即 0.034cm/μs。声波是往返,所以距离 = 时间 × 0.034 / 2。

// ---- HC-SR04 引脚(接线以 /sensor/hc-sr04/ 为准)----
const int TRIG = 5;
const int ECHO = 18;

void setup() {
  Serial.begin(115200);
  pinMode(TRIG, OUTPUT);
  pinMode(ECHO, INPUT);
}

// 返回距离(cm);超时(没回波)返回 -1
float readDistanceCm() {
  // 1. 先拉低确保干净的触发
  digitalWrite(TRIG, LOW);
  delayMicroseconds(2);
  // 2. 给 10μs 高脉冲触发一次测量
  digitalWrite(TRIG, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG, LOW);

  // 3. 量 Echo 高电平时长,单位 μs;30ms 超时(约 5m 外算无回波)
  unsigned long us = pulseIn(ECHO, HIGH, 30000UL);
  if (us == 0) return -1;            // 超时,没测到障碍

  // 4. 声速 0.034cm/μs,往返要除以 2
  return us * 0.034f / 2.0f;
}

void loop() {
  float d = readDistanceCm();
  if (d < 0) Serial.println("无回波(前方很空或测到软物)");
  else { Serial.print(d); Serial.println(" cm"); }
  delay(100);
}

先单独烧这段,打开串口监视器,拿手在传感器前面晃,看数值跟着变。数值靠谱了,再往下加避障。

第二步:差速底盘的避障状态机

避障逻辑别写成一坨乱糟糟的 if。用状态机来组织最清爽:小车在「前进 / 后退 / 转向」几个状态间切换,每个状态干一件事。

差速底盘的电机控制函数(forward / backward / turnRight / stop)直接复用 差速底盘那篇 写好的,这里只贴避障主逻辑。

const float STOP_CM = 20.0f;   // 近于 20cm 就认为有障碍

enum State { DRIVING, BACKING, TURNING };
State state = DRIVING;
unsigned long stateStart = 0;

void enter(State s) { state = s; stateStart = millis(); }

void loop() {
  float d = readDistanceCm();
  // 无回波(-1)当作前方空旷处理,避免误把软物当成墙后乱转
  bool blocked = (d > 0 && d < STOP_CM);

  switch (state) {
    case DRIVING:
      forward();
      if (blocked) { stop(); enter(BACKING); }  // 撞前停下
      break;

    case BACKING:
      backward();
      if (millis() - stateStart > 350) { stop(); enter(TURNING); }  // 后退一小段
      break;

    case TURNING:
      turnRight();
      if (millis() - stateStart > 400) {        // 转个弯换方向
        // 转完再测一次,前方空了才恢复前进,否则继续转
        if (!blocked) { stop(); enter(DRIVING); }
        else stateStart = millis();             // 还堵着,再转一会
      }
      break;
  }
}

注意两个设计:一是millis() 计时而不是 delay(),这样测距不会被卡住;二是转完弯先复测,确认前方真空了才走,否则在墙角里它会一直撞。

你应该看到什么

烧进去,把小车放地上开机。正常的话:

  • 它一路向前开。
  • 接近墙壁(约 20cm)时猛地停住
  • 然后倒一小段、原地右转
  • 转到前方空旷,继续前进
  • 走到墙角(两面都有墙)时,它会连转几次直到找到出路。

如果它该停的时候不停、直接撞上去,八成是测距没读对,回第一步单独调串口。

故障排查

现象 可能原因 怎么办
距离数值乱跳/不准 电机干扰电源、Echo 线太长 传感器和电机供电分离;Echo 线短一点、远离电机线
撞上软物(窗帘/沙发) 声波被吸收,没回波 换 VL53L0X;或把「无回波」也当障碍保守处理
斜面/桌沿漏检直接掉 声波被斜面反射偏走 加红外做防跌落兜底,或低头装传感器
反应慢、停不住 测距太慢或阈值太小 测距间隔缩到 60ms,STOP_CM 调大到 25~30
转向后又立刻撞回去 转完没复测就走 用上面状态机里「转完复测」的写法

供电务必分离:电机启停的瞬间会把电压拉低,直接拖累传感器读数。给逻辑(ESP32+传感器)和电机各走各的电源,共地即可。这是避障小车最容易翻车的地方。

两个变体,让它更聪明

变体一:舵机云台扫描,选更空的方向走

固定朝前的超声波,撞墙了只能瞎转。更聪明的做法:把 HC-SR04 装在一个舵机上做成「云台」,撞墙后让舵机摆头左、中、右各测一次,哪边最空就往哪边转。舵机控制见 /guide/l2-servo/

#include <ESP32Servo.h>
Servo pan;
const int PAN_PIN = 13;

void setup() { pan.attach(PAN_PIN); }

// 摆头扫三个方向,返回最空方向: 0=左 1=中 2=右
int scanBestDir() {
  int angles[3] = {150, 90, 30};   // 左、中、右
  float best = -1; int bestIdx = 1;
  for (int i = 0; i < 3; i++) {
    pan.write(angles[i]);
    delay(300);                    // 等舵机摆到位
    float d = readDistanceCm();
    if (d < 0) d = 400;            // 无回波视为很空
    if (d > best) { best = d; bestIdx = i; }
  }
  pan.write(90);                   // 摆回正前方
  delay(200);
  return bestIdx;
}

把状态机里 TURNING 的固定右转,换成「先 scanBestDir() 再朝最空方向转」,小车就从「撞了乱转」进化成「看看哪边宽,往哪边走」。

变体二:把测距换成 VL53L0X

想让避障更准、不再怕软物,把 readDistanceCm() 整个换掉就行,上层状态机一行不用动——这就是把测距单独封装成一个函数的好处。

#include <Adafruit_VL53L0X.h>
Adafruit_VL53L0X lox;

void setupToF() { lox.begin(); }   // I2C 默认地址 0x29

float readDistanceCm() {
  VL53L0X_RangingMeasurementData_t m;
  lox.rangingTest(&m, false);
  if (m.RangeStatus == 4) return -1;       // 超量程
  return m.RangeMilliMeter / 10.0f;        // mm 转 cm
}

接上 SDA/SCL 两根 I2C 线,上面那套避障状态机直接原样跑,而且测软物、测斜面都比超声波稳得多。

动手挑战

在地上随手摆一堆杂物:几个纸盒、一个抱枕(软的)、一本斜靠着的书。让你的小车从这头走到那头,中途不许被卡死、不许撞翻东西。

你大概率会发现:纯超声波版本会在抱枕和斜书上栽跟头(测不到)。这正是逼你升级的好时机——把测距换成变体二的 VL53L0X,或者加一个红外做兜底,再跑一遍。能稳稳走通,你就真正理解了「为什么一个机器人需要不止一种传感器」。

小结与下一步

避障的核心从来不是某个神奇模块,而是**「测距 + 决策」这两件事**。测距选什么传感器,取决于你要躲什么、预算多少:入门 HC-SR04,精细 VL53L0X,导航 LiDAR。决策这层我们用状态机搭了个能跑的骨架。

但你可能已经注意到一个毛病:小车的转向是「定时硬转」,转多转少全靠拍脑袋。要让它转得又快又稳、不超调,得让控制量跟着误差走——那就是 PID 控制 要解决的问题。如果你想直接跳到「让机器人自己建图导航」,那条路通向 接入 ROS 与 SLAM,LiDAR 也会在那儿正式上场。

📄 来源 / 自校链接

本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为公开资料的学习整理,非亲测。涉接线/花钱/合规的步骤请结合实物与官方最新资料验证,风险自负。见免责声明