我具有从红外热像仪读取数据,处理数据并返回值的功能。在下面的代码中,它返回检测到的最低温度。此函数的计算强度很高,因此我想在单独的线程中运行它。
在下面的示例中,我有一个启动线程的类。这个工作一次。它读取传感器并返回温度。但是它再也不会运行该功能。即使我更改传感器的输入,它仍会返回相同的温度值。该功能可以在单独的程序中单独运行,并不断更新温度。
我希望函数在线程中运行,因为我也在播放声音并控制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()
非常感谢您对下一步的帮助或建议。预先谢谢你。
答案 0 :(得分: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()
.
.
.