第9章:运动与碰撞检测
本章介绍人体红外热释传感器(PIR)的工作原理、信号检测方法与智能家居应用。
9.1 人体红外热释传感器
9.1.1 工作原理
PIR(Passive Infrared)传感器检测人体发出的红外辐射:
菲涅尔透镜
↓
╔═══════════╗
║ / | | | \ ║ ← 分区检测
║ ║
╚═══════════╝
↓
热释电元件
↓
信号处理电路
↓
数字输出
核心原理:
- 人体恒温约 37°C,发出红外辐射
- 菲涅尔透镜将红外线聚焦到热释电元件
- 人体移动时,红外辐射变化
- 热释电元件产生微弱电信号
- 放大电路处理后输出数字信号
9.1.2 RE200B-P 元件
套件中的 PIR 模块采用 RE200B-P 热释电元件:
- 双元件差分结构
- 探测角度:约 100°
- 探测距离:3-7 米
9.1.3 输出逻辑
| 状态 | 信号值 | LED |
|---|---|---|
| 检测到人移动 | 高电平 (1) | 灭 |
| 无人移动 | 低电平 (0) | 亮 |
注意:PIR 检测的是运动,静止的人无法检测到。
9.1.4 延时与灵敏度
模块通常有两个电位器:
- 灵敏度调节:调节探测距离
- 延时调节:触发后保持高电平的时间(通常 5 秒 ~ 5 分钟)
9.1.5 基础检测代码
'''
实验:人体红外检测
接线:S 端连接 GP19
'''
from machine import Pin
import time
pir = Pin(19, Pin.IN)
while True:
if pir.value() == 1:
print("检测到人体移动!")
else:
print("无人移动")
time.sleep(0.5)
9.2 智能照明系统
9.2.1 基础版
'''
实验:人体感应灯
PIR → GP19, LED → GP0
'''
from machine import Pin
import time
pir = Pin(19, Pin.IN)
led = Pin(0, Pin.OUT)
while True:
if pir.value() == 1:
led.value(1)
print("有人,灯亮")
else:
led.value(0)
print("无人,灯灭")
time.sleep(0.3)
9.2.2 带延时关灯
'''
实验:延时关灯
人离开后延时 30 秒关灯
'''
from machine import Pin
import time
pir = Pin(19, Pin.IN)
led = Pin(0, Pin.OUT)
DELAY_SECONDS = 30
last_motion_time = 0
while True:
if pir.value() == 1:
led.value(1)
last_motion_time = time.time()
print("检测到移动,灯亮")
else:
if led.value() == 1:
if time.time() - last_motion_time > DELAY_SECONDS:
led.value(0)
print(f"{DELAY_SECONDS}秒无人移动,灯灭")
time.sleep(0.5)
9.2.3 结合光敏传感器
'''
实验:智能感应灯
仅在夜间且有人时开灯
'''
from machine import Pin, ADC
import time
pir = Pin(19, Pin.IN)
led = Pin(0, Pin.OUT)
light_sensor = ADC(26)
LIGHT_THRESHOLD = 30000 # 光线阈值
def is_dark():
"""判断是否为暗环境"""
return light_sensor.read_u16() < LIGHT_THRESHOLD
while True:
motion = pir.value() == 1
dark = is_dark()
if motion and dark:
led.value(1)
print("暗环境+有人 → 开灯")
elif not dark:
led.value(0)
print("环境明亮 → 关灯")
else:
# 暗环境无人:保持当前状态或延时关灯
pass
time.sleep(0.5)
9.3 安防报警系统
9.3.1 基础报警
'''
实验:PIR 入侵报警
'''
from machine import Pin
import time
pir = Pin(19, Pin.IN)
buzzer = Pin(20, Pin.OUT)
led = Pin(0, Pin.OUT)
ARMED = True # 布防状态
def alarm_on():
"""触发报警"""
for _ in range(5):
buzzer.value(1)
led.value(1)
time.sleep(0.1)
buzzer.value(0)
led.value(0)
time.sleep(0.1)
while True:
if ARMED and pir.value() == 1:
print("入侵警报!")
alarm_on()
time.sleep(0.3)
9.3.2 多区域监控
'''
实验:多区域 PIR 监控
'''
from machine import Pin
import time
zones = {
'门厅': Pin(19, Pin.IN),
'客厅': Pin(18, Pin.IN),
'卧室': Pin(17, Pin.IN),
}
buzzer = Pin(20, Pin.OUT)
def check_zones():
"""检查所有区域"""
alerts = []
for name, sensor in zones.items():
if sensor.value() == 1:
alerts.append(name)
return alerts
while True:
alerts = check_zones()
if alerts:
print(f"警报区域: {', '.join(alerts)}")
buzzer.value(1)
time.sleep(0.5)
buzzer.value(0)
time.sleep(0.3)
9.4 人员计数系统
9.4.1 单向计数
'''
实验:进出人数统计
双 PIR 传感器判断方向
'''
from machine import Pin
import time
sensor_a = Pin(18, Pin.IN) # 外侧
sensor_b = Pin(19, Pin.IN) # 内侧
count = 0
state = 'idle'
while True:
a = sensor_a.value()
b = sensor_b.value()
if state == 'idle':
if a == 1 and b == 0:
state = 'entering'
elif a == 0 and b == 1:
state = 'exiting'
elif state == 'entering':
if a == 1 and b == 1:
state = 'enter_confirmed'
elif a == 0 and b == 0:
state = 'idle'
elif state == 'enter_confirmed':
if a == 0 and b == 1:
count += 1
print(f"有人进入,当前人数: {count}")
state = 'idle'
elif state == 'exiting':
if a == 1 and b == 1:
state = 'exit_confirmed'
elif a == 0 and b == 0:
state = 'idle'
elif state == 'exit_confirmed':
if a == 1 and b == 0:
count = max(0, count - 1)
print(f"有人离开,当前人数: {count}")
state = 'idle'
time.sleep(0.05)
9.5 ADXL345 加速度传感器
9.5.1 传感器介绍
ADXL345 是一款三轴数字加速度传感器:
- 测量范围:±2g / ±4g / ±8g / ±16g
- 分辨率:13 位
- 接口:I2C / SPI
- I2C 地址:0x53(ALT 接 GND)或 0x1D(ALT 接 VCC)
9.5.2 数据编码格式
ADXL345 输出为 16 位有符号整数:
每轴数据 = 2 字节 (低字节在前)
数据格式: 二进制补码
数据转换:
# 读取原始数据
raw_x = (data[1] << 8) | data[0]
# 转换为有符号数
if raw_x > 32767:
raw_x -= 65536
# 转换为 g 值(默认 ±2g 范围,灵敏度 256 LSB/g)
g_x = raw_x / 256
9.5.3 I2C 读取代码
'''
实验:ADXL345 加速度读取
接线:SDA→GP20, SCL→GP21
'''
from machine import I2C, Pin
import time
# I2C 初始化
i2c = I2C(0, sda=Pin(20), scl=Pin(21), freq=400000)
ADXL345_ADDR = 0x53
# 寄存器地址
REG_POWER_CTL = 0x2D
REG_DATA_FORMAT = 0x31
REG_DATAX0 = 0x32
def write_reg(reg, value):
i2c.writeto_mem(ADXL345_ADDR, reg, bytes([value]))
def read_reg(reg, length=1):
return i2c.readfrom_mem(ADXL345_ADDR, reg, length)
def init_adxl345():
"""初始化 ADXL345"""
# 设置测量模式
write_reg(REG_POWER_CTL, 0x08)
# 设置数据格式: ±2g, 全分辨率
write_reg(REG_DATA_FORMAT, 0x08)
def read_xyz():
"""读取三轴加速度"""
data = read_reg(REG_DATAX0, 6)
# 解析数据(小端序,有符号)
x = (data[1] << 8) | data[0]
y = (data[3] << 8) | data[2]
z = (data[5] << 8) | data[4]
# 转换为有符号数
if x > 32767: x -= 65536
if y > 32767: y -= 65536
if z > 32767: z -= 65536
# 转换为 mg
return x * 3.9, y * 3.9, z * 3.9
# 初始化
init_adxl345()
while True:
x, y, z = read_xyz()
print(f"X: {x:+.0f}mg Y: {y:+.0f}mg Z: {z:+.0f}mg")
time.sleep(0.1)
9.5.4 运动检测
'''
实验:运动/静止检测
'''
from machine import I2C, Pin
import time
import math
# ... (初始化代码同上)
MOTION_THRESHOLD = 100 # mg
def calculate_magnitude(x, y, z):
"""计算加速度幅值"""
return math.sqrt(x*x + y*y + z*z)
last_magnitude = 1000 # 静止时约 1000mg (1g)
while True:
x, y, z = read_xyz()
magnitude = calculate_magnitude(x, y, z)
delta = abs(magnitude - last_magnitude)
if delta > MOTION_THRESHOLD:
print(f"检测到运动! 变化量: {delta:.0f}mg")
last_magnitude = magnitude
time.sleep(0.1)
9.5.5 倾斜角度计算
'''
实验:计算倾斜角度
'''
import math
def calculate_angles(x, y, z):
"""计算相对于水平面的倾斜角度"""
# 转换为 g
gx = x / 1000
gy = y / 1000
gz = z / 1000
# 计算角度(弧度转角度)
pitch = math.atan2(gx, math.sqrt(gy*gy + gz*gz)) * 180 / math.pi
roll = math.atan2(gy, math.sqrt(gx*gx + gz*gz)) * 180 / math.pi
return pitch, roll
while True:
x, y, z = read_xyz()
pitch, roll = calculate_angles(x, y, z)
print(f"俯仰: {pitch:+.1f}° 横滚: {roll:+.1f}°")
time.sleep(0.2)
9.6 跌倒检测
9.6.1 检测原理
跌倒过程中的加速度变化:
- 自由落体:总加速度接近 0g
- 撞击:加速度峰值 > 3g
- 静止:恢复约 1g
9.6.2 检测代码
'''
实验:跌倒检测
'''
import math
FREEFALL_THRESHOLD = 300 # 自由落体阈值 (mg)
IMPACT_THRESHOLD = 3000 # 撞击阈值 (mg)
state = 'normal'
freefall_start = 0
while True:
x, y, z = read_xyz()
magnitude = math.sqrt(x*x + y*y + z*z)
if state == 'normal':
if magnitude < FREEFALL_THRESHOLD:
state = 'freefall'
freefall_start = time.ticks_ms()
print("检测到自由落体!")
elif state == 'freefall':
if magnitude > IMPACT_THRESHOLD:
duration = time.ticks_diff(time.ticks_ms(), freefall_start)
if duration > 50: # 自由落体持续超过 50ms
print(f"⚠️ 跌倒警报! 下落时间: {duration}ms")
state = 'normal'
elif time.ticks_diff(time.ticks_ms(), freefall_start) > 500:
# 超时,可能是误判
state = 'normal'
time.sleep(0.01)
9.7 本章小结
本章介绍了运动与碰撞检测传感器:
- PIR 传感器:
- 检测人体红外辐射变化
- 只能检测移动,无法检测静止目标
- 适用于智能照明、安防报警
- ADXL345 加速度传感器:
- 三轴数字加速度检测
- I2C 通信,16 位有符号数据
- 可计算倾斜角度、检测运动/跌倒
- 数据编码:
- PIR:简单数字信号(高/低)
- ADXL345:I2C + 二进制补码
下一章将学习蜂鸣器与声音输出。