Python 线程只运行函数一次,并且只返回值一次?如何连续返回函数值?
我有一个函数,从红外热像仪读取数据,处理数据并返回一个值。在下面的代码中,它返回检测到的最低温度。这个函数计算量很大,所以我想在一个单独的线程中运行它 在下面的示例中,我有一个启动线程的类。这只管用一次。它读取传感器并返回温度。但它再也不会运行该函数了。即使我更改传感器的输入,它也会不断返回相同的温度值。该函数在一个单独的程序中自行运行良好,并不断更新temp 我希望函数在线程中运行,因为我也在播放声音和控制LED 如何让函数在线程中多次运行,以便连续或定期在主线程中获取临时值 我尝试使用线程类,但一定是做错了什么。我还尝试使用队列,但从未得到任何要返回的数据Python 线程只运行函数一次,并且只返回值一次?如何连续返回函数值?,python,multithreading,sensors,nonblocking,Python,Multithreading,Sensors,Nonblocking,我有一个函数,从红外热像仪读取数据,处理数据并返回一个值。在下面的代码中,它返回检测到的最低温度。这个函数计算量很大,所以我想在一个单独的线程中运行它 在下面的示例中,我有一个启动线程的类。这只管用一次。它读取传感器并返回温度。但它再也不会运行该函数了。即使我更改传感器的输入,它也会不断返回相同的温度值。该函数在一个单独的程序中自行运行良好,并不断更新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:
循环,因为它在接近结尾处有asreturn
,防止它多次迭代。这样做并不重要,但我建议您删除它
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()
.
.
.