1. 项目概述
红外传感器,这玩意儿在电子制作和机器人领域里,几乎是每个项目都绕不开的基础元件。你可能在扫地机器人里见过它,也可能在自动感应水龙头里感受过它的存在。简单来说,它就像给机器装上了一双“看不见的眼睛”,能感知前方有没有物体、是黑是白,或者距离有多远。对于刚接触Arduino的朋友,红外传感器是绝佳的入门选择,因为它原理直观、接线简单、应用场景丰富,能让你快速体验到“让硬件感知世界”的乐趣。今天,我就结合自己这些年做过的几十个小项目,从最底层的原理讲起,手把手带你玩转三种最常见的红外传感器模块,让你不仅知道怎么连线和写代码,更能理解背后的门道,以后遇到任何红外传感需求都能自己搞定。
2. 红外传感器工作原理深度解析
2.1 红外光的本质与传感器分类
要玩转红外传感器,首先得明白它到底在“看”什么。我们人眼能看到的可见光波长范围大约在380纳米到780纳米之间,而红外光的波长更长,大约在780纳米到1毫米之间,处于可见红光之外,因此得名“红外线”。这种光我们肉眼看不见,但它无处不在,任何有温度的物体都在向外辐射红外线,比如我们的身体、一杯热水,甚至是一块冰冷的石头。
基于这个原理,市面上常见的、用于Arduino项目的红外传感器主要分为两大类:主动式和被动式。我们平时在机器人小车、循线比赛中用的,绝大多数都是主动式红外传感器。它内部自带一个红外发射管(IRED)和一个红外接收管(通常是光电晶体管或光电二极管)。工作时,发射管持续发出调制过的红外光(防止环境光干扰),接收管则负责检测是否有光被反射回来。被动式红外传感器(PIR)则不同,它不发射红外光,只被动接收环境中物体辐射出的红外线变化,常用于人体感应,比如走廊的自动灯。本文主要聚焦于应用更广泛的主动式红外传感器。
2.2 核心探测原理:反射与接收
主动式红外传感器的工作流程,可以类比成蝙蝠的回声定位,只不过用的是光而不是声波。传感器上的发射管就像蝙蝠的嘴巴,不断向外“喊”出红外光脉冲。当这些光脉冲前方空无一物时,它们就一去不复返了,接收管“听”不到任何“回声”。此时,传感器输出一种状态(比如高电平)。一旦前方出现物体,红外光脉冲打到物体表面后,会有一部分被反射回来,就像声音碰到了墙壁产生回音。接收管检测到这个“光回声”,内部的光电元件会产生相应的电流变化,经过传感器板载的比较器电路处理后,输出另一种状态(比如低电平)。
这里有个关键点:反射率。不同颜色和材质的物体,对红外光的反射率天差地别。白色表面反射率高,大部分红外光被弹回,传感器容易检测到;黑色表面吸收率高,反射回来的光极少,传感器可能就“看”不见。这就是为什么红外传感器能用来做黑白线循迹。同理,表面粗糙、深色的物体(比如黑绒布)比表面光滑、浅色的物体(比如白纸)更难被探测,有效探测距离也会缩短。在实际项目中,这是调参和排错时必须考虑的因素。
2.3 传感器模块的“三板斧”:电源、信号与调节
拆开一个典型的红外传感器模块(比如文章里提到的第一种四引脚型号),你会发现它远比一个简单的发射接收对管复杂。模块化设计为我们省去了大量外围电路搭建的麻烦。其核心功能可以归结为三点:
- 供电与稳压:模块上有VCC和GND引脚,接入Arduino的5V和GND即可工作。模块内部通常有稳压芯片,确保发射管和接收电路工作电压稳定。
- 信号输出:这是模块最核心的功能。它提供了两种输出方式:
- 数字输出(DO):模块内部集成了一个电压比较器。它会将接收管产生的连续变化的模拟电压信号,与一个预设的阈值电压进行比较。高于阈值输出高电平(如5V),低于阈值输出低电平(0V)。这个阈值,通常可以通过模块上的一个蓝色可调电阻(电位器)来改变,从而调节传感器的灵敏度或探测距离。输出结果非0即1,Arduino直接用
digitalRead()读取,非常方便。 - 模拟输出(AO):这个引脚直接将接收管产生的原始模拟电压信号引出来。物体越近、反射越强,电压值可能越高(或越低,取决于电路设计)。Arduino通过
analogRead()读取的是一个0到1023之间的数值,能更精细地反映反射信号的强弱,适合需要量化距离或反射强度的场景。
- 数字输出(DO):模块内部集成了一个电压比较器。它会将接收管产生的连续变化的模拟电压信号,与一个预设的阈值电压进行比较。高于阈值输出高电平(如5V),低于阈值输出低电平(0V)。这个阈值,通常可以通过模块上的一个蓝色可调电阻(电位器)来改变,从而调节传感器的灵敏度或探测距离。输出结果非0即1,Arduino直接用
- 灵敏度调节:那个蓝色的可调电阻是关键。顺时针或逆时针旋转它,实质上是改变比较器的参考电压阈值。调低阈值,传感器变得更“敏感”,一点微弱反射就能触发;调高阈值,传感器变得更“迟钝”,需要更强的反射信号才动作。这个调节过程就是你在项目初期必须做的“校准”。
注意:不同厂家、不同型号的模块,其数字输出逻辑可能相反。常见的是“检测到物体时输出低电平(0),未检测到时输出高电平(1)”,但也有一些模块设计正好相反。务必在接线前查看模块说明书,或者用串口监视器读取一下状态,通过实验确认逻辑,这是避免后续代码逻辑混乱的第一步。
3. 三种典型红外传感器模块实战详解
3.1 类型一:四引脚通用红外避障模块
这是最常见、最通用的一种模块,文章开头介绍的就是它。它有四个引脚:VCC, GND, DO(数字输出), AO(模拟输出)。模块上通常有一个LED指示灯,检测到物体时会亮起,非常直观。我们用它来完成两个经典实验:黑白表面识别和障碍物距离感应。
硬件连接(基于Arduino Uno):
- 传感器模块:VCC -> 5V; GND -> GND; DO -> 数字引脚2; AO -> 模拟引脚A0。
- 输出设备:两个LED(建议一绿一黄)分别通过220Ω限流电阻接在数字引脚3和4上;一个有源蜂鸣器正极接数字引脚5,负极接GND。
代码逻辑深度剖析:
提供的代码将两个功能封装成了函数balckwhite()和obstacle()。我们拆开看:
void balckwhite() { bool led = digitalRead(2); // 读取数字引脚2的状态 if (led == 0) { // 假设模块逻辑:检测到白色(反射强)输出0 Serial.println("White"); digitalWrite(3, HIGH); // 绿色LED亮,表示白色 digitalWrite(4, LOW); // 黄色LED灭 } else { Serial.println("Black"); digitalWrite(4, HIGH); // 黄色LED亮,表示黑色 digitalWrite(3, LOW); // 绿色LED灭 } }这个函数实现了黑白识别。关键在于digitalRead(2)的值与物体颜色的对应关系。这需要通过实验校准:将传感器分别对准白纸和黑纸,观察串口输出的是“White”还是“Black”,并记录下对应的led值是0还是1。代码中的if (led == 0)这个条件就是根据你的校准结果来写的。如果实际逻辑相反,把0改成1即可。
void obstacle() { int buzzer = analogRead(A0); // 读取模拟引脚A0的原始值 if (buzzer <= 80) { digitalWrite(5, HIGH); Serial.println("Buzzer on"); } else { digitalWrite(5, LOW); Serial.println("Buzzer off"); } }这个函数实现了简单的距离/存在感应。analogRead(A0)读取的值范围是0-1023,值越小通常代表接收到的反射光越强(物体越近或反射率越高)。阈值80是一个需要动态调整的经验值。你需要这样做:将传感器对准一个标准物体(比如你的手),缓慢由远及近移动,同时打开串口监视器观察buzzer变量的数值变化。找到一个当物体到达你期望的报警距离时,对应的模拟值,就用这个值作为阈值。不同环境光、不同物体表面都会影响这个值,所以它不是一个固定数。
实操心得:在调试这类传感器时,一定要把串口监视器用起来。将
analogRead的值实时打印出来,你就能清晰地看到传感器“眼中”的世界是怎样的。这是理解传感器行为、确定阈值最可靠的方法,没有之一。
3.2 类型二:三引脚简易红外接收模块
第二种模块更精简,只有三个引脚:VCC, GND, OUT(数字输出)。它去掉了模拟输出和可调电阻,通常将灵敏度做成了固定值,或者通过内部固定电阻预设了一个常用阈值。成本更低,使用也更简单,常用于只需要判断“有”或“无”的场景,比如检测传送带上是否有物品通过、计算产品数量(光电计数器)等。
硬件连接(以Arduino Nano为例):
- 传感器模块:VCC -> 5V; GND -> GND; OUT -> 数字引脚2。
- 输出设备:一个LED通过220Ω电阻接在数字引脚3上。
代码解析:
代码非常简洁,就是一个典型的数字输入检测逻辑:
void loop() { bool value = digitalRead(2); // 读取传感器状态 if (value == 1) { // 假设检测到物体输出高电平 Serial.println("ON"); digitalWrite(3,HIGH); // LED亮 } else { Serial.println("OFF"); digitalWrite(3,LOW); // LED灭 } }这里再次强调if (value == 1)这个条件需要根据实际模块验证。更健壮的写法是定义一个常量,比如const int DETECTED_STATE = HIGH;,然后在代码中使用这个常量,这样如果换用不同逻辑的模块,只需修改这一个常量定义即可。
3.3 类型三:一体化红外循迹传感器
第三种是专门为轮式机器人循迹设计的传感器,通常也被称为“巡线传感器”或“TCRT5000模块”。它集成了红外对管和一个比较器电路,输出也是数字信号。其结构特点是发射和接收管并排朝下安装,非常适合检测地面反射。模块上同样有一个可调电阻,用于调节检测地面的灵敏度,以适配不同颜色、反光度的跑道。
硬件连接与代码特点:
连接方式与类型二类似,OUT引脚接数字引脚。代码逻辑也基本一致:检测到白线(高反射)输出一种电平,检测到黑线(低反射)输出另一种电平。在机器人应用中,通常会使用多个(3-5个)这样的传感器并排安装,组成传感器阵列,通过判断哪个传感器压到了黑线,来获知机器人偏离路径的方向和程度,从而实现精确的PID循迹控制。
文章提供的代码用了#define来定义引脚,这是一个好习惯,提高了代码的可读性和可维护性。逻辑是:当传感器检测到白线(value == 0)时,蜂鸣器和绿灯亮;检测到黑线(value == 1)时,红灯亮。
#define Sensor 2 #define Buzzer 3 #define Red 4 #define Green 5 // ... 引脚模式设置 void loop() { bool value = digitalRead(Sensor); if (value == 0) { // 检测到白线 digitalWrite(Buzzer, HIGH); digitalWrite(Green, HIGH); digitalWrite(Red, LOW); } else if (value == 1) { // 检测到黑线 digitalWrite(Buzzer, LOW); digitalWrite(Green, LOW); digitalWrite(Red, HIGH); } }注意事项:循迹传感器的调试需要在最终使用的场地上进行。环境光(特别是日光灯、太阳光)对红外传感器干扰很大。最好在传感器贴近地面的位置加装遮光罩(可以用黑色热缩管或电工胶布制作),只留下正下方一个很小的探测孔,这样可以极大减少环境光干扰,提高检测稳定性。
4. 项目实战:构建一个智能避障小车原型
理解了单个传感器的用法,我们就可以把它们组合起来,解决更复杂的问题。下面,我将带你设计一个简单的双轮智能小车原型,它能够在前方遇到障碍时自动转向。这个项目会综合运用前面所学的知识。
4.1 系统设计与元件清单
我们的目标是让小车在平面上自由行走,当左侧传感器检测到障碍时向右转,右侧传感器检测到障碍时向左转,正前方检测到障碍时后退或随机转向。我们需要以下材料:
- Arduino Uno 开发板 x1
- L298N电机驱动模块 x1(最常用的双路直流电机驱动)
- TT减速电机(带轮子) x2
- 红外避障传感器(类型一) x3
- 18650电池盒(两节串联,提供7.4V左右电压)x1
- 面包板、杜邦线、小车底盘若干
接线示意图核心部分:
- 电源:电池盒正负极接L298N的12V和GND输入口。同时,从L298N的5V输出口引线给Arduino Uno的VIN引脚供电(注意:不是5V引脚)。这样整个系统共用一套电池。
- 电机驱动:左电机两根线接L298N的OUT1和OUT2;右电机接OUT3和OUT4。L298N的ENA、IN1、IN2控制左电机;ENB、IN3、IN4控制右电机。我们将ENA和ENB通过跳线帽短接到高电平(使能),然后用Arduino的四个数字引脚(如4,5,6,7)分别控制IN1, IN2, IN3, IN4。
- 传感器:三个红外传感器的VCC和GND分别并联到Arduino的5V和GND。它们的DO引脚分别接到Arduino的数字引脚8(左)、9(中)、10(右)。
4.2 核心控制代码实现
代码的核心思想是持续扫描三个传感器的状态,根据不同的障碍物组合情况,给电机驱动模块发送不同的控制信号。
// 定义引脚 #define LEFT_SENSOR 8 #define CENTER_SENSOR 9 #define RIGHT_SENSOR 10 #define MOTOR_LEFT_IN1 4 #define MOTOR_LEFT_IN2 5 #define MOTOR_RIGHT_IN3 6 #define MOTOR_RIGHT_IN4 7 // 假设传感器检测到障碍物时返回 LOW const int OBSTACLE_DETECTED = LOW; void setup() { Serial.begin(9600); // 初始化传感器引脚为输入 pinMode(LEFT_SENSOR, INPUT); pinMode(CENTER_SENSOR, INPUT); pinMode(RIGHT_SENSOR, INPUT); // 初始化电机控制引脚为输出 pinMode(MOTOR_LEFT_IN1, OUTPUT); pinMode(MOTOR_LEFT_IN2, OUTPUT); pinMode(MOTOR_RIGHT_IN3, OUTPUT); pinMode(MOTOR_RIGHT_IN4, OUTPUT); // 初始状态:停止 stopCar(); } void loop() { bool leftDetect = (digitalRead(LEFT_SENSOR) == OBSTACLE_DETECTED); bool centerDetect = (digitalRead(CENTER_SENSOR) == OBSTACLE_DETECTED); bool rightDetect = (digitalRead(RIGHT_SENSOR) == OBSTACLE_DETECTED); // 决策逻辑 if (centerDetect) { // 前方有障碍,优先处理 if (leftDetect && rightDetect) { // 左右都有障碍,后退 Serial.println("All blocked! Backing up..."); moveBackward(500); // 后退500毫秒 turnRight(300); // 然后右转尝试 } else if (leftDetect) { // 前左有障碍,右转 Serial.println("Left & Center blocked, turn right"); turnRight(400); } else if (rightDetect) { // 前右有障碍,左转 Serial.println("Right & Center blocked, turn left"); turnLeft(400); } else { // 仅前方有障碍,随机左右转 Serial.println("Center blocked, random turn"); if (random(2) == 0) { turnLeft(300); } else { turnRight(300); } } } else if (leftDetect) { // 仅左侧有障碍,右转 Serial.println("Left blocked, turn right"); turnRight(200); } else if (rightDetect) { // 仅右侧有障碍,左转 Serial.println("Right blocked, turn left"); turnLeft(200); } else { // 无障碍,直行 Serial.println("Clear path, moving forward"); moveForward(); } delay(50); // 短暂延迟,防止循环过快 } // 以下是电机动作函数 void moveForward() { digitalWrite(MOTOR_LEFT_IN1, HIGH); digitalWrite(MOTOR_LEFT_IN2, LOW); digitalWrite(MOTOR_RIGHT_IN3, HIGH); digitalWrite(MOTOR_RIGHT_IN4, LOW); } void moveBackward(int duration) { digitalWrite(MOTOR_LEFT_IN1, LOW); digitalWrite(MOTOR_LEFT_IN2, HIGH); digitalWrite(MOTOR_RIGHT_IN3, LOW); digitalWrite(MOTOR_RIGHT_IN4, HIGH); delay(duration); stopCar(); } void turnLeft(int duration) { // 左轮后退,右轮前进,实现原地左转 digitalWrite(MOTOR_LEFT_IN1, LOW); digitalWrite(MOTOR_LEFT_IN2, HIGH); digitalWrite(MOTOR_RIGHT_IN3, HIGH); digitalWrite(MOTOR_RIGHT_IN4, LOW); delay(duration); stopCar(); } void turnRight(int duration) { // 左轮前进,右轮后退,实现原地右转 digitalWrite(MOTOR_LEFT_IN1, HIGH); digitalWrite(MOTOR_LEFT_IN2, LOW); digitalWrite(MOTOR_RIGHT_IN3, LOW); digitalWrite(MOTOR_RIGHT_IN4, HIGH); delay(duration); stopCar(); } void stopCar() { digitalWrite(MOTOR_LEFT_IN1, LOW); digitalWrite(MOTOR_LEFT_IN2, LOW); digitalWrite(MOTOR_RIGHT_IN3, LOW); digitalWrite(MOTOR_RIGHT_IN4, LOW); }这段代码实现了一个基于有限状态机的简单避障逻辑。通过Serial.println输出决策信息,在调试时非常有用。你可以看到,逻辑优先级是:前方障碍 > 单侧障碍 > 无障碍。moveBackward、turnLeft等函数都带有一个duration参数,控制动作持续时间,之后会停止,等待主循环的下一次检测。这种“动作-停止-检测”的循环是机器人基础控制中常见的模式。
4.3 传感器布局与调试技巧
三个传感器的安装位置直接影响小车的避障效果。理想的布局是:一个朝正前方(CENTER),另外两个稍微向外侧偏转一定角度(例如与前进方向呈30-45度角),分别作为左前(LEFT)和右前(RIGHT)传感器。这样布局可以提前感知到侧前方的障碍,给转向决策留出更多时间。
调试分两步走:
- 单体传感器调试:在上车之前,务必单独调试好每一个传感器。用串口监视器确认其检测距离是否合适(通常调到5-15cm为宜),输出逻辑是否正确。确保它们在相同距离检测同一物体时,反应一致。
- 整车联调:将小车放在空旷处,用手或书本从不同方向靠近传感器,观察小车的反应是否符合预期(左障右转、右障左转、正障后退/转向)。重点测试边界情况,比如障碍物同时出现在两个传感器前时,小车的决策是否合理。你可能需要微调代码中的转向持续时间(如
turnRight(200)中的200毫秒),让转弯角度更合适。
踩坑记录:第一次做避障小车时,我最常遇到的问题是电机干扰导致传感器误触发。因为电机启动瞬间电流很大,会在电源线上产生电压波动,这个波动如果传到Arduino和传感器,就可能被误读为信号变化。解决方案有两个:一是为电机驱动模块使用独立的电池供电(与Arduino电源共地即可),彻底隔离动力电和控制电;二是在每个传感器的VCC和GND引脚之间,以及Arduino的5V和GND之间,焊接一个10uF-100uF的电解电容和一个0.1uF的瓷片电容,用于滤波,吸收电源毛刺。后者成本低,在大多数情况下效果显著。
5. 进阶应用与性能优化指南
5.1 抗环境光干扰策略
环境光是红外传感器最大的敌人,特别是日光和某些频闪的LED灯。除了加装物理遮光罩,还可以在软件和电路上做文章:
- 调制解调技术:这是专业红外遥控和高级避障传感器采用的方法。让红外发射管以特定频率(如38kHz)闪烁,接收端只对这个频率的信号进行放大和解调。环境光是连续或杂乱频率的,因此被过滤掉。你可以购买专用的38kHz红外发射接收对管,或者直接使用集成了调制解调功能的模块(如VS1838B红外接收头配合红外LED)。
- 软件滤波:当读取到一次触发信号时,不要立即行动,而是连续读取多次(比如10毫秒内读5次),如果超过一定次数(比如3次)都是触发状态,才认为是有效检测。这可以过滤掉瞬间的干扰脉冲。这就是简单的“软件去抖”。
bool checkSensorStable(int pin, int checkTimes, int intervalMs) { int count = 0; for (int i = 0; i < checkTimes; i++) { if (digitalRead(pin) == OBSTACLE_DETECTED) { count++; } delay(intervalMs); } // 如果超过半数次数检测到,则认为稳定触发 return (count > (checkTimes / 2)); } // 在loop中使用 if (checkSensorStable(LEFT_SENSOR, 5, 2)) { // 稳定检测到左侧障碍 }5.2 从模拟输出估算距离
虽然数字输出简单好用,但模拟输出(AO)能提供更多信息。通过建立模拟读数与物体距离之间的对应关系,可以实现粗略的测距功能。注意,这个关系不是线性的,且受物体表面特性影响极大,所以只能用于对精度要求不高的场景,比如判断“近、中、远”。
方法是:在固定环境下,针对特定物体(比如你的手),测量不同距离下的模拟读数,记录几组数据。然后,你可以:
- 分段判断:设定几个阈值,比如
value > 800为“远”,400 < value <= 800为“中”,value <= 400为“近”。 - 简单映射:使用Arduino的
map()函数,将一个距离范围(如5cm-30cm)映射到模拟值范围。但这需要你先知道最近和最远距离对应的模拟值。
int rawValue = analogRead(A0); // 假设经过测量,30cm时值约为50,5cm时值约为900 int distance = map(rawValue, 50, 900, 30, 5); // 将模拟值反向映射为厘米距离 distance = constrain(distance, 5, 30); // 将距离限制在5-30cm之间 Serial.print("Estimated Distance: "); Serial.print(distance); Serial.println(" cm");重要提醒:这种方法得到的“距离”非常不精确,且换一个物体或环境就可能完全失效。它更适合用于判断相对距离的变化趋势(比如物体在靠近还是远离),而不是获取绝对距离值。如需精确测距,应选用超声波传感器、激光测距或ToF传感器。
5.3 多传感器阵列与数据融合
在复杂的机器人项目中,单个传感器提供的信息是片面的。我们需要使用传感器阵列,并通过算法融合数据。例如,一个五路巡线传感器阵列会返回一个如[0, 0, 1, 0, 0]的数组,其中1代表传感器在黑线上。简单的逻辑是看1出现在哪个位置,就向反方向修正。
更高级的方法是计算偏差值。给每个传感器分配一个权重,例如从左到右权重为-2, -1, 0, 1, 2。将传感器状态(0或1)乘以权重后求和,得到一个偏差值。偏差为负表示偏左,需要向右转;偏差为正表示偏右,需要向左转。偏差的绝对值大小可以控制转向的幅度,这就是最简单的比例(P)控制思想,是实现平滑循迹的基础。
int sensorPins[5] = {A0, A1, A2, A3, A4}; // 假设五路传感器接模拟口 int sensorWeights[5] = {-2, -1, 0, 1, 2}; int threshold = 500; // 判断黑白的模拟阈值 int calculateError() { int error = 0; for (int i = 0; i < 5; i++) { int value = analogRead(sensorPins[i]); if (value < threshold) { // 低于阈值,认为是黑线 error += sensorWeights[i]; } } return error; // 返回偏差值 }6. 常见问题排查与实战心得
6.1 问题速查表
下表汇总了新手使用红外传感器时最常遇到的“坑”及其解决方案。
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 传感器始终触发或不触发 | 1. 电源接反或电压不对。 2. 检测逻辑搞反(常亮/常灭)。 3. 灵敏度电位器调到了极端。 4. 环境光过强干扰。 | 1. 检查VCC/GND是否接对,电压是否为5V。 2. 用串口打印 digitalRead值,确认物体靠近/远离时的实际输出是0还是1,据此修改代码判断条件。3. 缓慢旋转电位器,观察指示灯变化。 4. 遮挡环境光或加装遮光罩测试。 |
| 检测距离不稳定,时远时近 | 1. 环境光变化(如日光、灯光闪烁)。 2. 被测物体表面不均匀或反光。 3. 电源电压波动(电机干扰)。 | 1. 在稳定光源下测试,或采用调制解调型传感器。 2. 使用标准测试物(如白纸板)。 3. 为电机和控制系统分别供电,或在电源端加滤波电容。 |
| 模拟输出值跳动剧烈 | 1. 电源噪声。 2. 传感器本身噪声或接触不良。 | 1. 在模拟输入引脚与GND之间加一个0.1uF瓷片电容。 2. 在软件中采用多次读取取平均值的算法。 3. 检查杜邦线连接是否牢固。 |
| 循迹小车在线上“画龙”或跑飞 | 1. 传感器离地高度不合适。 2. 黑白阈值未校准。 3. 传感器响应速度跟不上小车速度。 4. 控制算法过于简单(只有开关量)。 | 1. 调整传感器高度,通常距地面0.5-2cm最佳。 2. 在最终赛道上,分别读取压在白线和黑线上的模拟值,取中间值作为阈值。 3. 降低小车速度,或使用中断来及时读取传感器。 4. 引入PID控制算法,根据偏差大小动态调整电机速度。 |
| 多个传感器互相干扰 | 并排安装的传感器红外光互相串扰。 | 1. 在传感器之间增加物理隔板。 2. 分时复用:让传感器轮流工作,一个发射时,相邻的关闭。 |
6.2 从原理到选型的核心心得
玩了这么多年传感器,我最大的体会是:没有“最好”的传感器,只有“最合适”的方案。红外传感器成本低、电路简单,在室内、中短距离、非强光干扰的场合下是性价比之王。但它怕强光、怕黑绒布、探测距离和精度有限。所以,在做项目选型时,一定要问自己几个问题:
- 检测对象是什么?表面颜色、材质、反光特性如何?
- 检测环境怎样?是室内还是室外?环境光是否稳定?
- 需要什么信息?只需要知道“有/无”,还是需要“距离/灰度”?
- 响应速度要求多高?是静态检测还是高速运动物体?
想清楚这些,你就能决定是选用本文讲的普通红外模块,还是选用抗光干扰更好的调制型红外,或者是精度更高的超声波、激光雷达。对于绝大多数入门和中级阶段的Arduino机器人项目,把本文介绍的几种红外传感器玩透,已经能解决80%的感知问题了。关键在于理解原理、动手调试、积累应对各种异常情况的“手感”。当你看着自己制作的小车灵巧地避开障碍,或者稳稳地沿着黑线奔跑时,那种成就感,正是电子制作的魅力所在。