指紋認証入門

私のいる高専ではICカードを使った認証をよく使っていて私もPasoriで遊んでみたりしていました。
blackuma.hatenablog.com

ただしこれにはデメリットもあり、例えば出席管理でICカードを使う場合は他の人に託してしまえるのでよく「監視しておく人」というのが設けられます。

せっかく自動化・効率化の技術を使うのに、一時的に生産性が著しく低い人間が生まれてしまうというのは私のプライドが許しませんでした。

他の認証を考えた時、生体認証しか思いつかなかった私は、Ali Express で指紋認証モジュールを約2000円で購入しました。(生体認証のデメリットは調べてない...)
ja.aliexpress.com

海外の文献は充実していたので基本的な認証動作はすぐにできました。
How to work with Fingerprint scanner using Arduino nano — Steemit

これだけでも十分使えますが、出席管理を想定して誰がいないかを表示できるようにステップ3.2を編集します。

#include <Adafruit_Fingerprint.h>

#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3);

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

// 127個の1の配列
int List[127];

void setup()  
{
  int i;
  for (i = 0; i < 127; i++ ) {
    List[i] = 1;
  }
  
  Serial.begin(9600);
  while (!Serial);  // For Yun/Leo/Micro/Zero/...
  delay(100);
  Serial.println("\n\nAdafruit finger detect test");

  // set the data rate for the sensor serial port
  finger.begin(57600);
  
  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1) { delay(1); }
  }

  finger.getTemplateCount();
  Serial.print("Sensor contains "); Serial.print(finger.templateCount); Serial.println(" templates");
  Serial.println("Waiting for valid finger...");
}

void loop()                  
{
  if(Serial.available()>0)
  {
    switch(Serial.read())
    {
      case 'a': //データが'a'であれば次を実行する
        int i;
        for (i = 0; i < 127; i = i + 1) {
          if (List[i]==1){
            Serial.print(i+1);Serial.print(" ");
          }
        }
        break; 
      default:
        break;
    }
  }
  getFingerprintIDez();
  delay(50);            
}

uint8_t getFingerprintID() {
  uint8_t p = finger.getImage();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image taken");
      break;
    case FINGERPRINT_NOFINGER:
      Serial.println("No finger detected");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_IMAGEFAIL:
      Serial.println("Imaging error");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }

  // OK success!

  p = finger.image2Tz();
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      return p;
    default:
      Serial.println("Unknown error");
      return p;
  }
  
  // OK converted!
  p = finger.fingerFastSearch();
  if (p == FINGERPRINT_OK) {
    Serial.println("Found a print match!");
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    return p;
  } else if (p == FINGERPRINT_NOTFOUND) {
    Serial.println("Did not find a match");
    return p;
  } else {
    Serial.println("Unknown error");
    return p;
  }   
  
  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID); 
  Serial.print(" with confidence of "); Serial.println(finger.confidence); 

  return finger.fingerID;
}

// returns -1 if failed, otherwise returns ID #
int getFingerprintIDez() {
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  return -1;
  
  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID); 
  Serial.print(" with confidence of "); Serial.println(finger.confidence);
  List[finger.fingerID-1] = 0;
  return finger.fingerID; 
}

ステップ3.1に「from 1 to 127」とあったから127個の配列にしていますが128でも登録&認証できたのでたぶん無限に増やせます。

シリアル通信で"a"を送信してやると配列が表示されるようになっていて、認証が済んだ番号の配列は次から表示されなくなります。

番号と人との対応表を作ったうえで、127の所をすべて登録人数に置き換えてやると出席管理ツールの出来上がりです。

Codamaでお地蔵さん

ユカイ工学でインターンをさせて頂きました!

「Codama」という音声コミュニケーションキットを使ってお地蔵さんのような形をした司会者支援ツールを作ることにした私は、3日目かけてハードとソフトの両方に取り組みました。

まずハードの面では2時間ほどで大まかに設計した後、業務用3Dプリンターで出力させてもらいました。造形時間の関係で修正がいくつかありましたが、出来上がりはすごく綺麗でした。

f:id:massiro-myaon:20190402020507j:plain

ソフトの面では、Google Assistantをベースにするため公式サイトの手順に沿って準備しました。私はGoogle Assistant Serviceの方を選びましたが、手順通りとはいえすごく時間がかかりました。

https://developers.google.com/assistant/sdk/guides/service/python/

pushtotalk まで使えるようになったら、pushtotalk.pyの460行目付近を編集して、ウェイクアップワードで起動するようにします。(pushtotalk.pyの階層はgooglesamples-assistant-pushtotalkをctl+cで停止したときのエラー文に表示される)

while True:
if GPIO.input(27) == GPIO.HIGH:
        #if wait_for_user_trigger:
        #    click.pause(info='Press Enter to send a new request...')
        continue_conversation = assistant.assist()
        # wait for user trigger if there is no follow-up turn in
        # the conversation.
        wait_for_user_trigger = not continue_conversation

次にpushtotalk.py とひたすら睨めっこして、Assistant が認識した音声によって分岐を作ることに取り組みました。

AIY キットを使ったものは試したことがありましたが、1から image を書き換えるわけにもいかないので苦労しました。 

どうやらrespというjsonファイルに認識結果を格納しているようですが、150行目付近の%sがそのまま使えそうなので、今回はよく理解しないままここを使いました。

if resp.speech_results:
    logging.info('Transcript of user request: "%s".',
        ' '.join(r.transcript
            for r in resp.speech_results))
    word = (' '.join(r.transcript
                         for r in resp.speech_results))
if word == u"今日は":
    ~

LEDやサーボの機能もcodama ボードで実装出来ているので、気が向いたらGoogle Assistantの分岐先で組み合わせて行こうかと思います!

画像処理入門

周南ロボコンの際ねじやナットが大量に混ざり合い何時間もかけて

分別・収納する期間があります。

メカトロ部の1年生も毎年オフシーズンに苦労するそうです。

 

というわけで近いうちにねじの分別装置作ろうと思います!

今回はその前段階というわけで先輩の記事を参考にマッチング処理をしてみます。


m12watanabe1a.hatenablog.com


こちらの後半にあるプログラム、基本どんな画像でもマッチングしてくれます。

今回はマッチするかどうかでON or OFFをさせたいのでその部分を紹介致します。


マッチしているかを判断する方法はいくつかあると思いますが、今回でいうと描画する線の数が気になるところです。

それをどこで判断しているかというと# マッチング精度が高いもののみ抽出のところ。

ratioがマッチング判断の範囲を表しているわけですが、goodという配列を用意して
画素の特徴量を1つずつ検証して高いものだけを追加しているようです。

つまり
good内の要素の数=マッチした画素の数(描画する線の数)
となっていることが分かる。
配列の要素の数はlen関数で求まるので以下のようになる。

# マッチング精度が高いもののみ抽出
MIN_MATCH_COUNT = 8
ratio = 0.8
good = []
for m, n in matches:
    if m.distance < ratio * n.distance:
        good.append([m])
if len(good) < MIN_MATCH_COUNT:
    print("OFF")
if len(good) > MIN_MATCH_COUNT:
    print("ON")

すこし論証みたいになってしまいましたが、あとは基準を決めて線が何個以上ならON(今回は8個)以下ならOFFにするか決めて必要な処理を書いてやるだけです。

ついでにラズパイでのOpenCVのセットアップ方法とラズパイカメラを使ったマッチング処理プログラムを載せておくので参考にしてみてください!

*install of opencv の方だけでopencvは基本動作しますが、Akaze自体の導入は比較的最近なので今回の場合は additional の方も必要になります。

# install of opencv
$ sudo apt-get update
$ sudo apt-get install libopencv-dev python-opencv

# additional install for Akaze
$ sudo pip3 install opencv-contrib-python
$ sudo apt-get install libatlas-base-dev
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import RPi.GPIO as GPIO
import time
import picamera
import subprocess

import cv2
import numpy as np

from datetime import datetime

MIN_MATCH_COUNT = 8

camera = picamera.PiCamera()
camera.resolution = (640,480)
filename = datetime.now().strftime('assets/img/sample/test.jpg')


# 画像読み込み先のパス,結果保存用のパスの設定
template_path = "assets/img/template/"
template_filename = "maku.jpg"

sample_path = "assets/img/sample/"
sample_filename = "test.jpg"

result_path = "assets/img/result_AKAZE/"
result_name = "perfect.jpg"

akaze = cv2.AKAZE_create() 

camera.capture(filename)

# 文字画像を読み込んで特徴量計算
expand_template=2
whitespace = 20
template_temp = cv2.imread(template_path + template_filename, 0)
height, width = template_temp.shape[:2]
template_img=np.ones((height+whitespace*2, width+whitespace*2),np.uint8)*255
template_img[whitespace:whitespace + height, whitespace:whitespace+width] = template_temp
template_img = cv2.resize(template_img, None, fx = expand_template, fy = expand_template)
kp_temp, des_temp = akaze.detectAndCompute(template_img, None)

# 間取り図を読み込んで特徴量計算
expand_sample = 2
sample_img = cv2.imread(sample_path + sample_filename, 0)
sample_img = cv2.resize(sample_img, None, fx = expand_sample, fy = expand_sample)
kp_samp, des_samp = akaze.detectAndCompute(sample_img, None)

# 特徴量マッチング実行
bf = cv2.BFMatcher()
matches = bf.knnMatch(des_temp, des_samp, k=2)


# マッチング精度が高いもののみ抽出
ratio = 0.8
good = []
for m, n in matches:
    if m.distance < ratio * n.distance:
        good.append([m])
if len(good) < MIN_MATCH_COUNT:
    print("OFF")
if len(good) > MIN_MATCH_COUNT:
    print("ON")

dualshock3でメカナム制御

メカナムホイールは4つのタイヤにそれぞれ45度傾いたタルを付けることで

全方向駆動を可能にするタイヤである。

tosadenshi.co.jp

今回はそれをPS3用コントローラ「dualshock3」で制御する。

 

先に参考文献を紹介。

Arduino + USBホストシールドの実験。 - robo8080のブログ

ArduinoでRC可動戦車(5) - Ganponブログ

 

通信周りの構成としては Arduino + USBホストシールド + Bluetoothトングル の組み

合わせが無難であるため真似して作った。モータードライバーにはTA7291Pを使用。

 

プログラム

GitHub - Myaon/PS3_Mecanum

 

今後もdualshock3を使って多方面で遊んでいきます!

 

 

再起動を自動起動

今回はラズパイのプログラム自動起動にrebootを書いてしまい

永遠に再起動を繰り返すラズパイを何とかして元に戻す方法を紹介します。

hendigi.karaage.xyz

自動起動に関しては上のサイトの最初にある「/etc/rc.local」を編集する

という方法をとりました。

 

ラズパイは再起動を繰り返すのでrc.localを編集することは出来ず、Windows でSDカードを編集しようにもroots ディレクトリが開けませんでした。

 

結論から言うとLinux導入しましょうという話。

 

ラズパイに主として利用されるRasbianと同じLinux環境であればbootやrootsのディレクトリが開けるので後は管理者権限で編集してあげるだけです。

 

LinuxのインストールについてはGoogle検索してください。

(参考)黒猫はLinux mintを使用。空のUSBが1つあれば導入できる。

 

環境が整ったら問題のSD cardをLinuxが動作しているPCに差し込んでコマンドプロンプトから

cd /media/user/rootfs/etc/

sudo nano rc.local

を実行し、rebootを取り除いて保存しましょう。

 

さすれば元通りのRasbianが動くようになるでしょう。

めでたしめでたし!

 

周南ロボコン

11/3~4に高専祭がありました!

自分は初日にありました、周南ロボコンを部署長として

1年間進めてきて、現在無事に終了したことを

しみじみと実感しているところです☕

 

<周南ロボコンとは>

  • 徳山高専高専祭初日に行われるロボットコンテスト
  • 子供から大人まで誰でも参加できる(景品あり)
  • 夏休みに競技を最低限クリアできるサンプルロボットを作る工作教室を実施
  • 企画・設計・運営までを現役高専生がすべて行います!

http://www.tokuyama.ac.jp/robocon/2018/robocon2018/top.html

 

詳細はHPに毎年掲載していますのでそちらをご覧くださいm(__)m

 

今年の競技はあらゆる障害物を2つのロボットで共にクリアしてもらう

「障害物共走」。

フィールドの盲点をついたロボットが順調に競技をクリアしていく中

今年はメカトロシステム部(OB含む)が活躍し、ベスト4に子供チーム

が残らないという結果になりました( ;∀;)。

しかし、あらゆる技術を駆使したロボットたちは会場を大いに盛り上げてくれました!

 ↓ 競技後に戯れるロボットたち

f:id:massiro-myaon:20181106175045j:plain

参加してくれた子供たちがこれを機に

より高い技術に興味を持ってくれると嬉しいですね!

ロボットダンスシステム

台風21号が接近する中、私は大阪でインターンシップに取り組みました。

f:id:massiro-myaon:20180924122202j:plain

4日はリモートでの作業↴

f:id:massiro-myaon:20180924122328j:plainコミュニケーションロボットであるSotaを音楽に合わせて躍らせることを目標に1週間の研究に取り組みました。

 

Sotaとコミュニケーションを取りながら、音楽プレイヤー役のRaspberry Piを操作します。そして音楽のリズムをセンサで取得し、それに合わせてRaspberry Piから送り返す信号を変えて、Sotaの踊りを変化させます。

 

Sotaをクライアント。Raspberry PiをサーバーとしたTCP/IP通信を用いました。

開発環境は以下の通りです。

「Vstone Magic」・・・Vstoneが提供するビジュアルプログラミングソフト(Java

「Thonny Python IDE」・・・RaspbianにデフォルトであるPython IDE

 

苦戦しながらも形にはなっていく中、問題となったのはリズムを取得する方法です。

当初、Pythonのモジュールとして「librosa」というものがありました。

github.com

見たところすごく優秀なモジュールなのですが、結論から言いますと「Raspberry Piでの実装方法」が期間内に分からず断念しました(泣)。ぜひ誰か分かる方いたら教えてくださると幸せます。

 

とはいえ、モノは期間内に完成させたい…ということで以下の方法をひらめきました。

f:id:massiro-myaon:20180924130005p:plain

今回はデモ用にこれだけにしましたが、②の計測を常に行えば曲のテンポの変化に対応できたり、③の分割も細かくすることで、バリエーションを増やすことができます。

 

何より、この仕組みはシンプルながら利用シーンにピッタリだということで、良い評価を得られました (*'ω'*)。

 

最後に今回のプログラム等を掲載しておきます。今後どんなハードウェアにも対応できるようなシステムに改良していこうと考えているので、良いアイデアがあれば、ご教授願います。

 

github.com