Поток запускает функцию только один раз и возвращает значение только один раз? Как непрерывно возвращать значение функции? - PullRequest
1 голос
/ 25 марта 2019

У меня есть функция, которая считывает данные с инфракрасной тепловой камеры, обрабатывает данные и возвращает значение.В приведенном ниже коде возвращается минимальная обнаруженная температура.Эта функция требует значительных вычислительных ресурсов, поэтому я хочу запустить ее в отдельном потоке.

В приведенном ниже примере у меня есть класс, который запускает поток.Это работает один раз.Он читает датчик и возвращает темп.Но он никогда не запускает функцию снова.Он продолжает возвращать одно и то же значение температуры, даже если я изменяю вход датчика.Функция отлично работает сама по себе в отдельной программе и постоянно обновляет временные значения.

Я хочу, чтобы функция выполнялась в потоке, потому что я также играю звуки и управляю светодиодами.

Как мнезаставить функцию запускаться несколько раз в потоке, чтобы я мог непрерывно или периодически получать временные значения в основном потоке?

Я пытался использовать класс потока, но я должен что-то делать не так.Я также пытался использовать очередь, но никогда не получал никаких данных для возврата.

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

Любая помощь или предложения о том, что делать дальше, очень ценится.Заранее спасибо.

1 Ответ

1 голос
/ 25 марта 2019

Чтобы он постоянно обновлялся, вам нужно изменить класс 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()

В дополнение к этому вам необходимо изменить место чтения атрибута shared в основном цикле (см. Строку # 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()
                   .
                   .
                   .
...