2016年11月30日水曜日

NFCワークショップ@久辺テクノフェスタ

毎年参加させて頂いている久辺テクノフェスタ、今年も11/23に盛大に開催されました!

弊社ブースでは、

・NFCの通信原理を活用した光るお絵かきワークショップ
・UHF帯RFIDを使った宝探し
・VR体験コーナー

みたいな感じの展示で大盛況!



とくに人気だったのは、自分で書いた絵がNFCの電波で光る!という簡単な工作。

手順は、

1.好きな絵を描く
2.光らせたいところに穴をあける
3.コイルを手巻きしてアンテナを作る
4.LEDの足にコイルの両端をくくりつける
5.絵の穴の部分にLEDを差し込んでテープで固定
6.NFCリーダーの上にのせる

という流れで、ワークショップ的に丁度よい作業ボリュームでしょうか。

できあがりはこんな(↓)感じで、こどもたちのクリエイティビティにビックリ。




いつもは手巻きのアンテナを作るところまでで終わることが多いNFCワークショップですが、お絵かきのプロセスを入れるだけでずいぶん盛り上がるものですね!

2016年10月21日金曜日

Raspberry Pi で複数のACRリーダを使う

Raspberry Pi で ACR-122U のリーダをPC/SCで使う場合、SCardListReaders でリーダ名を取得して使用します。で、2台のリーダを接続すると、取得するリーダ名も2つになります。

例) ACS ACR122U PICC 00 00 と ACS ACR122U PICC 01 00

こんな風に後ろの数字が1番、2番と増えていきます。で、後のプログラムではこの名前を指定することでリーダを指定できるわけですね。

なーんだ、リーダいっぱい繋いだって問題無いじゃん、使えるじゃん、へへーんだ、ちょろいちょろい、と日々何事もなく?過ごしていたわけですがそれがなんと大間違いということがわかりました。

実はこの後ろにつく番号、システムをリブートすると変わることがあるのです。変わるといっても3番、4番とかが出てくるわけではなくて、出てくるのは0番と1番なのですが、さっき0番だったリーダがリブートすると1番になってたとかそういうことがあるわけです。

これは一大事です。何とか各リーダを識別する方法を見つけなければなりません。パソリでは SCardGetAttrib で SCARD_ATTR_VENDOR_IFD_SERIAL_NO を指定すればリーダのシリアル番号を取得できるそうですので、ACRリーダも試してみましたがこれがことごとく SCARD_E_UNEXPECTED を返してきて取得できません。どうもACRリーダがこれに対応していないようです。

さー、困りました。とりあえずUSBの接続状態を見てみます。

pi@raspberrypi:~ $ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/5p, 480M
        |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=smsc95xx, 480M
        |__ Port 3: Dev 4, If 0, Class=Hub, Driver=hub/5p, 480M
            |__ Port 1: Dev 5, If 0, Class=Human Interface Device, Driver=usbhid, 480M
            |__ Port 2: Dev 6, If 0, Class=Chip/SmartCard, Driver=usbfs, 12M
            |__ Port 4: Dev 7, If 0, Class=Chip/SmartCard, Driver=usbfs, 12M

複数のACRリーダをつなぐと給電が足りないため、AC付きのUSBハブにリーダを接続しています。

ここには Bus、Port、Dev と3つの番号が出ています。これが使えないか?ということで調べてみると Dev番号が libusb の usb_device->filename で取得できました。この番号は階層の高い方、Portの若い方から割り振られているような気がしますね!きっとそうに違いない!

というわけで PC/SC は諦め、libusb で直接USBにアクセスする方法に変えてみました。

まず Dev番号の若い順に usb_device を取得します。

struct usb_bus *bus;
struct usb_device *dev;

struct usb_device *rdev[READER_NUM];

usb_init();

if (!usb_get_busses()) {
usb_find_busses();
usb_find_devices();
}

min = 0;
for (i = 0; i < READER_NUM; i++) {
max = 999;
for (bus = usb_get_busses(); bus; bus = bus->next) {
for (dev = bus->devices; dev; dev = dev->next) {
if (dev->descriptor.idVendor == 0x072f && dev->descriptor.idProduct == 0x2200) {
if (atoi(dev->filename) < max && atoi(dev->filename) > min) {
rdev[i] = dev;
max = atoi(dev->filename);
}
}
}
}
min = max;
seq[i] = 0x00;
}

で、詳細の説明は省きますがUSBに直接データを送受信します。devに上記で取得した usb_device を指定します。全部のリーダで読む場合は、これを繰り返せばOKです。

usb_dev_handle *handle;
// EndPointを取得
struct usb_config_descriptor *config = &dev->config[0];
struct usb_interface *interface = &config->interface[0];
struct usb_interface_descriptor *altsetting = &interface->altsetting[0];
struct usb_endpoint_descriptor *endpoint = &altsetting->endpoint[0];
uint8_t ep = endpoint->bEndpointAddress;

struct usb_endpoint_descriptor *out_ep = &altsetting->endpoint[1];
struct usb_endpoint_descriptor *in_ep = &altsetting->endpoint[2];

unsigned char buf[256];
ssize_t read_size;
int i,j;

unsigned char command_D[] = { 0x6f, 0x05, 0x00, 0x00, 0x00, 0x00, 0xff, 0x04, 0x00, 0x00, 0xff, 0xca, 0x00, 0x00, 0x00 };
unsigned char command_A[] = { 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00 };

handle = usb_open(dev);

usb_claim_interface(handle, altsetting->bInterfaceNumber);
// リーダの初期化コマンド これで赤ランプが点く
usb_command(handle, out_ep, in_ep, (char *)command_A, sizeof(command_A), (char *)buf, readerIndex);

// カードを認識すると 0x50 0x03 が飛んできて、カードが無くなると 0x50 0x02 が飛んでくるのを読み捨て
read_size = usb_bulk_read(handle, ep, (char *)buf, endpoint->wMaxPacketSize, 1000);
// Get UID コマンドを送信
read_size = usb_command(handle, out_ep, in_ep, (char *)command_D, sizeof(command_D), (char *)buf, readerIndex);
if (read_size > 12 && buf[read_size - 2] == 0x90 && buf[read_size - 1] == 0x00) {
for (j = 0; j < read_size - 12; j++)
sprintf(UID, "%s%02X", UID, buf[10 + j]);
printf("UID = %s\n", UID);
}

usb_resetep(handle, ep);
usb_release_interface(handle, altsetting->bInterfaceNumber);

usb_close(handle);

上記で使っている usb_command 関数は以下の通り

int usb_command(usb_dev_handle *dev, struct usb_endpoint_descriptor *out_ep, struct usb_endpoint_descriptor *in_ep, char *send, int size, char *recv, int readerIndex)
{
ssize_t ret;
int i;

seq[readerIndex]++;
*(send + 6) = seq[readerIndex];

ret = usb_bulk_write(dev, out_ep->bEndpointAddress, send, size, 1000);
if (ret < 0) {
printf("Write Error.(%d,%s)\n",ret, usb_strerror());
return ret;
}

while (1) {
ret = usb_bulk_read(dev, in_ep->bEndpointAddress, recv, in_ep->wMaxPacketSize, 5000);
if (ret <= 0) {
printf("Read Error.(%d,%s)\n",ret, usb_strerror());
return ret;
}
if (*(recv + 6) == seq[readerIndex])
break;
}
return ret;
}

これでめでたく Dev 番号の若い順を意識したリーダへのアクセスが可能になりました。まぁ、リブートしてもDev番号(というか順番)は変わらないよねという前提がありますがー(泣。


2016年8月22日月曜日

EPCの謎に迫る その3

UHFのタグにはEPCというIDが振られていて、EPCエリアにそのID(通常96bit)が書き込まれています。ただEPCエリアにはEPCのIDだけ書かれているわけではなくて、CRCとPCというのが書かれています。CRCはエラーを検出するためのもので、タグが生成しますので読み取り専用です。

で、今回はPCのお話です。通常はEPCが書ければ問題ないので気にすることはないのですが、どうも不思議な動きをしているのを見つけたので調べてみました。仕様については GS1 のサイトに資料がありますのでこちらを参照。

http://www.gs1.org/epc-rfid

PCは16bitで構成されていて、順番に以下の構造になっています。

EPC Length (5bit)  -  EPCの長さです。ワード(2byte)単位に指定します。5bitなので、00000~11111となり、0バイト~62バイトの指定ができます。通常はEPCの長さは 96bit(=12byte)なので、00110が設定されています。

UMI(User Memory Indicator) (1bit)  - ユーザメモリを使用している場合は1、使用していない場合は0を設定するようです。これがちょっと曲者です。後述します。

XPC Indicator (1bit) -  タグが、XPCという拡張機能を実装している場合は1、していない場合は0のようです。

Toggle (1bit) - ここが0だと EPC Globalの仕様のタグで、1だと ISO15961の仕様のタグと見なされます。

Attribute/AFI (8bit) - EPC Globalの場合は EPC Globalで定義されたフォーマットを指定し、ISOの場合はAFIで定義されているフォーマットを指定します。

EPC Lengthは割と使う(EPCの長さを変える)んですが後のはあまり意識したことないですよねー。ほとんどデータフォーマットの情報(Toggle と Attribute/AFI)なんですが、UMIとXPCというのがあります。

XPCはタグの仕様で決まりそうなのでいいとして、問題はUMIです。ユーザメモリを使用している場合は1なんですが、使用してるかどうかって何だろうと思って仕様を見ると、「ユーザメモリの3bitから7bitに1がある場合、自動的に1になる(要約)」だと!

自動的に1になるだぁ?そんな勝手なことでいいのかぁ?そもそも何で3bitから7bitって中途半端なんだぁ?と疑問は尽きません。調べてみるとユーザ領域もお作法があるようです。

ユーザ領域の先頭部分に、DSFIDとPrecursorというそれぞれ 8bit の書式があり、DSFIDの4ビット目~8ビット目にData Formatを格納するところがあって、ここにユーザエリアのフォーマットを入れられるようになっています。この4ビット目から8ビット目というのが、上記の3bitから7bitに相当します(0からはじまるので)。

要するに、ユーザ領域にきちんとした書式が設定(0以外)されていると、UMIが自動的に1になるということのようです。

自動的とかホントか?ということで試してみました。
まず普通のタグ読みます。


画面にはPC+EPCが表示されていて、PCが3000なのでEPCのレングスが6になっています。
で、ユーザ領域のDSFIDをいじります。


そしてもう一回読み取りすると...


あー、ホントだ―、変わってるー。
ってこれ結構厄介だなー、何で今まで気付かなかったんだろう。



2016年8月10日水曜日

同じEPCを持つタグをTIDを使って識別したい

RFIDのタグは貼って使うと、物を識別することができます。それでは、タグ自体を識別することはできるのでしょうか。もちろん異なるIDのタグ同士であれば、簡単に識別することが可能ですね。それでは、タグのIDが同じだった場合はどうなるのでしょうか。
普通はRFIDシステムを使う上で、タグのIDが同じかなんて気にする必要はありません。誰かが重複しないように作成してくれています。この記事はその誰かが確認する場合の方法を検討しながら、RFIDへの理解を深めるのが目的になります。但し、他システムや他社との間でタグIDが重複しないようにする枠組み( ISO国際規格やGS1標準)については扱いません。
RFIDタグは一般的にIDと呼ばれるEPC領域に書き込まれた値の他に、TIDと呼ばれるものを持ちます。TIDにはチップベンダー出荷時点で一意の値が書き込まれており、書換できません。このTIDを使ってタグ自体を識別することができないかを実験しながら確かめてみたいと思います。

それでは、実際にタグのIDとTIDを読んでみましょう。以下のものを用意しました。

  • Alienリーダとアンテナ
  • Alien RFID Gateway(リーダ付属のソフトです)
  • タグ何枚か

Alienリーダが起動したら、Alien RFID Gatewayを立ち上げます。接続されたリーダを選択したら、右下の「Tag Programmer」というボタンをクリックします。下のような画面が表示されます。


Tag Programmerはタグの詳細情報を見たり、タグにIDを書き込んだりすることができます。画面のEPCの部分がタグのIDを、Higgs UTIDの部分がTIDをそれぞれ表しています。

Tag Programmerの機能を使って、今回は3枚のタグに同じIDを書込みました。アンテナにタグを載せ、EPC Dataのボックスに好きなIDを入力し、「ProgramEPC」ボタンを押すと書き込めます。ちなみに書込むときは電波出力は抑えた方がよいです。画面では、RF Attenuationを最大の15 dBに設定しています。

同じIDのタグが準備できたところで、タグを読んでみましょう。アンテナにはIDが同じタグを載せておきます。

複数枚一度に読むので、一旦画面を閉じ、今度は「Tag Grid」という機能を使ってみます。おそらく下の画面のように表示されます。


Tag Grid画面では、1つのIDが1つのアイコンとして表示されるので、3枚のタグのIDが同一のため、一つのアイコンとして表示されてしまっているようです。

それでは、IDに加えTIDも合わせて読んでみるとどうなるでしょうか。リーダの設定を変えて試してみます。

設定の変更にはTag Grid画面の「Alin> Command Line」の「Send>」のところに一行ずつ各行を入力し、エンターを押します。
  1. AcqG2TagData = 2 2 4
  2. TagStreamFormat = Custom
  3. TagStreamCustomFormat = EPC:%i ${G2DATA1}, Disc:%d %t, Last:%D %T, Count:%r, Ant:%a, Proto:%p
一行目はIDを読むときに、TIDも一緒に読むための設定です。二行目はリーダからの送信データに独自書式を使うための設定です。三行目は独自書式の詳細となります。詳細はAlienリーダのマニュアルに載っています。

全行入力し終わると、以下のような表示に変わります。



各アイコンの上に表示される十六進数が従来はIDのみだったのに対し、IDにTIDを付加した形になっており、それぞれ別のタグとして識別することができました。大量のタグのIDが重複しているか確認するには、「Persist Forever」を選択した上で、対象のタグを読ませ、「Save Data」すると、タグのIDがCSV形式で保存されます。あとはExcelなどで加工・集計すれば、確認できると思います。

もう少し使い勝手をよくするために、ExcelToolでIDとTIDを合わせて読みたいと考えているのですが、実現できていません。もちろんアンテナの読取領域に同一IDのタグが一枚だけならば、実現できるのですが、肝心の複数枚存在するパタンでうまくいきません。SessionやSelect、TargetなどRFIDに関するより深い理解が必要なようです。何か分かったら、また投稿したいと思います。

2016年4月8日金曜日

RFIDでディープラーニング その2

前回の投稿ではあまりよい結果が出ませんでしたが、まー、いろいろ端折ってやったしー、みたいなところがあったので、もう少しましな結果が出るように試してみました。

前回ミスったと思われるところは、

・トレーニングのバッチ内のデータが全部 [1.0, 0.0] か [0.0, 1.0] のどちらかに揃っていて、バラバラに入り混じった状態でトレーニングしていない。
・トレーニング数が少ない。

他にも

・学習発散を抑えるために勾配降下のステップ長を学習の進み具合で変化させる。
・ドロップアウト率を学習の進み具合で変化させる。

のようなのもありますがひとまず上側の2つをやってみました。

コードは前回と違うところだけ。データ読み込みの関数を変更。読み込んでリストを返すだけにしました。
※前回同様モバイルでご覧の方はコードが表示されません。すいません。すいません。

# ファイルからデータ読込
def fromFile(file_name, y__):
  rssi = np.zeros([1, X_DATA_SIZE])
  count = 0
  rssilist = open(file_name, 'r')
  for rssivalue in rssilist:
    rssi = np.roll(rssi, 1)
    rssi[0, 0] = float(rssivalue) / 60000.
    count += 1
    if count == X_DATA_SIZE:
      x_list = rssi
      y_list = y__
    if count > X_DATA_SIZE:
      x_list = np.vstack((x_list, rssi))
      y_list = np.vstack((y_list, y__))
  rssilist.close()
  return x_list, y_list
で、データの読み込みと学習を実施。

data_num = 0

# 動いてないときのデータを読み込み
print 'no move phase read'
lists = fromFile('nomove.dat', np.array([[1., 0.]]))

x_all = lists[0]
y_all = lists[1]

# 動いているときのデータを読み込み
print 'move phase read'
lists = fromFile('move.dat', np.array([[0., 1.]]))

x_all = np.vstack((x_all, lists[0]))
y_all = np.vstack((y_all, lists[1]))

print 'total data:' + str(x_all.shape[0])
data_num = x_all.shape[0]

start = 0
for i in range(10000):
  if start + BATCH_SIZE > data_num:
    start = 0
  if start == 0:
    # データをシャッフル
    perm = np.arange(data_num)
    np.random.shuffle(perm)
    x_all = x_all[perm]
    y_all = y_all[perm]
  sess.run(train_step, feed_dict={x: x_all[start:start + BATCH_SIZE], y_: y_all[start:start + BATCH_SIZE], keep_prob: 0.5})
  if i%500 == 0:
    # 正確性と交差エントロピーを表示
    print sess.run(accuracy, feed_dict={x: x_all[start:start + BATCH_SIZE], y_: y_all[start:start + BATCH_SIZE], keep_prob: 1.0})
    print sess.run(cross_entropy, feed_dict={x: x_all[start:start + BATCH_SIZE], y_: y_all[start:start + BATCH_SIZE], keep_prob: 1.0})

学習が終わったら実際にタグが止まっている状態のデータを受信すると...

[ 0.91049373  0.08950625]

キタ―!止まってると認識してますね。

さらにタグを動かしてみます...

[ 0.85994738  0.14005256]
[ 0.78625971  0.21374026]
[ 0.73439723  0.2656028 ]
[ 0.71011215  0.28988785]
[ 0.65806454  0.34193543]
[ 0.27353901  0.72646099]
[ 0.01137005  0.98862994]

※データは見やすいように間引きしています。
おー、徐々に動いてる状態へと変化していきます。すごーい。MNISTのチュートリアルでも十分な精度が出ますねー。
これで何とか足がかりがついた感じです。実際の環境にどれだけ耐えられるかはまだまだ検証が必要ですけどね。



2016年4月1日金曜日

RFIDでディープラーニング その1

RFIDを使ったシステムの特徴の1つはデータがしこたま溜まるということですね。リーダが読み続ける限りアンテナの近くにあるタグは読まれ続け、データが通知され続けるわけです。

となると、

RFID → データいっぱい → ビッグデータ! → ディープラーニング!!

というのはきっと自然の流れですよね。たぶん。

そんなわけでRFIDリーダから得られたデータを使ってディープラーニングにトライしてみました。

リーダから得られるデータですが、基本的にはタグのIDとRSSI値と呼ばれる電波強度が得られます。このRSSI値に注目してやってみました。

ディープラーニングはいろんなツールが出ていますが何となくで Google の TensorFlow を使います。

チュートリアルが単純な順伝搬型のMNISTからはじまって、次がCNNを使ったディープMNISTと進んでいるのでまずはこれを参考にしてみます。

MNISTは手書きの数字の画像を対象としているのですが、こっちはRSSIしかない1次元ですのであんまりしっくりこないですが自然言語処理でも使われてるようなので気にしないでやってみます。

何を学習させるかですが、RSSIのみでとりあえずタグが動いているのか止まっているのかを判別させたいと思います。簡単そうに見えますが、これの難しいところはタグが止まっていても周囲の環境(人が通ったとか)でRSSIもぐぐっと変わるところです。このRSSIの変化をタグが動いているのか周辺のせいなのかを判別させたいとそういうことです。

で、RSSIは連続して得られますので、「過去32個のRSSIの値をみて動いているか止まっているか判別させる」という風にします。

学習用に大量のデータが必要になりますのでデータを溜めます。アンテナの上にタグを吊るしておきます。

止まった状態でデータを採り続ける

扇風機でタグを動かし続けてデータを採り続ける

これでそれぞれ10000個ほどのデータが採れました。

ここから TensorFlow を使ったコード。まずはパラメータの設定とか各処理の関数。関数の部分はチュートリアルとほぼ一緒です。データが1次元なのでプーリングのマトリクスを1×2の1次元にしています。
※モバイルでご覧の方はコードが表示されません。すいません。

import tensorflow as tf
import numpy as np
import socket
from contextlib import closing

X_DATA_SIZE = 32  # RSSIの個数
BATCH_SIZE = 128  # トレーニングのバッチサイズ

FILTER_SIZE = 5   # 畳み込みフィルタのサイズ

CONV_1_CHANNEL_SIZE = 16  # 畳み込み層1の出力チャンネル数
CONV_2_CHANNEL_SIZE = 32  # 畳み込み層2の出力チャンネル数

FC_NEURON_SIZE = 512  # 結合層のニューロン数

# Weightの初期化
def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)

# Biasの初期化
def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)

# 畳み込み
def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='VALID')

# プーリング
def max_pool_2x1(x):
  return tf.nn.max_pool(x, ksize=[1, 1, 2, 1], strides=[1, 1, 2, 1], padding='VALID')

そしてモデルの作成と初期化。モデルはチュートリアルと同じで畳み込み+プーリングの2層になっています。

# プレースホルダを設定
x = tf.placeholder(tf.float32, [BATCH_SIZE, X_DATA_SIZE])
y_ = tf.placeholder(tf.float32, [BATCH_SIZE, 2])

# xを4次元テンソルに変形
x_image = tf.reshape(x, [-1, 1, X_DATA_SIZE, 1])

# 畳み込み層1つめ
W_conv1 = weight_variable([1, FILTER_SIZE, 1, CONV_1_CHANNEL_SIZE])
b_conv1 = bias_variable([CONV_1_CHANNEL_SIZE])

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# プーリング層
h_pool1 = max_pool_2x1(h_conv1)

# 畳み込み層2つめ
W_conv2 = weight_variable([1, FILTER_SIZE, CONV_1_CHANNEL_SIZE, CONV_2_CHANNEL_SIZE])
b_conv2 = bias_variable([CONV_2_CHANNEL_SIZE])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# プーリング層
h_pool2 = max_pool_2x1(h_conv2)

# 畳み込み、プーリングが完了したあとのデータサイズを計算
after_conv_size = ((X_DATA_SIZE - (FILTER_SIZE - 1))/2 - (FILTER_SIZE - 1))/2

# 高密度結合層
W_fc1 = weight_variable([after_conv_size * 1 * CONV_2_CHANNEL_SIZE, FC_NEURON_SIZE])
b_fc1 = bias_variable([FC_NEURON_SIZE])

h_pool2_flat = tf.reshape(h_pool2, [-1, after_conv_size * 1 * CONV_2_CHANNEL_SIZE])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

# ドロップアウト
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# ソフトマックスで出力
W_fc2 = weight_variable([FC_NEURON_SIZE, 2])
b_fc2 = bias_variable([2])

y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

# トレーニング
cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))
train_step = tf.train.AdamOptimizer(0.0001).minimize(cross_entropy)
#train_step = tf.train.GradientDescentOptimizer(0.002).minimize(cross_entropy)

# 評価
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

saver = tf.train.Saver(max_to_keep=10)

init = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init)   # 初期化

続いて先ほど溜めこんだRSSIのデータファイルからデータを読み込んで学習する関数。

# ファイルからデータ読み込んでトレーニングする
def fromFile(file_name, y__, batch_size):
  rssi = np.zeros([1, X_DATA_SIZE])
  # 教師用データは先に作っておく
  y_batch = y__
  for i in range(batch_size - 1):
    y_batch = np.vstack((y_batch, y__))
  count = 0
  rssilist = open(file_name, 'r')
  for rssivalue in rssilist:
    rssi = np.roll(rssi, 1)
    rssi[0, 0] = float(rssivalue) / 60000.
    count += 1
    if count == X_DATA_SIZE:
      rssi_batch = rssi
    if count > X_DATA_SIZE:
      rssi_batch = np.vstack((rssi_batch, rssi))
    if count == (batch_size + X_DATA_SIZE - 1):
      # 学習データが揃ったところでトレーニング
      sess.run(train_step, feed_dict={x: rssi_batch, y_: y_batch, keep_prob: 0.5})
      print sess.run(accuracy, feed_dict={x: rssi_batch, y_: y_batch, keep_prob: 1.0})
      count = 0
  rssilist.close()    

リーダからデータを受信しながら評価する関数。トレーニングのバッチサイズを128にしたので評価時も128個必要なんですが面倒なのでRSSIが32個溜まったらそれをコピーして突っ込んでます。

# リーダからのデータを受信して評価してみる
def receiveRSSI():
  host = '192.168.75.107'
  port = 4000
  bufsize = 4096

  rssi = np.zeros([1, X_DATA_SIZE])
  count = 0

  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  with closing(sock):
    # リーダからのストリーミングを待ちうけ
    sock.bind((host, port))
    sock.listen(1)
    while True:
      conn, address = sock.accept()
      with closing(conn):
        msg = conn.recv(bufsize)
        list = msg.split('\x00')
        for m in list:
          if m.startswith("EPC"):
            epclist = m.split('\r\n')
            for epc in epclist:
              if len(epc) > 0 and epc.startswith("EPC:E200 1AC1 9288"): # 特定のタグで
                tmp = epc.split(',')[2].split(':')[1]
                rssi = np.roll(rssi, 1)
                rssi[0, 0] = float(tmp) / 60000.
                count += 1
                print count
                if count >= X_DATA_SIZE:
                  # バッチサイズ複製して突っ込む
                  rssi_batch = rssi
                  for i in range(BATCH_SIZE - 1):
                    rssi_batch = np.vstack((rssi_batch, rssi))
                  print sess.run(y_conv, feed_dict={x: rssi_batch, keep_prob: 1.0})[1]
                  if count >= 100:  # とりあえず100回でおわり
                    return

で、実際に実行します。止まっているときの教師データは [1.0, 0.0] にして、動いているときの教師データは [0.0, 1.0] にして学習させます。

# 動いてないときのデータで訓練
print 'no move phase start'
fromFile('nomove.dat', np.array([[1., 0.]]), BATCH_SIZE)

# 動いているときのデータで訓練
print 'move phase start'
fromFile('move.dat', np.array([[0., 1.]]), BATCH_SIZE)

# 結果は保存しておく
save_path = saver.save(sess, 'reader_deep', global_step = 0)
print "Model:%s" % save_path

# 実際にリーダから受信して試してみる
print 'valid phase start'
receiveRSSI()


結果
学習後にタグが止まった状態で評価してみると、

[  3.66762222e-04   9.99633193e-01] 

うはー!、ほとんど動いてると思ってますねー(笑 どうも過学習が強いようです。いろいろパラメータ変えてみても、

[ 0.36865199  0.63134807]

とちょっとはましですがどうもというところですね。しかしこの状態でタグを動かすと、0.63 が 0.67 くらいまで上がったりします。より動いてるのでは?という判断はしているようです。ここの判断が大きく値として現れるフィルタが必要なんですかね。

今回はここまで。やはりRNNまで進まないとダメでしょうか。先は長そうです。

2016年2月2日火曜日

レゴマインドストームでお手製の検証装置



先日(って結構まえですが)、

本社@四谷をウロウロしてたら変な音が聞こえてきました。





ピピピッ、ピピピッ、と部屋の片隅で鳴り続ける怪やしい音、


おそるおそる近寄ってみると、見るからに不思議な装置が稼動してました。







なんか観覧車がクルクルと回り続けているんですが、


観覧車の先端にはICカードが取り付けられてますね。


最下部には、リーダーモジュールが潜んでる模様。






ICカードが順次、リーダーモジュールのところに回ってくると、

読み取りに成功して、ピピピッと音が出るわけです。






なるほど。。。


どうやら、リーダーモジュール制御アプリの耐久テスト中っぽい。


開発したプログラムが長時間、問題なく動作してくれるかどうかを確認するために


ひたすら読み取りテストを繰り返していくわけなんですが、


人間がリーダーモジュールにカードを夜通しタッチし続けるのは大変なので、


その辺に転がってたレゴマインドストームで検証装置を作ってみましたよ、


という話でした。






そっかー、 そこまで人手が足りないのかー

いろいろ考えてるわけですね(笑