线程运行只运行一次,只返回一次值?如何连续返回功能值?

问题描述 投票:1回答:1

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

在下面的示例中,我有一个启动线程的类。这个工作一次。它读取传感器并返回温度。但它永远不会再次运行该功能。即使我将输入更改为传感器,它也会保持返回相同的临时值。该功能在单独的程序中可以正常工作,并不断更新临时值。

我希望函数在一个线程中运行,因为我也在播放声音和控制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()

我搜索了论坛并尝试了很多东西,但我不知道接下来要尝试什么。

这是上面完整代码的片段,我在其中创建线程类并启动线程

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()

非常感谢任何有关下一步尝试的帮助或建议。先感谢您。

python multithreading sensor nonblocking
1个回答
1
投票

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

注意,不清楚是否确实需要Lock,因为除了在主线程中读取TempTask实例的属性之外什么都不做 - 但是需要使用update_temp()方法中的循环来继续运行irCounter()函数。您还可能希望更改while True:以引用另一个(附加)实例属性,该属性控制是否继续运行。

附加说明:

目前还不清楚为什么你在while True:中有irCounter()循环,因为它在末端附近有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()

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

     .
     .
     .
    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()
                   .
                   .
                   .
© www.soinside.com 2019 - 2024. All rights reserved.