树莓派四、外设传感器
1.传感器

还有一个USB摄像头和激光雷达,接下来会都试一试。
2.HC-SRO4超声波雷达
https://shumeipai.nxez.com/2019/01/02/hc-sr04-ultrasonic-ranging-module-on-raspberry-pi.html
就底层逻辑而言,这个完全够用了,注意接线时echo分压即可。
或者直接使用封装好的库。
from gpiozero import DistanceSensor
from time import sleep
# 定义传感器
# echo 接在 GPIO 24,trigger 接在 GPIO 23
# max_distance=2.0 表示我们最多只测 2 米内的东西
sensor = DistanceSensor(echo=24, trigger=23, max_distance=2.0)
print("超声波雷达启动!按 Ctrl+C 停止。")
try:
while True:
# sensor.distance 默认返回的是“米”,我们乘以 100 换算成“厘米”
distance_cm = sensor.distance * 100
print(f"当前距离: {distance_cm:.1f} cm")
sleep(0.5) # 每 0.5 秒测一次
except KeyboardInterrupt:
print("测试结束。")

3.MPU-6050
3.1接线
一般只用最上面的四个pin就行,下面四个是扩展的用法。


MPU-6050使用了I2C,所以在接线时把前四个引脚按照pin图接就行,注意VCC接3.3V.
3.2配置依赖
guthub上有https://github.com/Bilydesty/MPU6050-DMP-driver-for-raspi
原理是类似于利用MPU自带的CPU模块(DMP)进行计算,但是配置太麻烦,我直接换成python实现:
先在树莓派上开启I2C
再下载相关库
安装系统级 I2C 核心工具和
sudo apt-get update
sudo apt-get install python3-smbus i2c-tools -y
sudo pip3 install mpu6050-raspberrypi --break-system-packages
3.3读取与处理数据
3.3.1原始方法
3.3.2互补滤波算法?
后期导航时再修改。
from mpu6050 import mpu6050
import time
import math
# 1. 初始化传感器 (0x68 是 I2C 地址)
sensor = mpu6050(0x68)
# 2. 互补滤波的核心权重参数 (K)
# 陀螺仪信任度 98%,加速度计信任度 2%
K = 0.98
# 初始姿态角度
pitch = 0.0 # 俯仰角
roll = 0.0 # 横滚角
yaw = 0.0 # 航向角
# 记录上一次循环的时间
last_time = time.time()
def get_accel_angles():
"""
通过纯加速度计算角度 (不受随时间推移的漂移影响,但在小车震动时数据会乱跳)
"""
accel_data = sensor.get_accel_data()
x = accel_data['x']
y = accel_data['y']
z = accel_data['z']
# 物理防除零保护
if z == 0: z = 0.001
# 使用反正切函数将三轴重力转化为角度
acc_pitch = math.atan2(y, math.sqrt(x*x + z*z)) * 180 / math.pi
acc_roll = math.atan2(-x, math.sqrt(y*y + z*z)) * 180 / math.pi
return acc_pitch, acc_roll
print("🚀 MPU-6050 纯 Python 互补滤波算法已启动...")
print("请平稳放置传感器 2 秒钟...")
time.sleep(2)
try:
while True:
# 计算本次循环用了多少时间 (dt,通常是 0.01~0.02秒)
current_time = time.time()
dt = current_time - last_time
last_time = current_time
# 1. 获取陀螺仪角速度 (度/秒)
gyro_data = sensor.get_gyro_data()
gyro_x = gyro_data['x'] # 绕 X 轴转动的速度
gyro_y = gyro_data['y'] # 绕 Y 轴转动的速度
gyro_z = gyro_data['z'] # 绕 Z 轴转动的速度
# 2. 获取加速度计计算的绝对角度
acc_pitch, acc_roll = get_accel_angles()
# ==========================================
# 🌟 见证魔法的时刻:互补滤波核心公式 🌟
# ==========================================
# 新角度 = 0.98 * (旧角度 + 陀螺仪积分出来的角度) + 0.02 * 加速度计计算的绝对角度
# 这完美抵消了陀螺仪的“零点漂移”和加速度计的“高频震动噪点”
pitch = K * (pitch + gyro_x * dt) + (1 - K) * acc_pitch
roll = K * (roll + gyro_y * dt) + (1 - K) * acc_roll
# 注意:由于普通 MPU-6050 没有地磁计(指南针),Yaw(航向角)无法被加速度计纠正。
# 所以 Yaw 只能纯靠陀螺仪积分。它在静止时可能会以极慢的速度漂移(比如1分钟偏1度),
# 但对于小车短期转向(比如转个90度)来说,精度已经完全足够了!
yaw = yaw + gyro_z * dt
# 美化打印输出格式
print(f"航向(Yaw): {yaw:6.1f}° | 俯仰(Pitch): {pitch:6.1f}° | 横滚(Roll): {roll:6.1f}°")
# 延时,保持约 50Hz 的刷新率 (50次/秒对小车来说足够了)
time.sleep(0.02)
except KeyboardInterrupt:
print("\n测试结束,传感器休眠。")

4.红外传感器

其实到这里已经可以封装为一个小项目了,参考如下;也可以通过这个看看完整项目的结构。
因为树莓派要输入模拟量还要额外的板子,所以剩下俩红外不使用了,仅使用四路大红外。
4.1接线
因为四路红外连接GPIO的输出电压和其VCC差不多,因此我们要给它的VCC接树莓派的3.3V
四个输出接BOARD编码的7 11 13 15.对应GPIO 4 17 27 22
4.2代码
from gpiozero import LineSensor
import time
# 定义 4 路传感器,对应刚才接的 GPIO 引脚
# 假设 IN1 是最左边,IN4 是最右边
sensor_L2 = LineSensor(4) # 最左侧
sensor_L1 = LineSensor(17) # 偏左侧
sensor_R1 = LineSensor(27) # 偏右侧
sensor_R2 = LineSensor(22) # 最右侧
print("🚀 4路红外循迹阵列已启动!按 Ctrl+C 退出。")
print("请在探头下方滑动黑纸或白纸,观察数据矩阵:")
print("-" * 40)
try:
while True:
# 读取 4 个探头的状态
# .value 会返回 1 (触发,通常是看到黑线或悬空) 或 0 (安全,看到白地)
v_L2 = sensor_L2.value
v_L1 = sensor_L1.value
v_R1 = sensor_R1.value
v_R2 = sensor_R2.value
# 打印状态矩阵,end="\r" 可以让终端里的文字在同一行原地刷新,看起来像仪表盘
print(f"路面状态 [左到右]: [{v_L2}] [{v_L1}] [{v_R1}] [{v_R2}] ", end="\r")
# 刷新率 0.1 秒
time.sleep(0.1)
except KeyboardInterrupt:
print("\n\n测试结束,雷达关闭。")
然后运行代码,并且根据结果,旋转模块各支路的旋钮,直到四者相对统一。
5.USB摄像头
5.1检查设备
插入usb后 lsusb查看设备

如图我的device 004就是usb摄像头
5.2配置opencv
sudo apt-get update
sudo apt-get install python3-opencv -y
5.3拍摄图片
这里拍个图片练手即可,高级应用在上位机联合部署。
import cv2
import time
# 0 代表第一个 USB 摄像头(也就是刚才看到的 /dev/video0)
cap = cv2.VideoCapture(0)
# ⚠️ 性能保护:树莓派 3B 算力有限,强推降低分辨率保证流畅度
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
print("摄像头预热中...")
time.sleep(2) # 给摄像头一点时间自动调整白平衡和曝光
# 读取一帧画面
# ret 是一个布尔值,代表是否读取成功;frame 就是那张图片的数据矩阵
ret, frame = cap.read()
if ret:
print("📸 咔嚓!读取成功!")
# 将图片保存为 test_photo.jpg
cv2.imwrite("test_photo.jpg", frame)
print("照片已保存为 test_photo.jpg,快去文件夹里看看吧!")
else:
print("❌ 读取失败,请检查摄像头是否被其他程序占用。")
# 用完记得释放资源,这是好习惯
cap.release()
6.激光雷达
这是小米/追觅扫地机器人拆机的Delta2C-PRO 单线LDS激光雷达;我让它通过ttl转usb模块与树莓派3b相连接。

6.1误区
激光雷达看似一个整体实则可以分为激光发射接受器和马达两部分对待,马达对应针脚“+”,“-”,激光对应剩下那三个,所以使用ttl转usb模块并不能控制激光雷达帽子的转速。它只会以全速转,这样雷达未来安全反而把激光关了。

6.2接线
那么GPIO可行吗?很遗憾,GPIO虽然有PWD,却没有足够强的电流来驱动马达转动,过低或过高的转速不能使能激光部分。
所以要使用电机驱动,把“+-”当作一个真正的马达来对待。
| TB6612 引脚 | 连接目标 | 说明 |
| VM | 树莓派 5V | 电机动力来源 |
| VCC | 树莓派 3.3V | 芯片逻辑供电 |
| GND | 树莓派 GND | 共地 |
| PWMA | 物理引脚12 | 调速信号 |
| AIN1 / AIN2 | 物理引脚16 / 18 | 控制旋转方向 |
| STBY | 树莓派 3.3V | 唤醒驱动板 |
| AO1 / AO2 | 雷达 +/- | 输出给电机 |
此外,激光雷达的T接树莓派RX,V接5V,G接GND。在此放个图记录一下:最后记得开启serial串口通信。

6.3调试PWD
调试PWD占空比直到雷达输出大量数据即可。我这里是38.5
import serial
import time
import RPi.GPIO as GPIO
# --- 硬件引脚配置 ---
PWMA = 18 # 物理引脚 12
AIN1 = 23 # 物理引脚 16
AIN2 = 24 # 物理引脚 18
# --- 调速油门(PWD占空比) ---
DUTY_CYCLE = 38.5
# --- 串口配置 ---
SERIAL_PORT = '/dev/serial0'
BAUD_RATE = 115200
# GPIO 初始化
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup([PWMA, AIN1, AIN2], GPIO.OUT)
# 电机正转逻辑
GPIO.output(AIN1, GPIO.HIGH)
GPIO.output(AIN2, GPIO.LOW)
# 启动 PWM
pwm = GPIO.PWM(PWMA, 1000)
pwm.start(DUTY_CYCLE)
try:
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=0.1)
print(f"已开启 PWM {DUTY_CYCLE}%, 正在从 {SERIAL_PORT} 读取原始数据...")
while True:
if ser.in_waiting > 0:
# 直接读取缓冲区所有字节
raw_data = ser.read(ser.in_waiting)
# 像之前那样打印空格分隔的十六进制
print(raw_data.hex(' '))
time.sleep(0.01)
except KeyboardInterrupt:
print("\n用户停止")
finally:
pwm.stop()
GPIO.cleanup()
if 'ser' in locals() and ser.is_open:
ser.close()
更多推荐
所有评论(0)