我正在从我的 com 端口读取每 100us 一次的数据(16 字节/4 个浮点数)。同时,我使用当时的数据以 60fps(0.016667 秒/帧)的速率进行动画实时更新。我已附上下面的代码。
整个系统背后的想法是 readdata 函数使用 pyserial 被动读取和解析该数据。但是,我只想使用每 0.016667 秒显示一次的数据。当 readdata 函数运行时,我的动画会并行更新帧和所有内容。现在我的思考过程是有一个标志,当我需要抓取数据来更新框架时,该标志会升起。但是,我当前的代码使动画崩溃。我是线程新手,所以有什么关于更好的方法的建议,我可能会错过理解的事情,或者解决这个问题的方法吗?
class Pipeline:
"""
Class to allow a single element pipeline between producer and consumer.
"""
def __init__(self):
self.message = 0
self.flag = 0
self.producer_lock = threading.Lock()
self.consumer_lock = threading.Lock()
self.flag_set_lock = threading.Lock()
self.flag_get_lock = threading.Lock()
self.flag_get_lock.acquire()
self.consumer_lock.acquire()
def get_message(self, name):
self.consumer_lock.acquire()
message = self.message
self.producer_lock.release()
return message
def set_message(self, message, name):
self.producer_lock.acquire()
self.message = message
self.consumer_lock.release()
def consumer_get_flag(self, name):
self.flag_get_lock.acquire()
flag = self.flag
self.flag_set_lock.release()
return flag
def set_flag(self, flag, name):
self.flag_set_lock.acquire()
self.flag = flag
self.flag_get_lock.release()
def producer_get_flag(self,name):
return self.flag
# reading port function
def readport(pipeline):
# global flag
#read incoming data comming in every 100us
with serial.Serial('COM3',baudrate=3000000,bytesize=8, timeout=None, stopbits=serial.STOPBITS_ONE, parity=serial.PARITY_NONE, rtscts=True, dsrdtr=False) as s:
print('Opened',s.name)
while True:
res=s.read(16)
#parse data
ypos=struct.unpack('f',res[0:4])[0]
xpos=struct.unpack('f',res[4:8])[0]
yamp=struct.unpack('f',res[8:12])[0]
xamp=struct.unpack('f',res[12:16])[0]
#flag every 60 fps to set the pipline
flag = pipline.consumer_get_flag('Flag')
if flag == 1:
pipeline.set_message([xpos,ypos], "Data")
pipeline.set_flag(0,'Flag Down')
if xamp != 5.0:
print('values', [xpos,ypos,xamp,yamp])
break
# print('something wrong')
def animation(pipline):
# global flag
running = True
simtime = 0
prevx = 0
prevy = 0
while running:
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
# The user closed the window or pressed escape
running = False
screen.fill((255, 255, 255, 255))
# Draw the world
for body in world.bodies:
for fixture in body.fixtures:
fixture.shape.draw(body, fixture)
#update robot position from the data
pipline.set_flag(1,'Flag Up')
if pipline.producer_get_flag('current flag') == 1:
xpos = prevx
ypos = prevy
else:
xpos, ypos = pipline.get_message('Robot')
prevx = xpos
prevy = ypos
robot1.position = b2Vec2(xpos,ypos+12)
world.Step(TIME_STEP, 10, 10)
simtime = simtime + TIME_STEP
print(simtime)
# Flip the screen and try to keep at the target FPS
pygame.display.flip()
clock.tick(TARGET_FPS)
pygame.quit()
print('Done!')
# --- main game loop ---
pipline = Pipeline()
flag = 0
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
executor.submit(readport, pipline)
executor.submit(animation,pipline)`
您可以更简单地做到这一点。在Python中,线程共享内存。如果您小心的话,可以跨线程使用相同的对象。我认为你可以利用这一点并消除整个 Pipeline 类。你不需要它。
串口数据总是被操作系统缓冲的,所以无论如何你都必须不断地读取端口,否则字符会堆积在缓冲区中。由于端口向您发送数据的速度比您处理数据的速度快,因此大部分数据将被丢弃。所以只需将端口的最新数据放入一些变量中即可。大多数时候,即使您从未使用过最后一组数据,您也会覆盖它。这根本没有坏处。
当您的动画循环准备好接收另一组数据时,它只需从相同的变量中获取它即可。数据将会在那里。不需要任何标志或同步。
一个微妙之处是数据可能作为一个集合出现:x 和 y 坐标。他们必须一起更新。因此,在下面的清单中,我使用一个简单的线程。RLock 来确保两个变量不能一次访问一个。
就是这个想法。您还需要一些逻辑来处理用户退出或串行数据出现问题时的情况。我对此进行了尝试。由于我无法运行您的程序,以下只是该想法的示意图。您可能需要在运行之前进行一些调试。
class Position:
def __init__(self):
self.xpos = 0
self.ypos = 0
self.lock = threading.RLock()
def set(self, xpos, ypos):
with self.lock:
self.xpos = xpos
self.ypos = ypos
def get(self):
with self.lock:
return self.xpos, self.ypos
POSITION = Position()
running = True
# reading port function
def readport():
#read incoming data comming in every 100us
with serial.Serial('COM3', baudrate=3000000, bytesize=8, timeout=None,
stopbits=serial.STOPBITS_ONE, parity=serial.PARITY_NONE,
rtscts=True, dsrdtr=False) as s:
print('Opened',s.name)
while running:
res=s.read(16)
#parse data
ypos = struct.unpack('f',res[0:4])[0]
xpos = struct.unpack('f',res[4:8])[0]
yamp = struct.unpack('f',res[8:12])[0]
xamp = struct.unpack('f',res[12:16])[0]
if xamp != 5.0:
print('values', [xpos,ypos,xamp,yamp])
break
POSITION.set(xpos, ypos)
def animation_wrapper(port_thread):
global running
try:
animation(port_thread)
finally:
pygame.quit()
print('Done!')
running = False
def animation(port_thread):
simtime = 0
prevx = 0
prevy = 0
while port_thread.is_alive():
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and
event.key == K_ESCAPE):
# The user closed the window or pressed escape
return
screen.fill((255, 255, 255, 255))
# Draw the world
for body in world.bodies:
for fixture in body.fixtures:
fixture.shape.draw(body, fixture)
#update robot position from the data
xpos, ypos = POSITION.get()
robot1.position = b2Vec2(xpos, ypos+12)
world.Step(TIME_STEP, 10, 10)
simtime = simtime + TIME_STEP
print(simtime)
# Flip the screen and try to keep at the target FPS
pygame.display.flip()
clock.tick(TARGET_FPS)
# --- main game loop ---
port_thread = threading.Thread(target=port_thread)
port_thread.start()
# wait for port_thread to start
while not port_thread.is_running():
time.sleep(0.02)
animation_thread = threading.Thread(target=animation_wrapper,
args=(port_thread))
animation_thread.start()
我还修复了你的缩进。使用 StackOverflow 时请小心这一点,尤其是对缩进敏感的 Python 代码。