■
シリアル通信でマイコンとBlenderを連携させる(サーボモーター編)
はじめに
ここ半年ほど、Blender用の自作入力デバイスというものを制作しています。
この入力デバイス作りで得られた知見を残しておこうと思い記事を書くことにしました。 今回はマイコンとBlenderをシリアル通信を使って連携することで、BlenderからLEDやモーターを制御してみます。
これは Blender Advent Calendar 2020参加記事です。
セットアップ
必要なもの・環境
ソフトウェア
Blender 2.91(2.8以降で動作確認) https://www.blender.org/download/
pyserial https://pypi.org/project/pyserial/
(必要であれば)7-Zip等のtar.gzを解凍できるツール
ハードウェア
M5StickC
マイコンにESP32、その他に液晶ディスプレイ、6軸センサ、マイク、バッテリなどを搭載した便利な開発機器です。 スイッチサイエンスやマルツで約2000円で購入できます。
https://www.switch-science.com/catalog/5517/
https://www.marutsu.co.jp/pc/i/1526331/
以上は腕に巻くためのベルトがセットになっているものですが、本体のみも販売されています。少し安いです。
ジャンパワイヤやブレッドボード
M5StickCとモーターやLEDをつなぐために必要です。
-
FEETECH サブマイクロサーボ FS0307 https://www.switch-science.com/catalog/3714/
Windows10とmacOS High Sierraで動作確認しています。
pyserialのインストール
Blenderはすでにインストールされていることとして話を進めます。 まず初めにPythonでシリアル通信を扱うためのモジュールであるpyserialをBlenderにインストールします。
Download filesからpyserial-3.5.tar.gz
をクリックし、tar.gzファイルをダウンロードしてください。
7-Zip等の展開ソフトでtar.gzファイルを展開します。Macの場合はダブルクリックで展開できます。
展開して生成されたdist
フォルダを開きます。
pyserial-3.5.tar
を先ほどと同様に展開します。
展開してできたpyserial-3.5
フォルダを開いてserial
フォルダがあるか確認してください。
Windowsの場合は
C:\Program Files\Blender Foundation\Blender 2.91\2.91\scripts\modules
(数字の箇所はバージョンによって異なります。)
Macの場合はアプリケーションフォルダ内にあるBlender.appを右クリックして、パッケージの内容を表示
を選択し同様にmodules
フォルダに移動します。
modules
フォルダにserial
フォルダをコピーすればインストール完了です。
Arduino IDEのインストール
マイコン用のプログラムを書く開発環境にはArduino IDEを使用します。
https://www.arduino.cc/en/software
Arduinoとはマイコンを簡単に扱えるようにした開発システムです。C++のようなプログラムを書くことでマイコンを制御することができます。Arduino IDEはそのプログラムを書くためのソフトウェアです。今回つかうM5StickCはArduinoではありませんが、Arduino IDEに対応しており同様に扱うことができます。
ただその代わりにインストールに特別な手順を踏む必要があります。
https://docs.m5stack.com/#/ja/quick_start/m5stickc/m5stickc_quick_start_with_arduino_Windows
これらの記事を参考にセットアップを行ってみてください。
以上でセットアップは終了です。Blenderを起動しましょう。
BlenderからM5StickCに値を送る
ようやく本題に入ります。まずはM5StickCにつないだサーボモータを、Blenderから操作していこうと思います。
BlenderからM5StickCにつないだサーボモーターを動かす
うまくいけばこのようにデフォルトキューブを回すことでサーボモータが回るはずです。
シリアル通信とは
Blenderとマイコンとの間ではシリアル通信というものを使ってデータの受け渡しを行います。
シリアル通信というのはすごく平たく説明すると、小さいデータを一個づつ高速で送る通信方式のことです。マイコンとPCだけでなく、マイコン同士のデータのやり取りでもよく使われる方式です。
プログラムの流れ
Blenderから一定間隔でM5StickCに値を送り、M5StickCは受け取り次第値を使ってサーボモータを動かします。
サーボモータをM5StickCにつなぐ
サーボモータの三本線のうち、赤色をM5StickCの3.3V出力に、茶色をGNDに、オレンジ色の制御線をG26につなぎます。
Arduino IDEでソースコードを書く
サーボモーターを使うためのESP32Servoライブラリをあらかじめインストールしてください。
ライブラリを管理
から、esp32servoを検索してインストールします。
プログラムは以下の通りです。
#include <ESP32Servo.h> //esp32servoライブラリの読み込み #include <M5StickC.h> //M5StickCを使うためのライブラリの読み込み Servo myservo; //サーボモーターを扱うクラスの宣言 void setup(){ //マイコン起動時に一回だけ実行されます。初期設定をここで行います。 M5.begin(); //M5StickCの各種機能の初期化を行います。 myservo.attach(G26, 650, 2500); //サーボモーターの設定を行います。引数は順にサーボの制御線が繋がっているピン番号、 //回転角度が0の時のパルス幅、回転角度が180度の時のパルス幅です Serial.begin(115200); //シリアル通信を初期化します。引数はシリアル通信の速度です。 } void loop(){ //setup()終了後、電源が入っている間はずっと繰り返し実行されます。 if (Serial.available() > 0) { //シリアル通信で受信したデータの有無を確認します。 byte readvalue = (byte)Serial.read(); //byte型変数"readvalue"にSerial.read()で受信したデータを格納します。 //型変換(キャスト)するために(byte)をつけます。 uint8_t angle = (uint8_t)readvalue; //uint8_t型変数"data"にreadvalueを格納します。 //型変換(キャスト)するために(uint8_t)をつけます。 myservo.write( angle ); //何度の位置までサーボモーターを回すか指定します。 delay(20); //20ミリ秒待ちます。 } }
コードが書けたらマイコンに書き込みましょう。
赤線部のボードがM5StickC、シリアルポートがM5StickCが繋がっているポートになっていることを確認し、左上の矢印ボタンを押してください。
BlenderでPythonを実行する
BlenderにはPythonというプログラミング環境が内蔵されていて、Pythonでプログラムを書くことでアドオン(追加機能)や処理の自動化プログラムを制作することができます。 以下の手順でBlender上でPythonプログラムを実行することができます。
上部のタブの[Scripting]をクリックします。
このようなレイアウトに変わります。
テキストから新規を選ぶとソースコードを入力できるようになります。
ソースコードを実行するときは上部ファイル名横の三角形ボタンを押してください。
Blenderアドオンのソースコードを書く
M5StickCとシリアル通信を行うプログラムのソースコードは次の通りです。
import serial import bpy import math import mathutils import struct bl_info = { "name": "Blender with M5StickC", "author": "しましま", "version": (3, 0), "blender": (2, 80, 0), "location": "3Dビューポート > Sidebar > Blender with M5StickC", "description": "BlenderとM5StickCを連携させるアドオン", "warning": "", "support": "TESTING", "wiki_url": "", "tracker_url": "", "category": "Object" } # モーダルモードでオブジェクトを動かすオペレータ class BLENDER_WITH_M5(bpy.types.Operator): bl_idname = "object.blender_with_m5" bl_label = "M5StickCと連携" bl_description = "Blender with M5StickC" # タイマのハンドラ __timer = None #シリアルポートの設定 #最初の引数'COM5'にはM5StickCのCOMポート名を入れてください。 __ser = serial.Serial('COM5', 115200, timeout = 0) @classmethod def is_running(cls): # モーダルモード中はTrue return True if cls.__timer else False def __handle_add(self, context): blwm5 = BLENDER_WITH_M5 if not self.is_running(): # タイマを登録 blwm5.__timer = \ context.window_manager.event_timer_add( 0.1, window=context.window ) # モーダルモードへの移行 context.window_manager.modal_handler_add(self) def __handle_remove(self, context): if self.is_running(): # タイマの登録を解除 context.window_manager.event_timer_remove( BLENDER_WITH_M5.__timer) BLENDER_WITH_M5.__timer = None def modal(self, context, event): blwm5 = BLENDER_WITH_M5 active_obj = context.active_object # エリアを再描画 if context.area: context.area.tag_redraw() # パネル [Blender with M5StickC] のボタン [終了] を押したときに、モーダルモードを終了 if not self.is_running(): return {'FINISHED'} #メインのループ内処理はここから if event.type == 'TIMER': value = int(active_obj.rotation_euler[2] * 180/math.pi) #送信処理(オブジェクトの角度が0度から180度の時だけ値を送る) if value >= 0 and value <= 180: blwm5.__ser.write( bytes([value]) ) return {'PASS_THROUGH'} #ここまで def invoke(self, context, event): blwm5 = BLENDER_WITH_M5 active_obj = context.active_object if context.area.type == 'VIEW_3D': # [開始] ボタンが押された時の処理 if not blwm5.is_running(): # モーダルモードを開始 self.__handle_add(context) if not blwm5.__ser.is_open: blwm5.__ser.open() print("START") return {'RUNNING_MODAL'} # [終了] ボタンが押された時の処理 else: # モーダルモードを終了 self.__handle_remove(context) blwm5.__ser.close() print("STOP") return {'FINISHED'} else: return {'CANCELLED'} # UI class BLENDER_WITH_M5_UI(bpy.types.Panel): bl_label = "Blender with M5StickC" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "Blender with M5StickC" bl_context = "objectmode" def draw(self, context): blwm5 = BLENDER_WITH_M5 layout = self.layout # [開始] / [停止] ボタンを追加 if not blwm5.is_running(): layout.operator(blwm5.bl_idname, text="開始", icon="PLAY") else: layout.operator(blwm5.bl_idname, text="終了", icon="PAUSE") classes = [ BLENDER_WITH_M5, BLENDER_WITH_M5_UI, ] def register(): for c in classes: bpy.utils.register_class(c) print("Registed") def unregister(): for c in classes: bpy.utils.unregister_class(c) print("Unregisted") if __name__ == "__main__": register()
ぬっち氏著の「はじめてのBlenderアドオン開発 (Blender 2.8版)」を参考にBlenderアドオンとして書いています。
https://colorful-pico.net/introduction-to-addon-development-in-blender/2.8/index.html
特にModalメゾッドを使用するため、以下の記事が参考になりました。
必ず確認していただきたいのが、34行目のシリアルポートを設定する一行です。
__ser = serial.Serial('COM5', 115200, timeout = 0)
引数の一番目'COM5'のところにはM5StickCが繋がっているシリアルポート名をいれてください。 ArduinoIDEで設定したものと同じものです。
if event.type == 'TIMER':
からreturn {'PASS_THROUGH'}
までの間がメインのループ処理、Arduinoでいうところのloop()にあたると考えてください。
#メインのループ内処理はここから if event.type == 'TIMER': value = int(active_obj.rotation_euler[2] * 180/math.pi) #送信処理(オブジェクトの角度が0度から180度の時だけ値を送る) if value >= 0 and value <= 180: blwm5.__ser.write( bytes([value]) ) return {'PASS_THROUGH'} #ここまで
この箇所を書き換えることで、様々なデータをシリアル通信で送ることができます。 今回は選択したオブジェクトのZ軸角度が0から180の時だけ、シリアル通信で角度を送信するようになっています。
ここまで出来たらマイコンをつないだ状態で実行してみてください。
3DビューポートでNキーを押して出てくる右側のメニューから[Blender with M5StickC]を選んで開始ボタンを押してください。オブジェクトを回転させるとサーボモーターも回るはずです。
終わりに
サーボモーターは動きましたか?3D空間内の動きが現実世界に反映されるっていうのは結構面白いんじゃないかと思います。
次はM5StickC内蔵の6軸センサを使ってデフォルトキューブを動かす記事を書く予定です...
参考記事
Arduino Blender & Maya Serial通信テスト(Facebook内容転載) ~ 3DCGと映像とINTERACTIVE http://dekapoppo.blogspot.com/2017/11/arduino-serial-blender-facebook.html
MikeBeradino/Python-Blender-serial_comm: Using blender to control arduino board via serial com. https://github.com/MikeBeradino/Python-Blender-serial_comm