AWS

【駐車場Nシステムの開発】Raspberry Pi 4とAWSのサービス(S3とLambda)を連携

nsystem構成図

突然ですが、駐車場を管理することになりました。
この駐車場は契約車両のみ駐車できる駐車場となっています。
本来なら契約車両以外の車両が駐車されることがないように見回りに行く必要があるのですが、それが面倒なので駐車されているナンバープレートを識別し、契約車両以外の車両が駐車されている場合は私にメールを送信するシステムを開発します。

わたしはITの勉強はしていますが、ハードウェアについては詳しくありません。
できるだけ、早く安く開発したいので、AWSのサービスを使うことにしました。

このシステムは完成していないことをお詫びします。
ほぼテストは完了し後は駐車場に設置するのみということで発注者からストップがかかりました。

せっかくここまで成功しているので誰かの参考になれば嬉しいと思い公開することにしました。

 

最終的な構成

Raspberry Pi 4から送信した画像でナンバーを認識して、登録したナンバー以外ならメール送信します。

nsystem構成図

AWSの環境はできるだけ安く使いたいので、リージョンはオハイオ(us-east-2)を利用しました。

 

カメラ

防犯にもなると思い、目立ったところにカメラを取り付けたいと思いました。

カメラの設置について

カメラを設置するにあたり、駐車場にポールが必要になりました。

このポールに必要な条件は

  • 100Vの電源があること
  • ある程度の高さがありいたずらされないこと
  • 車の邪魔にならないこと
  • 台風などにも強いこと

です。

この条件をみたすために、専門の業者に依頼しました。
見積もりをお願いしたところ、22万円という見積もりがでてきました。

 

カメラについて

カメラがないと話になりません。

カメラに必要な条件は、

静止画をAmazon S3に保管、もしくは動画をAmazon Kinesisに送信できること

防水・防塵仕様で屋外に設置できること

でした。

AWSに送信するためには、SIMを入れて無線通信で行う必要もありました。

しかし、いくら探してもカメラは見つかりません。

いろいろ調べた結果、自分で作ることにしました。

メインモジュールはRaspberry Pi 4とカメラを使います。

メモリーは小さめの32GBにしました。

電源はケースと一緒になっているものを購入しました。
この商品はケースと電源の他にヒートシンクもついていました。

 

Raspberry Pi 4を収めるケースに防犯カメラ用を使います。

 

Raspberry Pi 4の撮影テスト

ラズパイ4とカメラ
Raspberry Piで撮影するの初めてなんですが、めっちゃ簡単でした。

カメラの接続が終わったら、画面から「Raspberry Piの設定」でインターフェースのカメラを有効にします。

$ raspistill -o test.jpg

撮影用のコマンドを実行します。

無事に撮影できました。

 

Amazon S3に送信

撮影した画像をAmazon S3に送信します。

事前にAWSのマネージメントコンソールで作業しておきます。

  • IAMユーザー作成
  • S3バケット作成

IAMユーザーには、「AmazonS3FullAccess」のアクセス権限を追加しておきます。

pip3 install awscli --upgrade

aws cliをインストールします。
ググると色んなサイトがヒットしますが、わたしはこの方法でインストールできました。

aws configure

実行してIAMユーザの情報とリージョンを入力します。
リージョンは、オハイオなので「us-east-2」とします。
output formatは「json」とします。

#!/bin/sh
fn=$(date +%Y%m%d%H%M%S)
raspistill -o /tmp/${fn}.jpg -rot 180
/home/pi/.local/bin/aws s3 cp /tmp/${fn}.jpg s3://nsystem-ohaio/pi-ibs001

写真を撮影してAWSにアップロードするシェルとなります。
カメラの置き方の関係で画像を180度回転しています。

$ crontab -e

0 */4 * * * /home/pi/camera.sh > /tmp/camera.log 2>&1

シェルをクーロンに登録します。
4時間おきにシェルを実行します。

これで自動でAWS S3に画像が保管されます。

 

DynamoDBにテーブルの作成

DynamoDBには車のナンバーや所有者の情報を登録します。

carsテーブル情報

テーブル名は「cars」
項目は、車のナンバーを保管する「car_number」、車の名前を保管する「car_name」、車の所有者を保管する「owner」としました。

4桁のナンバーについてはハイフンが入り、3桁のナンバーについてはスペースが入ります。

 

SESの設定

LambdaからSESを呼んでメール送信します。

受信するメールアドレスを設定します。
設定はこちらを参考にしました。

 

IAMの設定

IAMポリシーの作成

LambdaからSESにアクセスするためにポリシーを作成します。

名前は「lambda_to_ses」としました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendRawEmail"
            ],
            "Resource": "*"
        }
    ]
}

作成したポリシーはこちらです。
sesのメール送信のみ許可しています。

 

IAMロールの作成

lambda_role

名前は「lambda_role」としました。
デフォルトのポリシーを割り当てるのと、前の項目で作成したses用のポリシーを割り当てます。
本番ではもう少し絞ったポリシーを作成して割り当てた方がいいと思います。

 

Lambdaの作成

名前は「nsystem-ohaio」としました。

import os

import boto3
import json

s3 = boto3.resource('s3')

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('cars')

SRC_MAIL = "nsystem@example.jp"
DST_MAIL = "nsystem@example.jp"
REGION = "us-east-2"

def get_car(id):
    response = table.get_item(
        Key={
            'car_number': id
        }
    )
    return response['Item']
    
def send_email(source, to, subject, body):
    client = boto3.client('ses', region_name=REGION)

    response = client.send_email(
        Source=source,
        Destination={
            'ToAddresses': [
                to,
            ]
        },
        Message={
            'Subject': {
                'Data': subject,
            },
            'Body': {
                'Text': {
                    'Data': body,
                },
            }
        }
    )
    
    return response
    

def lambda_handler(event, context):
    
    confidence_threshold = 80
    
    # S3にアップされた画像の情報を取得する
    bucket_name = event['Records'][0]['s3']['bucket']['name']
    object_key = event['Records'][0]['s3']['object']['key']
    # bucket_name = 'ibsnsystem-ohaio'
    # object_key = 'pi-ibs001/cars-short.jpg'
    # file_name = os.path.basename(object_key)
    
    # 画像をRekognitionで解析する
    rekognition = boto3.client('rekognition')
    response = rekognition.detect_text(Image={
        'S3Object': {
            'Bucket': bucket_name,
            'Name': object_key
        }
    })
    
    textDetections = response['TextDetections']
    for text in textDetections:
        confidence_score = text['Confidence']
        if confidence_score > confidence_threshold:
            print ('confidence_score:' + str(text['Confidence']))
            print ('Detected text:' + text['DetectedText'])
            
            # dtに車のナンバーを入れる
            dt = text['DetectedText']
            if "-" in dt: #4桁ナンバー
                try:
                    car = get_car(dt)
                    return car
                except KeyError:
                    email = "駐車場Nシステム"
                    message = "登録していない車両を検知しました。"
                    r = send_email(SRC_MAIL, DST_MAIL, email, message)
                    return r
            if " " in dt: #3桁ナンバー
                try:
                    car = get_car(dt)
                    return car
                except KeyError:
                    email = "駐車場Nシステム"
                    message = "登録していない車両を検知しました。"
                    r = send_email(SRC_MAIL, DST_MAIL, email, message)
                    return r

 

このLambdaには先程作成したIAMロール「lambda_role」を割り当てます。

 

S3の設定

バケットにファイルがアップロードされるとLambdaが動作するように設定します。

lambda_trigger

S3バケットのプロパティでファイルがアップロードされた時のイベント通知を作成します。
これでLambdaが起動できるようになります。

 

このシステムで達成できてないこと

  • 車を認識できていない
  • 1桁と2桁のナンバーに対応できていない
  • 夜間に撮影できない

 

車を認識して文字認識しているわけではない

車を認識してないので、人がナンバーを持って立っていても反応してしまいます。

これについての対応は、Amazon Rekognition Custom Labelsを利用すればうまくできそうですが試していません。

 

1桁と2桁のナンバーに対応していません

対応方法がわかりません。
とりあえず、契約車両にはそのナンバーがなかったので対応していません。

 

夜間に撮影できない

ラズパイにつけているカメラでは夜間に撮影できませんでした。

夜間に撮影できるカメラを探しています。

 

まとめ

Amazonのサービスをうまく使えば安く早く構築できることがわかりました。

問題はカメラですね。
カメラの性能次第でこのシステムが格段に良くなります。

今回作成したプログラムはGitHubで公開します。