
都会に生息するハトは多くの人にとって愛らしい生き物ですが、ハトの排泄物によるいわゆる「糞害」は景観を損なうだけでなく、建物の腐食や劣化、生活環境の汚染、さらにはアレルギーや感染症などの健康被害を引き起こす可能性もあります。カールスルーエ工科大学コンピューター サイエンスを専攻する修士課程の学生であるマックス ナジさんも、自宅のバルコニーにハトが飛んでくるのに悩まされていましたが、ハトを自動的に阻止する独自のシステムを作成することで問題を解決しました。
ハトの問題に対する過剰設計の解決策 :: Max Nagy
https://maxnagy.com/posts/pigeons/
ハトのフン被害に業を煮やしたナジさんは、まず一般的なハトを撃退する方法をインターネットで調べ、結果を表にまとめた。
| 撃退方法 | 良くない理由 |
|---|---|
| カラスモデルを配置する | 鳩は慣れます |
| カラスステッカーを貼る | 鳩は慣れます |
| 光る風車/CD | 鳩は慣れます |
| 動物の鳴き声 | 鳩は慣れます |
| 犬や猫の毛 | 鳩は慣れます |
| 超音波 | 鳩には聞こえない |
| 鳩を撃つ | ドイツでは禁止されている |
| 猫を飼う | ベランダは5階なので危険です。 |
| 鳥よけスパイク | ベランダには置きたくない |
| 防鳥ネット | 見た目も良くないのでメンテナンスが必要 |
| 手で追い払う | ベランダで待ち続けるのは不可能だ。 |
以上をまとめた上で、ナジ氏は「人間がベランダで待ち続けるのは無理でも、ロボットに委託することはできるのではないか」と考え、思いついたアイデアを整理すると以下のようになります。
・ベランダに電動水鉄砲を設置
・Webカメラでハトを検知
– 電動水鉄砲を自動操作してハトを撃つ
多くの撃退法で問題となっていた「ハトが慣れてしまう」問題は、そもそもハトが水をかけられることに慣れることは不可能なので克服できます。バルコニーを台無しにすることはありませんし、何よりもハトを傷つけたり殺したりすることはありません。そこでまずはAmazonで安い水鉄砲を購入しました。試しに撃ってみると射程は3~4メートルほどですが、ベランダ射程なら十分でした。水鉄砲を迎撃システムに組み込むためには、水鉄砲をインターネットに接続する必要があったので、水鉄砲を分解してみました。
水鉄砲を制御するためのベースは、ウィーモス D1 ミニ選択されました。小さくて安いし、Wi-Fiも付いてます。リレーシールドを追加することで電源の制御が可能となり、電源供給用のUSBケーブルを通す穴も本体に開けました。
古いiPhone 6Sをウェブカメラとして使用しました。ベランダに面した窓の適切な位置を見つけ、3Dプリンターで固定金具を作成し、両面テープ窓に取り付けます。鳩を発見するためにiPCカメラアプリで 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 ボードとラップトップを直接接続するのをやめ、HTTP リクエストを Wemos ボードに中継する「リフレクター」を Go プログラムとして作成することにしました。つまり、リフレクターは HTTP リクエストを受信すると、「PEW!」というメッセージを送信します。接続されている TCP クライアント (つまり、Wemos ボード) に送信します。唯一の難しい部分は、TCP クライアントの再接続を適切に処理することですが、Go ではチャネルそしてゴルーチンを使用して簡単に実装できます。リフレクターの実装は次のようになります。
func main() {
channel := make(chan byte, 42)
http.HandleFunc("https://gigazine.net/", func(w http.ResponseWriter, r *http.Request) {
if len(channel) == 0 {
channel
水鉄砲はTCP経由で接続し、「PEW!」というメッセージを受信すると水を発射します。あなたがしなければならないことは、ラップトップ上に Python スクリプトを残し、Wemos ボードに直接 HTTP リクエストを送信するのをやめ、代わりにサーバー上のリフレクタに送信することだけです。 Wemos ボードでメッセージを受信するプログラムは microPython を使用して実装されました。ナギ氏は、このスニペットにはセキュリティ上の問題があることを認めましたが、個人で使用する分には十分であり、被害があっても「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が電波をうまく拾えないため、ハトは逃げてしまいます。反射板が設置され自動迎撃システムが本格的に機能するようになって初めてハトが訪れ、初めてハト撃退に成功した。
しかし数日後、ハトが現れ、ベランダに置いてあったテーブルによじ登って水鉄砲の射程から逃れた。
ナジさんはハトとの闘いについて「当面はテーブルを室内に置く。つづく」と結論づけた。
この記事のタイトルとURLをコピーします





