树莓派传感器开发:从光线到车灯的全方位监测
1. 光线传感器电路搭建
光线传感器的搭建需要利用树莓派GPIO引脚的上拉电阻。光敏电阻(LDR)无极性,连接方向不影响使用。此项目仅需两个引脚:树莓派的3.3V引脚和用于感应电阻的GPIO引脚。具体连接方式如下:
| 物理引脚 | BCM编号 | 功能 |
| ---- | ---- | ---- |
| 1 | 3.3V | 3.3V电源 |
| 10 | 15 | GPIO 15 |
连接步骤:
1. 将GPIO 15引脚连接到LDR的一侧。
2. 将LDR的另一侧连接到树莓派的3.3V引脚。
3. 仔细检查连接无误后,接通电源并启动树莓派。
2. 测试驱动开发(TDD)的光线传感器测试
测试驱动开发是在编写代码之前先编写单元测试的方法。通过考虑边界情况和测试标准,可以编写出更优质、简洁的代码,避免添加不必要的功能。光线感应逻辑与boto传感器逻辑类似,使用
gpiozero
模块的
LightSensor
类,并进行基本的异常处理。新函数可能返回三种状态:
- Unknown:传感器工作出现问题。
- Daytime:外界明亮。
- Nighttime:外界黑暗。
以下是用于核心异常处理的两个测试代码:
@patch("Pi_Car.sensors.LightSensor")
def test_get_light_status_bad_pin_factory(self, mock_light_sensor):
mock_light_sensor.side_effect = exc.BadPinFactory
result = Sensors.get_light_status()
assert result == "Unknown"
@patch("Pi_Car.sensors.LightSensor")
def test_get_light_status_other_pin_error(self, mock_light_sensor):
mock_light_sensor.side_effect = TypeError
result = Sensors.get_light_status()
assert result == "Unknown"
LDR返回值在0到1之间,可据此判断昼夜。大于0.5为白天,其他情况为夜晚或出错。通常只需测试边界情况,以下是测试这些边界情况的单元测试:
@pytest.mark.parametrize(
"sensor_value, expected_result",
[
(1, "Daytime"),
(0.9, "Daytime"),
(0.55, "Daytime"),
(0.5, "Daytime"),
(0.49, "Nighttime"),
(0.4, "Nighttime"),
(0, "Nighttime"),
(None, "Unknown"),
],
)
@patch("Pi_Car.sensors.LightSensor")
def test_get_light_status_values(
self, mock_light_sensor, sensor_value, expected_result
):
mock_light_sensor.return_value = type("Button", (), {"value": sensor_value})
result = Sensors.get_light_status()
assert result == expected_result
3. Flask光线传感器逻辑实现
在
Sensors
类中,导入
LightSensor
类:
from gpiozero import Button, exc, LightSensor
创建静态方法
get_light_status
:
@staticmethod
def get_light_status():
"""
安全读取可用光线并计算状态 - 白天/黄昏/夜晚/未知
:return: 字符串 - 光线状态
"""
app.logger.info("Starting to read available light")
status = -1
try:
sensor = LightSensor(pin=15)
status = float(sensor.value)
except exc.BadPinFactory as e:
app.logger.warning(f"Unable to use light sensor in this environment: {e}")
except Exception as e:
app.logger.error(f"Unknown problem with light sensor: {e}")
if status == -1:
result = "Unknown"
elif status >= 0.5:
result = "Daytime"
else:
result = "Nighttime"
app.logger.debug(f"Light: {status} - {result}")
app.logger.info("Finished reading available light")
return result
代码逻辑如下:
1. 初始化状态为 -1。
2. 尝试创建
LightSensor
对象并读取传感器值。
3. 处理可能出现的异常。
4. 根据传感器值判断状态。
5. 记录日志并返回结果。
4. 代码测试与验证
完成代码编写后,使用Pytest运行单元测试:
pytest
若测试失败,可使用
-s
标志查看测试日志:
pytest -s
当所有14个测试通过后,在
data.py
的主路由中实现新的光线传感器功能:
light_status = Sensors.get_light_status()
result = {"temperature": temperature, "boot": boot_status, "light": light_status}
运行应用程序并进行本地验证,确保一切正常。最后进行功能测试,通过遮挡传感器或关闭灯光模拟夜晚,刷新页面查看传感器值的变化。
5. 倒车和雾灯传感器硬件配置
倒车和雾灯传感器的项目目标是检测汽车是否处于倒车档或雾灯是否开启。在进行硬件连接时,务必先断开树莓派的电源。
大多数近30年生产的汽车使用12V电气系统,但需通过以下三种方式确认汽车电压:
- 打开引擎盖查看电池标签。
- 在线搜索汽车型号信息。
- 阅读汽车制造商手册。
由于汽车电路为12V,树莓派为3.3V,需安装电阻保护树莓派。若不考虑物理原理,可使用两个23k欧姆、0.25W的电阻,每个传感器电路一个。若需计算电阻大小,步骤如下:
1. 计算电压降:$V_{drop} = 14.7V - 3.3V = 11.4V$(12V汽车电池系统在发动机运行时实际电压在12V - 14.7V之间)。
2. 根据欧姆定律计算所需电阻:$R = V / I$,树莓派需要0.5mA电流,$11.4V / 0.0005A = 22800$欧姆,约为23k欧姆。
3. 计算所需功率:$P = I^2R$,$0.0005A \times 0.0005A \times 22800\Omega = 0.0057W$,建议使用0.25W的电阻。
连接方式如下:
| 物理引脚 | BCM编号 | 连接对象 |
| ---- | ---- | ---- |
| 15 | 22 | 汽车倒车灯的+12V线路 |
| 16 | 23 | 汽车雾灯的+12V线路 |
连接时,确保电阻位于汽车电路和树莓派GPIO引脚之间,并将树莓派的接地端连接到汽车的接地端(底盘或灯泡插座的0V连接器)。测试时,可在面包板上连接开关或临时连接到树莓派的3.3V(不使用电阻)。连接汽车时,每个GPIO引脚需添加一个尽可能小的保险丝(250mA或更低),以保护树莓派。
6. 倒车和雾灯传感器逻辑实现
在编写代码前,先进行代码重构。创建新的静态方法
get_bool_pin
,用于读取按钮或类似按钮的引脚状态:
@staticmethod
def get_bool_pin(pin):
"""
防御性地将布尔引脚作为按钮读取
:param pin: 要读取的GPIO引脚(BCM布局)
:return: 布尔结果 - 是否按下,或None
"""
app.logger.info(f"Starting to read boolean value from pin: {pin}")
try:
button = Button(pin=pin, pull_up=False)
result = button.is_pressed
except exc.BadPinFactory as e:
app.logger.warning(f"Unable to use boot sensor in this environment: {e}")
result = None
except Exception as e:
app.logger.error(f"Unknown problem with boot sensor: {e}")
result = None
app.logger.info(f"Finished reading boolean value from pin: {pin}")
return result
修改
get_boot_status
方法为类方法,并使用
get_bool_pin
函数:
@classmethod
def get_boot_status(cls):
"""
安全读取启动传感器并计算状态 - 打开/关闭/不可用/未知
:return: 字符串 - 启动状态
"""
app.logger.info("Starting to read boot sensor")
button_status = cls.get_bool_pin(pin=14)
if button_status is None:
result = "Unknown"
else:
if button_status:
result = "Closed"
else:
result = "Open"
app.logger.debug(f"Boot: {result}")
app.logger.info("Finished reading boot sensor")
return result
为
get_bool_pin
函数编写两个新的单元测试:
@patch("Pi_Car.sensors.Button")
def test_get_bool_pin_bad_pin_factory(self, mock_button):
mock_button.side_effect = exc.BadPinFactory
result = Sensors.get_bool_pin(pin=None)
assert result is None
@patch("Pi_Car.sensors.Button")
def test_get_bool_pin_other_pin_error(self, mock_button):
mock_button.side_effect = TypeError
result = Sensors.get_bool_pin(pin=None)
assert result is None
创建倒车和雾灯状态读取函数:
@classmethod
def get_reverse_status(cls):
"""
安全读取倒车传感器
:return: 布尔值 - 是否倒车,或None
"""
app.logger.info("Starting to read reverse sensor")
result = cls.get_bool_pin(pin=22)
app.logger.debug(f"Boot: {result}")
app.logger.info("Finished reading reverse sensor")
return result
@classmethod
def get_fog_light_status(cls):
"""
安全读取雾灯传感器
:return: 布尔值 - 雾灯是否开启,或None
"""
app.logger.info("Starting to read fog light sensor")
result = cls.get_bool_pin(pin=23)
app.logger.debug(f"Fog: {result}")
app.logger.info("Finished reading fog light sensor")
return result
最后,在
data.py
的主路由中实现这些新功能:
reverse_light = Sensors.get_reverse_status()
fog_light = Sensors.get_fog_light_status()
result = {
"temperature": temperature,
"boot": boot_status,
"light": light_status,
"reverse": reverse_light,
"fog": fog_light,
}
通过以上步骤,我们完成了光线传感器、倒车传感器和雾灯传感器的开发与实现,利用树莓派实现了对汽车环境的全方位监测。
树莓派传感器开发:从光线到车灯的全方位监测
7. 各传感器开发流程总结
为了更清晰地了解整个传感器开发过程,下面用 mermaid 流程图展示从硬件连接到代码实现及测试的完整流程:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([开始]):::startend --> B(硬件连接):::process
B --> C{选择传感器类型}:::decision
C -->|光线传感器| D(连接LDR到树莓派):::process
C -->|倒车/雾灯传感器| E(连接汽车电路到树莓派):::process
D --> F(编写光线传感器测试代码):::process
E --> G(编写倒车/雾灯传感器测试代码):::process
F --> H(实现光线传感器逻辑代码):::process
G --> I(重构代码并实现倒车/雾灯传感器逻辑):::process
H --> J(运行光线传感器单元测试):::process
I --> K(运行倒车/雾灯传感器单元测试):::process
J --> L{光线测试是否通过}:::decision
K --> M{倒车/雾灯测试是否通过}:::decision
L -->|是| N(在主路由实现光线功能):::process
M -->|是| O(在主路由实现倒车/雾灯功能):::process
L -->|否| P(调试光线代码):::process
M -->|否| Q(调试倒车/雾灯代码):::process
P --> F
Q --> G
N --> R(进行光线功能测试):::process
O --> S(进行倒车/雾灯功能测试):::process
R --> T([结束光线传感器开发]):::startend
S --> U([结束倒车/雾灯传感器开发]):::startend
这个流程图清晰地展示了从硬件连接开始,到不同类型传感器的测试代码编写、逻辑代码实现、单元测试,再到功能测试的完整过程。如果测试不通过,会回到相应的测试代码编写阶段进行调试。
8. 传感器开发的关键技术点分析
-
测试驱动开发(TDD)
:TDD 是一种先编写测试代码,再实现功能代码的开发方式。在光线传感器开发中,我们先编写了多个单元测试,如
test_get_light_status_bad_pin_factory和test_get_light_status_values等,通过这些测试来驱动代码的实现。这种方式有助于我们提前考虑各种边界情况,使代码更加健壮,减少后期维护的成本。 -
异常处理
:在各个传感器的代码实现中,都进行了异常处理。例如在光线传感器的
get_light_status函数中,捕获了exc.BadPinFactory和其他异常,并记录相应的日志。这样可以确保在出现异常情况时,程序不会崩溃,而是能够给出相应的提示信息,提高了系统的稳定性。 -
代码重构
:在倒车和雾灯传感器开发中,我们进行了代码重构。创建了
get_bool_pin函数,将一些重复的异常处理和按钮状态读取逻辑封装起来,提高了代码的复用性。同时,将get_boot_status方法改为类方法,使其能够调用get_bool_pin函数,简化了代码结构。
9. 开发过程中的注意事项
- 硬件连接安全 :在连接汽车电路到树莓派时,一定要先断开树莓派的电源,避免短路损坏设备。同时,要使用合适的电阻和保险丝来保护树莓派,防止因汽车电路的高电压和大电流对树莓派造成损害。
- 电压确认 :在进行倒车和雾灯传感器开发时,必须准确确认汽车的电压。不同车型的电压可能不同,错误的电压可能导致电阻计算错误,从而影响传感器的正常工作,甚至损坏树莓派。
- 代码测试 :在编写代码后,一定要进行充分的单元测试和功能测试。单元测试可以确保每个函数的功能正确,功能测试可以验证整个系统的实际运行情况。如果测试不通过,要仔细检查代码逻辑和硬件连接,找出问题所在。
10. 总结
通过本次开发,我们利用树莓派成功实现了光线传感器、倒车传感器和雾灯传感器的功能。从硬件连接到代码实现,再到测试验证,每个环节都需要我们认真对待。在开发过程中,我们运用了测试驱动开发、异常处理和代码重构等技术,提高了代码的质量和系统的稳定性。
以下是各传感器开发的关键信息总结表格:
| 传感器类型 | 硬件连接要点 | 代码实现关键函数 | 测试重点 |
| ---- | ---- | ---- | ---- |
| 光线传感器 | 连接LDR到树莓派3.3V和GPIO 15引脚 |
get_light_status
| 边界值测试、异常处理测试 |
| 倒车传感器 | 连接汽车倒车灯+12V线路到树莓派BCM 22引脚,加电阻和保险丝 |
get_reverse_status
、
get_bool_pin
| 异常处理测试、状态读取测试 |
| 雾灯传感器 | 连接汽车雾灯+12V线路到树莓派BCM 23引脚,加电阻和保险丝 |
get_fog_light_status
、
get_bool_pin
| 异常处理测试、状态读取测试 |
未来,我们可以基于这些传感器开发更多的功能,如根据光线和倒车状态自动调整车内显示屏的亮度和显示内容,或者在雾灯开启时提供额外的安全提示等。通过不断的拓展和优化,让树莓派在汽车环境监测方面发挥更大的作用。
超级会员免费看
59

被折叠的 条评论
为什么被折叠?



