jiaxv 发表于 2016-03-27 17:13

求助: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多线程的例子,也做了些试验,但一直也没能理出个头绪,所以还请诸位大侠打个帮手,毕竟已经答应了孩子,而且孩子都给小伙伴们许出赠送小鸡的承诺了。
火线求助!万分感谢!!!

Windows19 发表于 2016-03-28 10:20

进来看看后,感觉很厉害的样子,看来楼主是搞科研的,这么有科学性,意义性的实验,在此预祝楼主取得成功哈。
但这个py版块,很多真正py高手估计都很少时间上来,楼主可能要耐心点等待一下py高手到来

amduroncn 发表于 2016-03-28 15:18

本帖最后由 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()

jiaxv 发表于 2016-03-28 22:43

回复 2# Windows19

谢谢您的关注,厉害可不敢当,接触python的时间不多,所以有点一筹莫展了。
   

jiaxv 发表于 2016-03-28 22:49

回复 3# amduroncn


非常感谢您如此具体的指教和帮助!我这就去试试看。关于python的多线程编程,我还只停留在start,join和setdeamon等函数上,希望能进一步请教您。谢谢!

amduroncn 发表于 2016-03-29 00:21

本帖最后由 amduroncn 于 2016-03-29 00:30 编辑

不用客气,我也是初学。从你的描叙来看,6秒就会使温度超出范围,可见你的发热丝,功率貎似得大啊。何不使用PWM控制发热丝,发热丝可以用低压的发热丝(安全),结合PID控制,可把温度做得很恒定哟。你现在的方案,继电器会经常动作吗?

bskay 发表于 2016-03-29 15:06

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秒

amduroncn 发表于 2016-03-29 19:53

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:16

本帖最后由 bskay 于 2016-03-30 11:29 编辑

回复 8# amduroncn


主线程一个loop下来,就会有一个进程被产生出来(<=5)基本上瞬间就会使进程池满掉,造成主线程阻塞。

最好先实地验证一下,验证过是没有关系的


if old == (temp_lists,temp_avg):
            continue

只是相同的不去显示,因为前一次的显示过程可能还没有完成,没有什么影响(逻辑上肯定没有问题的,如果长时间保持恒温..做不做都没有关系的)

amduroncn 发表于 2016-03-30 13:33

回复 9# bskay


    以前看到apply_async说是池满了,会阻塞主线程。实验了一下不会。我说错了。这个方法可行,只要将POOL(1)即可。
页: [1] 2
查看完整版本: 求助:python树莓派多线程编程