工作と競馬2

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

APIGatewayとLambdaを一緒に使うためのメモ

背景と目的

APIGatewayとLambdaを一緒に使う場合、設定項目がたくさん用意されていて自由度は高いのだが、いろいろありすぎてどんな時に何をすればいいかわからなくなる。公式ドキュメントを読まずともある程度のことが分かるようにメモしておく。



前提

  • APIGatewayに入ってきたリクエストを、APIGateway>統合リクエスト>マッピングテンプレートを用いてJSONに成型して、Lambdaの引数eventに引き渡す


APIGateway

設定

バイナリメディアタイプ

バイナリとして受け入れるContent-Typeを設定する。たとえば、application/octet-streamやimage/jpegなど。設定しておかないと、Unsupported Media Typeというレスポンスが帰ってきてしまう。ここで設定したら、統合リクエスト>マッピングテンプレートの$input.bodyにbase64エンコードされた文字列が入るようになる。


リソース


メソッドリクエス

編集中


統合リクエス

HTTPヘッダー

  • Lambdaの引数event.params.headerに渡すデータの設定ができる。
  • クエリパラメータ、パスパラメータ、リクエストヘッダーなどいろいろなものをマッピング可能。

マッピングテンプレート

リクエストのパススルー

  • そのままだと、リクエストボディがJSONであることを期待して、jsonパースしてLambdaの引数event.body-jsonに入れる。JSONパースできない場合、Lambdaに到達する前に怒られる。
  • Content-Typeがtext/plainの場合、以下のようにすれば文字列を入れられる。
"body-json": "$input.body"

統合レスポンス

編集中

メソッドレスポンス

編集中


Lambda

引数event

マッピングテンプレートの生成 = リクエストのパススルーのとき

{
    "body-json": "{ボディ}",
    "params": {
        "path": {
            "pathParameter": "value",
            :
        },
        "querystring": {
            "queryParameter": "value",
            :
        },
        "header": {
            "Accept": "*/*",
            "Accept-Encoding": "gzip, deflate, br",
            "Cache-Control": "no-cache",
            "CloudFront-Forwarded-Proto": "https",
            "CloudFront-Is-Desktop-Viewer": "true",
            "CloudFront-Is-Mobile-Viewer": "false",
            "CloudFront-Is-SmartTV-Viewer": "false",
            "CloudFront-Is-Tablet-Viewer": "false",
            "CloudFront-Viewer-Country": "US",
            "Content-Type": "{Content-Type}",
            "Host": "{api-id}.execute-api.ap-northeast-1.amazonaws.com",
            "User-Agent": "PostmanRuntime/7.29.0",
            "Via": "1.1 22512dca1de1fae848b2509fed0309aa.cloudfront.net (CloudFront)",
            "X-Amz-Cf-Id": "FXfKr5sbNKhgGGfyTwehKew20UNqXsoPp7xyDL-y8TqgAkwCnWr94w==",
            "X-Amzn-Trace-Id": "Root=1-6215f7c0-12c8260b2b11d6fe7f74d914",
            "X-Forwarded-For": "54.86.50.139, 130.176.137.78",
            "X-Forwarded-Port": "443",
            "X-Forwarded-Proto": "https"
        }
    },
    "stage-variables": {
        "type": "object",
        "description": "ステージ変数"
    },
    "context": {
        "account-id": "",
        "api-id": "{api-id}",
        "api-key": "",
        "authorizer-principal-id": "",
        "caller": "",
        "cognito-authentication-provider": "",
        "cognito-authentication-type": "",
        "cognito-identity-id": "",
        "cognito-identity-pool-id": "",
        "http-method": "POST",
        "stage": "{デプロイ先ステージ}",
        "source-ip": "{アクセス元IPアドレス}",
        "user": "",
        "user-agent": "********************",
        "user-arn": "",
        "request-id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "resource-id": "{リソースID}",
        "resource-path": "{リソースパス}"
    }
}

event.params.header

  • リクエストヘッダーは、何もしなければそのままここに入る。
  • APIGateway>統合リクエスト>HTTPヘッダーで対応を定義すると、別のヘッダー名に変えて追加することもできる。
  • クエリパラメータや、

event.body-json

  • APIGateway>統合リクエスト>マッピングテンプレートで、リクエストのパススルーを選んだ場合、リクエストボディがそのまま入るが、JSONである必要がある。
  • JSON以外の場合は、APIGateway>統合リクエスト>マッピングテンプレートを参照。

event.stage-variables

APIGatewayのステージ変数で定義した値が入る。

{
    "stage-variables": {
        "abc": "value",
        :
    }
}


水耕栽培の成長記録システム(1) --- 構想検討 ---

概要

芽ねぎの水耕栽培を始めるにあたり、記録装置の製作を行った。



背景と目的

昨年、家庭菜園でしそベビーリーフを育てる楽しさを味わうことができた。そこで、次の作物にチャレンジしたいと思い検討した結果、芽ねぎを水耕栽培することにした。理由は以下である。

  • 芽ねぎが好き
    • シャキシャキした食感で非常にうまい
    • 海鮮系ではないにもかかわらずそれほど安くない寿司ネタなので、自宅で育てられたらちょっと得した気分になれそう
  • 室内で育てられる
    • 私自身、今まで室内で育てたことがないので、これを機にチャレンジしたい

というわけで、まず栽培にあたり水耕栽培も芽ねぎも初めてということで、栽培の様子を記録したい。そこで、まずは成長記録システムを製作することにした。



詳細

1. 参考


2. 記録装置に求める仕様

  • 培地全体を撮影し、クラウドへ送信して保存
  • 室内(周囲)環境を同時に記録

といったところだ。写真で残せると、便利なのは昨年の経験から明らかなので外せないポイント。また、周囲環境をセンシングし、のちのちの考察に生かせるとよい。他にも、成長を促す照明もつけたりしたいという欲求はあるのだが、1発目なのでシンプルに。(急に気が変わってつけるかもしれないが)


3. 設計

3.1 撮影装置

撮影装置として、以下のようなものを用意する。

まず、培地を置くベースを用意する。そこに、ポールを立て先端にカメラを固定する。カメラは、先日買ったUnit CAMを利用する。解像度が高いとは言えないのだが、事前チェックでとりあえず採用可能と判断した。また、撮影範囲に入るように、温湿度計を置く。

f:id:dekuo-03:20220211205634p:plain

3.2 全体

全体イメージは以下。後述の撮影装置で撮影した写真をクラウド上に保存する。画像保存に必要な経路は、 昨年作成した画像保存用のシステム の一部を使う。撮影した画像に映る温湿度計の数値をAWS Recognitionで検出し、Lambdaを介して温湿度値としてS3に記録する、という仕組みだ。

f:id:dekuo-03:20220211210507p:plain



まとめと今後の課題

記録装置の全体イメージが固まった。次回は製作を行う。


NuGetパッケージマネージャーが "情報を取得しています" の状態から動かないとき(Visual Studio 2013 Update5)

条件


症状

以下のように、"情報を取得しています" からずっと動かない。

f:id:dekuo-03:20220207174349p:plain


やるべきこと

Visual Studioツールバー

ツール > NuGet パッケージマネージャー > パッケージマネージャーコンソール

と進み、パッケージマネージャーコンソールを立ち上げる。

立ち上げた後、

プロジェクト > NuGet パッケージ管理

と進むと、以下のようにちゃんと参照できた。理由は不明。

f:id:dekuo-03:20220207174328p:plain


参考

他にも、Web上ではNuGet自体のアップデートだとか、キャッシュのクリアだとか、いろんな手段が試され解決したとかしないとか情報が錯綜しているが、私の場合これで改善した。ひとまず良しとする。


ベビーリーフを育てる(2) --- まとめ ---

概要

ビーリーフの栽培を一旦終了するので、ここまでのまとめをする。



背景と目的

ビーリーフの栽培を始めてから、約3か月が経った。次の作物の栽培を始めるのでいったん栽培を終了しなければならないのと、発芽以降の記録をさぼっていたので、ここまでのまとめを書き記す。



詳細

1. まとめ動画

以下にまとめた。朝7:30頃に撮影しているが、冬場で明るくなるのが遅いせいか、夏場よりだいぶ画質が悪い。


www.youtube.com


2. 種まきから発芽まで

  • 発芽までの日数
    • 動画を見る限り、2021/11/07に種まきしてから6日で発芽したようだ。種の説明書などを見ると5日程度とあったので、まずまず説明書通り。いつもそうだが、発芽するまではドキドキ。
  • 発芽率
    • 種を全体にバラバラ巻いたので数値を出すことはできないが、その後の成長具合を見ると全体的にそれなりに芽が出てきてくれているので、発芽率は悪くないと思われる。


3. 収穫まで

  • 動画を見る限り、発芽から1か月でそこそこ大きい葉が育って食べられるくらいにはなっているが、2021/12/21に葉の量が減っているので収穫していると思われる。結局発芽から初収穫まで1か月以上かかったのかも?でも、それよりも前に収穫したような気がする。自分で育てたのに全然覚えていない。動画を見てもわからなかった。


4. 収穫以降

  • 初収穫以降、本格的に寒い時期に入り、あまり成長は速くないが、順調に育ってくれた。1週間に1回くらいは収穫しただろうか。1回の収穫量は片手いっぱいになるくらいなので、サラダの足しくらいにしかならなかったが、味は良好。


5. 感想

  • ベランダの日当たりがいまいちなせいもあり、冬場の午前中は特に日照時間が少ない。
  • 全体の収穫量が少ない。寒さで育ちが遅いせいもあるかもしれないが、絶対量としてプランター1個では少ないのだろう。もう1つくらい用意してもいいのかも。
  • 収穫量は少なかったが、味はいいので、また育てたい。



まとめと今後の課題

ビーリーフの栽培ができた。結構おいしかったので、次の機会があればぜひ育てたい。


M5STACKのUnit CAMにArduinoのプログラム書き込む

概要

M5STACKのUnit CAMに配線を施し、Arduinoのプログラム書き込んでみた。



背景と目的

以前、以下の記事で、OV2640というカメラ基板とESP32を接続したものを作成した。

dekuo-03.hatenablog.jp

そして、また同様のものが欲しくなったのだが、同じものを作ると手間がかかる。しかし、Amazonで相変わらず大量に出回っている技適の通っていない怪しい格安基板に手を出すわけにもいかないので、何かいいものがないか探していたところ、 M5STACKのUnit CAM というものをスイッチサイエンスで見つけた。1000円ちょっとと非常に手ごろなので、これを買って動かしてみることにした。

※先に結論を書くと、

当然ながら、ちゃんと動く。ただし、UXGAは撮れない。商品が悪いわけではなく、PSRAMがないことを見落とした自分が悪い。

OV2640が載っており最大解像度2MPixelという文言を見て、てっきりPSRAMが載っているものと思っていたが載っていない。すなわち、この記事で書いた通り、UXGAでは撮れなかった。同じ轍を2回踏むという、ひどいミスをしてしまった。とはいえ、まあSXGAくらいでは撮れるし、いずれ活躍の機会がないとは言えないのでひとまず動作の記録を残して、部品箱に眠らせておくことにした。



詳細

1. 配線

Unit CAMは、本来Grove端子で接続してM5STACK関連のデバイスとつないで遊ぶ想定のものなので、ファームウェア書き込みも専用シリアルライターを使う想定。しかし、私の手元にあるのは、これなので、同じ接続ができない。これについてもロクに考えずに買ってしまったせいで、面倒な配線作業が発生した。以下のように、これを参考に、けっこういろいろ配線した。

  • R1からTXD0に斜めに伸びる1kΩは、ESP32でプログラム書き込みに失敗したときの解決方法に倣ったプルアップ抵抗。これって、書き込めない症状は確かに改善するものの、最近自分のライターがおかしいだけ???のような気もしてきた。あまり自信がない。でも、今回もこの1kΩがないと書き込めなかったのは確か。
  • 3.3V, EN, IO0は写真の外側にESP32が書き込みモードに入るためのタクトスイッチを設けて接続している
  • 3.3V、SY8089AAACというレギュレータの3.3V出力に繋がるR1から取り出す

f:id:dekuo-03:20220206175841j:plain


2. Arduinoプログラム書き込み

プログラムは、arduino-esp32のサンプルプログラムにあるCameraWebServerとする。変更点は、ここを参考にピン接続のGPIO番号だけ定義する。

2022/03/27追記 arduino-esp32の最新バージョンは2.0.2だが、本記事はv1.0.6のCameraWebServerサンプルを基にして作成する必要があることがわかった。v2.0.2だと必要なファイルがインクルードできず、コンパイルできない。

// ピン設定
#define Y2_GPIO_NUM 32
#define Y3_GPIO_NUM 35
#define Y4_GPIO_NUM 34
#define Y5_GPIO_NUM 5
#define Y6_GPIO_NUM 39
#define Y7_GPIO_NUM 18
#define Y8_GPIO_NUM 36
#define Y9_GPIO_NUM 19
#define XCLK_GPIO_NUM 27 // XCLK
#define PCLK_GPIO_NUM 21 // PCLK
#define VSYNC_GPIO_NUM 22 // VSYNC
#define HREF_GPIO_NUM 26 // HREF
#define SIOD_GPIO_NUM 25 // SIOD
#define SIOC_GPIO_NUM 23 // SIOC
#define PWDN_GPIO_NUM -1 // 接続なし
#define RESET_GPIO_NUM 15 // RESET

そして、

// Select camera model
#define CAMERA_MODEL_WROVER_KIT // Has PSRAM
//#define CAMERA_MODEL_ESP_EYE // Has PSRAM
:

#include "camera_pins.h"

の部分は、参照しないので削除する。

3. 動作確認

Webブラウザで、所定のアドレスにアクセスしたところ、ちゃんと表示された。撮れた画像は以下。SXGAである。UXGAも被写体によっては撮れるが、複雑な絵柄だと、エンコードされたjpegバイナリのサイズも大きいのでRAMに入りきらず撮れない。実質的にSXGAくらいが最大だろう。まあ、悲しいが想定通りの動き。

f:id:dekuo-03:20220206180437j:plain



まとめと今後の課題

Unit CAMにArduinoプログラムを入れて動かすことができた。買ったもの自体は失敗なのだが、まあ後々メモとして役立てばよい。


天気図画像データから、24時間以内の降雨の有無を予測する

概要

天気図画像データから、24時間以内の降雨の有無を予測してみた。



背景と目的

ある事情で、ディープラーニングを使ったモデルを作成する必要が出た。テーマは、データが集めやすそうな天気関連のものとして、記事タイトルにある内容で行うこととした。画像データから降雨に関わる重要な特徴を学習してうまく予測するモデルが作れるか試す。



詳細

1. 実施内容

予測対象地点は、さいたま市とした。


2. データの収集

  • 天気図データは、tenki.jpの過去の天気図データを用いる。2021年1月1日~2021年12月20日までの約1年分とする。Webスクレイピング等により収集した。3時間ごとに1枚の天気図がある。(ただし、0時はない)
https://tenki.jp/guide/chart/
  • 予測対象の降雨の有無は、さいたま市の過去のデータを以下の気象庁Webサイトからダウンロードした。
https://www.data.jma.go.jp/obd/stats/etrn/index.php


3. データセットの作成

3.1 天気図データ

今回使用する天気図データは、

  • 太平洋やロシアなど広範囲をカバーする
  • 地形や緯線、経線も含まれている

となっているが、

  • 一般的に、予測対象地点から大きく離れる場所や東側の大部分はあまり予測に重要でなさそうである
  • 同じ画角で切り出したとすると、予測対象地点は常に画像中の同じ位置だから、画像中の地形や緯線や経線は予測と無関係であり、天気図記号の位置関係のみが予測に関係する

と考えられた。そこで、

  • すべての画像から平均画像を作成し、各画像から減算して地形や緯度経度の線地形を消去
  • 予測対象地点がおおむね中心となるように日本付近を160 * 160pixelで切り出す
  • RGBチャンネルを分解してコントラストなどを調整
    • 青は寒冷前線マークの検出
    • 緑は天気図記号抽出がしにくいため不使用
    • 赤は等圧線、高気圧/低気圧マーク、温暖前線の検出
  • 青チャンネルと赤チャンネルを加算
  • 計算量削減のため天気図記号が認識可能な程度の80 * 80pixelに圧縮

といった方法で、画像を約2500枚作成した。すなわち、80pixel * 80pixel * 1channelのcreatedチャンネルが最終的に使用される画像である。なお、さらっと書いているが、実は天気図記号を消さずに地形や緯度経度の線を消すには結構試行錯誤した。データセット作成で苦労するのはよくある話だが。

f:id:dekuo-03:20220117233701p:plain

参考: 平均画像

f:id:dekuo-03:20220117231659j:plain


3.2 降雨データ

降雨データは、天気図の時刻を起点に、24時間以内の合計降雨量が1mmでもあれば降雨あり、0mmなら降雨なしとして、2次元ワンホットベクトル化した。データ全体における降雨なし/降雨ありの比率はおよそ68%である。


4. モデルの作成

畳み込み層と全結合層を組み合わせたニューラルネットワークとした。実装は、tensorflowを用いた。 層の深さ、チャンネル数、フィルタサイズ、正則化パラメータ等は、かなり試行錯誤して決めた。フィルタサイズやプーリングの有無は、天気図記号のピクセル数等を考慮した結果である。正則化は強すぎると学習しないし、弱ければ意味がない。

input = Input(shape=(H, W, 1), name="sma_input1")

x = Conv2D(64, 5, padding="same", activation="relu")(input)

x = MaxPooling2D()(x)

x = Conv2D(192, 3, padding="same", activation="relu", kernel_regularizer=regularizers.l2(0.005))(x)

x = MaxPooling2D()(x)

x = Conv2D(192, 3, padding="same", activation="relu", kernel_regularizer=regularizers.l2(0.005))(x)

x = MaxPooling2D(pool_size=(5, 5))(x)

x = Flatten()(x)

x = Dropout(rate=0.5)(x)

output = Dense(128, activation="softmax")(x)

x = Dropout(rate=0.5)(x)

output = Dense(2, activation="softmax")(x)

model = Model(inputs=input, outputs=output)

model.compile(
    loss="categorical_crossentropy", 
    optimizer='Adam',
    metrics=["accuracy"]
)

model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
sma_input1 (InputLayer)      [(None, 80, 80, 1)]       0         
_________________________________________________________________
conv2d_9 (Conv2D)            (None, 80, 80, 64)        1664      
_________________________________________________________________
max_pooling2d_9 (MaxPooling2 (None, 40, 40, 64)        0         
_________________________________________________________________
conv2d_10 (Conv2D)           (None, 40, 40, 192)       110784    
_________________________________________________________________
max_pooling2d_10 (MaxPooling (None, 20, 20, 192)       0         
_________________________________________________________________
conv2d_11 (Conv2D)           (None, 20, 20, 192)       331968    
_________________________________________________________________
max_pooling2d_11 (MaxPooling (None, 4, 4, 192)         0         
_________________________________________________________________
flatten_3 (Flatten)          (None, 3072)              0         
_________________________________________________________________
dropout_6 (Dropout)          (None, 3072)              0         
_________________________________________________________________
dropout_7 (Dropout)          (None, 3072)              0         
_________________________________________________________________
dense_7 (Dense)              (None, 2)                 6146
=================================================================
Total params: 450,562
Trainable params: 450,562
Non-trainable params: 0
_________________________________________________________________


5. 学習と評価

5.1 学習

  • ホールドアウト法で学習/検証/テストを行う
    • データセット全体の順序をシャッフル
    • 全体を0.75:0.25に分割し、学習/検証用とテスト用とした
    • 学習/検証用を0.75:0.25に分割し、学習用と検証用とした
  • バッチサイズは128
  • エポック数は60

以下が、60エポック完了後の損失と精度。精度は80%台前半。40-50エポックあたりで、検証データに対するロスが下がらなくなっているので、60エポックは少しやりすぎ。

f:id:dekuo-03:20220117233648p:plain


5.2 テストデータによる評価

検証精度とほぼ同様の81%となった。


6. 考察と改善案

6.1 考察

精度は80%台前半で、データ全体における降雨なし/降雨ありの比率はおよそ68%なので、このモデルは天気図から降雨に関連する何らかの特徴をある程度認識できていると考えられる。


6.2 改善案

今回使用したものは、基本的なCNNを使ったネットワーク構成なので、より発展的なネットワーク要素を取り入れることで、精度の向上が図れるのではないかと考える。また、入力がある時点の1枚の画像であるが、天気は時間経過とともに変化することを考えると、時系列性を考慮することも有効と考えられる。具体的には、

  • (案1)ResNetやDenseNetのような残差接続を導入する
  • (案2)時間的に並んだ3枚の天気図をまとめ、3チャンネルの入力データとする

などがある。時間があったらやってみたい。



まとめと今後の課題

天気図画像データから、24時間以内の降雨の有無を予測することにトライしてみた。


KiCad 6をインストールしたら、日本語が使えるようになった

KiCad 6をインストールしたら、日本語が使えるようになった。今まで、回路図にメモを書き込みたいときに、困っていたが、これからは直接メモできる。

f:id:dekuo-03:20220116163701p:plain

プロジェクトファイルをバージョン6で保存すると、バージョン5とは異なる拡張子(.kicad_pro)で保存されるようだ。なので、KiCadのWebサイトには前のバージョンでは開けないと注意書きがあったが、とりあえず前のバージョンのファイル一式は残るので最悪開けなくなるわけではないらしい。