引言:智能小车要实现的功能很多——避障、跟随、贴边、灭火、直线行驶,每一个模块单独调试起来都有自己的难点。如果一开始就把所有模块塞进一个工程里联调,出了问题很难分清到底是传感器硬件的问题、电平极性的问题,还是上层控制算法的问题

        所以这一系列教程的思路是:先把每个功能模块单独拎出来,配一个不依赖其他硬件的最小测试程序,把模块本身调通调透,再回头看它在完整项目里是怎么被使用的

目录

一、引言

二、红外对射传感器硬件原理

2.2 原理图讲解

三、硬件接线说明

3.1 红外跟随模块引脚定义

3.2 接线方案表

3.3 连接示意图

四、独立测试程序

五、调试流程演示

5.1 测试流程

5.2 演示视频

六、项目跟随功能实现

6.1 IRSensor底层感知层

6.2 FollowBehavior跟随状态机

6.3 系统流程图

七、常见问题解答(FAQ)

Q1:传感器接好了,跟随时小车原地转但不追目标,是什么问题?

Q2:红外传感器在阳光下失效怎么办?


一、引言

        在前两篇教程里,我们把循迹传感器和超声波云台都调通了,这一篇讲红外双目跟随——用两个固定在车头左右两侧的红外对射传感器,让小车识别目标方向,像影子一样跟在目标后面走,相比超声波云台,双目红外跟随的优势在:

  • 响应速度极快:毫秒级,无需舵机扫描等待
  • 电路简单、成本极低:只需读两个GPIO电平

        劣势是探测距离短(2~30cm可调)、无法测量具体距离、对高反射率目标(白色物体)更灵敏。正是因为这个特性,在本项目里这两个模块同时承担了两个角色:跟随模式(G命令)用于追踪目标,避障模式(B命令)的辅助IR用于检测45°斜前方近距离障碍


        这篇教程会把传感器原理、模块电路、跟随算法设计、调试过程和遇到的坑都完整梳理一遍,包括项目里这个模块走弯路的调试历程

这个系列教程拆成以下几篇:

篇次 内容
第一篇(已完成) 五路循迹模块(TCRT5000 + LM393/LM339)
第二篇(已完成) HC-SR04 超声波测距 + SG90 舵机云台三模式应用
第三篇(本文) 红外双目跟随模块(红外避障对管)
第四篇 霍尔编码器测速与直线行驶 PID(含 PID 自整定)
第五篇 DHT11 温湿度、火焰传感器、风扇灭火模块
第六篇 蓝牙/串口双通道通信协议设计
终篇 整车项目架构总览,把前面六篇串起来讲整体设计思路

二、红外对射传感器硬件原理

        红外双目跟随模块使用的是主动式红外避障传感器,每个传感器内部集成了一个红外发射管(发射红外光)和一个红外接收管(接收反射回来的红外光)

2.1 红外发射+光电接收

红外跟随模块红外发射器与光电三极管集成在一起,适用于近接检测和反射传感

  • 红外发射管是由红外发光二极管组成发光体,用红外辐射效率高的材料(常用砷化镓)制成 PN 结,正向偏压向 PN 结注入电流激发红外光
  • 红外接收管则是将红外线光信号变成电信号的半导体器件,其核心部件是一个特殊材料的 PN 结,随着红外光强度的增加,电流也随之增大

①核心检测原理

        ① 发射管持续发出 940nm 红外脉冲光 → ② 目标物体反射红外光(反射率与目标材质、颜色相关) → ③ 接收管检测反射光强度 → LM393 比较器将模拟信号转为数字电平输出

不同表面对红外光的反射特性

表面特性 红外反射 接收管状态 输出电平
浅色/光滑表面 强反射 导通 LOW(检测到物体)
深色/粗糙表面 弱反射/吸收 截止 HIGH(未检测到)
黑色物体 几乎吸收 截止 HIGH(未检测到)

②输出电平与检测状态

        当探测到障碍物在检测范围内时,模块会输出低电平(LOW)。该模块检测距离为 2~30cm,检测角度 35°,距离可通过电位器调节

注意:不同厂家、不同批次的模块,输出电平与检测状态的对应关系可能相反。本项目实测确认:检测到物体 → 输出 LOW(0),无物体 → 输出 HIGH(1)

③LM393 比较器

        LM393 是一款双差分比较器 IC,内部包含两个独立的运算放大器(比较器)。比较器对两路输入电压进行比较,判断哪路更大,然后给出输出

比较电压工作原理

  • 当同相输入端(+) 电压高于反相输入端(-)电压时,比较器输出高电平。
  • 当同相输入端(+)电压低于反相输入端(-)电压时,比较器输出低电平

比较器内部电路解析

        晶体管Q1、Q2(绿色)构成同相输入端的输入级。反相输入端的输入级由晶体管Q3、Q4(黄色)组成。晶体管Q5、Q6(灰色)用作电流镜。晶体管Q7、Q8(红色)为开关级

  • 各级晶体管均由恒流源供电、一旦同相输入端(+)的输入电压高于反相输入端(-)的电压,比较器输出端的晶体管(Q8)就会截止
  • 若通过电阻(RL)为输出端提供5伏电压,即可引出高电平脉冲、同相输入端电压等于或低于反相输入端电压,输出端晶体管导通,比较器输出端向电源负电位侧切换,此时可引出低电平脉冲

        电压变化发生在反相输入端还是同相输入端,对开关功能没有影响。唯一关键的因素是两个输入端之间的电压差值。各类开关示例中,将不同的电压变化情况(蓝色箭头)分开展示:

(1)同相输入端电压上升

同相输入端电压升高会使输出端电压随之升高

(2)同相输入端的电压降低

同相输入端产生电压降会使输出端出现电压降

(3)反相输入端电压上升

反相输入端电压升高会使输出端电压降低

(4)反相输入端的电压降低

同相输入端产生电压压降时,输出端也会出现电压压降

④电位器调节

        模块上那颗 蓝色方形可调3362电位器,本质上是 可调分压器——旋转旋钮改变分压比例,从而改变送入 LM393 比较器的参考电压(阈值)

调节效果

调节方向 参考电压变化 检测距离变化
顺时针旋转 阈值降低 检测距离增大(更灵敏)
逆时针旋转 阈值升高 检测距离减小(更不灵敏)

实际调试方法

  • 将模块正对目标物体(如手掌),距离约 10~15cm
  • 慢慢旋转电位器,观察模块上的状态指示灯
  • 找到指示灯刚好点亮(或熄灭)的临界点
  • 再往灵敏方向回调一点点

两个模块需要分别调节,因为每个模块的电位器是独立的,灵敏度和检测距离不会自动一致

2.2 原理图讲解

①单路 红外跟随电路

典型的 IR333 + PT333 + LM393 红外避障模块单路原理图

元件 作用
100Ω限流电阻R3 限制 IR333发射管电流,通常 100Ω ~ 200Ω
10KΩ上拉电阻R5 LM393 集电极开路输出需要上拉到 VCC,通常 10kΩ
10kΩ电位器R2 调节比较器参考阈值电压,通常 10kΩ 3362型

②探测逻辑

两个红外传感器在车头两侧呈八字形安装:

        左传感器检测到目标意味着目标在左侧,小车应该左转对准;右传感器检测到目标意味着目标在右侧,小车应该右转对准

左传感器 右传感器 判断结果 执行动作
检测到(LOW) 未检测(HIGH) 目标在左侧 左转
未检测(HIGH) 检测到(LOW) 目标在右侧 右转
检测到(LOW) 检测到(LOW) 目标在正前方 停车(目标已近)
未检测(HIGH) 未检测(HIGH) 目标丢失 原地旋转搜索

如果方向反了,小车会朝着远离目标的方向转,永远跟不住

三、硬件接线说明

3.1 红外跟随模块引脚定义

本项目跟随模块的引脚定义(与 pinsdefine.h 一致)

传感器位置 引脚号 代码定义
左侧IR模块 21 ServoPWM3
右侧IR模块 24 ServoPWM4

注意:这两个引脚虽然命名为 ServoPWM3/4,但在本项目中被复用为红外传感器的信号输入引脚

3.2 接线方案表

每个红外跟随模块有 3个引脚:

模块 引脚 零知派迷你板引脚 说明
左红外传感器 VCC 5V 电源正极
左红外传感器 GND GND 电源地
左红外传感器 OUT D21(ServoPWM3) 信号输出
右红外传感器 VCC 5V 电源正极
右红外传感器 GND GND 电源地
右红外传感器 OUT D24(ServoPWM4) 信号输出

3.3 连接示意图

左侧传感器(直插小车扩展板——舵机接口 PWM3)和右侧传感器(直插小车扩展板——舵机接口 PWM4)接反后,代码里 lIRrIR 含义会颠倒,导致目标在右侧时车向左转。如果跟随方向错误,第一步先用独立测试程序确认左右传感器方向是否正确,再排查代码

四、独立测试程序

        下面这个程序只依赖两个红外传感器,不需要电机、超声波等其他模块,在接入整车工程之前,先用这个程序独立验证传感器的方向和灵敏度

/**************************************************************************************
 * 文件: IR_Follow_Standalone_Test.ino
 * 作者:零知实验室(深圳市在芯间科技有限公司)
 * 功能:红外双目跟随模块 独立测试程序
 *       引脚定义与主项目 pinsdefine.h 完全一致
 *
 * 使用方法:
 *   1. 烧录本程序到零知派迷你板
 *   2. 打开串口监视器,波特率115200
 *   3. 用手或物体在左右传感器前方移动
 *   4. 观察串口输出的检测状态变化
 *   5. 通过电位器调节每个传感器的检测距离
 **************************************************************************************/

#define IR_LEFT_PIN   21   // ServoPWM3
#define IR_RIGHT_PIN  24   // ServoPWM4

void setup()
{
    Serial.begin(115200);
    delay(200);
    pinMode(IR_LEFT_PIN, INPUT_PULLUP);
    pinMode(IR_RIGHT_PIN, INPUT_PULLUP);

    Serial.println(F("========================================"));
    Serial.println(F(" 红外双目跟随模块 独立测试程序"));
    Serial.println(F("========================================"));
    Serial.println(F("L=1 表示左传感器检测到物体"));
    Serial.println(F("R=1 表示右传感器检测到物体"));
    Serial.println(F(""));
    Serial.println(F("状态说明:"));
    Serial.println(F("  L=0 R=0  → 目标丢失"));
    Serial.println(F("  L=1 R=0  → 目标在左侧 → 左转"));
    Serial.println(F("  L=0 R=1  → 目标在右侧 → 右转"));
    Serial.println(F("  L=1 R=1  → 目标在正前方 → 停车"));
    Serial.println(F(""));
}

void loop()
{
    int leftVal  = digitalRead(IR_LEFT_PIN);
    int rightVal = digitalRead(IR_RIGHT_PIN);
    
    // 传感器检测到物体输出 LOW,取反后 1 表示检测到
    bool leftDetected  = (leftVal == LOW);
    bool rightDetected = (rightVal == LOW);

    Serial.print(F("L="));
    Serial.print(leftDetected ? 1 : 0);
    Serial.print(F(" R="));
    Serial.print(rightDetected ? 1 : 0);

    // 状态判断
    if (leftDetected && rightDetected) {
        Serial.println(F("  → 停车(目标居中且已近)"));
    } else if (leftDetected && !rightDetected) {
        Serial.println(F("  → 左转(目标偏左)"));
    } else if (!leftDetected && rightDetected) {
        Serial.println(F("  → 右转(目标偏右)"));
    } else {
        Serial.println(F("  → 搜索(目标丢失)"));
    }

    delay(100);
}

五、调试流程演示

5.1 测试流程

①灵敏度测试

        上电,传感器前无遮挡、串口显示 L=0 R=0 → 搜索(目标丢失)、如有误检测,调节电位器降低灵敏度

②遮挡测试

        用手掌挡住左传感器,串口显示 L=1 R=0 → 左转(目标偏左)、用手掌挡住右传感器,串口显示 L=0 R=1 → 右转(目标偏右)、用手掌同时挡住两个传感器,串口显示 L=1 R=1 → 停车(目标居中且已近)

③跟随模式测试

        发送命令"G",手掌从左侧移到右侧,状态从左转、停车到右转状态依次变化,确认状态切换平滑

5.2 演示视频

零知派迷你板智能小车-红外双目跟随模块调试

展示独立测试程序在串口监视器上的实时输出,用手在不同位置遮挡两个传感器,对应串口输出的状态变化;演示灵敏度调节——用螺丝刀旋转电位器,观察触发距离的变化;展示整车跟随效果

六、项目跟随功能实现

确认模块本身没问题之后,再看完整项目里跟随功能是怎么组织的。这部分代码分布在两个文件里:

文件 职责
sc_perception.h/.cpp IRSensor::read() ——底层读取两个 GPIO 电平,转换为布尔值
sc_behaviors.h/.cpp FollowBehavior::update() ——上层跟随状态机

6.1 IRSensor底层感知层

// sc_perception.cpp
IRSensor::IRSensor()
{
    // INPUT_PULLUP:无物体时保持HIGH,避免悬空导致误触发
    pinMode(ServoPWM3, INPUT_PULLUP);
    pinMode(ServoPWM4, INPUT_PULLUP);
}

void IRSensor::read(bool *isLeft, bool *isRight)
{
    // 检测到物体 → LOW → isLeft/isRight = true
    // 无物体     → HIGH → false
    *isLeft  = (digitalRead(ServoPWM3) == LOW);
    *isRight = (digitalRead(ServoPWM4) == LOW);
}

6.2 FollowBehavior跟随状态机

最终版本状态机

        FOLLOW(跟随状态)
        lIR && rIR → stop()(双触发=正对且近,停止)
        lIR && !rIR → 左转对准(-FOLLOW_TURN, FOLLOW_TURN)
        !lIR && rIR → 右转对准(FOLLOW_TURN, -FOLLOW_TURN)
        连续 5 帧无触发(_lostFrames >= LOST_THRESH)
        切换回 SEARCH_CW  

void FollowBehavior::update()
{
    bool lIR = false, rIR = false;
    g_ir.read(&lIR, &rIR);

    // 调试输出(正式发布可屏蔽)
    Serial.print(F("[FOLLOW] L=")); Serial.print(lIR);
    Serial.print(F(" R=")); Serial.println(rIR);

    // ── 有目标:进入跟随 ─────────────────────────────────────────
    if (lIR || rIR) {
        _lostFrames = 0;

        if (_st != FOLLOW) {
            _st = FOLLOW;
            Serial.println(F("[FOLLOW] 发现目标,进入跟随"));
            BTSerial.println(F("[FOLLOW] Target found"));
        }

        if (lIR && rIR) {
            // 双触发:目标正对且已非常近,停止
            g_motor.stop();
        } else if (lIR && !rIR) {
            // 左侧触发:目标在左侧 → 左转对准
            g_motor.motorRun(-FOLLOW_TURN, FOLLOW_TURN);
        } else {
            // 右侧触发:目标在右侧 → 右转对准
            g_motor.motorRun(FOLLOW_TURN, -FOLLOW_TURN);
        }
        return;
    }

    // ── 无目标:消抖后进入搜索 ───────────────────────────────────
    if (_st == FOLLOW) {
        _lostFrames++;
        if (_lostFrames < LOST_THRESH) {
            g_motor.stop();   // 消抖期间保持停止
            return;
        }
        _st = SEARCH_CW; _ts = millis(); _phase = 0; _lostFrames = 0;
        g_motor.stop();
        Serial.println(F("[FOLLOW] 目标丢失,开始搜索"));
        BTSerial.println(F("[FOLLOW] Lost, searching"));
        return;
    }

    // ── 搜索阶段 ─────────────────────────────────────────────────
    unsigned long elapsed = millis() - _ts;
    switch (_phase) {
        case 0: g_motor.motorRun(SEARCH_SPEED, -SEARCH_SPEED);
                if (elapsed >= SPIN_MS) { g_motor.stop(); _phase = 1; _ts = millis(); }
                break;
        case 1: if (elapsed >= PAUSE_MS) { _phase = 2; _ts = millis(); } break;
        case 2: g_motor.motorRun(-SEARCH_SPEED, SEARCH_SPEED);
                if (elapsed >= SPIN_MS * 2) { g_motor.stop(); _phase = 3; _ts = millis(); }
                break;
        case 3: if (elapsed >= PAUSE_MS) { _phase = 0; _ts = millis(); } break;
        default: _phase = 0; _ts = millis(); break;
    }
}
调试历程 功能迭代内容
卡死不动 最初 onEnter() 进入搜索状态旋转,IR引脚没有接线,读到的是拉高的 HIGH
跟随逻辑方向反了 motorRun(-FOLLOW_TURN, FOLLOW_TURN) 实际上是转向弄反
跟随效果不稳定,抖动明显 降低搜索和跟随转速,加入丢失目标消抖(连续5帧确认丢失才切换)

6.3 系统流程图

①参数调优指南

问题现象 可能原因 调整方案
搜索时转速过快,找到目标有明显顿挫 SEARCH_SPEED 太大 降低到 50~65
对准时过冲,来回抖动 FOLLOW_TURN 太大 降低到 45~55
目标稍微晃动就进入搜索 LOST_THRESH 太小 增加到 8~10
丢失目标后反应太慢才开始搜索 LOST_THRESH 太大 减小到 3~5
双触发区间太窄,小车总在左右摆 两传感器间距太大 缩小传感器间距
传感器始终触发(误触发) 灵敏度太高 逆时针调电位器降低灵敏度

②与避障模式(B命令)的功能共用说明

同一对 IR 传感器在两种模式下复用,逻辑完全不同:

对比项 跟随模式(G) 避障辅助(B)
传感器安装推荐角度 正前方,偏内 ±15° 斜前方 45°/135°
触发含义 目标在那一侧,应转向靠近 那一侧有障碍,应转向远离
转向决策 转向触发侧(左触发→左转) 转离触发侧(左触发→右转)
速度控制 慢速跟随,双触发停车 紧急规避,直接全速转向

        这也是代码里两个模块功能共用同一套硬件的典型案例,上层算法完全不同,但底层 IRSensor::read() 接口是相同的,体现了感知层与行为层分离的设计价值

七、常见问题解答(FAQ)

Q1:传感器接好了,跟随时小车原地转但不追目标,是什么问题?

        A:首先用独立测试程序检查:把手放在左侧传感器前,串口显示的是 L=1 还是 R=1。如果显示 R=1,说明左右信号线接反了,换线即可。如果双侧都是0,检查供电和电位器

Q2:红外传感器在阳光下失效怎么办?

        A:该模块对环境光有一定的适应性,但在强阳光直射的情况下,阳光中的红外分量会干扰接收管,导致误触发。解决方法:逆时针调小电位器降低灵敏度,同时给传感器加装遮光外壳(可用黑色热缩管套住模块两侧)

 资料整合

LM393 数据手册(TI):         lm393.pdf

LM393 应用电路:        差分比较器应用电路

红外跟随和红外避障虽然用了同样的硬件,但控制逻辑完全不同。避障只需要二值判断(有/没有障碍),而跟随需要正确理解“目标在左还是在右”并执行对应的转向动作——方向一旦搞反,整个功能就完全失效了

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐