资讯中心

基于Arduino的导盲机器人原型:三路超声波避障与差速控制实现

📅 2026/8/28 12:13:21
基于Arduino的导盲机器人原型:三路超声波避障与差速控制实现
说实话第一次看到“盲人自己动手造导盲机器人”这种消息时我被触动到的不是“黑科技”而是一种等不及的被需求推着走的行动力。导盲犬训练周期长、数量少很多城市排队几年都不一定有结果传统导盲杖又难以感知高处障碍和快速接近的风险。与其干等不如先做一个能跑起来的原型哪怕它不能立刻替代导盲犬至少能让需求被看见、让技术有方向。这篇文章就以这个项目为载体完整拆解一个低成本导盲机器人的设计过程。我们会从需求分析、硬件选型、原理拆解开始一直讲到 Arduino 代码实现、组装接线、运行验证和常见问题排查。如果你有单片机或嵌入式基础这篇文章可以帮你快速把方案落地如果你刚接触 Arduino只要按照步骤来也能在半天内搭出一个可以动、能报警、会选路的导盲机器人原型。1. 项目背景不等了自己动手做导盲机器人1.1 导盲设备为什么这么难等导盲犬是许多视障人士理想的出行伙伴但它的培养成本极高。一只合格的导盲犬需要经过专业训练、严格筛选最终能够稳定配合主人的步伐和指令。这个过程决定了它不可能大规模普及大部分视障人士排队数年也等不到一只。电子导盲设备的价值在于用传感器和算法去分担一部分“导盲”工作。虽然它不能像导盲犬一样理解复杂的交通语义但它可以持续检测前方、左右两侧的障碍物在危险发生前给出声音或振动提示。这门技术一旦成熟成本可以做到很低量产和维修也远比生物培养可控。这也是为什么“自己动手造一个导盲机器人”这个项目很有意义。它不等于民用产品但它是验证技术路径、收集真实需求、推动产品化的重要一步。1.2 导盲机器人能做什么本文要实现的导盲机器人原型本质上是一个“移动感知平台”。它安装在带有驱动轮的底盘上通过超声波传感器持续测量前方、左侧和右侧的距离当发现障碍物距离过近时自动停车、评估可通行方向、转向躲避并通过蜂鸣器发出不同的声音提示让使用者知道机器人正在选择哪条路线。这个功能看起来和普通避障小车很像但在导盲场景下有几个特殊要求第一检测必须连续且稳定不能频繁漏报或误报第二转向决策必须考虑人的通行空间而不是简单地“随机避让”第三反馈必须清晰让视障用户不依赖屏幕也能理解机器人的意图。1.3 这个项目对普通开发者的意义从技术角度来说这个项目涵盖了嵌入式开发中最经典的三块内容传感器数据采集、电机执行控制、逻辑决策与反馈。这三块内容是智能硬件的基础掌握了它后续再去做语音导盲杖、视觉识别导盲设备甚至服务机器人都能沿用同一套架构。从产品角度来说这个项目让我们思考一个问题技术不是先做出来再去寻找用户而是应该从真实需求出发先定义场景再选择传感器和算法。文章结尾我也会给出工程化建议把原型推向更可靠形态时需要考虑哪些点。2. 导盲机器人整体方案设计2.1 需求分析先明确“必须做什么”在做任何硬件项目之前我都建议先写需求不写清楚需求就买硬件最后大概率会走弯路。导盲机器人原型最核心的需求只有四个检测前方障碍物在距离过近时停止避免直接碰撞。检测左右两侧的空间判断哪一个方向可以通行。自动做出转向决策并执行转向动作。通过声音编码反馈当前决策让人理解机器人的意图。这四个需求决定了系统必须有感知单元、决策单元、执行单元和反馈单元。也决定了我们不需要一开始就引入摄像头、激光雷达、语音识别等高成本模块先把最基础的运动与避障链路跑通再逐步扩展是性价比最高的路径。2.2 形态选择移动底盘方案导盲机器人有几种常见形态一种是像导盲棍一样的手持设备用户拿在手里设备负责检测障碍物另一种是带有轮子的移动底盘像小型导盲犬一样走在用户前面或侧面。本文选择移动底盘方案原因是它更接近“导盲机器人”的定义也更有项目完整度。移动底盘需要同时处理驱动、供电、转向和传感器布局知识密度更高。实际制作时也可以理解为“导盲犬的第一代原型”。需要说明的是这是一个学习和验证用的原型不是可以直接让视障用户独立使用的医疗设备。在实验阶段必须由明眼人陪同在开阔、安全的环境中测试。2.3 系统架构感知、决策、执行、反馈整个系统的数据流非常清晰可以拆成四个环节感知层三个 HC-SR04 超声波传感器分别负责前方、左侧、右侧的障碍物测距。决策层Arduino 主控根据三路距离值判断当前是否安全、是否需要转向、转向角度大概是多少。执行层L298N 电机驱动模块控制两个直流电机通过差速方式实现前进、后退、左转、右转。反馈层蜂鸣器按不同频率和次数发声告知用户当前决策。在代码实现上决策层逻辑是核心不能简单写成“近了就转弯”。要区分“前方有障碍、左前方狭窄、右前方狭窄、三向受阻”等不同情况再分别给出对应动作。2.4 为什么选择 Arduino 作为主控Arduino Uno 是很多初学者的第一块开发板也是这个项目比较合适的选型。原因有三点第一Arduino Uno 是 5V 逻辑电平HC-SR04 超声波模块正好也是 5V 电平不需要额外做电平转换接线最简单。如果换成 ESP32 或树莓派虽然功能更强但需要额外处理 3.3V 与 5V 之间的电平匹配容易踩坑。第二Arduino 生态成熟pulseIn()等函数可以直接测量脉冲宽度底层代码量小适合把注意力集中在决策逻辑上。第三L298N 电机驱动模块在 Arduino 下有非常成熟的控制方式只需几个数字引脚和 PWM 引脚就能控制电机转速和方向。如果你的目标是后续加入语音识别、摄像头视觉、蓝牙交互学习中期可以换成 ESP32 或树莓派但第一版原型用 Arduino 已经足够。3. 硬件准备与环境说明3.1 硬件清单为了搭建这个导盲机器人原型你需要准备以下硬件。清单里不包含具体品牌兼容型号都可以。模块数量作用Arduino Uno/Nano 开发板1主控负责逻辑决策HC-SR04 超声波模块3测量前、左、右三个方向的距离L298N 电机驱动板1驱动直流电机控制方向和速度TT 直流减速电机带轮2提供驱动力安装在底盘左右两侧万向轮1 或 2底盘后部支撑实现原地转向蜂鸣器模块1声音反馈提示转向或停止7.4V 锂电池组或 4 节 5 号电池盒1给电机驱动板供电9V 电池或独立供电1给 Arduino 供电可选亚克力底盘/木板/3D 打印底板1固定所有模块杜邦线、铜柱、扎带、螺丝若干接线和固定这里要特别提醒电机是大功率设备启动瞬间电流很大如果和 Arduino 共用一组电池很容易导致主控复位。实验阶段建议使用两组独立电源一组给电机驱动板另一组给 Arduino同时必须把两组电源的负极接到一起也就是“共地”否则数字信号无法稳定传输。3.2 软件环境编程和烧录使用的软件主要是 Arduino IDE。你可以在 Arduino 官网下载最新版本也可以使用 Arduino 2.x 版本。本项目的代码不依赖任何第三方库只用 Arduino 内置函数所以不同 IDE 版本都能编译通过。如果你使用的是兼容开发板通常还需要安装 CH340 或 CP2102 串口驱动。具体安装哪个取决于开发板上的 USB 转串口芯片型号可以在电脑的设备管理器里查看。系统版本方面Windows、macOS、Linux 都可以只要 Arduino IDE 能识别到开发板对应的串口端口。3.3 引脚分配为了不引起混乱我先给出完整的引脚分配表。后面的接线和代码都会严格使用这张表。模块功能Arduino 引脚左电机驱动L298NL_IN1D2左电机驱动L_IN2D3左电机驱动L_EN使能D5右电机驱动R_IN3D6右电机驱动R_IN4D7右电机驱动R_EN使能D8蜂鸣器信号脚D13前方超声波TrigD11前方超声波EchoD12左侧超声波TrigA0左侧超声波EchoA1右侧超声波TrigA2右侧超声波EchoA3D5、D6、D8 在 Arduino Uno 上都是 PWM 引脚可以输出模拟电压来控制电机转速。蜂鸣器接 D13是因为 D13 旁边有板载 LED调试时能看到闪烁状态。4. 核心模块原理拆解4.1 超声波测距原理HC-SR04 通过发送和接收超声波脉冲来计算距离工作方式非常直观。我们向 Trig 引脚发送一个至少 10 微秒的高电平模块就会自动发出 40kHz 的超声波脉冲同时 Echo 引脚会输出一个高电平脉冲。这个高电平脉冲的宽度就是从发射到接收到回波的时间。距离计算公式为距离厘米 高电平时间微秒 × 0.034 / 2因为声音在空气中的传播速度大概是 340 米/秒即 0.034 厘米/微秒而声波从发射到接收走了两倍距离所以要除以 2。Arduino 中有一个非常方便的函数pulseIn(echoPin, HIGH, timeout)它会等待 Echo 引脚变为高电平然后测量高电平持续的时间单位是微秒。我们可以把计算封装成一个公用函数传入 Trig 和 Echo 引脚号就能返回距离值。float readDistance(int trigPin, int echoPin) { digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); long duration pulseIn(echoPin, HIGH, 30000L); if (duration 0) { return 500.0; } float dist duration * 0.034 / 2.0; return dist; }超时时间设置为 30000 微秒也就是 30 毫秒对应的最大测量距离大约 5 米。如果pulseIn超时返回 0说明没有收到回波这时候返回一个较大的值 500 厘米避免误判为障碍物。这里还有一个小细节三个超声波传感器同时测量时声波可能相互干扰。因此主循环中读每个传感器之间最好间隔几毫秒让声波充分消散。4.2 L298N 电机驱动与差速转向L298N 是一个双 H 桥电机驱动模块可以独立控制两个直流电机的正转、反转和停止。对每个电机来说有两个逻辑输入引脚和一个使能引脚。一个电机对应 IN1、IN2、ENA。当 IN1 HIGH、IN2 LOW 时电机正转。当 IN1 LOW、IN2 HIGH 时电机反转。当 IN1 IN2 LOW 或 HIGH 时电机停止。ENA 引脚接受 PWM 信号用来控制电机速度数值范围是 0 到 255。两个电机分别位于底盘左右两侧这样我们可以通过差速控制实现转向。所谓差速转向就是让左右轮转速不同机器人就会向转速慢的一侧转弯。如果我们让左轮停止、右轮前进机器人就会以左轮为圆心向左原地转弯。以下是把电机控制封装成两个函数的核心逻辑后面的完整代码会在此基础上定义前进、后退、左转、右转四个动作。void setLeftMotor(int dir, int speedValue) { if (dir 0) { digitalWrite(L_IN1, HIGH); digitalWrite(L_IN2, LOW); } else if (dir 0) { digitalWrite(L_IN1, LOW); digitalWrite(L_IN2, HIGH); } else { digitalWrite(L_IN1, LOW); digitalWrite(L_IN2, LOW); } analogWrite(L_EN, speedValue); } void setRightMotor(int dir, int speedValue) { if (dir 0) { digitalWrite(R_IN3, HIGH); digitalWrite(R_IN4, LOW); } else if (dir 0) { digitalWrite(R_IN3, LOW); digitalWrite(R_IN4, HIGH); } else { digitalWrite(R_IN3, LOW); digitalWrite(R_IN4, LOW); } analogWrite(R_EN, speedValue); }实际接线时要注意L298N 模块上通常会标注 OUT1、OUT2 对应一个电机OUT3、OUT4 对应另一个电机。如果你发现左右轮转向相反只需要交换该侧电机的两根输出线。4.3 三向障碍判断与避障策略导盲机器人的决策不能只用前方一个传感器必须结合左右两侧信息。我采用的策略是这样的第一步读取前方、左侧、右侧三个距离值。设定两个阈值前方安全距离 35 厘米侧面安全距离 20 厘米。第二步当前方距离大于 35 厘米说明前方相对安全机器人继续前进。但如果左侧或右侧距离小于 20 厘米说明走廊或通道很窄机器人会向空间更大的一侧微调方向避免被夹住。第三步当前方距离小于 35 厘米机器人立即停车然后比较左右距离。如果左侧更宽就向左转弯如果右侧更宽就向右转弯。如果左右都不足 20 厘米说明三面都被堵住机器人会后退一段距离然后向右转弯尝试寻找新路径。这个策略故意做得保守宁可多停车也不能让机器人贴近障碍物。对于导盲场景稳定和安全远比分秒必争的“快速通过”更重要。4.4 蜂鸣器反馈编码为了让不使用屏幕的用户理解机器人的选择我用蜂鸣器设计了一套简单的声音编码响 1 声机器人选择向右转向。响 2 声机器人选择向左转向。响 3 声机器人检测到三面受阻正在后退。连续短响系统检测到持续危险需要人工介入。这个反馈编码在整个代码中保持统一后续如果想增加更多提示可以改成语音模块比如用文字语音合成播报“左侧可通过”“右侧可通过”但原型的蜂鸣器版本已经可以验证完整的交互逻辑。5. 完整实战组装与编写代码5.1 车体与传感器组装先完成车体的机械组装。找一个尺寸合适的亚克力底盘或木板把两个 TT 电机分别固定在底盘左右两侧尽量让两个轮子保持在同一水平线上。底盘后部安装万向轮形成三点支撑保证机器人不会向前或向后倾倒。固定好电机之后安装三个超声波传感器。这里要注意传感器的朝向前方的传感器朝向正前方左右传感器分别朝向左前方和右前方夹角大约 30 到 45 度最合适。超声波传感器有一定探测角度倾斜安装能提前感知侧面障碍物给转向留出更多时间。传感器安装高度也很关键。建议距离地面 10 到 15 厘米高于地面常见的小障碍物但又能覆盖人体腰部高度范围内的障碍。这个高度你可以根据底盘实际结构微调。5.2 按引脚表接线接线是整个项目中最容易出错的环节建议严格按照 3.3 小节的引脚分配表逐项接线。L298N 的逻辑电源可以用 Arduino 的 5V 引脚供电电机电源接 7.4V 电池组。Arduino 的电源可以独立接 9V 电池也可以通过 L298N 板载的 5V 输出供电。这里强调两组电源共地把 L298N 的 GND 和 Arduino 的 GND 用杜邦线连接起来。如果不共地Arduino 输出的高电平信号在电机驱动板看来可能是悬空的高阻态会导致电机乱转或者失控。超声波模块的 Trig 和 Echo 严格按引脚表连接。HC-SR04 的 VCC 和 GND 可以接到 Arduino 的 5V 和 GND但要注意三个传感器总共的电流不大Arduino 板载稳压器可以承受。如果后续增加更多传感器建议单独供电并共地。5.3 完整 Arduino 代码下面是一份可以完整编译并烧录的 Arduino 代码。我把它命名为guide_robot.ino你需要把文件放到名为guide_robot的文件夹里否则 Arduino IDE 可能会拒绝打开。/* * 导盲机器人原型guide_robot.ino * 功能三路超声波测距 差速电机避障 蜂鸣器反馈 */ // 超声波引脚 #define LEFT_TRIG A0 #define LEFT_ECHO A1 #define RIGHT_TRIG A2 #define RIGHT_ECHO A3 #define FRONT_TRIG 11 #define FRONT_ECHO 12 // 蜂鸣器引脚 #define BUZZER 13 // L298N 电机驱动引脚 #define L_IN1 2 #define L_IN2 3 #define L_EN 5 #define R_IN3 6 #define R_IN4 7 #define R_EN 8 // 速度与距离阈值 const int SPEED 150; // PWM 速度范围 0-255 const int FRONT_SAFE_DIST 35; // 前方安全距离单位 cm const int SIDE_SAFE_DIST 20; // 侧面安全距离单位 cm const int TURN_DELAY 350; // 转向持续时间单位 ms /* * 读取超声波距离单位 cm。 * 如果超时没有收到回波返回 500表示没有障碍。 */ float readDistance(int trigPin, int echoPin) { digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); long duration pulseIn(echoPin, HIGH, 30000L); if (duration 0) { return 500.0; } float dist duration * 0.034 / 2.0; return dist; } // 左轮控制dir0 正转dir0 反转dir0 停止 void setLeftMotor(int dir, int speedValue) { if (dir 0) { digitalWrite(L_IN1, HIGH); digitalWrite(L_IN2, LOW); } else if (dir 0) { digitalWrite(L_IN1, LOW); digitalWrite(L_IN2, HIGH); } else { digitalWrite(L_IN1, LOW); digitalWrite(L_IN2, LOW); } analogWrite(L_EN, speedValue); } // 右轮控制dir0 正转dir0 反转dir0 停止 void setRightMotor(int dir, int speedValue) { if (dir 0) { digitalWrite(R_IN3, HIGH); digitalWrite(R_IN4, LOW); } else if (dir 0) { digitalWrite(R_IN3, LOW); digitalWrite(R_IN4, HIGH); } else { digitalWrite(R_IN3, LOW); digitalWrite(R_IN4, LOW); } analogWrite(R_EN, speedValue); } void stop() { setLeftMotor(0, 0); setRightMotor(0, 0); } void forward() { setLeftMotor(1, SPEED); setRightMotor(1, SPEED); } void backward() { setLeftMotor(-1, SPEED); setRightMotor(-1, SPEED); } void turnLeftInPlace() { setLeftMotor(0, 0); setRightMotor(1, SPEED); } void turnRightInPlace() { setLeftMotor(1, SPEED); setRightMotor(0, 0); } /* * 蜂鸣器提示 * times 表示响声次数interval 表示单次时长和间隔。 */ void beep(int times, int interval) { for (int i 0; i times; i) { digitalWrite(BUZZER, HIGH); delay(interval); digitalWrite(BUZZER, LOW); delay(interval); } } void setup() { pinMode(LEFT_TRIG, OUTPUT); pinMode(LEFT_ECHO, INPUT); pinMode(RIGHT_TRIG, OUTPUT); pinMode(RIGHT_ECHO, INPUT); pinMode(FRONT_TRIG, OUTPUT); pinMode(FRONT_ECHO, INPUT); pinMode(BUZZER, OUTPUT); pinMode(L_IN1, OUTPUT); pinMode(L_IN2, OUTPUT); pinMode(L_EN, OUTPUT); pinMode(R_IN3, OUTPUT); pinMode(R_IN4, OUTPUT); pinMode(R_EN, OUTPUT); Serial.begin(9600); // 上电提示三声表示系统启动 beep(3, 150); delay(500); } void loop() { float front readDistance(FRONT_TRIG, FRONT_ECHO); delay(5); float left readDistance(LEFT_TRIG, LEFT_ECHO); delay(5); float right readDistance(RIGHT_TRIG, RIGHT_ECHO); // 串口实时输出方便调试阈值 Serial.print(F); Serial.print(front); Serial.print( L); Serial.print(left); Serial.print( R); Serial.println(right); if (front FRONT_SAFE_DIST) { // 前方有障碍先停车 stop(); delay(80); if (left right left SIDE_SAFE_DIST) { // 左侧空间更大且右侧不足以通行 beep(2, 200); turnLeftInPlace(); delay(TURN_DELAY); stop(); } else if (right left right SIDE_SAFE_DIST) { // 右侧空间更大 beep(1, 200); turnRightInPlace(); delay(TURN_DELAY); stop(); } else { // 三面受阻后退后向右转 beep(3, 200); backward(); delay(300); stop(); turnRightInPlace(); delay(TURN_DELAY); stop(); } } else if (left SIDE_SAFE_DIST) { // 左侧过窄向右微调 beep(1, 200); turnRightInPlace(); delay(150); stop(); } else if (right SIDE_SAFE_DIST) { // 右侧过窄向左微调 beep(2, 200); turnLeftInPlace(); delay(150); stop(); } else { // 前方和两侧都安全继续前进 forward(); } delay(20); }代码里我特意保留了注释每个函数的职责都比较清楚。你拿到代码后先用默认参数跑一遍再根据自己底盘的电机速度和转弯半径调整SPEED、TURN_DELAY、FRONT_SAFE_DIST、SIDE_SAFE_DIST这几个常量。5.4 编译烧录与串口调试打开 Arduino IDE把上面的代码粘贴到编辑器中。在“工具 → 开发板”菜单中选择你的板型比如“Arduino Uno”然后在“工具 → 端口”中选择开发板对应的串口。点击