HC-SR501 人体感应灯与警报:两颗电位器与误触发全解

Cartoon of a large HC-SR501 PIR sensor with a white honeycomb dome on a royal blue board with two orange trimmers and a yellow jumper, casting a pale teal detection fan towards a small glowing red LED

HC-SR501 侦测的其实不是”人”,而是落在它两片感应晶片上的红外线变化这一句就解释了两个经典抱怨:灯一直不肯熄,以及你一站定不动,感应器就”忘了”你。做法其实很短 —— Tx 电位器转到最小、跳线拨到 L、保持时间交给程序管 —— 之后就换成 Arduino 掌控时间,而不是跟感应器互相拉扯。

做一盏 HC-SR501 人体感应灯要准备什么?

HC-SR501 只要三条线,别的都不用:电源、接地,再加一条信号线接进 Arduino。感应器以下全是输出端 —— 一颗 LED 让你看见触发、一个蜂鸣器让你听见触发,再加一块面包板,整个作品一个焊点都不必动。

Parts list

ItemPriceQty
Relay Module 3.3V 5V 12V 1/2/4/8 Ways Optocoupler Trigger Relay Module 1 2 4 8 Channel Relay Module - 5V RELAY MODULE(1WAY)Relay Module 3.3V 5V 12V 1/2/4/8 Ways Optocoupler Trigger Relay Module 1 2 4 8 Channel Relay Module - 5V RELAY MODULE(1WAY)1WRELAYRM3.15

把那颗 LED 换成真正的 12 V LED 灯条或直流灯 —— 12 V 由你自己另外供,绝对不是从 Arduino 拿。只做低压直流;要接更大的负载之前,请先读我们的继电器安全指南。

有两个”公母”细节决定这个作品是不是真的免焊。HC-SR501MH-FMD 蜂鸣器模块露出来的都是针,所以要接公对母杜邦线的母头那一端;公对公杜邦线负责面包板到 Uno 之间的走线,而5 mm 红色 LED 和电阻的脚直接插进面包板就行。Uno 是用全尺寸 USB-B 烧录的,所以需要那条 A 转 B 数据线。手上已经有电阻的话,220 Ω 到 1 kΩ 之间任何一颗都可以;400 支电阻包是本店固定电阻的选项。

为什么 PIR 感应器看得见动作,却看不见人?

HC-SR501 的感应元件是一颗热释电探测器,里面有片反向串接的晶片。温热的物体会辐射波长约 8–14 µm 的红外线;当这份辐射均匀落在两片晶片上时,两者的输出互相抵消,模块读到的就是零。一屋子温热的家具,或者一个人完全静止站着,画面正好就是这种平衡而不变的状态 —— 所以不管身体多温暖,PIR 对静止的人就是视而不见。

白色蜂窝状的罩子是一片菲涅尔透镜,正是它把上面那个限制变成了动作侦测。每一个小面把房间里不同的一道窄扇形聚焦到感应元件上,所以视野并不是一个平滑的圆锥,而是在大约 120°、可及 3–7 m 的锥形范围内交错排开的数十道光束。你横向穿过这些光束时,体温先落在一片晶片、再落到另一片,产生一个正脉冲接着一个负脉冲 —— 这才是放大器看得见的变化。若你正对着感应器直直走过去,跨过的光束边界就少得多,这也是为什么正面接近往往触发得更慢、更不可靠,横向经过反而灵。

同一套物理也解释了预热。整条放大链路增益极高,而且只放大变化,所以一上电它得先稳定到自己的基准线。请给模块通电后大约一分钟的时间,这段期间它会毫无来由地乱触发。

卡通对比图:一个人横向穿过 PIR 感应器青色梳齿状的光束因而被侦测到,另一个人静止站在其中一道光束里则被漏掉
横穿光束会让两片感应晶片意见不合,那就是一次侦测;站在同一道光束里则让它们始终一致,也就是一片安静。

HC-SR501、LED 和蜂鸣器要怎么接到 Uno?

HC-SR501 的输出是 3.3 V,不是 5 V,因为模块会把自己的供电降压,输出级摆动的就是那条内部电源轨。Uno 认定 HIGH 的门槛是 0.6 × VCC 以上 —— 5 V 板子上就是 3.0 V —— 所以 3.3 V 还留着 0.3 V 的余裕。

接到 为什么
HC-SR501 VCC(外侧其中一支脚) Uno 5V 模块可吃 5–20 V,自己产生逻辑电源轨
HC-SR501 OUT(永远是中间那一支 Uno D2 触发时 3.3 V,闲置时 0 V
HC-SR501 GND(另一支外侧脚) Uno GND 共地回路。外侧那两支脚在不同版本之间会对调,而且 VCC/GND 的丝印通常印在白色罩子底下的板面上,不在排针旁边 —— 上电前请先看清楚自己手上这一块,因为 VCC 和 GND 接反等于把模块反着推,有可能直接烧掉它
蜂鸣器 VCC / GND Uno 5V / GND 模块自带驱动三极管
蜂鸣器 I/O Uno D8 这支脚被拉时发声
LED 长脚(正极)+ 220 Ω 电阻的一只脚 面包板上同一排 把它们接在一起的正是面包板 —— 同一排的五个孔就是同一个节点,所以不用焊
电阻的另一只脚(自己独立一排) Uno D9 (5 − 2.0) V ÷ 220 Ω ≈ 14 mA,在每支脚 20 mA 的规格之内
LED 短脚(负极,自己独立一排) Uno GND 用公对公杜邦线把回路接通

MH-FMD 模块的丝印把 I/O 标为低电平触发,也就是这支脚变低时它的三极管才让蜂鸣器 —— 这意味着一支刚设定好的输出脚停在 0 V,正好会让它一开机就尖叫。下面这份程序在呼叫 pinMode(OUTPUT) 之前先写入 HIGH,之所以行得通,是因为 digitalWrite() 两种情况下动到的是同一个端口寄存器位:脚位还是输入时,这个位只是打开上拉电阻;而脚位一变成输出,这个位已经就是输出电平,所以脚位从头到尾都没有被驱动到 0 V。

两颗橙色电位器和那个黄色跳线到底在做什么?

HC-SR501 自己身上就带着一套计时硬件,而那正是暗中跟你的程序作对的部分。一颗橙色电位器设定灵敏度 —— 也就是人在多远之外还认得出来,大约 3 m 到 7 m。另一颗设定 Tx,也就是触发之后 OUT 维持高电平多久,从一端的几秒钟到另一端的大约五分钟。黄色跳线选的则是重复触发模式。拨在 H 时,Tx 期间再有动作会把计时重新起算,所以只要有人持续在动,OUT 就一直是高的。拨在 L 时它只跑一次,时间到就放掉,不管你还动不动。两种模式之后都跟着一段固定约 2.5 s 的盲区,期间什么都无法再触发 —— 在 H 上看不出来,在 L 上则每次脉冲之后都感受得到。

HC-SR501 的板子出自好几家工厂,网上的教程对于哪一颗电位器在哪个位置说法互相矛盾,所以请在通电、程序也跑着的情况下,花一分钟认清自己这一块:

  1. 拿一把小的一字螺丝起子,把两颗电位器都轻轻逆时针转到底。别硬转。
  2. 挥手一次,然后计时 LED 亮多久。把其中一颗电位器顺时针转四分之一圈,再挥一次手。如果亮灯时间变了,那颗就是 Tx;如果变的只是多远之外才被认到,那颗就是灵敏度。
  3. 用马克笔在 Tx 那颗上点一个点做记号。

让软件计时真正说了算的设定是 Tx 转到最小、跳线拨到 L。转到最小时,Tx 让输出维持高电平大约三秒,接着那段盲区也是同一个量级,所以一个持续走动的人仍然大约每六秒左右至少会把输出重新触发一次。程序里任何明显长过这个数字的保持时间 —— 十秒以上 —— 就绝不可能被感应器自己的死区截断。

症状 原因 解法
人都走光了,灯还亮好几分钟 Tx 被顺时针转大了;在 H 模式下,每一个路过的人都会把它重新起算 Tx 转到最小,跳线拨到 L,保持时间交给程序
人还在房间里,输出却掉下来了 你不动了 —— 两片晶片重新平衡,没有变化 这是正常行为;把程序里的保持时间拉长
刚挥完手马上再挥一次,第二次没反应 每次脉冲之后的那段盲区 等几秒钟,或者改用 H,让计时被刷新而不是重来
上电后第一分钟乱触发 放大器还在稳定 预热时间过完之前,先不要理会那支脚
delay() 的数值完全没有差别 Tx 比你程序里的计时还长 先把 Tx 转小,再去怪程序

亲眼看着螺丝起子转动那两颗电位器,会更有感觉:

卡通特写:HC-SR501 宝蓝色板子上有两颗橙色电位器,其中一颗插着银色螺丝起子,旁边是一个亮黄色跳线帽,青色弧形箭头标示旋转与滑动的方向
三个控制项全在同一条边上:两颗用小一字螺丝起子调整的橙色电位器,以及一个可以在两个位置之间滑动的黄色跳线帽。

程序:让 Arduino 自己掌管保持时间

不需要任何库:PIR 就是一个普通的数字输入,所以 digitalRead()millis() 就是全部工具。开发板选 Arduino Uno 上传,然后把串口监视器开在 9600 baud。这里没有任何一处呼叫 delay(),所以那声短鸣和灯的计时各跑各的。编译出来 2,318 字节,大约占 Uno 闪存的 7%。

// HC-SR501 感应灯 + 警报短鸣,保持时间由程序自己掌管。
// 这份程序假设的感应器设定:Tx 电位器转到最小,跳线拨到 L。

const uint8_t PIR_PIN    = 2;   // HC-SR501 OUT(三针排针的中间那一支)
const uint8_t LED_PIN    = 9;   // LED 正极经一颗 220 欧姆电阻接到 GND
const uint8_t BUZZER_PIN = 8;   // MH-FMD 有源蜂鸣器 I/O,低电平时发声

const unsigned long WARMUP_MS = 60000UL;  // 感应器稳定之前先不理它
const unsigned long HOLD_MS   = 15000UL;  // 灯要亮多久
const unsigned long BEEP_MS   = 300UL;    // 每来一位新访客响一声短鸣

bool lightOn = false;
unsigned long lastMotionMs = 0;

bool beeping = false;
unsigned long beepStartedMs = 0;

// 这个蜂鸣器模块是低电平触发:把 I/O 拉低它才发声。
void buzzer(bool on) {
  digitalWrite(BUZZER_PIN, on ? LOW : HIGH);
}

void setup() {
  Serial.begin(9600);

  pinMode(PIR_PIN, INPUT);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  // 在脚位变成输出之前先把电平设好。刚设定的输出脚起点是 LOW,
  // 而 LOW 正是让这个蜂鸣器尖叫的电平,所以先写 HIGH
  //(此时它只是打开上拉电阻)就能让开机保持安静。
  digitalWrite(BUZZER_PIN, HIGH);
  pinMode(BUZZER_PIN, OUTPUT);

  Serial.println(F("Warming up - ignoring the sensor for about a minute."));
}

void loop() {
  unsigned long now = millis();

  // 无符号长整数相减在 millis() 溢出回绕时依然正确,所以下面的保持与
  // 短鸣计时都写成 (now - then >= limit) 的形式。预热则是直接拿 millis()
  // 本身做的一次性判断,因此若开发板撑到第 49 天回绕,它只会再预热一分钟。
  bool warmedUp = (now >= WARMUP_MS);
  bool motion   = (digitalRead(PIR_PIN) == HIGH);

  if (motion && warmedUp) {
    lastMotionMs = now;               // 每一个脉冲都把保持时间刷新

    if (!lightOn) {                   // 是新的访客,而不是同一个人还在动
      lightOn = true;
      digitalWrite(LED_PIN, HIGH);
      beeping = true;
      beepStartedMs = now;
      buzzer(true);
      Serial.println(F("Motion detected"));
    }
  }

  if (lightOn && (now - lastMotionMs >= HOLD_MS)) {
    lightOn = false;
    digitalWrite(LED_PIN, LOW);
    Serial.println(F("Quiet again"));
  }

  if (beeping && (now - beepStartedMs >= BEEP_MS)) {
    beeping = false;
    buzzer(false);
  }
}

走廊灯就把 HOLD_MS 调大,或者像我们的火焰传感器火灾警报器教程那样,把 BEEP_MS 拉长成一个像样的警报。

为什么没人的时候它也会触发?

HC-SR501 的误触发,几乎都是你没把它当成”动作”的真实红外线变化。冷气和风扇是头号元凶 —— 不是因为感应器看得见流动的空气,而是因为气流把温热和凉的表面在视野里扫来扫去,同时不均匀地冷却感应元件,于是两片晶片就不再一致了。请把罩子朝向避开出风口,也绝对不要装在直吹的风口前。

阳光是第二个原因。日光里的红外线份量很足,所以一块日照斑在地板上缓缓移动、或者云影掠过窗口,都是画面里货真价实的变化。请让窗户和玻璃门待在锥形视野之外,或者用时间、用光敏传感器把输出关掉。

第三个原因是电。模块放大的是微伏等级的信号,所以一条塌陷或带噪声的 5 V 电源轨,落在比较器输入端就跟一个真信号没两样。跟 Uno 共用 5 V 脚的继电器线圈或马达,每次动作都会把那条轨拉低一下 —— 误触发恰好在你的输出启动那一刻出现,就是这个原因,不是感应器闹鬼。请给这些负载独立的供电,两边的地接在一起。

灵敏度电位器要留到这三项都排除之后再动。把它转小只会缩短侦测距离,那是把症状藏起来,不是把病治好。

卡通房间剖面图:灰色墙上的 HC-SR501 感应器射出青色侦测锥,锥里同时罩住冷气吹出的气流、窗边配着米色窗帘的黄色阳光斑,以及一个冒出青色电火花的灰色电源
房间明明没人却让 PIR 触发的三件事:出风口吹出的气流、扫过地板的阳光斑,以及一条带噪声的共用电源轨。

从一颗 LED 换成真正的灯

LED_PIN 改接到继电器上,同一份程序就能开关 12 V LED 灯条或直流灯。5 V 继电器模块的线圈是从 Uno 的 5 V 脚取电的 —— 一颗线圈大约吃 70 mA,USB 供电的 Uno 撑得住,一整排就不行了 —— 而它的控制端是一个三位螺丝端子,杜邦线裸露的公针可以直接夹进去。

被切换的那一侧是一对干接点,不是输出 —— 继电器只是在 COM 和 NO 之间合上一个开关,所以灯和它自己的 12 V 电源自成一个回路,完全碰不到 Arduino 的地。

写程序之前先看清楚模块上那个小小的高/低电平触发跳线:它决定线圈是在 HIGH 还是 LOW 时吸合,而出厂时两种设定都可能。用一次 digitalWrite 加你的耳朵就能确认。如果你本来就有一套 12 V 系统,那么12 V 线圈版本才是对的零件 —— 只是在这个作品里没有 12 V 电源轨可以给它用。

被切换的那一侧请保持在低压直流。这些接点的额定确实涵盖市电,但市电切换牵涉到电气间隙、绝缘和外壳等要求,那些属于我们的继电器模块安全指南 —— 靠近 240 V 之前,请先把它读完。

真实客户身上常见的错误

接好马上就测,人还趴在板子上。预热那一分钟里,你自己的手臂就填满了整个侦测锥。通电、走开,然后再回来。

把感应器的计时算在程序头上。如果灯亮的时间远远超过 HOLD_MS,那是 Tx 电位器被转长了,Arduino 只是在等 OUT 变低而已。软件缩不短硬件。

把蜂鸣器当成高电平触发来接。它会从开机一路尖叫下去,看起来就像一个坏掉的模块。HIGH 才是安静。

指望 PIR 能数人头或量距离。它只回报”锥里有东西变了”,仅此而已;测距是我们HC-SR04 教程的活儿。

常见问题

两颗橙色电位器,哪一颗是灵敏度、哪一颗是延时?

把其中一颗转四分之一圈,然后挥挥手。如果亮灯时间变了,那颗就是 Tx,也就是延时;如果变的只有侦测距离,那颗就是灵敏度。

为什么我的感应灯会亮上好几分钟?

Tx 电位器被转大了,而跳线又在 H,所以每一个新动作都会重新起算一段很长的硬件计时。把 Tx 转到最小、跳线移到 L,时间长短交给程序设定。

HC-SR501 可以接 ESP32 或 Raspberry Pi Pico 吗?

可以。它的输出摆到的是 3.3 V,而不是 VCC,所以不需要电平转换。VCC 请从板子的 5 V 脚取电 —— 模块自己的稳压器需要高于 3.3 V 的输入。

黄色跳线该用 H 还是 L?

由程序掌管计时时用 L,感应器就不会自己锁在高电平。若是感应器直接驱动继电器或灯、完全没有单片机,那就用 H。

为什么它在门口认得到我,在书桌前却认不到?

门口的人流会横切过许多道菲涅尔光束;坐在书桌前的人待在同一道光束里,而且几乎不动。安装时请让人从它视野横向经过,而不是正面走向它。

最后更新:2026年8月。有问题?WhatsApp 联系我们

Leave a Reply

Your email address will not be published. Required fields are marked *