ハードウェア

ベランダに飛来するハトを水鉄砲で自動迎撃する自作システムを構築


街に棲むハトは少なからぬ人にとって愛らしい存在である一方で、ハトの排泄物によって引き起こされるいわゆる「フン害」は単に景観を損ねるだけでなく建造物の腐食劣化を招いたり、生活環境の汚染を引き起こしたり、さらにはアレルギー・感染症といった健康被害の原因になることさえあります。カールスルーエ工科大学の修士課程でコンピューター科学を専攻するMax Nagy氏も自宅のベランダに飛来するハトに悩まされていた一人でしたが、彼はハトを自動迎撃するシステムを自作することで解決を図りました。

The overengineered Solution to my Pigeon Problem :: Max Nagy
https://maxnagy.com/posts/pigeons/

ハトのフン害に閉口したNagy氏はまず一般的なハトの撃退方法を知るべくネット検索を行い、調査結果を表にまとめました。


撃退方法ダメな理由
カラスの模型を置くハトが慣れる
カラスのステッカーを貼るハトが慣れる
ピカピカの風車・CDハトが慣れる
動物が吠える声ハトが慣れる
イヌやネコの毛ハトが慣れる
超音波ハトには聞こえない
ハトを撃つドイツでは禁止されている
ネコを飼うバルコニーは5階なので危険
防鳥スパイクバルコニーに置きたくない
防鳥ネット見た目が悪い上にメンテナンスが必要
人の手で追い払うバルコニーで待ち続けるのは無理


ここまでまとめたところでNagy氏は「人間がバルコニーで待ち続けるのは無理だとしても、ロボットにアウトソーシングできるのではないか?」と考えました。思いついたアイデアを整理すると以下のようになりました。

・バルコニーに電動水鉄砲を設置する
・ウェブカメラでハトを検知する
・自動的に電動水鉄砲を操作してハトを撃つ

多くの撃退方法で課題だった「ハトが慣れる」問題についても、そもそもハトが水を掛けられることに慣れることはあり得ないのでクリアできます。バルコニーを台無しにすることもありませんし、何よりハトを傷つけたり殺したりすることがありません。そこでまずはAmazonにて安い水鉄砲を購入しました。試し撃ちしたところ射程距離は3~4メートル程度でしたが、バルコニーの範囲なら十分でした。迎撃システムに組み込むには水鉄砲をインターネットに接続する必要があるため一旦水鉄砲を分解しました。


水鉄砲を制御するための基盤にはWemos D1 Miniを選択しました。小型で安価である上にWi-Fiも搭載しています。リレーシールドを追加することで電源の制御ができるようにし、給電用のUSBケーブルを通す穴を本体に開けました。


ウェブカメラには古いiPhone 6Sを流用しました。ベランダに面した窓の適当な位置を見つけて固定用のブラケットを3Dプリンターで作成し、両面テープで窓に設置。ハトを検知するためにiPCameraアプリでMJPEGストリームを取得します。

ハードウェアはそろったので次はソフトウェアを作成します。まずはハトの飛来を画像解析で検知する必要があるため、ノートパソコンでOpenCVを使ったPythonスクリプトを実行して「通常の背景画像」と「現在の画像」を比較することにしました。画像の全ピクセルについて差分を測定し、差分の平均値が閾値を超えた場合にハトが来たと判定するわけです。より判定に確実性を持たせるため以下を考慮するようにします。

・ピクセルの差分が10%未満なら、差分0として変化なしと扱う
・変化量が過剰に大きい場合は、誰かがカメラに触れたものとして判定を無視する
・風に揺れる花や誰かが窓に近づいた際の写り込みを無視するよう、画像にマスクを追加する
・一旦変化を検出した後は、変化がなくなるまで待機する

stream = CamGear(source="http://IP_OF_IPHONE/live").start()

    background = None
    while True:
        frame = stream.read()
        # マスクを適用する
        frame = cv2.bitwise_and(frame, frame, mask=mask)

        # グレイスケール変換
        frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)

        if background is None:
            background = frame
            continue

        # 背景の更新
        background = cv2.addWeighted(
            background, background_alpha, frame, (1 - background_alpha), 0
        )

        # 現在の画像と背景との差分を求める
        diff = cv2.absdiff(frame, background)

        # 閾値の適用
        _, diff = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)

        score = np.mean(diff)
        if armed:
            if score > 10:
                # カメラが動かされた
                time.sleep(10)
                armed = False
            elif score > 0.5:
                # ハトが来た!
                armed = False
                time.sleep(1)
                frame = stream.read()
                cv2.imwrite(
                    f"{datetime.now()}.jpg",
                    frame, # 後で使用するために画像を保存する
                )


                requests.get("http://IP_OF_SERVER", timeout=1)

        else:
            # 画像が落ち着いたら再実行する
            armed = (score == 0)

最初の実装ではWemosボード上のHTTPサーバーを使用しリクエストを受信するたびに起動する方法を採用していました。しかしながらボードのWi-Fiアンテナでは自宅のWi-Fiスポットまで電波が届かないという問題が発覚。解決策としてボードをiPhoneのホットスポットに接続することにしました。ただ、Wemosとノートパソコンが別のネットワークに接続されてしまい直接通信できなくなるという別の問題が発生。

問題を根本的に解決するためにWemosボードとノートパソコンを直接するのをやめ、WemosボードへHTTPリクエストをリレーするための「リフレクター」をGoプログラムとして作成することに。つまり、HTTPリクエストをリフレクターが受信すると接続中のTCPクライアント(つまりWemosボード)に「PEW!」というメッセージを送信するわけです。唯一難しいのはTCPクライアントの再接続を適切に処理するところですが、Go言語ならチャネルゴルーチンを使えば簡単に実装できます。リフレクターの実装は以下の通りです。

func main() {
	channel := make(chan byte, 42)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if len(channel) == 0 {
			channel <- 42
		}
	})

	go http.ListenAndServe(":4242", nil)

	sock, err := net.Listen("tcp", ":4243")
	handle(err)

	for {
		conn, err := sock.Accept()
		if err != nil {
			continue
		}
		go func() {
			defer conn.Close()
			for {
				<-channel
				_, err = conn.Write([]byte("PEW!\n"))
				if err != nil {
					break
				}
			}
		}()
	}
}

水鉄砲はTCP経由で接続し「PEW!」というメッセージを受信すると水を発射します。ノートパソコンのPythonスクリプトはそのまま残し、HTTPリクエストをWemosボードに直接送信していたのをやめて、代わりにサーバー上のリフレクターに送信すれば万事解決となります。Wemosボードでメッセージを受け取るプログラムはmicroPythonで実装しました。なお、Nagy氏はこのスニペットにはセキュリティ上の問題があることを認めた上で個人的な使用目的には十分であるとし、たとえ被害にあったとしても「200mlもの水をバルコニーに捨ててしまうようなこと」で収まるため一応問題なしとしています。

def main():
    # リフレクターに接続
    pin = machine.Pin(5, machine.Pin.OUT)
    addr = ("IP_OF_MY_SERVER", 4243)
    while True:
        s = socket.socket()
        s.connect(addr)
        try:
            while True:
                if b"PEW" in s.recv(255):
                    print("PEW!")
                    pin.value(1)
                    time.sleep(0.5)
                    pin.value(0)
        finally:
            s.close()

自動迎撃システムが稼働して最初に飛来したハト。


ハトは水鉄砲の射程範囲内にいますが、この時点ではリフレクターが導入されておらず水鉄砲のWi-Fiが信号をうまく拾えていなかったためハトは難を逃れています。リフレクターを導入して自動迎撃システムが完全に機能するようになって初めて訪問してきたハトは、初の撃退に成功したとのこと。


ただし、さらに数日後に現れたハトはバルコニーに出しっぱなしにしていたテーブルに上ることで水鉄砲の射程を逃れてしまいました。


Nagy氏はハトとの攻防について「とりあえずテーブルは室内に置くようにします。つづく」と締めくくりました。

この記事のタイトルとURLをコピーする

・関連記事
「ハトにカメラを仕込んで撮影」など動物を使ったスパイ活動の成功例と失敗例について国際安全保障の専門家が解説 - GIGAZINE

ウェブサイトに侵入してくる相手にZIP爆弾を送りつけて撃退する方法 - GIGAZINE

「インターネット」と「伝書ハト」はどっちが速いのか? - GIGAZINE

戦闘機が麻薬密輸機を撃退しフランスの田舎に麻薬の雨が降る - GIGAZINE

「最もインスタ映えする鳥」が研究によって明らかに - GIGAZINE

in ハードウェア,   ソフトウェア,   生き物, Posted by log1c_sh

You can read the machine translated English article I built a homemade system that automatic….