首页 / 资讯中心 / 文章详情

树莓派实战:DHT11温控风扇与PWM调速

树莓派实战:DHT11温控风扇与PWM调速 ★ FEATURED ARTICLE
1. 项目缘起与整体设计思路1.1 为什么选择这个项目作为树莓派入门实战树莓派基础系列教程走到第七篇前面六篇基本把系统烧录、远程连接、GPIO引脚定义、Python基础操作这些前置知识铺完了。到了这个节点如果还停留在点亮一颗LED或者读一个按键状态说实话有点对不起手里这块板子。自动温控小风扇这个项目恰好是一个能把前面所有零散知识点串起来的综合性实战——它涉及传感器数据采集、条件判断逻辑、PWM调速控制、以及硬件电路的基本搭建。我当初选这个题目给身边几个刚入门的朋友练手反馈出奇地一致看起来简单真动手做的时候坑不少。比如有人把三极管引脚顺序搞反了导致风扇死活不转有人读出来的温度永远是85度还有人PWM频率设错了导致风扇发出刺耳的啸叫。这些问题在纯软件项目里很少遇到但一旦涉及硬件和物理世界打交道各种意外就会冒出来。所以这个项目的价值不在于功能多复杂而在于它能让你完整体验一次“从传感器到执行器”的闭环控制流程。从应用场景来说自动温控风扇的实用性其实很强。你可以把它放在弱电箱里给路由器散热可以放在书桌上给树莓派自身降温甚至可以改装成宠物窝的通风系统。核心逻辑就一句话温度高了就转温度低了就停中间地带用PWM平滑过渡。但要把这句话变成稳定运行的系统需要处理传感器通信、GPIO电平匹配、风扇驱动能力、软件消抖等一系列工程细节。这个项目适合谁如果你已经能用Python在树莓派上跑脚本知道GPIO.setmode和GPIO.setup是干什么的但还没做过一个完整的硬件控制项目那这个内容就是为你准备的。如果你连树莓派系统都还没装好建议先翻前面的基础篇否则后面讲PWM占空比的时候你会一脸茫然。1.2 系统架构与核心元件选型逻辑整个系统的架构可以拆成三层感知层、决策层、执行层。感知层负责采集环境温度决策层根据温度值判断是否需要启动风扇以及以多大功率运行执行层负责驱动风扇电机转动。这三层之间通过树莓派的GPIO接口和Python程序串联起来。感知层我选的是DHT11温湿度传感器。有人会问为什么不选DS18B20或者BMP280。DHT11的精度确实一般温度误差在正负2摄氏度左右湿度误差在正负5%但对于风扇控制这种场景完全够用。它的优势在于单总线通信只需要一个GPIO引脚就能同时读温度和湿度接线极其简单而且价格便宜坏了换一个不心疼。DS18B20精度更高但只测温度BMP280精度高但需要I2C接线且价格贵不少。对于入门项目DHT11的性价比和易用性是最优解。执行层我选的是5V直流小风扇配NPN三极管驱动。这里有个关键点树莓派GPIO引脚输出的高电平是3.3V而大多数5V风扇需要5V供电才能达到额定转速。直接用GPIO驱动风扇要么转不起来要么转速极低。所以必须用三极管做开关和放大。我选的是S8050 NPN三极管它的集电极电流最大能到700mA驱动一个额定电流200mA左右的小风扇绰绰有余。基极串联一个1kΩ电阻限流防止烧毁三极管或树莓派引脚。风扇两端并联一个续流二极管用来吸收电机断电时产生的反向电动势保护三极管不被击穿。决策层就是树莓派本身跑Python脚本。程序逻辑不复杂每隔两秒读一次温度如果温度超过阈值就启动风扇低于阈值就关闭中间设置一个回差区间防止频繁启停。进阶一点可以用PWM根据温度线性调节转速温度越高转得越快。PWM频率我建议设在100Hz到1kHz之间太低电机会抖动太高三极管开关损耗大且可能产生啸叫。实测下来200Hz左右比较平衡既听不到明显噪音电机运转也平稳。2. 硬件连接与核心细节解析2.1 元件清单与接线前的准备工作先把需要的元件列清楚免得做到一半发现少东西。树莓派任意型号一块建议用带40Pin GPIO的版本DHT11温湿度传感器一个注意买带PCB板载上拉电阻的模块版裸传感器需要自己加4.7kΩ到10kΩ的上拉电阻对新手不友好5V直流小风扇一个尺寸根据你的散热需求选一般4cm或5cm的够用S8050 NPN三极管一只1kΩ电阻一只1N4148或1N4007二极管一只面包板和杜邦线若干如果需要独立给风扇供5V电还要准备一个5V电源模块或者从树莓派5V引脚取电。接线之前有几个安全事项必须说清楚。第一树莓派的GPIO引脚是3.3V逻辑电平绝对不能直接往引脚上接5V电压否则会烧毁SoC。第二风扇如果额定电流超过500mA不要从树莓派的5V引脚取电因为树莓派板载的5V稳压电路输出能力有限大电流会导致树莓派重启或损坏。第三所有接线操作必须在树莓派断电状态下进行接好后再上电避免短路打火。DHT11模块一般有三个引脚VCC、DATA、GND。VCC接树莓派3.3V或5V都可以模块内部有稳压和电平转换的版本建议接5V裸传感器接3.3V更安全。DATA接任意一个GPIO我习惯用GPIO4也就是物理引脚第7号。GND接树莓派GND。风扇驱动部分风扇正极接5V电源正极负极接三极管集电极三极管发射极接GND基极通过1kΩ电阻接树莓派GPIO比如GPIO17也就是物理引脚第11号续流二极管并联在风扇两端注意二极管方向带条纹的一端接风扇正极侧也就是接5V那端这是关键接反了起不到保护作用。2.2 DHT11通信协议与读取稳定性优化DHT11用的是单总线协议一次完整通信大概需要4毫秒左右。树莓派发送一个至少18毫秒的低电平启动信号然后拉高20到40微秒等待传感器响应。传感器会先拉低80微秒再拉高80微秒作为应答接着连续输出40位数据高位在前。前16位是湿度整数和小数中间16位是温度整数和小数最后8位是校验和。校验和等于前32位数据相加取低8位。听起来不复杂但实际用Python读取的时候很容易失败。原因在于树莓派的Linux系统不是实时操作系统Python的time.sleep精度受系统调度影响可能在关键时刻被其他进程打断导致时序错乱。我试过直接用Adafruit_DHT库读取成功率大概在70%左右十次里有三次返回None。后来改用pigpio库的DHT11读取函数成功率提升到95%以上因为pigpio在底层用了DMA和硬件定时器不依赖Linux调度。如果你不想装额外库也可以用纯Python写读取逻辑但必须加异常处理和重试机制。我的做法是连续读三次取其中有效值的平均值如果三次都失败就跳过本次采集等下一轮再试。另外DHT11的采样率不能太高官方手册写的是最快1Hz也就是每秒最多读一次。你如果每100毫秒读一次它根本来不及完成转换读出来的全是错误值。我一般设2秒读一次既给传感器留足转换时间也避免频繁读写占用CPU。还有一个坑是DHT11上电后需要1秒左右的稳定时间刚上电立刻读大概率失败。所以程序启动后先sleep 2秒再开始采集。另外传感器附近如果有发热元件或者被阳光直射读出来的温度会偏高安装位置要避开这些干扰源。2.3 三极管驱动电路参数计算与选型验证三极管驱动电路的设计核心是让三极管工作在饱和导通状态也就是把它当成一个可控开关来用而不是放大器。S8050的直流电流增益hFE典型值在120到400之间我们取最保守的100来计算。风扇额定电流假设为200mA那么基极电流至少需要200mA除以100等于2mA。树莓派GPIO在高电平状态下输出3.3V三极管基极到发射极的导通压降约0.7V所以基极电阻上的电压是3.3V减去0.7V等于2.6V。根据欧姆定律基极电阻等于2.6V除以2mA等于1300Ω。我选1kΩ对应基极电流2.6mA比最低要求大30%确保三极管深度饱和导通压降更小发热更少。有人会问能不能用MOSFET代替三极管。可以而且MOSFET是电压驱动不需要基极电流逻辑电平MOSFET如2N7000或者IRLZ44N可以直接用3.3V驱动。但MOSFET的栅极电容会导致开关瞬间有尖峰电流需要串联一个小电阻限流而且静电敏感新手操作不当容易击穿。三极管虽然需要计算基极电阻但皮实耐用更适合入门。续流二极管的选择也有讲究。1N4148是信号二极管额定电流200mA刚好卡在风扇电流的临界点长期用余量不足。1N4007是整流二极管额定电流1A耐压1000V余量充足价格也差不多所以我更推荐1N4007。二极管的方向绝对不能接反带银色条纹的一端是阴极要接在风扇正极和5V电源之间。如果接反了相当于在风扇两端并联了一个正向导通的二极管一上电就会短路可能烧毁电源或三极管。3. 软件实现与PWM调速实战3.1 基础版温控逻辑阈值开关控制先写一个最简版本不涉及PWM只做温度阈值判断。程序流程是这样的初始化GPIO模式为BCM设置风扇控制引脚为输出模式设置DHT11数据引脚为输入模式。然后进入主循环每隔2秒读一次温度。如果温度大于等于30摄氏度风扇引脚输出高电平风扇全速转动如果温度小于等于28摄氏度风扇引脚输出低电平风扇停止。28到30之间保持当前状态不变这就是回差控制防止温度在阈值附近波动时风扇频繁启停。回差的概念很重要。假设你只设一个阈值30度温度在29.9和30.1之间跳动时风扇就会一会儿开一会儿关不仅噪音烦人还容易损坏电机和驱动电路。加了2度的回差之后温度必须降到28度以下风扇才会停升到30度以上才会开中间有4度的缓冲区间运行就稳定多了。回差的大小根据你的实际场景调整如果环境温度变化剧烈回差可以设大一点比如3到5度。代码里读取DHT11的部分要加try-except包裹因为传感器偶尔读失败是正常现象。读失败时不要改变风扇状态继续用上一次的有效温度值做判断或者直接跳过本次循环。我习惯维护一个last_valid_temp变量每次读取成功就更新它读取失败就用旧值。这样即使传感器偶尔抽风风扇也不会突然停转或狂转。import RPi.GPIO as GPIO import time import Adafruit_DHT FAN_PIN 17 DHT_PIN 4 TEMP_HIGH 30 TEMP_LOW 28 GPIO.setmode(GPIO.BCM) GPIO.setup(FAN_PIN, GPIO.OUT) GPIO.output(FAN_PIN, GPIO.LOW) last_valid_temp 25.0 fan_state False try: while True: humidity, temperature Adafruit_DHT.read_retry(Adafruit_DHT.DHT11, DHT_PIN) if temperature is not None: last_valid_temp temperature else: temperature last_valid_temp if temperature TEMP_HIGH and not fan_state: GPIO.output(FAN_PIN, GPIO.HIGH) fan_state True elif temperature TEMP_LOW and fan_state: GPIO.output(FAN_PIN, GPIO.LOW) fan_state False time.sleep(2) except KeyboardInterrupt: GPIO.cleanup()这段代码跑起来之后你可以用手捂住DHT11或者用吹风机开热风档远远吹一下观察风扇是否启动。注意吹风机不要靠太近否则温度瞬间飙升可能损坏传感器。正常室温下如果风扇不转你可以把TEMP_HIGH临时改成26度测试一下确认硬件没问题再改回来。3.2 进阶版PWM调速让风扇转速随温度线性变化开关控制只能让风扇全速转或者完全不转体验比较粗糙。PWM调速可以让风扇在低温时慢转高温时快转既省电又安静。树莓派的RPi.GPIO库自带PWM功能但它的PWM是通过软件定时器实现的精度一般而且当CPU负载高时会出现抖动。如果你对调速平滑度要求高可以用pigpio库的硬件PWM树莓派所有GPIO都支持硬件PWM频率和占空比都非常稳定。PWM的两个关键参数是频率和占空比。频率决定开关的快慢占空比决定在一个周期内高电平所占的比例。占空比0%就是全关100%就是全开50%就是半速。对于风扇电机PWM频率建议在100Hz到1kHz之间。太低电机会听到明显的嗡嗡声太高三极管开关损耗增加且可能产生人耳能听到的啸叫。我实测200Hz到500Hz之间比较理想风扇运转平滑噪音也在可接受范围。温度到占空比的映射逻辑可以这样设计设一个最低启动温度T_min和一个最高温度T_max。温度低于T_min时占空比为0风扇不转温度高于T_max时占空比为100风扇全速中间线性插值。比如T_min等于28度T_max等于38度那么温度33度时占空比就是50%。这样温度每升高1度占空比增加10%转速变化很平滑。import RPi.GPIO as GPIO import time import Adafruit_DHT FAN_PIN 17 DHT_PIN 4 T_MIN 28.0 T_MAX 38.0 PWM_FREQ 200 GPIO.setmode(GPIO.BCM) GPIO.setup(FAN_PIN, GPIO.OUT) pwm GPIO.PWM(FAN_PIN, PWM_FREQ) pwm.start(0) last_valid_temp 25.0 def temp_to_duty(temp): if temp T_MIN: return 0 elif temp T_MAX: return 100 else: return (temp - T_MIN) / (T_MAX - T_MIN) * 100 try: while True: humidity, temperature Adafruit_DHT.read_retry(Adafruit_DHT.DHT11, DHT_PIN) if temperature is not None: last_valid_temp temperature else: temperature last_valid_temp duty temp_to_duty(temperature) pwm.ChangeDutyCycle(duty) time.sleep(2) except KeyboardInterrupt: pwm.stop() GPIO.cleanup()这里有个细节要注意RPi.GPIO的PWM在占空比设为0时引脚并不是完全输出低电平而是输出一个极窄的脉冲可能导致风扇在“关闭”状态下微微抖动或者发出微弱声音。如果你遇到这个问题可以在占空比为0时手动调用GPIO.output(FAN_PIN, GPIO.LOW)把引脚拉低需要启动时再切回PWM模式。或者直接用pigpio库它的set_PWM_dutycycle函数在占空比为0时就是纯低电平没有残留脉冲。3.3 数据记录与可视化扩展基础功能跑通之后可以加一点扩展功能让项目更有实用价值。比如把温度数据记录到CSV文件然后用matplotlib画一张温度变化曲线图。这样你可以直观看到风扇启动后温度下降的效果也能分析房间的温度变化规律。记录数据的逻辑很简单每次读取成功后把时间戳和温度值追加写入文件。注意文件不要一直开着每次写入后关闭避免程序异常退出时数据丢失。时间戳用datetime模块生成格式化成“年-月-日 时:分:秒”的字符串。CSV文件放在树莓派用户目录下比如/home/pi/temp_log.csv。可视化可以用matplotlib的pyplot模块读取CSV文件后画折线图横轴是时间纵轴是温度再画一条水平虚线标出风扇启动阈值。如果你想让图表实时更新可以用matplotlib的动画功能但那个比较吃CPU树莓派上跑起来会有点卡。我建议先记录一段时间的数据然后单独跑一个脚本生成图表这样更稳定。还有一个扩展方向是加一个OLED小屏幕实时显示当前温度和风扇转速百分比。SSD1306驱动的128x64 OLED通过I2C连接只需要两根线用luma.oled库驱动显示效果很清晰。这样即使不连电脑你也能直接看到系统运行状态像个小型气象站。4. 常见问题排查与避坑经验实录4.1 温度读数异常问题速查现象可能原因排查方法解决方案温度始终显示85度DHT11通信失败返回默认值检查DATA引脚是否接对上拉电阻是否缺失换用带板载上拉的模块或外接4.7kΩ上拉电阻到3.3V温度读数偶尔为None时序受系统调度干扰观察失败频率查看系统负载改用pigpio库读取或增加重试次数温度明显偏高传感器靠近发热源用手触摸传感器周围是否有温热元件移开传感器远离树莓派CPU和电源模块湿度正常但温度不变传感器损坏或引脚虚接更换传感器测试检查杜邦线是否松动更换DHT11焊接接线代替杜邦线读数跳动剧烈电源纹波干扰用万用表测传感器VCC电压是否稳定在VCC和GND之间并联100nF电容滤波DHT11的85度问题我遇到过不下五次基本都是通信失败导致的。传感器在读取失败时会返回一个固定的默认值温度部分就是85度。所以你在代码里看到85这个数字第一反应就应该是通信有问题而不是环境真的那么热。检查顺序是先看接线再看上拉电阻最后看库的兼容性。4.2 风扇不转或转速异常的排查思路风扇完全不转先别急着怀疑程序。拿万用表测三极管集电极和发射极之间的电压如果风扇启动时这个电压接近0V说明三极管已经饱和导通问题在风扇本身或者5V供电。如果电压接近5V说明三极管没导通问题在基极驱动信号或者三极管本身。基极驱动信号可以用万用表测基极对地电压。风扇启动时基极电压应该在0.7V左右如果测出来是0V说明树莓派GPIO没有输出高电平回去检查代码里引脚编号对不对GPIO.setup有没有设成OUT。如果基极电压正常但三极管不导通可能是三极管引脚顺序搞错了。S8050的引脚排列是面对印字面从左到右依次为发射极、基极、集电极但不同厂家封装可能不同最好用万用表二极管档测一下确认。风扇转速异常主要表现为两种一种是转速明显偏慢另一种是转速忽快忽慢。转速偏慢通常是供电电压不足如果你从树莓派5V引脚取电而风扇额定电流又比较大树莓派板载稳压芯片可能撑不住电压被拉低到4V甚至更低。解决办法是给风扇单独供电或者换一个电流小一点的风扇。转速忽快忽慢多半是PWM频率设置不当试着把频率调到200Hz到500Hz之间再观察。还有一个容易被忽略的问题是续流二极管接反。接反之后风扇一启动电源就短路轻则树莓派重启重则烧毁三极管。如果你上电后发现树莓派反复重启或者电源指示灯变暗立刻断电检查二极管方向。4.3 程序稳定性与长期运行注意事项树莓派上跑长期任务有几个坑必须提前避开。第一是内存泄漏Python脚本如果一直往列表里追加数据而不清理跑几天内存就满了。我的做法是只保留最近100条温度记录在内存里更早的数据直接写文件不留在内存。第二是日志文件无限增长如果你开了logging模块写文件记得加RotatingFileHandler限制单个文件大小和备份数量否则SD卡很快被写满。第三是异常处理要全面。除了KeyboardInterrupt还要捕获IOError和RuntimeError因为GPIO操作在系统资源紧张时可能抛异常。捕获之后不要直接pass至少打印一条日志方便事后排查。第四是开机自启动用systemd写一个service文件比rc.local靠谱得多rc.local在新版系统里默认不启用而且启动顺序不好控制。systemd可以设置Afternetwork.target和Restarton-failure程序崩溃后自动重启。最后说一个我踩过的坑树莓派在夏天高温环境下自身会降频保护如果你把风扇控制程序跑在树莓派上而树莓派又放在密闭空间里可能出现树莓派温度比环境温度还高的情况导致风扇一直全速转。解决办法是把树莓派和传感器分开放置传感器放在需要监测的位置树莓派放在通风处。或者干脆用两个温度传感器一个测环境一个测树莓派自身取平均值或者最大值做控制依据。4.4 硬件保护与电气安全经验三极管驱动感性负载时关断瞬间产生的反向电动势可能达到电源电压的好几倍。虽然续流二极管能吸收大部分能量但如果风扇功率较大或者导线较长仍然可能有尖峰残留。我习惯在三极管集电极和发射极之间再并联一个TVS二极管钳位电压选比电源电压高20%左右的型号比如5V系统选6.8V的TVS。这样双重保护三极管基本不会坏。面包板接线在长期运行中容易松动尤其是风扇这种有振动的负载。如果你打算让系统连续跑几周以上建议把驱动电路焊接到洞洞板上用排针和杜邦线连接树莓派。焊接时注意三极管和二极管的方向焊完用万用表通断档检查一遍有没有虚焊和短路。树莓派的GPIO引脚驱动能力有限单个引脚最大输出电流官方建议不超过16mA所有引脚总和不超过100mA。三极管基极电流2.6mA远低于这个限制所以安全。但如果你直接驱动多个风扇或者大功率继电器模块就要算一下总电流必要时用ULN2003这类达林顿阵列芯片做缓冲。5. 项目扩展方向与个人实操体会这个项目做完基础版之后可扩展的空间其实很大。比如加一个红外接收头用遥控器手动切换自动模式和手动模式加一个蜂鸣器温度超过警戒线时发出报警加一个WiFi模块或者用树莓派自带的无线网卡把温度数据上传到本地服务器用手机浏览器随时查看。这些扩展每一个都能让你学到新的知识点而且都是围绕同一个核心项目展开的不会觉得东一榔头西一棒子。我个人在实际操作中的体会是硬件项目最花时间的往往不是写代码而是排查硬件问题。代码逻辑错了你看一眼日志就知道哪里不对但硬件问题比如一个引脚虚焊或者一个电阻阻值不对你可能要花半小时才能定位。所以我的建议是每接完一根线就用万用表确认一下通断每焊完一个点就检查一下有没有短路。前期多花五分钟检查后期能省两小时debug。另外DHT11这个传感器虽然便宜好用但它的长期稳定性一般。我有个项目连续跑了三个月DHT11的湿度读数开始漂移温度倒是还算准。如果你对精度要求高可以考虑换成SHT30或者AHT20I2C接口精度高一个档次价格也就贵几块钱。树莓派的I2C引脚是固定的接线比DHT11的单总线还简单代码用现成的库几行就能跑起来。最后再分享一个小技巧调试PWM调速的时候不要一上来就接风扇先接一个LED和限流电阻。LED的亮度变化能直观反映占空比的变化而且LED不会像电机那样产生反向电动势和电磁干扰可以帮你排除驱动电路的问题。等LED调光逻辑跑通了再换成风扇这样排查问题的维度就少了一个效率高很多。
阅读完成 · 觉得有帮助?
咨询建站