求助:python树莓派多线程编程
小弟初接触树莓派,想给孩子做一个孵化小鸡的孵化箱。构想是用5个18b20温度传感器分布于箱子内不同位置读取箱内温度,根据读取到的温度来通过gpio引脚控制继电器线包,继而控制电热丝加热,或者控制风扇来降温。如果箱内温度在设定范围内,则保持恒温(不加热也不降温)。同时还有lcd1602用来显示孵化时间/剩余时间等,7段数码管组依次显示5个温度值,三色led显示加热/降温/恒温状态,以及定时写入web页,开启一个简单web server,可以通过电脑手机等远程查看状态。主控程序结构大概如下:main():
while 1:
temp_lists=temp_get()//读取传感器的值到一个list
temp_avg=get_avg(temp_lists)//计算温度平均值
if (temp_avg>temp_max):
dis_hot() //若平均值大于设定上限,则持续降温
elif (temp_avg<temp_min):
hot() //若平均值小于下限,则持续加热
else:
keep_warming() //否则保温(不加热也不降温)
temp_display() //在数码管上依次显示5个传感器温度以及平均温度,每秒1个,共耗时6秒
初步的程序已经调试通过,基本运行正常,但发现一个致命的问题是:数码管显示子程序因为要每隔1秒依次显示5组温度和平均温度,所以这个子程序需要花费6秒的时间。在这个子程序6秒的执行过程中,加热或降温子程序已经持续运行了6秒,有可能会使温度大大超出设定范围。所以想要把这个耗时较多的温度显示子程序设为多线程并发执行,使得它不要阻塞主程序中的其他部分。
我在网上看了不少关于python多线程的例子,也做了些试验,但一直也没能理出个头绪,所以还请诸位大侠打个帮手,毕竟已经答应了孩子,而且孩子都给小伙伴们许出赠送小鸡的承诺了。
火线求助!万分感谢!!! 进来看看后,感觉很厉害的样子,看来楼主是搞科研的,这么有科学性,意义性的实验,在此预祝楼主取得成功哈。
但这个py版块,很多真正py高手估计都很少时间上来,楼主可能要耐心点等待一下py高手到来 本帖最后由 amduroncn 于 2016-03-28 16:14 编辑
你应该建立一个线程同步事件,一个资源为1临界资源的访问的信号量
主线程 申请能临界区的使用-》测温》释放临界区访问-》发通知-》-----loop
显示线程 收通知-》重置事件-->申请临界区使用权-》快速复制一个副本-》释放临界区资源使用-》显示---loop
祝你小朋友成功孵出小鸡!
以下是代码# -*- coding: utf-8 -*-
import threading
import time
import random
# 建立一个继承threading的线程实现类
temp_lists = []
class DisplayClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
#初始化各种变量
def run(self):
while 1:
ready.wait()# 等待测温事件完成通知
ready.clear()# 重置事件
sep.acquire()# 对临界区访问,速度复制一个副本,
tmplists = temp_lists
tmpavg = temp_avg
sep.release()# 释放临界区访问
print(u"显示温度"+str(tmplists)+ u",平均温度:"+str(tmpavg))
time.sleep(6)
def temp_get():
# random.random()
temp_lists.append(random.random()*65)
if len(temp_lists) > 6:
temp_lists.pop(0)
return temp_lists
def get_avg(l):
return sum(l)/len(l)
def dis_hot():
pass
def keep_warming():
pass
def hot():
pass
def main():
global temp_lists
global temp_avg
global ready
global sep;
temp_max = 40
temp_min = 35
ready = threading.Event()
sep = threading.Semaphore(1)
ready.clear()
threaddisdis = DisplayClass()
threaddisdis.start()
while 1:
sep.acquire()
temp_lists = temp_get()# 读取传感器的值到一个list
temp_avg = get_avg(temp_lists)# 计算温度平均值
sep.release()
# 发出通知显示线程可以执行
ready.set()
if temp_avg > temp_max:
dis_hot()# 若平均值大于设定上限,则持续降温
print(u'降温')
elif temp_avg < temp_min:
hot()# 若平均值小于下限,则持续加热
print(u'加热')
else:
keep_warming() # 否则保温(不加热也不降温)
print(u'保温')
time.sleep(1)
if __name__ == "__main__":
main() 回复 2# Windows19
谢谢您的关注,厉害可不敢当,接触python的时间不多,所以有点一筹莫展了。
回复 3# amduroncn
非常感谢您如此具体的指教和帮助!我这就去试试看。关于python的多线程编程,我还只停留在start,join和setdeamon等函数上,希望能进一步请教您。谢谢! 本帖最后由 amduroncn 于 2016-03-29 00:30 编辑
不用客气,我也是初学。从你的描叙来看,6秒就会使温度超出范围,可见你的发热丝,功率貎似得大啊。何不使用PWM控制发热丝,发热丝可以用低压的发热丝(安全),结合PID控制,可把温度做得很恒定哟。你现在的方案,继电器会经常动作吗? import multiprocessing
def main():
pool = multiprocessing.Pool(5)
old = 0,0
while 1:
temp_lists=temp_get()#读取传感器的值到一个list
temp_avg=get_avg(temp_lists)#计算温度平均值
if (temp_avg>temp_max):
dis_hot() #若平均值大于设定上限,则持续降温
elif (temp_avg<temp_min):
hot() #若平均值小于下限,则持续加热
else:
keep_warming() #否则保温(不加热也不降温)
if old == (temp_lists,temp_avg):
continue
old = (temp_lists,temp_avg) #控制一下不要做可有可无的调用
pool.apply_async(temp_display)
##temp_display() #在数码管上依次显示5个传感器温度以及平均温度,每秒1个,共耗时6秒
bskay 发表于 2016-03-29 15:06 static/image/common/back.gif
import multiprocessing
def main():
说下这个思路的几个问题
1数码管与液晶不同,它是一个需要不停扫描才能显示显示的器件,如果几个温度相同,就不进行显示,数码管会没有输出。当然1602是可以正常显示的。
所以
if old == (temp_lists,temp_avg):
continue
old = (temp_lists,temp_avg) #控制一下不要做可有可无的调用
对数码管来说是错误的,它会导致数码管无显示。
2.bskay使用了进程池,这可以充分利用多核cpu的各个核心,但bskay开了5个进程,这是没必要的且是错误的。主线程的一个loop是很快的(不加sleep,测5个18b20,最多ms级吧),而显示进程是很慢的,主线程一个loop下来,就会有一个进程被产生出来(<=5)基本上瞬间就会使进程池满掉,造成主线程阻塞。
而且依楼主意思楼主只有一个数码管,是依次做的扫描。btw,你得把数据传进去,否则进程显示什么呢? 本帖最后由 bskay 于 2016-03-30 11:29 编辑
回复 8# amduroncn
主线程一个loop下来,就会有一个进程被产生出来(<=5)基本上瞬间就会使进程池满掉,造成主线程阻塞。
最好先实地验证一下,验证过是没有关系的
if old == (temp_lists,temp_avg):
continue
只是相同的不去显示,因为前一次的显示过程可能还没有完成,没有什么影响(逻辑上肯定没有问题的,如果长时间保持恒温..做不做都没有关系的)
回复 9# bskay
以前看到apply_async说是池满了,会阻塞主线程。实验了一下不会。我说错了。这个方法可行,只要将POOL(1)即可。
页:
[1]
2