2023年10月2日月曜日

GAE/J で排他制御したい

 GAEとっても便利なんですが、どうしても排他制御したい処理が出てきてしまいました。特定のファイルの更新とかそんな感じの処理です。syncronized で同じインスタンスなら排他制御できるので大丈夫なんですが、GAEの場合オートスケールするので同じインスタンスからのアクセスとは限らずその技は通用しません。

ググるとこういうときは Memcache を使って実現するようなのが多いようです。

Memcache の使用方法

これの、「同時書き込みの処理」のところを使って、ロックフラグみたいのをいじるようにすると実現できそうです。

というわけで実装してみたわけですが、、、ちゃんと動作しません。なんだろう、ソースそのまま使ってるしなーと思って上記のソースを眺めていると、、、あれ?これってキーがまだ無いときに複数同時に処理されると皆んな0で作っちゃうんじゃね?ということに気づきました。

ここの処理の肝は putIfUntouched を使って、特定の1スレッドだけが処理を完了できるところにあります。が、キーが無いときはただ 0 を put してるだけなのでここが被ると排他にならないわけです。

putIfUntouched で 0→1 は特定の1スレッド限定にできるんですが、 null→0 をやりたいときは putIfUntouched は IllegalArgumentException を投げてくるので使えません。

キーがない → 0でput → putIfUntouched で1にできた人勝ち

とやりたいところなんですが、勝ちが決まった瞬間に0でputの人が出てくると上書きされてしまうのです。

こまった。どうしたらいいんだ。ということで思いついた苦肉の策が、「putした人はしばらくロックバトルに参加できなくする」という作戦です。putした人の動きをしばらく止めておけば、後から来た人はキーが存在する状態なので putIfUntouched で勝者を決めることができます。

    public void LockFile(String filePath) {
        Random rand = new Random();
        MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();
        
        while (true) {
            byte[] oneValue = BigInteger.valueOf(1).toByteArray();
            byte[] zeroValue = BigInteger.valueOf(0).toByteArray();

            IdentifiableValue lockValue = syncCache.getIdentifiable(filePath);
            if (lockValue == null) {
                syncCache.put(filePath, zeroValue, Expiration.byDeltaSeconds(30));
                try {
                    Thread.sleep(1000);  // ロック情報を作成したスレッドは1秒間ロック取得競争に参加できないようにする
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                if (new BigInteger((byte[])lockValue.getValue()).intValue() == 0 && syncCache.putIfUntouched(filePath, lockValue, oneValue, Expiration.byDeltaSeconds(30))) {
                    // ここに来れるのは1スレッドのみ
                    logger.info("Lock File : " + filePath);
                    break;
                }                
            }
            
            try {
                Thread.sleep(rand.nextInt(100));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }        
    }
    
    public void UnlockFile(String filePath) throws Exception {
        MemcacheService syncCache = MemcacheServiceFactory.getMemcacheService();
        syncCache.put(filePath, BigInteger.valueOf(0).toByteArray(), Expiration.byDeltaSeconds(30));        
        logger.info("Unlock File : " + filePath);
    }

いちおうこれで考えてたような処理が無事できました。

競合が居ない(かつキーが無い)場合は最低1秒待たされることになりますがまぁ仕方ないかな。

2023年7月19日水曜日

AGCさんの電波吸収遮へいガラスを試してみた

 従来の電波吸収体といえばパネルやカーテンみたいなもので遮蔽すると向こうが見えないのが普通でした。これをシースルーで実現させたのが、先日のリテールテックでも展示してありましたAGCさんの電波吸収遮へいガラスです。

東芝テックのRFIDウォークスルーゲート、AGCの電磁波吸収/遮蔽ガラスを採用

この製品のサンプルをAGCさんのご厚意によりお借りすることができましたので実際に試してみました。

試すにあたっていろいろ設置してやってみたのですが、電波の反射や回り込みの影響があるのでなかなか効果を測定することが難しかったので最終的にこんな感じで測定をすることにしました。

簡易測定装置

床にアンテナを置いて、そのすぐ上に電波吸収ガラスを設置します。これだと反射や回り込みもあまり影響しないのでうまく測定できるかなと。上に付いてるのは簡易的なスペアナのアンテナです。

実際にガラスを置いたところ

リーダはマスプロさんの1W機を使いました。アンテナが大きいのが無かったのでシートアンテナを使いました。このアンテナは利得が3dBiなのでフルパワーとはいきませんがまぁ、これしか無かったのですいません。

電波を出してみて、スペアナで見てみます。

ガラス無しの状態

ガラスありの状態

明らかに山の形が小さくなっているのがわかります。ピークっぽいところを比較すると、ガラス無しで 20dB がガラスありで 35dB くらいになってるので電力で換算すると30分の1くらいになってます。これはスゴいですね!

実際にタグを置いて読ませると、


4枚置いてますが2枚しか読めませんでした。読めた2枚もRSSIで見ると-80くらいだったのでかろうじて読めてるという印象です。シースルーでここまで出来るとは驚きでした。

2023年7月12日水曜日

MANICAモバイル用のゲートを作る その3

 前回は1WリーダのSR7と人感センサーを組み合わせて作りましたが、やっぱりハンディも欲しいよねーということでこちらのリーダで実装してみました。

国産で信頼性バッチリのタカヤさんのハンディリーダーです。形はシンプルですが、手元で出力調整も出来て使いやすい仕上がりになっております。

完成したのがこちら

ハンディにトリガボタンがあるのでそれを使うことにして、前回使った人感センサーは使用しませんが、それだとつまらないので温湿度センサーと気圧センサーをつけました。リーダで読み取った環境データも同時に取得できるという無理やりくっつけた感満載の機能です。

実際に動作させてみたのがこちらです。


このセットだけでMANICAモバイルのゲートとして使えます。


2023年5月10日水曜日

導入事例:松竹衣装様

松竹衣装様の導入事例を公開しました!

https://www.hayato.info/home/jirei_shochiku.htm


MANICA Excel Toolをご利用いただき、膨大な衣装類の持出/返却/探索/棚卸業務の効率化を実現いただいています。


従来よりExcelベースでの在庫管理を運用されていたとのことで、Excel資産を活用してそのまま業務をRFID対応できる棚卸パッケージを、VBAカスタマイズも含めて存分にご活用いただいています。




また、定置式リーダーを使用する際には、周囲の不要なICタグを読み込んでしまう課題に直面しましたが、お手製の専用遮蔽ボックスを作成することで、見事に安定的な運用を実現されています。


詳細は下記リンクより、是非ご確認ください。

https://www.hayato.info/home/jirei_shochiku.htm




2023年5月4日木曜日

MANICAモバイル用のゲートを作る その2

 みなさまGWはどうお過ごしでしょうか。今回はGW恒例の?MANICAモバイル用のゲート作りをやってみました。前回の記事は何と5年前!5年も経ってるんだから相当進化してないとまずいよね?というチャレンジです。

MANICAモバイル用のゲートの要件としては、ゲートで読み取って、読み取った数をモニタに表示、そのデータをMANICAモバイルのサーバに送ることでスマホを使わずに入出庫ができるというものです。

数の表示というのが毎度困ってて、AndroidタブレットとかでもいいんですがACアダプタで常時利用となるとバッテリーの問題とかもあって丁度いいのがなかなかありません。何かないかと思ったらこれが机の上にあったので利用したいと思います。

M5Stackちゃんです。中身はいつものESP32なんですが、モニタも付いてるしボタンも付いてるしセンサも拡張できるので便利です。

肝心のリーダですが、前回はSTIXを使ってて250mWしか出なかったので、今回は1Wが出せるこちらを使ってみました。

SR7はBLEで接続しますが、ESP32はBLEが使用できるのでM5Stackそのままで利用できます。筐体はスリムなのに1Wが出せるということでとても進化を感じる端末ですね。

ゲートとはいえ電波出しっぱなしだと何なので、M5Stackに人感センサーをつけてこれが反応している間だけ電波を出すようにしました。

完成品がこちら。

数字だけなので寂しいですが後でいろいろ表示するようにしたいと思います。早速タグを持ってセンサの前に立ちます。

するとほら!読み取ってますねー。ESP32はWiFiも付いているのでWiFiを繋げばこのままクラウドへデータ送信可能です。便利。


2023年4月19日水曜日

機械学習でRFIDの向きを予測

過去にRFIDシールを指に貼りつけてデバイスを操作するという研究がありました。

半分に分けてつがいになったタグを指のうえで重ねて、出来上がったタグで複数のパターンを表現しています。

あとは完成したタグを読み取るだけでよいので確度の高い操作ができますが、機械学習でRSSIを学習させれば、特殊なタグを用いずともタグの状態を判別させていくつかのパターンを作れるのではないかと思いました。

そしてこんなものを用意してみました。



段ボール片の裏表に3枚ずつ、計6枚のタグを貼りつけました。
読み取らせるタグの数は多いほうが機械学習的によいかなと思いました。ただ、手持ちのリーダーの電波出力の制約上、6枚が限界みたいです。

今回は、とりあえずこれをリーダーに対して垂直、水平にかざして、それを分類するのを目標にしました。



はじめに、こんな感じで2,30分くらい読み取らせてデータセットを作ります。

このデータセットをTensorFlowで学習させます。
以下がコードです。

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

#データセットの読み込み・numpy配列に変換
h = (np.genfromtxt('data/h.csv', delimiter=',')).reshape(10000, 6, 2)
v = (np.genfromtxt('data/v.csv', delimiter=',')).reshape(10000, 6, 2)

#ラベルの作成・結合
h_labels = np.ones((10000, 1, 2))
v_labels = np.zeros((10000, 1, 2))
h = np.concatenate([h, h_labels], axis=1)
v = np.concatenate([v, v_labels], axis=1)
data = np.vstack((h, v))

#一応シャッフル
np.random.shuffle(data)

#訓練用とテスト用に分割
train, test = train_test_split(data, test_size=0.2)

#ターゲットデータとラベルの指定
train_x = train[:, :6, :]
train_y = train[:, -1, 1]
train_y = train_y.reshape(train_y.shape[0], 1)

test_x = test[:, :6, :]
test_y = test[:, -1, 1]
test_y = test_y.reshape(test_y.shape[0], 1)

#モデルの作成
model = tf.keras.Sequential([
    tf.keras.layers.Dense(32, activation='relu',
    kernel_initializer=tf.keras.initializers.HeNormal(),
    input_shape=(6, 2)),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

model.compile(optimizer=Adam(learning_rate=0.001),
              loss='binary_crossentropy',
              metrics=['accuracy'])

model.fit(train_x, train_y, epochs=10, batch_size=32)

#モデルの評価
loss, accuracy = model.evaluate(test_x, test_y, verbose=0)

print('loss:', loss, 'accuracy:', accuracy)

#実際に水平・垂直のデータを予測できているかテスト
input1 = (np.genfromtxt('data/hpredict.csv', delimiter=',')).reshape(16, 6, 2)
input2 = (np.genfromtxt('data/vpredict.csv', delimiter=',')).reshape(16, 6, 2)

prediction1 = model.predict(input1)
prediction2 = model.predict(input2)

print(prediction1)
print(prediction2)

評価は以下のようになりました。

Epoch 1/10
500/500 [==============================] - 2s 2ms/step - loss: 0.6687 - accuracy: 0.5763
(省略)
Epoch 10/10
500/500 [==============================] - 1s 2ms/step - loss: 0.3698 - accuracy: 0.8954
loss: 0.3550117611885071 accuracy: 0.8889164924621582

ニューロンの数やoptimizerをいろいろ変えたりして、正確性が89%まで出るようになりました。

実際にいくつかのデータを予測させると以下のようになりました。

水平のデータ
[[0.9883703 ]
  [0.9930196 ]
  [0.69164467]
  [0.8856425 ]
  [0.869069  ]
  [0.95285755]]

 [[0.6108472 ]
  [0.9883703 ]
  [0.83953756]
  [0.9930196 ]
  [0.8858758 ]
  [0.95285755]]

 [[0.83953756]
  [0.9883703 ]
  [0.9970184 ]
  [0.69164467]
  [0.8858758 ]
  [0.9757535 ]]

垂直のデータ
[[6.9464278e-01]
  [4.5149717e-03]
  [2.5530994e-01]
  [1.7064719e-02]
  [4.1041663e-01]
  [5.3049397e-01]]

 [[1.3248680e-02]
  [6.1752790e-01]
  [2.3598520e-01]
  [6.9464278e-01]
  [3.9535922e-01]
  [1.7615510e-02]]

 [[2.5456597e-03]
  [3.9535922e-01]
  [1.7038897e-02]
  [6.9464278e-01]
  [4.3180758e-01]
  [2.5530994e-01]]

水平データに0、垂直データに1のラベリングをしました。
正規化ができていないのでわかりづらいですが、水平と垂直で明らかに値が異なっているように見えます。成功でしょうか。そうであってほしいです・・・

ターゲットデータの形が6x2で、ラベルの形もそれに合わせたのでひとつのデータに対してひとつの予測が出てほしかったのですが、なぜか6個出てしまっています。まだまだ改善の余地があるので今後も勉強していきたいです。

2023年3月29日水曜日

フラッグタグで、読み取り距離10m以上を実現してみた

フラッグタグは、金属製品に貼り付け、金属自体で電波を受けることが可能なため、小さなサイズでも長い読み取り距離を実現しました。

フラッグタグを紹介する一部のサイトでは、金属部分の形状や大きさによっては、10m以上の読み取り距離を実現するとの記載もあるのですが、どのような条件で10m以上の読み取り距離が実現できるのか、試してみました。

タグ   : Smartrac Midas Flag Tag

リーダー : デンソー BHT-1281

距離の測定には、レーザー距離計を使用しました。

レーザー距離計はm(メートル)単位で、小数点3桁まで測定できるのですが、今回の実験では、4回以上読み取った距離を計測して、平均値の小数点3桁目を四捨五入しました。本文の距離の単位はメートルとなります。


(1)材質による読み取り距離の比較

 銅、鉄、アルミニウムの3種類の材質で読み取り距離を比較してみました。

  ※サイズは0.5×100×200(厚さ×縦×横)単位はmm

  ※銅、アルミニウムは純度99%以上(鉄には純度の表記なし)


 以下の写真のように、はみ出すようにフラッグタグを貼った金属板を、プラスチック製のタブレットスタンドに立てかけました。

 リーダーのMANICA EXCEL TOOL で電波を4番目にして、立てかけた金属板を正面から読み取りました。


 なお、レーザー距離計のレーザーを金属板に当てるのが難しかったので、10cm後方の壁に当てて距離を測定しました。

 以下の順位と読み取り距離になりました。

  1位 銅     : 5.55

  2位 アルミニウム: 5.48

  3位 鉄     : 5.36

 今回3種類の金属だけで実験しましたが、電気伝導率の順位と一致するようなので、伝導率が高い金属ほど読み取りが良くなると思われます。

 銅が1位でしたが、価格や入手の都合で、2位のアルミニウムで、次の実験を行いました。


(2)厚さによる読み取り距離の比較

 厚さが 0.3mm、1mm、2mm のアルミニウムで読み取り距離を比較してみました。

  ※縦横は100×200で純度99%以上

 (1)と同じ方法で実験し、以下の順位と読み取り距離になりました。

  1位 0.3mm : 5.53

  2位 0.5mm : 5.48    ※(1)で計測した値(異なる日に測定)

  3位 1mm  : 5.45

   4位 2mm  : 5.33

 それぞれの差は小さいのですが、1位の0.3mmと最下位の2mmは20cmの差があり、薄い方が読み取り距離が長くなりました。

 薄いアルミニウムという事で、安価で加工しやすいアルミ箔を使って、次の実験を行いました。


(3)読み取り距離10m以上に挑戦

 リーダーの出力を最大にし、アルミ箔を小さいサイズから順に読み取り距離を測定しました。

  ※100均で買った幅25cmのアルミ箔を使用

  ※アルミ箔の材質に「アルミニウムはく」とのみ記載されていて純度は不明

  ※(1)、(2)と同様、アルミ箔のサイズの単位はmmとする

 なお、風の影響でアルミ箔が動いてしまうので、プラスチックの下敷きで押さえました。


  ① サイズ250×200 : 5.87

   (2)のアルミニウム板の2倍以上の面積があったたのですが、思っていたほど距離が出ませんでした。(アルミニウムの純度が低い?)


  ② サイズ250×300 : 6.94


  ③ サイズ250×400 : 9.40

    最大で9.856mを測定したのですが、1度も10mを超える事はありませんでした。


  ④ サイズ250×500 : 10.40

   安定して10m以上を測定できました。

   ただし、10mを超えると、度々レーザー距離計が測定不可能となってしまいました。それらも考慮すると、平均距離は、10.4mより長かったと思います。


以上の実験から、サイズが250mm ×500mm以上のアルミ箔であれば、10m以上の読み取り距離を実現できる事が分かりました。


【おまけ】

金属製品で測定してみました。


物置  : 4.08


自動車 : 5.25


アルミ缶 : 5.16


アルミ蒸着PETフィルムの袋 : 8.08