ロボットで遊ぶ その1(LED操作)
はじめに
DarkPawにはWS2812というチップのLEDが6個搭載されていて、Adafruit NeoPixelライブラリでRGBの制御を行うことができます。
今回はサンプルプログラムのGUIからLEDの色を制御する仕組みを作りつつ、WEB GUIを自作して6個のLEDで遊んでみたいと思います。
WS2812、 NeoPixel
WS2812とNeoPixelはArduinoやRaspberryPiでよく利用されているフルカラーLEDのチップとライブラリのようです。
WS2812の仕組みについては以下のサイトが勉強になりました。
Raspberry Pi 3に PWM駆動方式 WS2812 RGBフルカラー LEDを接続する方法 (ラズパイに PWM制御方式のフルカラー LED WS2812Bモジュールを接続して制御する方法)
GUIでLEDを制御する仕組み
PC側
GUI.pyの赤緑青のスライドバーを操作すると、以下の関数によりRaspberryPiへwsR/wsG/wsBと数値データが送信されていました。
単純にLEDの色を変えるだけならGUI側はそのままでよさそうです。
def set_R(event): time.sleep(0.03) tcpClicSock.send(('wsR %s'%var_R.get()).encode()) def set_G(event): time.sleep(0.03) tcpClicSock.send(('wsG %s'%var_G.get()).encode()) def set_B(event): time.sleep(0.03) tcpClicSock.send(('wsB %s'%var_B.get()).encode())
RaspberryPi側
GUIからのデータ受信処理
前回も少し解説しましたが、RaspberryPi側はserver.pyのrun関数でGUIからのデータを受信しています。
def run(): while True: data = '' data = str(tcpCliSock.recv(BUFSIZ).decode()) if not data: continue elif 'forward' == data: print('1') SpiderG.walk('forward')
data変数の文字列を確認して、処理内容を識別しているようです。
今回の場合はGUIからwsR/wsG/wsBのいずれかの文字列が渡されるため、if分に追記して処理を追加します。
サンプルプログラムのGUIでLEDを操作する
追加内容
server.pyのrun関数
run関数のIF分にwsR/wsG/wsBの場合の処理を追加します。
なぜかwsBの処理のみが実装されていて、speed_setという値の変更に利用していました。
特に意味はない処理なので以下のようにwsBを修正して、wsG/wsRを追加します。
変更前
elif 'wsB' in data: try: set_B=data.split() speed_set = int(set_B[1]) except: pass
変更後
elif 'wsB' in data: set_B=data.split() param_wsB = int(set_B[1]) LED.colorWipe(param_wsR, param_wsG, param_wsB) elif 'wsG' in data: set_G=data.split() param_wsG = int(set_G[1]) LED.colorWipe(param_wsR, param_wsG, param_wsB) elif 'wsR' in data: set_R=data.split() param_wsR = int(set_R[1]) LED.colorWipe(param_wsR, param_wsG, param_wsB)
解説
ポイントを二つ解説します。
ポイント1:'wsB' in dataについて
GUIからwsR/wsG/wsBを送信する際に、wsR/wsG/wsBに加えて、数値をdataに格納して送信しています。
そのため、他のIFと同じようにwsB == dataを書いてしまうと、IF分が成立しないため、'wsB' in dataとしています。
ポイント2:LED.colorWipeについて
LED.colorWipeはLEDの色を設定するためにサンプルコードに実装されたメソッドです。
LED.pyを見てみると、Adafruit_NeoPixelで初期化したオブジェクトに対して、
colorWipeメソッドで指定したRGPを適用しています。
## LED.py抜粋 def colorWipe(self, R, G, B): for i in range(self.strip.numPixels()): self.strip.setPixelColor(i, color) self.strip.show()
forですべてのLEDに同じRGBを適用しているため、今の実装では6個のLEDが同じ色に変化します。
WEB GUIを自作してLEDを単体操作
サンプルコードの実装では6個のLEDがすべて同じ色に制御されてしまうため、任意のLEDの色を手軽に変更できる仕組みを作ります。
server.py、LED.pyへの追加実装
LED.py
任意の番号のLED色を変更するメソッドを追加します。
# 指定した番号のLEDの色を変更する def setLedColor(self, RGB, ledNum): color = Color(RGB[0],RGB[1],RGB[2]) for i in ledNum: self.strip.setPixelColor(int(i), color) self.strip.show()
RGBにRed/Green/Blueの値を格納して渡します。
また、ledNumに変更するLED番号(0~5)を格納して渡します。
server.py
独自にsetLedColorというデータを受け取ったら、setLedColorメソッドを呼び出す処理をrunメソッドへ追加します。
elif 'setLedColor' in data: # LEDの色を変更する # 連続でデータを受け取る場合があるため、 if(len(data)<=30): ledParams = data.split() wsRGB = tuple(int(ledParams[1][i:i+2], 16) for i in (0, 2, 4)) ledNum = ledParams[2].split(',') LED.setLedColor(wsRGB, ledNum)
dataの中身は後述するクライアント側で定義しますが、以下のようなデータが文字列として格納されています。
- フォーマット:"setLedColor
<変更するLED番号>" - 格納例:"setLedColor 00ff00 0,1,2,3"
- R(00)、G(ff)、B(00)、LED番号(0,1,2)
クライアントの実装
サンプルコードのGUIにはLEDを選択する画面がないため、WEB GUIを新しく作成します。
必要なライブラリをインストール
pip3 install flask imutils
GUI概要
- PythonのWEBライブラリであるFlaskを使ってWEBサーバを構築する。
- Javascriptでカラーピッカーを用意して、カラーピッカーで設定した値をPython経由でserver.pyへ送信する。
- カラーピッカーはColorPicker - jQuery pluginを利用
参考:【Python】フレームワークFlaskの基本をマスター | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
プログラム構成
GUIのプログラムは以下の構成になっています。
led_ColorPicker ├── led_color_picker.py ├── static │ ├── css │ │ ├── colorpicker.css │ │ └── layout.css │ ├── images │ ├── js │ │ ├── colorpicker.js │ │ ├── eye.js │ │ ├── jquery.js │ │ ├── layout.js │ │ └── utils.js └── templates └── color_picker.html
Javascript/CSSはcolorpickerのサンプルソースをそのまま使っているので今回重要なのはled_color_picker.py、layout.js、color_picker.htmlの3つだけです。
処理の流れ
-
- led_color_picker.pyを実行すると、flaskによりcolor_picker.htmlを読み込んだWEBサーバが起動します。
- color_picker.htmlで操作した色の変更、LEDの選択をlayout.js内のonChangeメソッドで受け取って、flaskの内部通信を使って、layout.jsからled_color_picker.pyへデータが渡されます。
- led_color_picker.pyはlayout.jsから受け取ったデータを"setLedColor
<変更するLED番号>" の文字列フォーマットでTCP通信でロボット(server.py)へ渡します。
led_color_picker.py 抜粋
# HTTPサーバのIPアドレス、ポート番号 SERVER_IP = "192.168.0.31" PORT_NUM = "8080" outputFrame = None lock = threading.Lock() app = Flask(__name__) # HTML生成 @app.route("/") def index(): data = {'serverIp' : SERVER_IP} return render_template("color_picker.html", data=data) # Javascriptからのデータを受信する関数を作成 @app.route('/setLedColor', methods=['POST']) def color_post(): result = request.form["param"] print(result) # Javascriptから受け取ったデータをsocket_connectで生成した通信ソケットを使ってロボットへ送信 tcpClicSock.send(result.encode()) return "OK" ## ロボットへ接続 (この辺はサンプルコードそのまま) def socket_connect(): global ADDR,tcpClicSock,BUFSIZ,ip_stu,ipaddr SERVER_PORT = 10223 BUFSIZ = 1024 ADDR = (SERVER_IP, SERVER_PORT) tcpClicSock = socket(AF_INET, SOCK_STREAM) print("Connecting to server @ %s:%d..." %(SERVER_IP, SERVER_PORT)) print("Connecting") tcpClicSock.connect(ADDR) print("Connected") if __name__ == '__main__': # robot通信用スレッド生成 sc=thread.Thread(target=socket_connect) sc.setDaemon(True) sc.start() # FlaskのWEBサーバ起動 app.run(host=SERVER_IP, port=PORT_NUM, debug=False, threaded=True, use_reloader=False)
layout.js 抜粋
javascriptはlayout.jsにonChangeを追加しました。
$('#colorpickerHolder').ColorPicker({ flat: true, // カラーピッカーが操作されたら呼び出されるメソッド onChange: function (hsb, hex, rgb) { // Pythonで設定した関数へのURLを指定 var url = "http://"+serverIp+":8080/setLedColor" // LEDのチェックボックスの値を取得 var e = document.getElementById('ledNum'); var ary = new Array(); var num = 0; //optionを順番に見て、selectedとなっているものの添え字を配列にいれる for(var i = 0; i < e.childElementCount; i++){ if(e.getElementsByTagName('option')[i].selected){ ary[num] = i; num++; } } // LEDが選択されている場合のみ変更を送信する。 if(num > 0) { let formData = new FormData(); formData.append('param', "setLedColor "+ hex + " " + ary ) ; fetch(url, { method: 'POST', // methodを指定しないとGETになる body: formData, // Postで送るパラメータを指定 }); } } });
color_picker.html 抜粋
color_picker.htmlはflaskで読み込まれるWEBページのテンプレートです。
LEDのチェックボックスとColorPickerを設置したシンプルな作りになってます。
<head> <script type="text/javascript"> var serverIp = "{{ data.serverIp }}"; </script> </head> <body> <div class="wrapper"> <ul class="navigationTabs"> <li><a href="#color" rel="ColorPicker">ColorPicker</a></li> <li><a href="#dummy" rel="dummy">Dummy</a></li> <li><a href="#dummy2" rel="dummy2">Dummy2</a></li> </ul> <div class="tabsContent"> <div class="tab"> <h2>ColorPicker</h2> <br> <select multiple id="ledNum"> <option value="0">LED#0</option> <option value="1">LED#1</option> <option value="2">LED#2</option> <option value="3">LED#3</option> <option value="4">LED#4</option> <option value="5">LED#5</option> </select> <p id="colorpickerHolder"> </p> </div> <div class="tab"> <h2>Dummy</h2> </div> <div class="tab"> <h2>Dummy2</h2> </div> </div> </div> </body> </html>
動作
GUIで指定したRGBとLED番号がRaspberryPiのPythonServerへ渡されて、任意のLED色を変更することができました。