施錠状態を監視するスマートシステム

2025年2月上旬~2025年3月末
開発形態:個人開発

背景・目的

外出時に家の鍵を施錠したかどうかを忘れ、不安から家まで戻る手間を省くために、このシステムを開発する。画像認識技術を活用した施錠状態の監視システムを構築することで、日常生活の利便性を向上させるとともに、関連技術の学習と実践を目的としている。

システム概要

特定の時間帯にカメラを用いて鍵の施錠状態を監視する。画像認識にはOpenCVを活用し、施錠状態を判断する。

この情報をデータベースにアップロードし、スマホアプリから「施錠中」「解除中」をリアルタイムで確認できるシステムを構築する。

使用技術

開発

ラズパイのカメラからpython言語を使った撮影OpenCVを使った画像認識プログラムの開発

OpenCVを使ってカメラモジュール(以下「カメラ」とする)から受け取った画像からArUcoマーカー(以下「マーカー」とする)を検出した場合は「Lock」、検出できなかった場合は「UnLock」と表記することにした。


    import cv2
    from picamera2 import Picamera2
    from time import sleep
    import numpy as np
    from PIL import Image
    from libcamera import controls
    import cv2.aruco as aruco
    cv2.startWindowThread()
    
    picam2 = Picamera2()
    picam2.start()
    
    # マーカー検出用の関数
    def detectArucoFromImage():
    # 画像を読み込む
    image = picam2.capture_array()
    
    # ArUco辞書を指定
    dictionary = aruco.Dictionary_get(aruco.DICT_6X6_250)
    parameters = aruco.DetectorParameters_create()
    
    # グレースケール変換
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # ArUcoマーカーの検出
    corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, dictionary, parameters=parameters)
    
    # マーカーを検出した場合
    if ids is not None:
        print("Lock")
    
    else:
        print("UnLock")
    
    #カメラ映像を表示
    cv2.imshow("Result",image)
    while True:
            detectArucoFromImage()
            
            key = cv2.waitKey(1)
    if key == 27:
        break
            picam2.stop()
    cv2.destroyAllWindows()
    print("成功")
        

運用

本番環境時の写真

本番環境時の写真

マーカーサイズを2.0cmにし、最大認識距離が70cm以内、電源位置などを考慮し本番環境に設置した。

なお、写真では鍵の上下にマーカーを設置しているが、認識するマーカーは上段の方である。

ラズパイに電源を入れたのち、別コンピュータでSSH接続をしプログラムを常時バックグラウンド実行した。

また、結果を詳細に確認するため以下の二点をプログラムに追記した。

そのため最終的なプログラムは次の通りになる。


    import cv2
    from picamera2 import Picamera2
    from time import sleep
    import numpy as np
    from PIL import Image
    from libcamera import controls
    import cv2.aruco as aruco
    import pymysql
    import traceback
    import datetime
    import subprocess

    cv2.startWindowThread()

    picam2 = Picamera2()
    picam2.set_controls({"ExposureTime":5000,"AnalogueGain":1.0})
    picam2.start()
    marker_now = None
        # マーカー検出用の関数
    def detectArucoFromImage():
        global marker_now

        # 画像を読み込む
        now = datetime.datetime.now().strftime("%H:%M:%S")
        file_path = f"/home/pi/picture/picture_{now}.jpg"
        image = picam2.capture_array()
        mor = "07:00:00" #1の終わり / 2の開始 7:00
        aft = "16:40:00" #1の開始 16:40
        ma = "06:45:00" # 1の開始 6:45
            at = "17:10:00" #1の終わり / 2の終わり 17:10
        # 時間の比較
        if now > ma:
            if now < mor:
                cv2.imwrite(file_path, image)
        if now > aft:
            if now < at:
                cv2.imwrite(file_path, image)


        # ArUco辞書を指定
        dictionary = aruco.Dictionary_get(aruco.DICT_6X6_250)
        parameters = aruco.DetectorParameters_create()
        parameters.adaptiveThreshWinSizeMin= 5
        parameters.adaptiveThreshWinSizeMax= 100
        parameters.adaptiveThreshWinSizeStep= 5
        parameters.minMarkerPerimeterRate = 0.005
        

        # グレースケール変換
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        gray = cv2.resize(gray,(960,540))

        # ArUcoマーカーの検出
        corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, dictionary, parameters=parameters)
        # マーカーを検出した場合
        if ids is not None:
            print("Lock")
            a=0
        
        else:
            print("UnLock")
            a=1

        # 結果を表示
        try:
            connection = pymysql.connect(
                host = "",
                user = "",
                password = "",
                database = "LSMSystem",
                port = 3306
                )
            #print("Excellent!")
            
            with connection.cursor() as cursor:
                if a!=marker_now:
                    print("変更有")
                    if a==0:
                        marker_now = a
                        sql = "UPDATE LSMS SET ima = 0 , today = %s WHERE id = 0"
                        cursor.execute(sql,(now))
                    else:
                        marker_now = a
                        sql = "UPDATE LSMS SET ima = 1 , today = %s WHERE id = 0"
                        cursor.execute(sql,(now))

                    connection.commit()
                    connection.close()

        except Exception as e:
            print("DBerr:",e)
            traceback.print_exc()
        
    while True:
        detectArucoFromImage()
        
        key = cv2.waitKey(1)
        if key == 27:
            break
        
    picam2.stop()
    cv2.destroyAllWindows()
        

結果と考察

グラフ1:2025/02/13 00:00:06 - 23:59:59までの施錠状況

グラフ1:2025/02/13 00:00:06 - 23:59:59までの施錠状況

グラフ1では、1日を通じた施錠状態の変化を可視化している。値が1の場合は「解除中」、0の場合は「施錠中」を示す。

グラフ2:2025/02/13 06:45:05 - 07:00:56までの施錠状況

グラフ2:2025/02/13 06:45:05 - 07:00:56までの施錠状況

朝6:55〜7:00の時間帯に絞って分析。

グラフ3:2025/02/13 07:00:07 - 16:39:49までの施錠状況

グラフ3:2025/02/13 07:00:07 - 16:39:49までの施錠状況

朝7時〜16時40分までの記録:

グラフ4:2025/02/13 16:40:00 - 17:10:56までの施錠状況

グラフ4:2025/02/13 16:40:00 - 17:10:56までの施錠状況

本来は16:40〜17:10に定期撮影を行う予定だったが、プログラムの時間指定ミスにより16:45までしか撮影されていなかった。

「解除中」判定が出た原因は、日没に近づき光量が不足し、マーカー認識精度が低下したことと推測される。

課題と考えられる解決策

夜間の施錠状態が認識できていない。

解決策:赤外線カメラまたはライトを設置し、夜間の認識制度を向上できると考えられる。

誤認識

解決策:AIを用いた画像認識や、ライトをつけるなどして光量が一定以上にすることで解決できると考えられる。

終わりに

本システムを数か月間運用した結果、施錠を忘れたか不安になった際にも、外出先から施錠状態を確認でき、実用的な成果が得られた。 また、PythonやJavaによる開発、AWS上でのサーバ構築、画像認識処理の実装など、広範な技術を実践的に学ぶことができた。 これにより、当初の目的であった「日常生活の利便性の向上」および「関連技術の習得」を達成することができた。