4

我有以下 python 脚本:

#! /usr/bin/python

import os
from gps import *
from time import *
import time
import threading
import sys

gpsd = None #seting the global variable

class GpsPoller(threading.Thread):
   def __init__(self):
      threading.Thread.__init__(self)
      global gpsd #bring it in scope
      gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info
      self.current_value = None
      self.running = True #setting the thread running to true

   def run(self):
      global gpsd
      while gpsp.running:
         gpsd.next() #this will continue to loop and grab EACH set of gpsd info to clear the buffer

if __name__ == '__main__':
   gpsp = GpsPoller() # create the thread
   try:
      gpsp.start() # start it up
      while True:

         print gpsd.fix.speed

         time.sleep(1) ## <<<< THIS LINE HERE

   except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
      print "\nKilling Thread..."
      gpsp.running = False
      gpsp.join() # wait for the thread to finish what it's doing
   print "Done.\nExiting."

不幸的是,我对 python 不是很好。该脚本应该以某种方式是多线程的(但这在这个问题的范围内可能并不重要)。

令我困惑的是这gpsd.next()条线。如果我做对了,它应该告诉脚本已经获取了新的 gps 数据并准备好读取。

但是,我使用无限循环读取数据,while True暂停 1 秒time.sleep(1)

然而,这样做的原因是它有时会重复两次相同的数据(传感器在最后一秒没有更新数据)。我认为它也会以某种方式跳过一些传感器数据。

我可以以某种方式更改脚本以打印当前速度,而不是每秒打印一次,而是每次传感器报告新数据时?根据数据表,它应该是每秒(1 Hz 传感器),但显然它不完全是 1 秒,而是以毫秒为单位变化。

4

2 回答 2

2

作为通用设计规则,您应该为每个输入通道或更通用的每个“循环阻塞调用”设置一个线程。阻塞意味着执行在该调用处停止,直到数据到达。eggpsd.next()就是这样一个电话。

要同步多个输入通道,请使用一个Queue和一个额外的线程。每个输入线程都应将其“事件”放在(相同的)队列中。额外的线程循环queue.get()并做出适当的反应。

从这个角度来看,您的脚本不需要是多线程的,因为只有一个输入通道,即gpsd.next()循环。

示例代码:

from gps import *

class GpsPoller(object):
   def __init__(self, action):
      self.gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info
      self.action=action

   def run(self):
      while True:
         self.gpsd.next()
         self.action(self.gpsd)

def myaction(gpsd):
    print gpsd.fix.speed

if __name__ == '__main__':
   gpsp = GpsPoller(myaction)
   gpsp.run() # runs until killed by Ctrl-C

请注意action回调的使用如何将管道与数据评估分开。

要将轮询器嵌入到执行其他操作(即也处理其他线程)的脚本中,请使用队列方法。基于类的示例代码GpsPoller

from threading import Thread
from Queue import Queue

class GpsThread(object):
    def __init__(self, valuefunc, queue):
        self.valuefunc = valuefunc
        self.queue = queue
        self.poller = GpsPoller(self.on_value)

    def start(self):
        self.t = Thread(target=self.poller.run)
        self.t.daemon = True  # kill thread when main thread exits
        self.t.start()

    def on_value(self, gpsd):
        # note that we extract the value right here.
        # Otherwise it could change while the event is in the queue.
        self.queue.put(('gps', self.valuefunc(gpsd)))


def main():
    q = Queue()
    gt = GpsThread(
            valuefunc=lambda gpsd: gpsd.fix.speed,
            queue = q
            )
    print 'press Ctrl-C to stop.'
    gt.start()
    while True:
        # blocks while q is empty.
        source, data = q.get()
        if source == 'gps':
            print data

我们给 GpsPoller 的“动作”说“通过 valuefunc 计算一个值并将其放入队列中”。主循环一直在那里,直到弹出一个值,然后打印它并继续。

将其他线程的事件放入队列并添加适当的处理代码也很简单。

于 2016-08-09T09:43:19.747 回答
1

我在这里看到两个选项:

  1. GpsPoller 将检查数据是否更改并引发标志
  2. GpsPoller 将检查 id 数据更改并将新数据放入队列中。

选项1:

global is_speed_changed = False

def run(self):
      global gpsd, is_speed_changed
      while gpsp.running:
         prev_speed = gpsd.fix.speed
         gpsd.next()
         if prev_speed != gpsd.fix.speed
            is_speed_changed = True  # raising flag

while True:
        if is_speed_changed:
           print gpsd.fix.speed
           is_speed_changed = False

选项#2(我更喜欢这个,因为它可以保护我们免受加注条件的影响):

gpsd_queue = Queue.Queue()

def run(self):
      global gpsd
      while gpsp.running:
         prev_speed = gpsd.fix.speed
         gpsd.next()
         curr_speed = gpsd.fix.speed
         if prev_speed != curr_speed:
            gpsd_queue.put(curr_speed) # putting new speed to queue

while True:
    # get will block if queue is empty 
    print gpsd_queue.get()
于 2016-08-07T05:47:30.557 回答