M5StickCによるジェスチャー認識
- 今回は、学習データを作成することを目標とします。
M5StickC系のプログラム関連記事
- M5StickCによるジェスチャ認識(1)~インストールから加速度取得まで
- M5StickCによるジェスチャ認識(2)~サンプルコードによるジェスチャ認識
- M5StickCによるジェスチャ認識(3)~Wi-Fi接続と学習データの生成(この記事)
- M5StickCによるジェスチャ認識(4)~オリジナルデータによるジェスチャ認識
- M5StickCによる音声認識
※現状の予定になります
学習データの作成
- 前回の記事では「〇」「W」「∠」の3種類のサンプルを認識することができた。
- 今回は、オリジナルのジェスチャを認識するために、まずは学習データを作成することを考えます。
- 学習データは、ある一定の長さのジェスチャとなる加速度データをたくさん用意する必要があります。
- 一般的には学習データが多ければ多いほど精度が良くなります。
- 前回のプログラムを応用して、シリアル通信で取得した加速度情報をある一定のフレームで取得し、ファイルを生成することもできますが、シリアル通信のためにコードをパソコンに挿したまま行う必要があり、煩わしくなります。
- そこで、今回はWiFiでデータを飛ばし、学習データを作成することを考えます。
- 今回の手順は次の通りです。
- Wi-Fi通信のプログラムを作成
- 受信側で学習データの生成
Wi-Fiデータで加速度データの送信
- M5StcikCは、通信機能として、Wi-Fiモジュール、Bluetoothモジュールがありますが、今回はWi-Fiを使います。
- 新しくプロジェクトを作成します。プロジェクトはGitHubにも置いて負います
- main.cppは次のような形になります
main.cpp
#include <M5StickC.h>
#include <WiFi.h>
#include <WiFiUDP.h>
#include "time.h"
const char* ssid = "XXXXXXXXXXXX"; // SSID
const char* password = "XXXXXXXXXXXXXX"; // Password
const char* ntpServer = "aaa.bbb.jp";
const char* saddr = "XXX.XXX.XXX.XXX"; //taeget PC address
const int sport = 55998;
const int kport = 5556;
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;
float accX = 0.0F;
float accY = 0.0F;
float accZ = 0.0F;
uint8_t mac3[6];
WiFiUDP Udp;
char packetBuffer[255];
void setup() {
// put your setup code here, to run once:
M5.begin();
M5.Lcd.setRotation(3);
M5.Lcd.fillScreen(BLACK);
M5.IMU.Init();
esp_read_mac(mac3, ESP_MAC_WIFI_STA);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(1, 0, 2);
M5.Lcd.println("Welcome \n If the connection fails, turn off and on the power again.");
// connect to WiFi
M5.Lcd.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.println("CONNECTED");
delay(1000);
// Set ntp time to local
configTime(9 * 3600, 0, ntpServer);
// Get local time
struct tm timeInfo;
if (getLocalTime(&timeInfo)) {
// Set RTC time
RTC_TimeTypeDef TimeStruct;
TimeStruct.Hours = timeInfo.tm_hour;
TimeStruct.Minutes = timeInfo.tm_min;
TimeStruct.Seconds = timeInfo.tm_sec;
M5.Rtc.SetTime(&TimeStruct);
RTC_DateTypeDef DateStruct;
DateStruct.WeekDay = timeInfo.tm_wday;
DateStruct.Month = timeInfo.tm_mon + 1;
DateStruct.Date = timeInfo.tm_mday;
DateStruct.Year = timeInfo.tm_year + 1900;
M5.Rtc.SetData(&DateStruct);
}
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0, 1, 2);
M5.Lcd.print("Connected \nto ");
M5.Lcd.println(WiFi.localIP());
uint8_t mac3[6];
esp_read_mac(mac3, ESP_MAC_WIFI_STA);
M5.Lcd.printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n", mac3[0], mac3[1], mac3[2], mac3[3], mac3[4], mac3[5]);
Udp.begin(kport);
}
void loop() {
int mil = millis();
// put your main code here, to run repeatedly:
M5.Rtc.GetTime(&RTC_TimeStruct);
M5.Rtc.GetData(&RTC_DateStruct);
M5.Lcd.setCursor(0, 50);
M5.Lcd.printf("%04d-%02d-%02d ", RTC_DateStruct.Year, RTC_DateStruct.Month, RTC_DateStruct.Date);
M5.Lcd.printf("%02d:%02d:%02d\n", RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds);
// 加速度データ取得
M5.IMU.getAccelData(&accX, &accY, &accZ);
//データを学習用に変換する
accX *= 1000;
accY *= 1000;
accZ *= 1000;
M5.Lcd.printf("%6.0f, %6.0f, %6.0f\n", accX, accY, accZ);
//UDP
Udp.beginPacket(saddr, sport);
Udp.printf("%5.2f, %5.2f, %5.2f", accX, accY, accZ);
Udp.endPacket();
delay(100);
}
- platformio.iniは次のように設定しました。
platformio.ini
[env:m5stick-c]
platform = espressif32
board = m5stick-c
framework = arduino
monitor_speed = 115200
build_flags = -DARDUINOSTL_M_H -Ilib/tfmicro/third_party/gemmlowp -Ilib/tfmicro/third_party/flatbuffers/include
upload_speed = 1500000
- プログラムの初めのほうの「SSID」「PASSWORD」「PCのIPアドレス」を適宜セットしてください。SSIDとPASSWORDは各自宅のWi-Fiの設定どおりに、「PCのIPアドレス」はコマンドプロンプトなどで「ipconfig」で確認できます。
- プログラムを書き終えたら「Build」を押して、ビルドする。エラーがなければ、そのまま「Upload」すると、加速度がディスプレイに表示されます。
受信プログラムの作成
- 今回は、Pythonを使って受信プログラムを作成します。
開発環境
- python 3.6
- pyqt
受信プログラム
- ジェスチャを学習するためのファイルの指定が「output_[LABELNAME]_[PERSON].txt」である。また、ジェスチャーの長さはジェスチャーによって変わるため、それをGUIから操作できるようにした。
- また、各自の環境によってはIPアドレスなども変わるため、そこもGUIから設定できるようにする。
- プログラムは、GitHubに公開している。
UDP_GUI.py
from socket import *
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QCoreApplication
import datetime
import time
import csv
import pprint
class Example(QMainWindow):
SrcIP = "XXX.XXX.XXX.XXX" #M5StickC Address
outputName = "sample"
labelName = "wave"
loop = 100
n = 0
#初期化
def __init__(self):
super().__init__()
self.initUI()
self.dt_now = datetime.datetime.now()
#UI create
def setIP(self, msg):
SrcIP = msg # 受信元IP
SrcPort = 55998 # 受信元ポート番号
self.SrcAddr = (SrcIP, SrcPort) # アドレスをtupleに格納
self.BUFSIZE = 1024 # バッファサイズ指定
self.udpServSock = socket(AF_INET, SOCK_DGRAM) # ソケット作成
self.udpServSock.bind(self.SrcAddr) # 受信元アドレスでバインド
#UI create
def initUI(self):
btn1 = QPushButton("Record", self)
btn1.move(30, 30)
btn2 = QPushButton("Quit", self)
btn2.move(150, 30)
btn3 = QPushButton('Set IP', self)
btn3.move(30, 80)
self.inputURL = QLineEdit(self)
self.inputURL.move(150, 80)
self.inputURL.setText("192.168.1.1")
btn4 = QPushButton('Set name', self)
btn4.move(30, 130)
self.inputFileName = QLineEdit(self)
self.inputFileName.move(150, 130)
self.inputFileName.setText("sample")
btn5 = QPushButton('Set Label', self)
btn5.move(30, 180)
self.inputLabelName = QLineEdit(self)
self.inputLabelName.move(150, 180)
self.inputLabelName.setText("wave")
btn6 = QPushButton('Set Length', self)
btn6.move(30, 230)
self.inputLength = QLineEdit(self)
self.inputLength.move(150, 230)
self.inputLength.setText("100")
# クリックされたらbuttonClickedの呼び出し
btn1.clicked.connect(self.recv)
btn2.clicked.connect(QCoreApplication.instance().quit)
btn3.clicked.connect(self.Output)
btn4.clicked.connect(self.Setfilename)
btn5.clicked.connect(self.Setlabel)
btn6.clicked.connect(self.Setlength)
self.statusBar()
self.setGeometry(300, 300, 290, 300)
self.setWindowTitle('Getting accel data')
self.show()
#UDP receive
def recv(self):
print('\n----------\nIP Adress: ', self.SrcIP)
print('loop count: ', self.loop)
print('Output filename: ', self.outputName)
print('label name:', self.labelName)
i = 0
#with open(self.outputName + '_' + self.dt_now.isoformat().replace(':','') + '.txt', 'a', newline='') as f:
with open('output_' + self.labelName + '_' + self.outputName + '.txt', 'a', newline='') as f:
f.write('\n\n\n -,-,-\n')
time.sleep(2)
self.setIP(self.SrcIP)
while i < self.loop: # 受信待ち
i = i + 1
data, addr = self.udpServSock.recvfrom(self.BUFSIZE) # 受信
l = data.decode().split(',')
# 受信データと送信アドレス表示
print(i, "\t", float(l[self.n]), float(l[self.n+1]), float(l[self.n+2]), addr)
s = 'x, y, z = ' + l[self.n] + ', ' + l[self.n+1] + ', ' + l[self.n+2]
self.statusBar().showMessage(s)
f.write(str(float(l[self.n])) + ',' + str(float(l[self.n+1])) + ',' + str(float(l[self.n+2])) + '\r\n')
i = 0
self.udpServSock.close()
#ULR set
def Output(self):
self.SrcIP = self.inputURL.text()
print('Change IP Adress:', self.SrcIP)
#ULR set
def Setlength(self):
self.loop = int(self.inputLength.text())
print('Change loop count:', self.loop)
#Label set
def Setlabel(self):
self.labelName = self.inputLabelName.text()
print('Change label name:', self.labelName)
#filename set
def Setfilename(self):
self.outputName = self.inputFileName.text()
print('Change output filename:', self.outputName)
#時間の取得
self.dt_now = datetime.datetime.now()
def buttonClicked(self):
# ステータスバーへメッセージの表示
sender = self.sender()
self.statusBar().showMessage(sender.text() + ' was pressed')
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
- プログラムは、以下のように起動する
> python UDP_GUI.py
- 起動すると以下のような画面が生成される。
- それぞれ、数値や文字を入力後にSetボタンを押す
- Set IP: PCのIPアドレスを設定する
- Set Name: 取得した人物の名前を登録(特に何でもよい)
- Set Label: ジェスチャの名前を登録
- Set Length: ジェスチャーを行っている長さを設定
- [Record]をクリックすると、M5StickCが起動していれば、受信し、ファイルを生成する。
- 同じ名前の人で何度も同じジェスチャーのデータを生成する場合は、続けて[Record]すると上書き保存する。
- すでに同じ名前のファイルがある場合は、上書き保存する設定になっているため、新しく作成しなおす場合は、事前に作成されているテキストファイルを削除する。