2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Wiki IoT/Bot Computing で裏庭に来る動物撮影

Last updated at Posted at 2025-12-15

はじめに

Wiki IoT/Bot Computing で、裏庭に来た動物を、昼も夜も、撮影できたので紹介します。

どうも最近鹿がきているらしいので、それが撮影できたら嬉しいな、と思ってやっているのですが、今の所撮影できたのは猫ばっかりです。

cat-01.jpg

昼間は上のような感じで撮影できました。

cat-03.jpg

夜は上のような感じで撮影できました。

ここで今回動物撮影に成功したシステムを、「動物撮影システム」と呼ぶことにします。

Wiki IoT/Bot Computing の概要

Bot Computing/Wiki IoT は、PukiWikiのページにかかれたスクリプトをIoTのEdge端末が実行し、実行結果をスクリプトが書かれたPukiWikiのページに書き加える、というものです。

Edge端末がWikiのページの記述で遠隔操作されるBot となります。これをWiki Botと呼んでいます。

wiki-bot-20250102-01.png

Wiki Bot が読み込むPukiWiki のページを途中で変更したり、Wiki Bot が実行するWiki ページに、別のPukiWikiのページの内容を挿入したりすることもできます。

Wiki Bot の PukiWiki のAPIとしての機能

Wiki Bot は、PukiWiki のAPI として機能します。

Read, Update, Delete

Wiki Bot によって、PukiWiki の、ページの読み込みとページの編集を行うことができます。

したがって、Wiki Botにより、Webページ操作の C (Create) R (Read) U (Update) D (Delete)のうち、RUDを行うことができることになります。このとき、ページの削除は、ページの編集によって可能です。
C (Create) は実装していないので、あらかじめ使用するページを人手によって準備しておく必要があります。

添付ファイルの操作

PukiWiki では各ページに画像や音声などのファイルを添付することができるのですが、Wiki Bot により、添付ファイルのページへのUpload、ページに添付されたファイルの名前の一覧の獲得、ページから指定したファイルの削除を行うことができます。

特定のユーザによるアクセス制御

PukiWiki は、その設定ファイルの pukiwiki.ini.php ファイルの設定により、特定のユーザによる、特定のページの読み書きの制御を行うことができますが、Wiki Bot では、特定のユーザによる、(すべての)ページの読み書きの制御を行うことができます。特定のページのみの読み書きの制御はまだ行うことができません。

システム概要

今回動物撮影システムを制作するにあたり、もともとあった、Wiki Bot による以下のIoTシステム(202503システム)のカメラを普通のRaspberry Pi カメラから、赤外線カメラに置き換え、赤外線LEDランプを加えました。
また、赤外線LEDランプをOn/Off するため、モータドライバ DRV8853
を加えました。

動きを検知したら、赤外線LEDランプをOnにして、撮影し、撮影が終わったら赤外線LEDランプをOffにする訳です。画像ファイルの数を制限するため、動きを検知して撮影したら、10分間、撮影は行わないようにしています。

wiki_iot_bot_setpage_include_01.png

使った赤外線カメラと赤外線LEDランプ。

ハードウェア

今回の動物撮影システムのハードウェア(センサBox)の外観を以下に示します。

sensor_box_outside_01.jpg

センサBox の中身は以下のようになっています。

sensor_box_inside_01.jpg

すでについていたPIR動きセンサのOn/Off の情報を、センサデータ取得のために使っている Raspberry Pi Pico だけでなく、映像データをUploadするために使っている Raspberry Pi (カメラRasPi)でも使うように、PIR動きセンサの出力を、カメラRasPi のGPIO端子の、GPIO18番端子にも配線しています。

赤外線LEDランプをON/Off するために、カメラRasPi の、GPIO17番端子から、DRV8853のA-in へ配線を行い、DRV8853のA-out1 とA-out2を赤外線LEDランプの+端子と-端子に接続しています。赤外線LEDランプは2つあるので、並列接続しています。

ソフトウェア

動物撮影システムは、既存の202503システムの、class-daily ファイルのPython プログラムを書き換えて実現しています。

オブジェクトページ

動物撮影システムのオブジェクトページは、既存の202503システムのものと同じです。以下の図は、12日に得られた様々なセンサから得られたデータの、1時間ごとの平均を格納した、オブジェクトページです。なお、このデータは、次の月のデータで上書きされるようになっています。

daily-page-02.png

このページの、上の方にある、

https://www**********/sensors-graph-01-daily.html

をクリックすると以下のような、202503システムによって獲得された、温度、湿度、明るさ、動き、二酸化炭素濃度、気圧、におい1、におい2、微粒子の、そのページに書かれた値の時系列変化をグラフで見ることができます。

daily-graph-01.png

元に戻って、オブジェクトページに下の方に、添付ファイルの一覧が表示されています。この一覧のなかで、"motion-<時間>_<分>"で表されているファイルは、動物や人間の動きを検知したとき(<時間>_<分>)に撮影された画像を表しています。

daily-page-03.png

例えば、"motion-13_21.jpg"のリンクをクリックすると、以下のような画像が表示されます。

cat-06.jpg

動きを検知したけど、なにも撮影されていない場合も結構あります。

クラスページ

オブジェクトページの上の方に

include <URI-1> or <URI-2> 

で表された、クラスページを挿入することを示した行があります。

daily-page-02-include-class-daily-01.png

この <URI-1>または <URI-2>に、動物撮影システムの class-daily のページが格納されています。

以下、class-daily のページの、上の方の一部です。

class-daily-01.png

このクラスページ全体は以下のようになっています。

command: set read_interval=1800000
command: set exec_interval=0
command: set report_length=216
command: py daily
py: import subprocess
py: import time
py: import RPi.GPIO as GPIO
py: date=self.Pico_Time.get_now()
py: #date=2024-12-31 14:25:16.61944
py: time_x=(date.split())[1]
py: #print('time_x='+time_x)
py: time_xx=time_x.split(':')
py: hm=time_xx[0]+'_'+time_xx[1]
py: ms=time_xx[1]
py: ss=time_xx[2]
py: dec_sec=ss[0]
py: this_hour=time_xx[0]
py: #print('hm='+hm+' ms='+ms+' ss='+ss+' desec='+dec_sec)
py: print('time='+hm)
py: attach_dir='/home/pi/python/pictures/'
py: attach_name=hm+'.jpg'
py: print('attach_name='+attach_name)
py: #
py: GPIO.setmode(GPIO.BCM)
py: #
py: GPIO.setup(17,GPIO.OUT)
py: GPIO.setup(18,GPIO.IN)
py: #
py: GPIO.output(17,GPIO.LOW)
py: #
py: GPIO.output(17,GPIO.HIGH)
py: #
py: cmd_line="/usr/bin/rpicam-jpeg -o "+attach_dir+attach_name+" -t 1000 --width 640 --height 480"
py: print(cmd_line)
py: cmd_line_list=cmd_line.split()
py: subprocess.run(cmd_line_list)
py: #
py: GPIO.output(17,GPIO.LOW)
py: # 
py: list=self.get_attachment_list()
py: if attach_name in list:
py:       self.delete_attachment(attach_name)
py: self.upload_file(attach_dir, attach_name)
py: #line="device=camera, Date="+self.Pico_Time.get_now()+", v="+attach_name
py: #self.write_line(line)
py: #time.sleep(6)
py: #   self.send_result()
py: #   time.sleep(1)
py: import re 
py: #print('start class_daily')
py: hours=[ '00','01','02','03','04','05','06','07','08','09',
py:         '10','11','12','13','14','15','16','17','18','19',
py:         '20','21','22','23'] 
py: days=['01','02','03','04','05','06','07','08','09','10',
py:         '11','12','13','14','15','16','17','18','19','20',
py:         '21','22','23','24','25','26','27','28','29','30',
py:         '31'] 
py: dataTable={}
py: columnLabel=[]
py: rowLabel=[]
py: devnames=['motion','lx','temp','humidity','co2','pressure','smell','smell2','particle']
py: sum={}
py: ave={}
py: avemax={}
py: avemin={}
py: #print('clear_report() start')
py: self.clear_report()
py: output=""
py: url="https://www***************/"
py: regex = r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?'
py: sensor_driver=self.Pico_Wiki_Driver
py: #print('sensor_driver created.')
py: for hx in hours:
py:     #print('hx='+hx)
py:     page='h'+hx
py:     #sensor_driver.set_url(url)
py:     #sensor_driver.set_page(page)
py:     uri=url+'?'+page
py:     r=sensor_driver.get_result(uri)
py:     #print('r='+r)
py:     ra=r.splitlines()
py:     #print('initialize dataTable, sum')
py:     for dv in devnames:
py:         ave[dv]=0
py:         dataTable[dv]=[]
py:         sum[dv]=0.0
py:         avemax[dv]=0
py:         #print('avemax[d]=0')
py:         avemin[dv]=65337
py:         #print('avemin[dn]=65337')
py:     #print('aggregate lines have a device')
py:     for line in ra:
py:         #print('line='+line)
py:         for dv in devnames:
py:             #print('dv='+dv)
py:             if 'device='+dv in line:
py:                 #print('dataTable[dv]=')
py:                 #print(dataTable[dv])
py:                 (dataTable[dv]).append(line)
py:                 #print('dataTable[dv]=')
py:                 #print(dataTable[dv])
py:     #print('sum')
py:     for dv in devnames:
py:         #print('sum ... dv='+dv)
py:         dev_lines=dataTable[dv]
py:         for line in dev_lines:
py:             #print('line='+line)
py:             elements=line.split(',')
py:             for e in elements:
py:                 if 'v=' in e:
py:                     lr=e.split('=')
py:                     r=lr[1]
py:                     #print('r='+r)
py:                     rna = re.findall(rf'({regex})', r)
py:                     #print(rna)
py:                     if len(rna)>0:
py:                         rn = rna[0]
py:                         #print('rn[0]='+rn[0])
py:                         fn=float(rn[0])
py:                         #print('fn='+str(fn))
py:                         sum[dv]=sum[dv]+fn
py:                         #print('sum['+dv+']='+str(sum[dv]))
py:                         #print('avemax')
py:                         if fn>float(avemax[dv]):
py:                             avemax[dv]=fn
py:                         #print('avemin')
py:                         if fn<float(avemin[dv]):
py:                             avemin[dv]=fn
py:                     #print('end of the dv')
py:                 if 'Date=' in e:
py:                     lr=e.split('=')
py:                     dx=lr[1]
py:                     #print('dx='+dx)
py:     print('make output line')
py:     for dv in devnames:
py:         #print('dv='+dv)
py:         ldt=len(dataTable[dv])
py:         #print('ldt='+str(ldt))
py:         if ldt>0:
py:           try:
py:             ave[dv]=sum[dv]/ldt
py:             line= "device="+dv+", Date="+dx+", ave="+str(ave[dv])+",max="+str(avemax[dv])+",min="+str(avemin[dv])
py:             #print(line)
py:             self.write_line(line)
py:           except Exception as e:
py:             print('error in output page')
py:             print(e)
py:             traceback.print_stack()
py:     #print('next hx')
py: #print('end hx loop')
py: self.send_result()
py: print('end send result')
py: #
py: # sensing
py: while True:
py:     date=self.Pico_Time.get_now()
py:     #date=2024-12-31 14:25:16.61944
py:     time_x=(date.split())[1]
py:     #print('time_x='+time_x)
py:     time_xx=time_x.split(':')
py:     hm=time_xx[0]+'_'+time_xx[1]
py:     ms=time_xx[1]
py:     ss=time_xx[2]
py:     dec_sec=ss[0]
py:     #
py:     if this_hour!=time_xx[0]:
py:         break
py:     if GPIO.input(18)==1:
py:         #print('hm='+hm+' ms='+ms+' ss='+ss+' desec='+dec_sec)
py:         print('time='+hm)
py:         attach_dir='/home/pi/python/pictures/'
py:         attach_name='motion-'+hm+'.jpg'
py:         print('attach_name='+attach_name)
py:         #
py:         GPIO.setmode(GPIO.BCM)
py:         #
py:         GPIO.setup(17,GPIO.OUT)
py:         #
py:         GPIO.output(17,GPIO.LOW)
py:         #
py:         GPIO.output(17,GPIO.HIGH)
py:         #
py:         cmd_line="/usr/bin/rpicam-jpeg -o "+attach_dir+attach_name+" -t 1000 --width 640 --height 480"
py:         print(cmd_line)
py:         cmd_line_list=cmd_line.split()
py:         subprocess.run(cmd_line_list)
py:         #
py:         GPIO.output(17,GPIO.LOW)
py:         # 
py:         list=self.get_attachment_list()
py:         if attach_name in list:
py:             self.delete_attachment(attach_name)
py:         self.upload_file(attach_dir, attach_name)
py:         time.sleep(600)
py:     else:
py:         time.sleep(1)
py:
command: end daily
command: run daily
command: set pageName="d<day>"

謝辞

本作品を制作するにあたり、その一部は、科研費 21K11858 の支援を受けています。感謝します。

その他の参考資料

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?