首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >线程只运行函数一次,并且只返回一次值?如何连续返回函数值?

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

Stack Overflow用户
提问于 2019-03-25 19:19:51
回答 1查看 121关注 0票数 1

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

在下面的例子中,我有一个启动线程的类。这只有效一次。它读取传感器并返回温度。但是它再也不会运行这个函数了。即使我更改了传感器的输入,它也始终返回相同的临时值。该函数在单独的程序中可以很好地工作,并不断更新临时数据。

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

如何让函数在线程中多次运行,这样才能连续或定期地获取主线程中的临时值?

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

代码语言:javascript
复制
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()

我已经搜索了论坛,尝试了很多东西,但我不知道下一步该怎么做。

下面是来自上面的完整代码的代码片段,其中我创建了thread类并启动了线程

代码语言:javascript
复制
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()

任何关于下一步尝试的帮助或建议都是非常感谢的。提前谢谢你。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-25 22:11:23

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

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

其他注意事项:

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

代码语言:javascript
复制
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()

除此之外,您还需要更改在主循环中读取共享属性的位置(参见# ADDED行):

代码语言:javascript
复制
     .
     .
     .
    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()
                   .
                   .
                   .
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55336644

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档