Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 线程只运行函数一次,并且只返回值一次?如何连续返回函数值?_Python_Multithreading_Sensors_Nonblocking - Fatal编程技术网

Python 线程只运行函数一次,并且只返回值一次?如何连续返回函数值?

Python 线程只运行函数一次,并且只返回值一次?如何连续返回函数值?,python,multithreading,sensors,nonblocking,Python,Multithreading,Sensors,Nonblocking,我有一个函数,从红外热像仪读取数据,处理数据并返回一个值。在下面的代码中,它返回检测到的最低温度。这个函数计算量很大,所以我想在一个单独的线程中运行它 在下面的示例中,我有一个启动线程的类。这只管用一次。它读取传感器并返回温度。但它再也不会运行该函数了。即使我更改传感器的输入,它也会不断返回相同的温度值。该函数在一个单独的程序中自行运行良好,并不断更新temp 我希望函数在线程中运行,因为我也在播放声音和控制LED 如何让函数在线程中多次运行,以便连续或定期在主线程中获取临时值 我尝试使用线程类

我有一个函数,从红外热像仪读取数据,处理数据并返回一个值。在下面的代码中,它返回检测到的最低温度。这个函数计算量很大,所以我想在一个单独的线程中运行它

在下面的示例中,我有一个启动线程的类。这只管用一次。它读取传感器并返回温度。但它再也不会运行该函数了。即使我更改传感器的输入,它也会不断返回相同的温度值。该函数在一个单独的程序中自行运行良好,并不断更新temp

我希望函数在线程中运行,因为我也在播放声音和控制LED

如何让函数在线程中多次运行,以便连续或定期在主线程中获取临时值

我尝试使用线程类,但一定是做错了什么。我还尝试使用队列,但从未得到任何要返回的数据

import queue
import sys
import pygame
import cv2
import random
import math
import colorsys
import time
from rpi_ws281x import *
from PIL import Image
import numpy as np
import threading

sys.path.insert(0, "/home/pi/irpython/build/lib.linux-armv7l-3.5")
import MLX90640 as mlx

# IR Function

def irCounter():
  while True:  
        img = Image.new( 'L', (24,32), "black") # make IR image

        mlx.setup(8) #set frame rate of MLX90640

        f = mlx.get_frame()

        mlx.cleanup()     

        for x in range(24):
            row = []
            for y in range(32):
                val = f[32 * (23-x) + y]
                row.append(val)
                img.putpixel((x, y), (int(val)))

        # convert raw temp data to numpy array
        imgIR = np.array(img)

        ## Threshold the -40C to 300 C temps to a more human range
        # Sensor seems to read a bit cold, calibrate in final setting
        rangeMin = 6 # low threshold temp in C
        rangeMax = 20 # high threshold temp in C

        # Apply thresholds based on min and max ranges
        depth_scale_factor = 255.0 / (rangeMax-rangeMin)
        depth_scale_beta_factor = -rangeMin*255.0/(rangeMax-rangeMin)

        depth_uint8 = imgIR*depth_scale_factor+depth_scale_beta_factor
        depth_uint8[depth_uint8>255] = 255
        depth_uint8[depth_uint8<0] = 0
        depth_uint8 = depth_uint8.astype('uint8')

        # increase the 24x32 px image to 240x320px for ease of seeing
        bigIR = cv2.resize(depth_uint8, dsize=(240,320), interpolation=cv2.INTER_CUBIC)

        # Normalize the image
        normIR = cv2.normalize(bigIR, bigIR, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)

        # Use a bilateral filter to blur while hopefully retaining edges
        brightBlurIR = cv2.bilateralFilter(normIR,9,150,150)

        # Threshold the image to black and white 
        retval, threshIR = cv2.threshold(brightBlurIR, 210, 255, cv2.THRESH_BINARY)

        # Define kernal for erosion and dilation and closing operations
        kernel = np.ones((5,5),np.uint8)

        erosionIR = cv2.erode(threshIR,kernel,iterations = 1)

        dilationIR = cv2.dilate(erosionIR,kernel,iterations = 1)

        closingIR = cv2.morphologyEx(dilationIR, cv2.MORPH_CLOSE, kernel)

        # Detect countours
        contours, hierarchy = cv2.findContours(closingIR, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

        # Get the number of contours ( contours count when touching edge of image while blobs don't)
        #ncontours = str(len(contours))
        ncontours = max(f)

        # Show images in window during testing
        #cv2.imshow("Combined", closingIR)

        return ncontours
        cv2.waitKey(1)

#initialize pygame
pygame.init()
pygame.mixer.init()
pygame.mixer.set_num_channels(30)
print("pygame initialized")

# assign sound chennels for pygame
channel0 = pygame.mixer.Channel(0)
channel1 = pygame.mixer.Channel(1)
channel2 = pygame.mixer.Channel(2)
channel3 = pygame.mixer.Channel(3)
channel4 = pygame.mixer.Channel(4)


# load soundfiles
echoballs = pygame.mixer.Sound("echo balls bounce.ogg")
organbounce = pygame.mixer.Sound("ORGAN BOUNCE.ogg")
jar = pygame.mixer.Sound("jar.ogg")
garland = pygame.mixer.Sound("GARLAND.ogg")
dribble= pygame.mixer.Sound("dribble.ogg")

# initializing sounds list  
soundsList = [echoballs, organbounce, jar, garland, dribble]
# use random.sample() to shuffle sounds list 
shuffledSounds = random.sample(soundsList, len(soundsList))

IRcount = 0 # for testing only

pygame.display.set_mode((32, 8)) # need display for keyboard input

# LED strip configuration:
LED_COUNT      = 256      # Number of LED pixels.
LED_PIN        = 18      # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN        = 10     # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
LED_DMA        = 10      # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 100     # Set to 0 for darkest and 255 for brightest
LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL    = 0       # set to '1' for GPIOs 13, 19, 41, 45 or 53

# Define functions which animate LEDs in various ways.
plasmaTime = 0.0 # time
plasmaSpeed = 0.5 # speed of time

def sineLED1 ():
    h = 8
    w = 32
    out = [ Color( 0, 0, 0 ) for x in range( h * w ) ]
    plasmaBright = 100.0
    for x in range( h ):
        for y in range( w ):
            hue = ((128+(128*math.sin(y + plasmaTime/ 8))))/256
            hsv = colorsys.hsv_to_rgb(.5, 1,hue )
            if y % 2 == 0: #even
                out[ x + (h * y)] = Color( *[ int( round( c * plasmaBright ) ) for c in hsv ] )
            else: #odd
                out[ (y * h) + (h -1 -x) ] = Color( *[ int( round( c * plasmaBright ) ) for c in hsv ] )
    for i in range( 0, strip.numPixels(), 1 ):# iterate over all LEDs - range(start_value, end_value, step)
        strip.setPixelColor(i, out[ i ]) # set pixel to color in picture
    strip.show()


# Threading class to get temp from IR function
class TempTask:

    def __init__(self):
        self.ir_temp = 0
        self.thread = threading.Thread(target=self.update_temp)

    def update_temp(self):
        self.ir_temp = irCounter()

    def start(self):
        self.thread.start()

# Main program logic follows:
if __name__ == '__main__':

    # start thread
    task = TempTask()
    task.start()

    # Create NeoPixel object with appropriate configuration.
    strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
    # Intialize the library (must be called once before other functions).
    strip.begin()

    print ('Press Ctrl-C to quit.')

    try:
        while True:
            #simulate increase / decreat of people count from IRsensor for testing until irCounter function non-blocking
            events = pygame.event.get()
            for event in events:
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        IRcount -= 1
                        print(IRcount)
                    if event.key == pygame.K_RIGHT:
                        IRcount += 1
                        print(IRcount)
                break
            if IRcount == 0:
                print(task.ir_temp) # print temp from sensor, only prints first time function runs
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(0).get_busy() == False: channel0.play(shuffledSounds[0],loops = -1)     
            elif IRcount == 1:
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(1).get_busy() == False: channel1.play(shuffledSounds[1],loops = -1)
            elif IRcount == 2:
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(2).get_busy() == False: channel2.play(shuffledSounds[2],loops = -1)
            elif IRcount == 3:
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(3).get_busy() == False: channel3.play(shuffledSounds[3],loops = -1)
            elif IRcount == 4:
                sineLED1()
                if pygame.mixer.Channel(4).get_busy() == False: channel4.play(shuffledSounds[4],loops = -1)

    except KeyboardInterrupt:
       colorWipe(strip, Color(0,0,0), 1)
       pygame.mixer.stop()

非常感谢您对下一步尝试的任何帮助或建议。提前感谢。

要让它持续更新,您需要修改
testask
类,使其
update_temp()
方法包含一个循环,并向其添加
锁以控制对
ir_temp
属性的并发访问,从而允许多个线程安全地访问它

请注意,不清楚是否真的需要
,因为您只需读取主线程中的
testask
实例属性,而需要
update\u temp()
方法中的循环来继续运行
irCounter()
函数。您还可能希望更改
while True:
以引用另一个(附加)实例属性,该属性控制是否保持运行

补充说明:

不清楚为什么在
irCounter()
中有
while True:
循环,因为它在接近结尾处有as
return
,防止它多次迭代。这样做并不重要,但我建议您删除它

class TempTask:

    def __init__(self):
        self.ir_temp = 0
        self.lock = threading.Lock()  # ADDED
        self.thread = threading.Thread(target=self.update_temp)

    def update_temp(self):  # MODIFIED
        while True:
            with self.lock:
                self.ir_temp = irCounter()
            time.sleep(0.1)  # Polling delay.

    def start(self):
        self.thread.start()
除此之外,您还需要更改在主循环中读取共享属性的位置(请参见添加的代码行):


我怀疑您在工作的单独程序中是否有行
return
?正确,我在测试程序中没有该return调用。在红外热传感器算法测试程序中,我使用
cv2.imshow()
显示测试图像。我还将最低和最高温度打印到控制台。你认为返回是问题所在吗?是的,
return
语句中断了while循环,使线程无所事事。@JohanL我与
return
单独编写了一个新的测试程序,它可以不带线程地工作,并不断地发回新的临时数据。如果返回中断,如何将临时信息发送回主线程?我以前试过使用队列,但一直没有成功。听起来很奇怪。那个节目看起来怎么样?您是在重复调用函数还是
return
语句不在同一位置?lukecv:我不知道为什么
ir\u temp
似乎没有更新(由于明显的原因,我自己无法运行代码)。写这篇文章时,我怀疑
irCounter()
可能会阻塞主线程太长时间。您可以通过在战略位置添加一个或多个对它的
time.sleep()调用
来缓解这种情况,从而给另一个线程一个运行的机会。在Python中,线程实际上并不并发运行,有一种称为GIL(全局解释器锁)的东西阻止了它——它更像是协作多线程。如果您只想获得一个值,请尝试使用
多处理
。顺便说一句,
任务
(因此
任务.ir_temp
)是一个全局变量。P.S。您还可以尝试增加
update\u temp()
中的轮询延迟,该延迟控制调用
irCounter()
的频率。
ir\u temp
没有更新,因为我有一个哑打字
irtemp
。它现在更新,但仍然减慢程序的LED部分。我试图有策略地将
time.sleep()
添加到
irCounter()
函数中,但没有效果。我也试着经常做,但还是口吃。如果我从主线程中删除带有task.lock的
,它将使用
print(task.ir_temp)
打印temp,所有程序都会全速运行。既然我没有意识到task.ir\u temp
也是全球性的,我也可以这样做。通过使用task.lock删除
,这会导致问题吗?如果您所做的只是读取主线程中的最新值,则不需要使用
锁,最糟糕的情况是它可能不是最新值。如果我的答案解决了你的问题,请考虑接受它。看见
class TempTask:

    def __init__(self):
        self.ir_temp = 0
        self.lock = threading.Lock()  # ADDED
        self.thread = threading.Thread(target=self.update_temp)

    def update_temp(self):  # MODIFIED
        while True:
            with self.lock:
                self.ir_temp = irCounter()
            time.sleep(0.1)  # Polling delay.

    def start(self):
        self.thread.start()
     .
     .
     .
    try:
        while True:  # This while loop doesn't iterate - suggest removal.
            #simulate increase / decrease of people count from IRsensor for testing until irCounter function non-blocking
            events = pygame.event.get()
            for event in events:
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        IRcount -= 1
                        print(IRcount)
                    if event.key == pygame.K_RIGHT:
                        IRcount += 1
                        print(IRcount)
                break
            if IRcount == 0:
                with task.lock:  # ADDED.
                    print(task.ir_temp) # print temp from sensor
                sineLED1()
                plasmaTime = plasmaTime + plasmaSpeed  # increment time
                if pygame.mixer.Channel(0).get_busy() == False: channel0.play(shuffledSounds[0],loops = -1)
            elif IRcount == 1:
                sineLED1()
                   .
                   .
                   .