2016-10-31 13 views
0

pythonコードで常に(cX, cY, angle)を更新し、30msごとにarduinoに送信する3つの変数(float)を持つスレッドを作成しようとしています。PythonのスレッドとArduinoの通信

私はこれまでにスレッドを使用したことがなく、初心者としてはそれを理解することが本当に難しいです。

誰かが私に与えた考えは、3つの変数のタプルを作成し、そのタプルをarduino(PCからシリアル経由でarduino)に送信するスレッドをメインPythonフローで作成することです。

誰でも私を助けたり、少なくとも私がどのように始めることができるかを見せてもらえますか?

答えて

1

スレッドは、コードを同時に実行する方法です。つまり、関数Aを実行することができますが、終了する前に、関数Bを開始することができます。同時に実行する必要はありません(並列プログラミング)。あなたが時間を止めてコード内で何が起こっているのかを見ると、関数Aはまだいくつかの文を実行していますが、関数Bの文も実行されていることがわかります。

Pythonスレッドモジュールでは、各スレッドはいくつかの引数を持つ関数(またはメソッド)であり、起動時に関数本体が実行されます。関数が戻ると、スレッドは実行され、スレッドの状態はnot alivethreading.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との間でデータの準備が整うのを待っている間に、他の処理を行うことができるためです。

私がしている3つの変数(フロート)かかりスレッドを作成しようとしています...

は、私はあなたが何かを達成したいと仮定して、あなたはツールとしてのスレッドを使用しています。私はスレッドを作成することはここの目標ではなく、あなたが望むものを達成する方法であることを意味します。 質問にはアプリケーションの詳細が表示されないので、正確なコードやサンプルは提供できませんので、アプリケーションにそのアイデアを適用できることを希望する抽象的な/一般的な回答に進みます。

アプリケーションはどこかのデータ(IO)からデータを受け取り、30msごとに新しいデータをardunio(IOのように機能する)に送るべきだと思います。したがって、アプリケーションには2 IOアームがあり、1つはデータを受信し、もう1つはardunioに送信します。したがって、スレッドを使用すると正当性があります。

私たちは2つの機能を持つことができます.1つはデータの読み込み、1はardunioの更新です。それらを2つのスレッドthread_read(データを読み込む)とthread_ardunio(ardunioを更新する)で実行します。

もしそうなら、スレッド間で情報を共有する方法が必要です。スレッドはメモリ(プロセスメモリ内で、変数からアクセス可能)を使いやすくするため、両方の関数からアクセス可能な変数を使用できます。変数が1スレッドで更新されると、もう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() 

これは1つの方法です。 IMHOはそれほどきれいではありません。そこにはグローバル変数があります。変数を削除するにはどうすればよいですか?キューを使用できます(queue.Queue参照)。

キューはスレッド間で通信するのに適した方法だと思います。したがって、データを持つスレッドはそれらをキュー(a.k.aプロデューサ)に置き、もう一方のスレッドはキューからアイテムを選択します(a.k.aコンシューマ)。

このような何か:

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を更新する必要がある場合、プロデューサは周波数を調整することができ、消費者はまったく躊躇せずに消費することができます。

また、スレッドの生成/消費を停止するように指示する方法が必要です。 Eventthreading.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を送ることによって)Pythonプロセスを停止しようとすると、終了しません。

ただし、タイムアウトを指定してスレッドに参加しようとすることはできます。だから毎回、メインスレッドは受信信号を見て、それらを処理することができます。

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()