DreamerDreamのブログ

夢想家の夢です。〜揚げたてのモヤっとしたものをラフレシアと共に〜

猫の体重管理のためにスマートトイレを自作してみた ⑦猫トイレの糞尿を判断をするアルゴリズムを考える

前回、サーバー側の基本的な処理を書いていましたが、今回はきちんと蓄積したデータが使えるように糞尿判断のアルゴリズムを見直します。

dreamerdream.hateblo.jp

 

データをたくさん収取してパーセプトロンなどで機械学習させようかとも思ったのですが、1匹なので多様性も考慮する必要ありませんし1日多くて4〜5回程度しか収集出来ないデータでは学習用データ収集とデータの取得には時間がかかりすぎます。(機械学習には何百という訓練データが必用です。)

割と特徴のある重量変化なので今回はアルゴリズムの手入力でどうにかしたほうが早いと判断しました。

機械学習であれば過去記事にあるようなkerasの機能を使うと割と組みやすいと思います。

dreamerdream.hateblo.jp

 

以下は大と小のグラフですが、グラフの赤が元データ、青が元データの3つの平均を求めた上下3gの値、黄色軸は約10秒間隔、緑は猫が乗ってから降りるまでの平均重量を示します。

まずは、青グラフの間に赤が収まっている間が「静止状態」として判断できそうです。

そして「どのぐらい連続して青線の中に収まっているか?」ということが課題になります。

大と小の場合、間10秒以上のスパンがあるので10秒以下の静止スパンの場合は連続した排泄行為、10秒以上間が開けば別々の排泄だと判別できそうです。

 

刷新して必要部分だけ抜き出したコードが以下です。

 

lv_move = 3

 

def analyse_weight( datas , f ):

 

  weights = datas.get('W')

  localip = datas.get('IP')

 

  #weight以外の場合、日時データだけ追記

  if weights is None :

    datas.update({'time':f})

    return datas

 

  cnt = 0

  w_stock =

  w_offset = 0

  w_pee = 0 #最終的差異から排泄量

  avl_ride = 0 #乗っている時の平均値

  cnt_ride = 0 #乗っている時間

  r_stock = #乗り降りの前後3秒を切る

  baba = 0 #ネコババ行為時間

  pee_time = 0 #排泄時間内部記録用

  cnt_pee = 1 #排泄時間の内部記録用

  p_stock = [] #排泄時間を分けて記録

 

  for w in reversed( weights ):

 

    #3つの数値の平均値

    w_stock.append( w )

    w_stock = w_stock[-3:]

    avl = statistics.mean( w_stock )

 

    #最後から3つまでを降りたときの重量とする

    if cnt == 3:

      w_pee = avl

 

    #上に乗っている時

    if w_pee + 2000 < avl:

      r_stock.append( avl )

      r_stock = r_stock[-3:]

      if 2 < len(r_stock) :

        avl_ride += avl

        cnt_ride += 1

 

        #w_stockは生の値、r_stockは3つの平均値 静止=2つの値が近い

        if w_stock[0] - lv_move < r_stock[0] and r_stock[0] < w_stock[0] + lv_move and w_stock[1] - lv_move < r_stock[1] and r_stock[1] < w_stock[1] + lv_move and w_stock[2] - lv_move < r_stock[2] and r_stock[2] < w_stock[2] + lv_move and w_stock[0] - lv_move < w_stock[1] and w_stock[1] < w_stock[0] + lv_move and w_stock[2] - lv_move < w_stock[1] and w_stock[1] < w_stock[2] + lv_move:

            if pee_time == 0:

              cnt_pee +=1

            if 3 <= cnt_pee:

              if cnt_pee + 10 < cnt:

                if pee_time != 0:

                  p_stock.append( pee_time )

                cnt_pee = 1

              pee_time +=1

              cnt_pee = cnt

 

        elif 1 < pee_time and baba == 0:

          baba = cnt_ride

 

    #乗る前の値=オフセット

    if w_pee + 1000 > avl and 3 < cnt_ride:

      if w_stock[0] == w_stock[1] and w_stock[1] == w_stock[2]:

        w_offset = avl

 

    cnt +=1

 

  if pee_time != 0:

    p_stock.append( pee_time )

  p_stock.reverse()

 

  avl_ride =  int( ( ( avl_ride-sum(r_stock) ) / (cnt_ride-3) ) - w_offset )

  w_pee = int( w_pee - w_offset )

 

  res = {'time':f, 'offset':w_offset , 'weight':avl_ride, 'stay':cnt_ride , 'pee':w_pee, 'time_p':p_stock, 'baba':baba }

 

  #臭気データがある場合は保存

  odor = get_odordata( f )

  if odor is not None:

    res.update( odor )

 

  return res

出力値はこうなります。

(odorの値はニオイセンサーの実験段階なのでまだ要素として反映させません。)

まず50秒以上のネコババ行為(baba)がある場合はほぼ確実に大であると判断できるのでネコババ行為50秒を1つの判断材料とします。

大の行為があり、且つ排泄時間記録(time_p)が2つ以上ある場合は大と小だと判断することができます。3つ以上になっているところは途中で体勢を微妙に変えた状態ですね。ここはもう少し工夫が必用そうです。

 

排泄中に何度も立ち会っていないと確実なことは言えないので継続した観察と実験が必要です。

また、実生活空間での動物の行動なので想定外の動作が当然発生します。

↓大の後のネコババ行為中に飼い主に呼ばれた例

こんなのは後から手で修正するか、或いはにおいセンサーで判断させるしかありません。

↓5分ぐらいトイレにいた例

ギリギリ収まっていますが、これは想定外の滞在時間です。これ以上滞在されたらデータとして取り上げられません。

この場合でも手動での操作が必用そうです。

↓一度入った後に出て、また入って大と小をした例

うまく分けて記録されていましたが先のものが1回として記録されました。

こういった判別はかなり難しそうです。

 

さて、大と小の両方をした場合のアルゴリズムですが、市販のCatlogBoardの場合は半分ずつの重量となるそうです。

つまり20gの両方の場合は大10g、小10gとして記録されるそうです。

大まかに見る場合はそれでも良いのですが、ここはせっかく自作するのでもう少し凝ってみたいところです。

 

これは過去の小や大のときの静止時間と排泄量が判明すれば大凡の目安になると考えます。

排泄データは1回のデータをこのようなリストでまとめます。

これは大のときのデータ

 

小のデータ

尿量と排泄時間が大体は比例していることが解ります。

 

両方した場合のデータ↓

複数回静止状態のある場合は時間を分けて記録しているので、それぞれの時間が記録されますが、排泄量は総量でしか記録されません。

観察すると最初に小をし、後に大をしているようなので上の例の場合は12:9で排泄量をs小:大で単純に分割して記録するというのがすぐ思い立ちますが、過去の排泄の記録から排泄量と排泄時間を蓄積しておき、それを元に算出する。という方法をとればもっと正確に記録できるのでは?と考えます。

まあ、そこまで細かくこだわるならそもそもロードセルの値が1g単位で正確なのか?という問題になりますが・・・、こういうロジックを考えるのは楽しいですね。

 

 

さて、冒頭で糞尿の判断をする基準値に「50秒」というネコババ時間が目安になりそうだと述べていましたが、あくまで「現在の猫の状態を人間が見て判断するのであれば」ということです。

年齢や体調によってこの目安は流動的に変えるべきでしょう。

 

そこでこんなアルゴリズムにしてみました。

 

最初は固定値を設けます。人が見て、ここが基準値として良さそうだと判断したポイント以下のものを小。ポイント以上のものを大と分類します(例はネコババ行為の時間)。

最終的に分類できないものは手動で分類します。

 

ある程度分類が行われると、大と小のそれぞれの平均値が求められます。

大と分類されたものの平均を求め、大の平均値値以上のものを大。

小と分類されたものの平均から、小の平均値以下のものを小と分類します。

実際の値を見てみても、これでほぼ正確に分類できています。

 

大とも小とも分類されなかったものは匂いセンサーの値を判断材料にします。

もし、分類が間違っていた場合、手動で分類を変更することで未来の分類用平均値を変えることが出来るようになります。

今回は30日分のデータを基準に平均値を算出するようにしました。

 

実際に約一ヶ月ぐらい記録を録りながら、上の方法で分析記録し、スマホからでも簡単に使えるようにWebアプリを作成しました。


動画でご紹介

youtu.be

 

手動でのデータ改変が出来るように組みましたが、ほぼ自動振り分けで問題なさそうです。

たまに猫砂が体重計の下に入ったり、予期せぬ挙動で重量がマイナス等のエラーになっています。

まだ改善の余地はありますが、記録している日数が多ければ平均値にあまり影響が出ないので気にしなくても良さそうです。

 

そして今回初の試みの「においセンサー」が思いのほか、しっかりと役立ってくれています。回路に不安がありましたが1ヶ月近く壊れず運用できているので問題ないでしょう。

dreamerdream.hateblo.jp

 

大の場合、ほぼセンサーが反応しています。

今回の自作スマートトイレは大成功でした。

 

 

第一回はこちら↓

dreamerdream.hateblo.jp

 

 

 

kampa.me