2016-10-31 8 views
0

python 코드에서 지속적으로 업데이트되는 의 3 가지 변수 (float)를 사용하여 30ms마다 arduino uno에 전송하는 스레드를 만들려고합니다.Python 스레딩 및 Arduino 통신

나는 전에 쓰레드를 사용하지 않았고 초보자로서 그것을 이해하기가 정말로 어렵다.

누군가가 준 개념은 세 변수의 튜플을 만들고 그 튜플을 arduino (PC를 직렬을 통해 arduino)로 보내는 스레드를 메인 파이썬 흐름에 생성하는 것입니다.

누구나 나를 도울 수 있거나 적어도 내가 어떻게 시작할 수 있는지 보여줄 수 있습니까?

답변

1

스레드는 코드를 동시에 실행하는 방법입니다. 즉, 함수 A를 실행할 수는 있지만, 함수 B가 시작되기 전에 함수 B를 시작할 수 있습니다. 동시에 둘 다 동시에 실행해야하는 것은 아닙니다 (병렬 프로그래밍). 특정 시점에서 생각하면 시간을 멈추고 코드에서 무슨 일이 벌어지고 있는지 살펴보면 함수 A가 아직 완료되지 않은 명령문을 실행했지만 함수 B의 일부 명령문도 실행되었음을 알 수 있습니다.

파이썬 스레딩 모듈에서 각 스레드는 가능한 인수가있는 함수 (또는 메소드)이며, 시작될 때 함수 본문이 실행됩니다. 함수가 반환되면 스레드가 실행되고 스레드의 상태가 not alive으로 변경됩니다 (threading.Thread.is_alive() 참조).

from threading import Thread 

simple_thread = Thread(target=func_with_no_args) 
thread_with_args = Thread(target=my_function, args=(arg1, arg2)) 

을 그리고 우리는 함수의 본문을 실행하는 스레드를 시작

그래서 우리는 함수/메소드 참조 가능한 인수를 전달하여 스레드를 생성합니다.

simple_thread.start() 
thread_with_args.start() 

그러나 코드의 현재 흐름은 계속 진행됩니다 (현재 스레드는 프로그램의 주 스레드가 항상 실행을 시작합니다). 따라서 우리는 함수 (func_with_no_args 또는 my_function)가 완료 될 때까지 기다릴 필요가 없습니다.

그리고 우리는 우리가 스레드에서 실행되는 기능이 완료되었는지 확인해야하는 경우가 살아 있다면, 우리가 확인할 수 있습니다

simple_thread.is_alive() # return bool 

을 우리가 스레드에서 함수가 실행을 완료 할 때까지 대기 할 경우, 우리는 스레드 join 수 있습니다. 작업이 끝나면 스레드를 join에게도 좋습니다.

simple_thread.join() 

프로그램이 데이터를 기다리는 동안하는 IO에서 다른 물건을 할 수 있습니다 /에 준비가되어 있기 때문, IO 작업에 주로 도움이됩니다.

나는이 세 변수 (플로트) 소요 스레드를 만들려고하고 있어요 ...

나는 당신이 뭔가를 달성한다고 가정하면 도구로 스레드를 사용하고 있습니다. 스레드를 만드는 것은 여기서 목표가 아니라 원하는 것을 성취하는 방법입니다. 응용 프로그램의 세부 정보가 질문에서 누락 되었기 때문에 정확한 코드/샘플을 제공 할 수 없으므로 응용 프로그램에 아이디어를 적용 할 수 있기를 희망하는 추상/일반 답변을 참조하겠습니다.

응용 프로그램이 어딘가에서 (따라서 IO) 데이터를받는 것으로 생각하고 30ms마다 새 데이터를 ardunio (다시 IO와 같은 역할을합니다)로 보내야한다고 생각합니다. 따라서 응용 프로그램에는 IO IO 무기 2 개와 데이터 수신 용 1 개, 다른 하나는 ardunio에 보내야합니다. 그래서 쓰레드를 사용하는 것은 정당화 될 수 있습니다.

우리는 2 개의 함수, 1은 데이터 읽기, 1은 ardunio를 업데이트 할 수 있습니다. 우리는 두 스레드, thread_read (데이터 읽기) 및 thread_ardunio (ardunio 업데이트)에서 실행합니다.

그렇다면 스레드간에 정보를 공유 할 방법이 필요합니다. 쓰레드는 (프로세스 메모리에서 변수를 통해 액세스 할 수있는) 메모리를 사용하기 쉽도록하기 때문에 두 함수에서 액세스 할 수있는 변수를 사용할 수 있습니다. 변수가 1 스레드에 의해 업데이트되면 다른 스레드도 업데이트 된 결과를 볼 수 있습니다.

storage = None 

def read_data(): 
    global storage 
    # read data from DB, web API, user input, etc. 
    # maybe in a loop to keep receiving data. Or generate the data from here 
    storage = (cX, cY, angle) 

def send_to_ardunio(): 
    global storage 
    # send storage data to ardunio, maybe in a loop 
    # sleeping 30ms after each update 


thread_read = Thread(target=read_data) 
thread_ardunio = Thread(target=send_to_ardunio) 
thread_read.start() 
thread_ardunio.start() 

그건 한 가지 방법입니다. IMHO는 너무 귀엽지 않습니다. 거기에 글로벌 변수가 있습니다. 변수를 제거하기 위해 우리는 무엇을 할 수 있습니까? 대기열을 사용할 수 있습니다 (queue.Queue 참조).

대기열은 스레드간에 통신하는 좋은 방법입니다. 따라서 데이터가있는 스레드는 큐 (생성자)에 큐를 넣고 다른 스레드는 큐 (즉, 소비자)에서 항목을 선택합니다. 이 같은

뭔가 :

def read_data(queue_): 
    # read data from DB, web API, user input, etc. 
    # maybe in a loop to keep receiving data, or generate the data from here 
    data = (cX, cY, angle) 
    queue_.put(data) 

def send_to_ardunio(queue_): 
    # send data to ardunio, maybe in a loop 
    # sleeping 30ms after each update 
    data = queue_.get() 
    cX, cY, angle = data 

queue_ = Queue() # this will be used to transfer data 
thread_read = Thread(target=read_data, args=(queue_,)) 
thread_ardunio = Thread(target=send_to_ardunio, args=(queue_,)) 
thread_read.start() 
thread_ardunio.start() 

더 나은 보인다.

이제 함수가 실행될 때까지 기다려야합니다. 그래서 우리는 스레드에서 join 메서드를 호출 할 수 있습니다. 또한 이번에는 데이터를 읽는데 얼마나 많은 시간이 걸릴지를 제어 할 수 있다고 가정 할 자유를 가졌습니다. 30ms마다 ardunio를 새로운 데이터로 업데이트해야한다면 생산자는 빈도를 조정할 수 있으며 소비자는 주저없이 소비 할 수 있습니다.

또한 생성/소비가 중단되도록 스레드에 알리는 방법이 필요합니다. Event (threading.Event 참조)을 사용할 수 있습니다. 또는 간단히 말해서 계약서에 따라 대기열의 데이터는 소비자가 중단해야 함을 나타냅니다.

def read_data(queue_): 
    while True: 
     # calculate/get cX, cY, angle 
     queue_.put((cX, cY, angle)) 
     # sleep 30ms 
     # when we finished producing data, put something to the queue to tell the consumer there is no more data. I'll assume None is good option here 
    queue_.put(None) 


def send_to_ardunio(queue_): 
    while True: 
     data = queue_.get() 
     if data is None: 
      break 
     cX, cY, angle = data 
     # update ardunio, because the data is updated every 30ms on the producer, we don't need to do anything special. We can just wait when the data is ready, we'll update. 

queue_ = Queue() 
thread_read = Thread(target=read_data, args=(queue_,)) 
thread_ardunio = Thread(target=send_to_ardunio, args=(queue_,)) 

thread_read.start() 
thread_ardunio.start() 

thread_read.join() 
thread_ardunio.join() 

위의 코드는 생산자 (thread_read)는 데이터를 생성 중지 알 수 있다고 가정합니다.

그런 경우가 아니라면 Event을 사용하여 두 기능을 모두 사용하여 생성 및 소비를 중지 할 수 있습니다.

결국 스레드를 결합 할 때 약간의 문제가 발생했습니다. 주 스레드가 다른 스레드에 합류하는 경우 차단되고 SIGINT에 제대로 응답하지 않습니다. 따라서 Ctrl + C를 누르거나 SIGINT를 보내서 파이썬 프로세스를 중단하려고 시도하면 종료되지 않습니다.

그러나 우리는 타임 아웃으로 스레드에 참가를 시도 할 수 있습니다. 그래서 매번 메인 스레드는 수신 된 신호를보고 처리 할 수 ​​있습니다.

def read_data(queue_, should_stop): 
    while not should_stop.is_set(): 
     # calculate/get cX, cY, angle 
     queue_.put((cX, cY, angle)) 
     # sleep 30ms 

def send_to_ardunio(queue_, should_stop): 
    while not should_stop.is_set(): 
     data = queue_.get() 
     cX, cY, angle = data 
     # update ardunio 

def tell_when_to_stop(should_stop): 
    # detect when to stop, and set the Event. For example, we'll wait for 10 seconds and then ask all to stop 
    time.sleep(10) 
    should_stop.set() 


queue_ = Queue() 
should_stop = Event() 

thread_stop_decider = Thread(target=tell_when_to_stop, args=(should_stop,)) 
thread_read = Thread(target=read_data, args=(queue_, should_stop)) 
thread_ardunio = Thread(target=send_to_ardunio, args=(queue_, should_stop)) 

thread_read.start() 
thread_ardunio.start() 
thread_stop_decider.start() 

try: 
    while thread_read.is_alive(): 
     thread_read.join(1) 
except KeyboardInterrupt: 
     should_stop.set() 
thread_read.join() 
thread_ardunio.join() 
thread_stop_decider.join()