diff --git a/raspi/line_detection/.env.template b/.env.template similarity index 73% rename from raspi/line_detection/.env.template rename to .env.template index 98cb3a33..36bba9ef 100644 --- a/raspi/line_detection/.env.template +++ b/.env.template @@ -2,12 +2,12 @@ HOME_DIR=/Users/ycn/Workspace/NUTMEG/tracking-parking # YOLOモデル -MODEL_PATH=${HOME_DIR}/yolo_fine_tuning/runs/detect/train14/weights/best.pt +MODEL_PATH=${HOME_DIR}/models/yolov8s.pt CONFIDENCE_THRESHOLD=0.25 IOU_THRESHOLD=0.7 VEHICLE_CLASSES=2,7 -# 速度比較条件(ROI方式と同じ値に揃える) +# 速度計測条件 YOLO_DEVICE=cpu YOLO_IMGSZ=640 YOLO_TRACKER=botsort.yaml @@ -15,19 +15,19 @@ WARMUP_FRAMES=30 EXP_DEVICE_NAME=raspi5 EXP_DEVICE_ACCELERATOR=cpu -# Line1設定(入口側ライン) - setup/setup_lines.pyで自動設定 +# Line1設定(入口側ライン) - scripts/setup_lines.pyで自動設定 LINE1_START_X=300 LINE1_START_Y=400 LINE1_END_X=800 LINE1_END_Y=400 -# Line2設定(駐車場側ライン) - setup/setup_lines.pyで自動設定 +# Line2設定(駐車場側ライン) - scripts/setup_lines.pyで自動設定 LINE2_START_X=300 LINE2_START_Y=200 LINE2_END_X=800 LINE2_END_Y=200 -# 駐車場基準点(駐車場側を定義) - setup/setup_lines.pyで自動設定 +# 駐車場基準点(駐車場側を定義) - scripts/setup_lines.pyで自動設定 PARKING_REF_X=550 PARKING_REF_Y=100 diff --git a/.gitignore b/.gitignore index 8191bc1b..6d5529ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,29 @@ -mongo/ -web/ -trapa/ +# Python __pycache__/ +.venv/ +.pytest_cache/ + +# 設定(画角ごとの.envを含む。テンプレートのみ追跡する) *.env -*.yaml -.DS_Store -!dataset.yaml -/sahi .env -raspi/result/prediction_visual.png +!.env.template + +# 入出力データ・モデル重み(サイズが大きくローカル資産) +# ディレクトリ自体を除外すると中の.gitkeepを再包含できないため、中身を除外する +/data/** +/models/** +!/data/**/ +!/data/**/.gitkeep +!/data/inputs/videos.example.json +!/models/.gitkeep *.pt -.venv/ -# 2ライン検知システムの出力ファイル -data +# docker-compose.prod.ymlがマウントするローカルディレクトリ +mongo/ +web/ -# ドキュメント -docs/ -docs/* -!raspi/line_detection/docs/ -!raspi/line_detection/docs/wandb_integration_spec_v2.md +# macOS +.DS_Store -# ローカルディレクトリ -**/*.local \ No newline at end of file +# ローカル専用ディレクトリ(docs.local など) +**/*.local diff --git a/.gitkeep b/.gitkeep index fc18a562..9d3da590 100644 --- a/.gitkeep +++ b/.gitkeep @@ -1,3 +1,3 @@ -yolo_fine_tuning/resorces/dataset/background_images -yolo_fine_tuning/resorces/dataset/output_ground_truth -yolo_fine_tuning/resorces/dataset/output_png \ No newline at end of file +training/resorces/dataset/background_images +training/resorces/dataset/output_ground_truth +training/resorces/dataset/output_png \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..f1bd12cd --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# tracking-parking + +駐車場の入出庫を検知し、駐車台数を管理するシステム。 + +車両の検知には**2ライン方式**を採用している。出入口側の `Line1` を主判定ライン、 +奥側の `Line2` を補助判定ラインとして、YOLOv8のトラッキング結果からライン通過を +検知し、入庫・出庫をカウントする。方式選定の経緯は +[docs/decisions/0001-two-line-method.md](docs/decisions/0001-two-line-method.md) を参照。 + +プロジェクト全体の背景は [Wiki](https://github.com/NUTFes/tracking-parking/wiki) にある。 + +## セットアップ + +```bash +uv sync +``` + +`src/tracking_parking` がeditable installされ、`import tracking_parking` が使えるようになる。 + +続いて設定ファイルとモデル重み、入力動画を用意する。いずれもGit管理外。 + +```bash +cp .env.template .env # HOME_DIR と MODEL_PATH を環境に合わせる +mkdir -p models data/inputs # models/ に .pt を、data/inputs/ に動画を置く +``` + +## 使い方 + +```bash +# 1. GUIでライン座標を設定する(動画の先頭フレームに5点クリック) +uv run python scripts/setup_lines.py --video data/inputs/test.mp4 + +# 2. 動画を処理して入出庫をカウントする +uv run python scripts/run_detection.py --input data/inputs/test.mp4 + +# 3. ライン位置と車両代表点の関係を確認する +uv run python scripts/visualize_lines.py data/inputs/test.mp4 + +# 4. 複数動画をまとめて処理し、サマリーへ集約する +# 対象動画は data/inputs/videos.json に書く(雛形は videos.example.json) +cp data/inputs/videos.example.json data/inputs/videos.json +uv run python scripts/run_multi_video.py +``` + +結果は `data/outputs/` に出る(アノテーション動画、イベントログのJSON/CSV、run manifest)。 + +## テスト + +```bash +uv run pytest -q +``` + +## ディレクトリ構成 + +| パス | 役割 | +|---|---| +| `scripts/` | CLIエントリポイント | +| `src/tracking_parking/` | 本体パッケージ(検知ロジック、出力、共通基盤、評価) | +| `tests/` | テスト | +| `docs/` | 設計・検証・決定の記録 | +| `plate_recognition/` | ナンバープレート認識 | +| `training/` | YOLOのファインチューニング | +| `view/` | フロントエンド(Next.js) | +| `data/`, `models/` | 入出力データとモデル重み(Git管理外) | + +詳細は [docs/two-line-system.md](docs/two-line-system.md) にある。 + +## ドキュメント + +- [docs/two-line-system.md](docs/two-line-system.md) — システム構成、設定パラメータ、アルゴリズム +- [docs/verification.md](docs/verification.md) — 実動画1本での検証手順 +- [docs/wandb_integration_spec_v2.md](docs/wandb_integration_spec_v2.md) — 実験記録(W&B連携)の仕様 +- [docs/decisions/](docs/decisions/) — 設計上の決定記録 diff --git a/Readme.md b/Readme.md deleted file mode 100644 index 91a3cc2e..00000000 --- a/Readme.md +++ /dev/null @@ -1,5 +0,0 @@ -# start - -READ WIKI PLEASE - -https://github.com/NUTFes/tracking-parking/wiki diff --git a/cron/main.py b/cron/main.py deleted file mode 100644 index 5bf62b66..00000000 --- a/cron/main.py +++ /dev/null @@ -1,69 +0,0 @@ -import os - -import requests -from dotenv import load_dotenv -from pymongo import MongoClient - -# -----------sakuraからデータを取得------------ -# TODO: あとで、send.pyの位置を変えて、importする - -# URLを指定 -load_dotenv() -token = os.environ["TOKEN"] -module = os.environ["MODULE"] -channel = os.environ["CHANNEL"] -url = f"https://api.sakura.io/datastore/v2/channels?token={token}&module={module}&channel={channel}" -print(url) - -# GETリクエストを送信 -response = requests.get(url) - -# ステータスコードが200 (OK) の場合のみ処理を続ける -if response.status_code == 200: - # JSONデータに変換 - data = response.json() - # sakuraのデータの中のresults内のデータを抽出 - results = data["results"] - # 最新のデータ - recentData = results[-1] -else: - print(f"Failed to fetch data. Status code: {response.status_code}") -# -------------------------------------------- - - -# -----------mongoDBへデータを送信-------------- -def connect_mongo(user, password, host, port, db_name, collection_name): - client = MongoClient(f"mongodb://{user}:{password}@{host}:{port}") - db = client[db_name] - collection = db[collection_name] - return collection, client - - -def make_doc(num): - num_int = f"{num}" - document = {"number": int(num_int)} - return document - - -def send_mongo(num, mongo_col): - print("send mongo") - doc = make_doc(num) - mongo_col.insert_one(doc) - print("send finish") - # mongo_cl.close() - return - - -# print(get_time()) - -if __name__ == "__main__": - USER = os.environ["USER_NAME"] - PASS = os.environ["PASS"] - HOST = os.environ["HOST"] - PORT = os.environ["PORT"] - DB_NAME = os.environ["DB_NAME"] - COLLECTION_NAME = os.environ["COLLECTION_NAME"] - collection, client = connect_mongo(USER, PASS, HOST, PORT, DB_NAME, COLLECTION_NAME) - send_mongo(recentData["value"], collection) - print(collection) -# -------------------------------------------- diff --git a/raspi/line_detection/data/inputs/.gitkeep b/data/inputs/.gitkeep similarity index 100% rename from raspi/line_detection/data/inputs/.gitkeep rename to data/inputs/.gitkeep diff --git a/data/inputs/videos.example.json b/data/inputs/videos.example.json new file mode 100644 index 00000000..dbe34951 --- /dev/null +++ b/data/inputs/videos.example.json @@ -0,0 +1,5 @@ +[ + { "video": "data/inputs/sample_a.mp4", "env": "camera_a.env" }, + { "video": "data/inputs/sample_b.mp4", "env": "camera_a.env" }, + { "video": "data/inputs/sample_c.MOV", "env": "camera_b.env" } +] diff --git a/raspi/line_detection/data/outputs/logs/.gitkeep b/data/outputs/.gitkeep similarity index 100% rename from raspi/line_detection/data/outputs/logs/.gitkeep rename to data/outputs/.gitkeep diff --git a/raspi/line_detection/data/outputs/videos/.gitkeep b/data/outputs/logs/.gitkeep similarity index 100% rename from raspi/line_detection/data/outputs/videos/.gitkeep rename to data/outputs/logs/.gitkeep diff --git a/raspi/common/__init__.py b/data/outputs/videos/.gitkeep similarity index 100% rename from raspi/common/__init__.py rename to data/outputs/videos/.gitkeep diff --git a/docs/decisions/0001-two-line-method.md b/docs/decisions/0001-two-line-method.md new file mode 100644 index 00000000..5667597d --- /dev/null +++ b/docs/decisions/0001-two-line-method.md @@ -0,0 +1,111 @@ +# 0001. 入出庫検知方式に2ライン方式を採用する + +- ステータス: 採用 +- 決定日: 2026-09-04 +- 関連: Issue #88(2ライン方式の検証)、Issue #89(ROI方式の検証)、PR #90 + +## 背景 + +駐車場の入出庫検知には、2つの方式を並行して実装し比較してきた。 + +- **2ライン方式**(Issue #88 / ブランチ `experiment/ucn/two-lines-detection`) + 出入口側の `Line1` を主判定、奥側の `Line2` を補助判定として、外積法でライン交差を検知する。 +- **ROI方式**(Issue #89 / ブランチ `feat/mike/89-bbox-analysis-within-roi`) + ROI内の進行度 `s` に対する閾値(`s_low` / `s_high`)による状態機械で検知する。 + +どちらを本番の検知方式として採用するかを決める必要があった。 + +## 決定 + +**2ライン方式を採用する。** ROI方式のコードは本リポジトリの `develop` へは取り込まず、 +`feat/mike/89-bbox-analysis-within-roi` ブランチに残す(`raspi/roi-counter/` 配下、 +本ADR作成時点のコミットは `3968e2d`)。ROI方式は `develop` にマージされたことがないため、 +整理前の状態を指す `archive/pre-cleanup` タグにもROI方式のコードは含まれない。 + +## 根拠 + +### 速度と集計精度には差がなかった + +当初、方式ごとに別々の時間帯で測った結果ではROI方式が2.27倍速いと出ていた。 +しかしこれは開発機の熱状態(サーマルスロットリング)によるもので、方式の差ではなかった。 +開始から約4000フレーム(約7分)でスロットリングが頭打ちになるため、 +別々の時間帯に測った2つのrun同士は比較できない。 + +そこで動画ごとにROI→2ライン→ROI→…と交互実行し、熱状態の上がり方を両方式へ均等にかけて +測り直した(2026-09-03〜04 実施)。結果、2.27倍の差は消えた。 + +| 動画 | ROI `core_ms_p95` | 2ライン `core_ms_p95` | 比(2ライン/ROI) | ROI誤差 | 2ライン誤差 | +|---|---|---|---|---|---| +| 1787008160.558032 | 416.2ms | 409.8ms | 0.98x | 0 | 0 | +| 1787009706.719727 | 500.6ms | 440.8ms | 0.88x | 0 | 0 | +| 1787011229.231516 | 364.3ms | 375.2ms | 1.03x | 0 | 0 | +| 1787012751.179971 | 352.9ms | 364.1ms | 1.03x | 0 | 0 | +| 1787014266.421887 | 303.2ms | 335.5ms | 1.11x | 0 | 0 | +| IMG_2787 | 368.4ms | 329.9ms | 0.90x | 0 | 0 | + +比の平均は0.99倍、範囲は0.88〜1.11倍で、どちらか一方へ偏ってもいない。 +この振れ幅は判定ロジックの差ではなく計測ごとのばらつきとして説明できる。 +YOLO推論が `core_ms` の99.9%以上を占め、ROI方式の進行度計算も2ライン方式の交差判定も +0.1ms未満で同水準であることとも整合する。 + +集計台数は6動画とも両方式でGTと一致した(`count_error=0`)。 +`comparison_key` も動画ごとに両方式で一致しており、比較条件の同一性は保証されている。 + +つまり**速度も集計精度も方式選択の決め手にならなかった**。 + +なお、この表の絶対値(300〜500ms)は確定run当時(ROI方式127〜142ms、2ライン方式275〜338ms)より +大幅に悪い。12runを約10時間ノンストップで回した連続負荷によるもので、実装の劣化ではない。 +開発機の計測値はもとより参考値であり、実機(Raspberry Pi / Jetson)での計測が別途必要である。 + +### 決め手は「実行と観測の基盤」 + +2ライン方式の実行スクリプトは、GTとの誤差計算、W&Bへの記録、フレームごとのタイミング計測、 +`comparison_key` 付きrun manifestの保存までを単体で内蔵している。 +ROI方式では同水準の計測が別の実験用スクリプト +(`raspi/roi-counter/scripts/04_multi_video_mae.py`)側にあり、 +通常の実行経路(`raspi/roi-counter/main.py`)には統合されていない。 + +これは検証の再現と障害調査のしやすさの差である。方式そのものの優位性ではなく、 +**現時点で実証運用へ進める基盤が整っている**ことを理由に2ライン方式を選ぶ。 + +## この決定が主張していないこと + +判断を後から読み違えないために、根拠が及ばない範囲を明記しておく。 + +- **イベント単位の精度は未評価。** `count_error=0` はIN/OUT合計値の一致を示すだけで、 + 見逃しと誤検出が相殺した場合を区別できない。正解データにイベント時刻がないため。 +- **エッジ実機でのロバスト性は未検証。** 2ライン方式には有限線分の端で交差が不成立になる + 失敗条件(`ENDPOINT_MARGIN_PX=0.0` のため余裕がない)、ROI方式には低fpsでの領域飛び越しがある。 + これらを同条件で分離計測していない。 +- **設置のしやすさは未計測。** ROI方式は4点、2ライン方式はライン2本と駐車場基準点で5点を + クリックする。ROI側には既存点のドラッグ・動画のシーク・形状検証があり、2ライン側は + 先頭フレーム上での点追加とリセットが中心と、機能にも差がある。 + 設定時間・初回成功率・作業者間のばらつきを測っていないため、優劣は判断しない。 +- **画角変更への強さと遮蔽への耐性は判断材料なし。** 6動画のうち5本が同じ画角、 + 出庫イベントは全体で9件。夜間・雨・逆光・複数車両の接近なども評価対象外。 +- **判定の解釈性も決め手にしていない。** 2ライン方式の規則は「Line1との交差」として + 説明しやすいが、交差判定には直前の安定点・現在点・線分との交点・駐車場基準点が必要で、 + 現在のイベントログはそれらを保存していない。該当フレーム1枚での事後監査はできない。 + +## パラメータ調整基盤の欠落(引き継ぐ課題) + +ROI方式には、YOLO推論を動画1本につき1回だけ実行し、検出結果のキャッシュに対して +カウントロジックだけを再生することで閾値の組み合わせを安く探索する仕組みがあった。 +2ライン方式には `MARGIN_PX` や `MAX_FRAME_GAP_SEC` を自動探索する対応物がない。 + +パラメータ調整が必要になった時点で、 +`feat/mike/89-bbox-analysis-within-roi` ブランチの +`raspi/roi-counter/scripts/03_sweep_params.py` を参照し、2ライン方式向けに作り直すこと。 + +ただしROI方式の探索にも限界があった。閾値探索と結果評価に同じ6動画を使っており、 +集計台数のMAEだけを目的関数にすると見逃しと誤検出の相殺も選べてしまう。 +作り直す際は、未使用動画とイベント時刻付きの正解データを評価に加えること。 + +## 残っている既知の課題 + +- **Line2通過の扱いが未定。** 6動画の128イベントのうち99件が `high`、29件が `normal` だった。 + Line2を通過しない `normal` も台数に含まれるため、この信頼度をAPI送信や運用判断で + どう扱うかを決める必要がある。 +- **入出庫イベントをAPIへ送る処理が両方式とも未実装。** 方式共通の統合作業として残る。 +- **Jetson Orin NXでライン座標設定GUIが起動しない**(Issue #103)。 + OpenCVのGUIバックエンドの問題で、`SHOW_DISPLAY=false` のカウント処理自体は動作する。 diff --git a/raspi/line_detection/README.md b/docs/two-line-system.md similarity index 82% rename from raspi/line_detection/README.md rename to docs/two-line-system.md index decbb996..4a0ea01c 100644 --- a/raspi/line_detection/README.md +++ b/docs/two-line-system.md @@ -14,27 +14,35 @@ ## ディレクトリ構造 ``` -raspi/line_detection/ -├── .env # 環境設定 +tracking-parking/ +├── .env # 環境設定(ローカル) ├── .env.template # 設定テンプレート -├── requirements.txt # 依存パッケージ -├── README.md # このファイル -├── main.py # メイン処理 +├── pyproject.toml # 依存パッケージ定義(uv) │ -├── line_setup/ # ライン座標設定ツール -│ └── setup_lines.py # GUIでライン位置を設定 +├── scripts/ # CLIエントリポイント +│ ├── run_detection.py # メイン処理 +│ ├── setup_lines.py # GUIでライン位置を設定 +│ ├── visualize_lines.py # ライン・車両代表点の可視化 +│ └── run_multi_video.py # 複数動画の一括処理と集約 │ -├── detection/ # 車両検知・ライン交差判定 +├── src/tracking_parking/ # 本体パッケージ │ ├── config.py # 設定管理 -│ ├── line_crossing.py # ライン交差検知(外積法) -│ └── tracker.py # 車両トラッキング・状態管理 +│ ├── detection/ # 車両検知・ライン交差判定 +│ │ ├── line_crossing.py # ライン交差検知(外積法) +│ │ └── tracker.py # 車両トラッキング・状態管理 +│ ├── output/ # 結果出力 +│ │ ├── video_writer.py # アノテーション動画生成 +│ │ └── event_logger.py # イベントログ出力 +│ ├── common/ # 計測・GT・W&Bの共通基盤 +│ └── eval/ # 精度評価 │ -├── result_output/ # 結果出力 -│ ├── video_writer.py # アノテーション動画生成 -│ └── event_logger.py # イベントログ出力 -│ -└── data/ # データ +├── tests/ # テスト +├── models/ # YOLOモデル重み(.pt、Git管理外) +└── data/ # データ(Git管理外) ├── inputs/ # 入力動画(.mp4) + │ ├── configs/ # 正解台数GT(<動画名>_gt.json) + │ ├── videos.example.json # 複数動画処理の対象リスト雛形(Git管理下) + │ └── videos.json # 同・実体 └── outputs/ # 出力結果 ├── videos/ # アノテーション済み動画 └── logs/ # イベントログ(JSON/CSV) @@ -67,7 +75,7 @@ uv sync GUIツールを使って2本のラインと駐車場基準点を設定します: ```bash -python line_setup/setup_lines.py --video data/inputs/test.mp4 +python scripts/setup_lines.py --video data/inputs/test.mp4 ``` **操作方法:** @@ -97,7 +105,7 @@ cp /path/to/your/video.mp4 data/inputs/ ### 基本的な使用方法 ```bash -python main.py --input data/inputs/test.mp4 +python scripts/run_detection.py --input data/inputs/test.mp4 ``` ### リアルタイム表示 @@ -105,19 +113,19 @@ python main.py --input data/inputs/test.mp4 処理中の動画を表示しながら実行: ```bash -python main.py --input data/inputs/test.mp4 --display +python scripts/run_detection.py --input data/inputs/test.mp4 --display ``` ### カメラからリアルタイム処理 ```bash -python main.py --camera 0 --display +python scripts/run_detection.py --camera 0 --display ``` ### 出力先を指定 ```bash -python main.py --input data/inputs/test.mp4 --output /path/to/output +python scripts/run_detection.py --input data/inputs/test.mp4 --output /path/to/output ``` ### 方式間の速度比較 @@ -135,7 +143,7 @@ ROI方式の現在の既定値は`VEHICLE_CLASSES=2,7`、`CONFIDENCE_THRESHOLD=0 ```bash WARMUP_FRAMES=30 YOLO_DEVICE=cpu YOLO_IMGSZ=640 \ YOLO_TRACKER=botsort.yaml SAVE_VIDEO=false SHOW_DISPLAY=false \ -WANDB_MODE=offline python main.py --input data/inputs/test.mp4 \ +WANDB_MODE=offline python scripts/run_detection.py --input data/inputs/test.mp4 \ --wandb --device-name raspi5 ``` @@ -160,12 +168,11 @@ wandb sync ### 台数精度の比較(GT) 正解台数(GT)のJSONを`--gt`で指定すると、検出結果との差を`count_error`として -記録する。ROI方式と同じGTファイルを共有し、`roi`キー等ROI方式固有の項目は -無視する。 +記録する。GTファイルは`in`/`out`以外のキーを持っていても無視される。 ```bash -python main.py --input data/inputs/test.mp4 \ - --gt ../roi-counter/data/inputs/configs/IMG_2787_gt.json +python scripts/run_detection.py --input data/inputs/test.mp4 \ + --gt data/inputs/configs/IMG_2787_gt.json ``` `--gt`を省略した場合は`<動画名>_gt.json`を入力動画と同じディレクトリから @@ -189,15 +196,13 @@ GTのJSON形式: - `gt_in` / `gt_out`: GTの値(未確認は`None`) - `count_error_in` / `count_error_out`: 方向ごとの絶対誤差(未評価の方向は`None`) -- `count_error`: 評価した方向の誤差合計。**評価方向数によってスケールが変わるため、 - ROI方式の`04_multi_video_mae.py`と直接比較してよいのはIN/OUT両方が評価済みのときだけ**。 +- `count_error`: 評価した方向の誤差合計。**評価方向数によってスケールが変わる**ため、 + run間で比較してよいのは評価方向が揃っているときだけ。 片方のみの評価では方向別キー(`count_error_in`等)を使うこと GT情報(`ground_truth_sha256`・`gt_in`・`gt_out`)は`condition_key`に含まれるため、 GTの有無・内容が変わると同一条件とはみなされなくなる。 -> **注意**: ROI方式の`04_multi_video_mae.py`はGTの`out`が`null`だとエラーになる。 -> `null`を含むGTをROI方式の`GT_DIR`に置かないこと。 ## 出力ファイル diff --git a/raspi/line_detection/VERIFICATION.md b/docs/verification.md similarity index 79% rename from raspi/line_detection/VERIFICATION.md rename to docs/verification.md index 31202f6b..729213d7 100644 --- a/raspi/line_detection/VERIFICATION.md +++ b/docs/verification.md @@ -3,22 +3,30 @@ 2ライン検知ロジックが期待どおり動くことを、実動画1本に対して端から端まで確認する手順。 新しい環境で動かすとき、判定ロジックを変更したとき、ROI方式と比較するときに使う。 -対象ブランチ: `experiment/ucn/two-lines-detection` -対象ディレクトリ: `raspi/line_detection/` - -ROI方式の検証手順は `raspi/roi-counter/VERIFICATION.md`(`feat/mike/89-bbox-analysis-within-roi`)にある。 +ROI方式は比較検討の末に不採用となり、本ブランチにコードは無い +(経緯は [decisions/0001-two-line-method.md](decisions/0001-two-line-method.md))。 +当時の検証手順は同じリポジトリの `feat/mike/89-bbox-analysis-within-roi` ブランチ、 +`raspi/roi-counter/VERIFICATION.md` に残っている。 ## 0. 前提条件 - リポジトリルートで `uv sync` 済みであること -- 実動画がローカルに存在すること。`data/` は `.gitignore` 対象なので、 - リポジトリを新しく取得した環境では別途配置が必要 - - `data/inputs/IMG_2787.MOV` - - GT(正解台数)は ROI方式と共有する: `../roi-counter/data/inputs/configs/IMG_2787_gt.json` +- 実動画とGTがローカルに存在すること。`data/` は `.gitignore` 対象のため + リポジトリには含まれず、チームで共有している資産を別途配置する必要がある + - `data/inputs/IMG_2787.MOV` — 本手順で使う検証動画 + - `data/inputs/configs/IMG_2787_gt.json` — 対応するGT(正解台数)。 + 書式は `{"in": 22, "out": 0}`(8章参照) - `.env` が存在すること(`.gitignore` 対象。無ければ 2章で作成する) - 1回の実行に数分〜十数分かかる(YOLO推論を含むため) -以降のコマンドは断りがない限り `raspi/line_detection/` をカレントディレクトリとして書く。 +> **GTファイルの出所(2026-09-10 追記)** +> `data/inputs/configs/*_gt.json` は元々ROI方式側のディレクトリに置かれていたが、 +> ディレクトリ整理の際に誤って削除した。台数の値は過去runのmanifestに記録されていた +> `gt_in` / `gt_out` から復元してあるが、**ファイルのSHA-256は元と一致しない**。 +> `condition_key` にGTのハッシュが含まれるため、2026-09-10 以降のrunは +> それ以前のrunと同一条件とはみなされない。 + +以降のコマンドは断りがない限り、リポジトリルートをカレントディレクトリとして書く。 ### W&B(実験記録)について @@ -47,7 +55,7 @@ grep -q "api.wandb.ai" ~/.netrc && echo "ログイン済み" || echo "未ログ 溜まり続けるだけで、W&B上では一切参照できない**。manifestに記録される `wandb_run_id` も、同期するまではW&B上に対応する実体が無い点に注意すること。 -> **`WANDB_DIR=data/outputs` を省略しないこと。** `raspi/common/wandb_logger.py` の +> **`WANDB_DIR=data/outputs` を省略しないこと。** `src/tracking_parking/common/wandb_logger.py` の > `wandb.init()` は `dir` を渡していないため、未指定だとwandbが**カレントディレクトリ直下**に > `wandb/` を作る。`data/` の外に出るとgitignoreの対象外になり、run一式を失いやすい > (ROI方式で実際に一度失っている)。 @@ -73,8 +81,8 @@ grep -q "api.wandb.ai" ~/.netrc && echo "ログイン済み" || echo "未ログ リトライを打ち切るには `x_graphql_retry_max`(`x_` 接頭辞は内部オプション)に頼るしかない。 打ち切って例外を上げた場合、現在の実装では計測が丸ごと失われる。 -`raspi/common/wandb_logger.py` の `wandb.init()` は try/except で包んでおらず、 -`ExperimentLogger` の生成は `main.py` のフレーム処理を囲む `try:` より前にある。 +`src/tracking_parking/common/wandb_logger.py` の `wandb.init()` は try/except で包んでおらず、 +`ExperimentLogger` の生成は `scripts/run_detection.py` のフレーム処理を囲む `try:` より前にある。 例外は `try`/`finally` の外側で発生するので、1フレームも処理しないまま終了し、 `manifests/` も残らない。 @@ -91,7 +99,7 @@ wandbはローカルへバッファして再送する設計だが、`finish()` ### 本番運用ではW&Bを有効にしない(2026-08-26 決定) -`main.py` は検証と本番運用の両方で使う。 +`scripts/run_detection.py` は検証と本番運用の両方で使う。 W&Bは `--wandb` または `USE_WANDB=true` で明示的に有効化したときだけ動くので、 本番運用ではどちらも付けない。 ネットワーク断が入出庫カウントの停止に直結する状態を、24/7で動く監視系に持ち込まないため。 @@ -104,20 +112,17 @@ W&Bは `--wandb` または `USE_WANDB=true` で明示的に有効化したとき リポジトリルートから実行する。 ```bash -uv run pytest raspi/ -q +uv run pytest -q ``` -**合格基準**: 全件パス(2026-08-20 時点で 161 passed)。 - -`raspi/common/` は ROI方式とbyte-identicalで共有しているため、ここを変更した場合は -ROI方式側のテストも実行すること。 +**合格基準**: 全件パス(2026-09-10 時点で 198 passed)。 ## 2. ライン座標の設定 `.env` が無い場合、または画角が変わった場合はGUIで設定し直す。 ```bash -uv run python line_setup/setup_lines.py --video data/inputs/IMG_2787.MOV +uv run python scripts/setup_lines.py --video data/inputs/IMG_2787.MOV ``` 動画の先頭フレームが表示されるので、次の5点をこの順にクリックする。 @@ -130,9 +135,9 @@ uv run python line_setup/setup_lines.py --video data/inputs/IMG_2787.MOV `r` キーでやり直し、`q` キーまたはウィンドウを閉じると保存される。 -> **注意**: このツールは `.env` を**丸ごと上書きする**。`MODEL_PATH` や `MARGIN_PX` など -> ライン座標以外の値もテンプレートの既定値で書き戻されるため、実行後に `.env` を開いて -> 意図した値になっているか確認すること。 +> このツールが書き換えるのはライン座標のキー(`LINE1_*` / `LINE2_*` / `PARKING_REF_*`)だけで、 +> `MODEL_PATH` や `MARGIN_PX` など他の値・コメント・並び順は保持される。 +> `.env` が存在しない場合のみ `.env.template` を土台にして新規作成する。 `.env` を手で作る場合は `.env.template` をコピーして `MODEL_PATH` を実在するモデルへ 書き換える(テンプレートはfine-tuned modelを指しているが、手元に無ければ `yolov8s.pt` でよい)。 @@ -143,13 +148,13 @@ uv run python line_setup/setup_lines.py --video data/inputs/IMG_2787.MOV 引数は位置指定で `<動画パス> [出力動画パス] [開始フレーム] [終了フレーム]`(既定は 0〜300フレーム)。 ```bash -uv run python visualize_lines_and_vehicles.py data/inputs/IMG_2787.MOV +uv run python scripts/visualize_lines.py data/inputs/IMG_2787.MOV ``` 出力動画として残す場合、およびフレーム範囲を変える場合: ```bash -uv run python visualize_lines_and_vehicles.py data/inputs/IMG_2787.MOV debug_vis.mp4 300 600 +uv run python scripts/visualize_lines.py data/inputs/IMG_2787.MOV debug_vis.mp4 300 600 ``` 起動時に `Line1` / `Line2` / `駐車場基準点` / `MARGIN_PX` の値が標準出力に表示される。 @@ -161,15 +166,14 @@ uv run python visualize_lines_and_vehicles.py data/inputs/IMG_2787.MOV debug_vis ```bash USE_WANDB=true WANDB_MODE=offline WANDB_DIR=data/outputs \ WANDB_PROJECT=tracking-parking \ - uv run python main.py \ + uv run python scripts/run_detection.py \ --input data/inputs/IMG_2787.MOV \ - --gt ../roi-counter/data/inputs/configs/IMG_2787_gt.json + --gt data/inputs/configs/IMG_2787_gt.json ``` `count_error` と confidence の内訳はこの run のW&B summary に入る。 W&Bを付けずに実行すると、合格判定の根拠がローカル成果物にしか残らず、 -ROI方式(`04_multi_video_mae.py` が既定でW&Bへ記録する)と粒度が揃わない。 -完走したら6章と同じ手順で `wandb sync` する。 +run間で追跡できなくなる。完走したら6章と同じ手順で `wandb sync` する。 **確認する項目**: 実行中に `GT比較: count_error=0 (in=0, out=0)` が出たあと、 末尾に次のサマリーが表示される。 @@ -225,9 +229,9 @@ print('count_error:', data.get('accuracy', {}).get('count_error')) | `videos/annotated_<動画名>.mp4` | 可視化済み動画(`SAVE_VIDEO=true` 時) | | `manifests/.json` | run識別子・再現情報・出力パスの相互参照 | -## 6. ROI方式との速度比較 +## 6. 速度比較のためのrun -比較可能なrunにするには、両方式で動画・モデル・classes・confidence・IoU・image size・ +速度をrun間で比較するには、動画・モデル・classes・confidence・IoU・image size・ tracker・device・warm-up・動画保存/表示設定を揃える必要がある。 これらから生成される `comparison_key` が一致するrun同士だけを直接比較する。 @@ -237,9 +241,9 @@ SAVE_VIDEO=false SHOW_DISPLAY=false SAVE_LOGS=true \ YOLO_DEVICE=cpu YOLO_IMGSZ=640 YOLO_TRACKER=botsort.yaml WARMUP_FRAMES=30 \ EXP_DEVICE_NAME=raspi5 EXP_DEVICE_ACCELERATOR=cpu \ USE_WANDB=true WANDB_MODE=offline WANDB_DIR=data/outputs \ -uv run python main.py \ +uv run python scripts/run_detection.py \ --input data/inputs/IMG_2787.MOV \ - --gt ../roi-counter/data/inputs/configs/IMG_2787_gt.json \ + --gt data/inputs/configs/IMG_2787_gt.json \ --wandb --device-name raspi5 ``` @@ -290,7 +294,7 @@ done 台数(`count_error`)ではなく、個々のイベントがGTと時刻レベルで対応するかを評価する。 ```bash -uv run python build_accuracy_report.py \ +uv run python -m tracking_parking.eval.build_accuracy_report \ --events-dir data/outputs/logs \ --output data/outputs/event_accuracy.csv ``` @@ -322,6 +326,8 @@ uv run python build_accuracy_report.py \ ## 10. 関連資料 -- `README.md` — システム構成、設定パラメータ、アルゴリズムの解説 +- `docs/two-line-system.md` — システム構成、設定パラメータ、アルゴリズムの解説 - `docs/wandb_integration_spec_v2.md` — 実験記録の仕様 -- `raspi/roi-counter/VERIFICATION.md` — ROI方式の検証手順(別ブランチ) +- [decisions/0001-two-line-method.md](decisions/0001-two-line-method.md) — 2ライン方式を採用した経緯と比較結果 +- `raspi/roi-counter/VERIFICATION.md` — 不採用となったROI方式の検証手順。 + 同リポジトリの `feat/mike/89-bbox-analysis-within-roi` ブランチにある diff --git a/raspi/line_detection/docs/wandb_integration_spec_v2.md b/docs/wandb_integration_spec_v2.md similarity index 93% rename from raspi/line_detection/docs/wandb_integration_spec_v2.md rename to docs/wandb_integration_spec_v2.md index d4f39177..61500f2e 100644 --- a/raspi/line_detection/docs/wandb_integration_spec_v2.md +++ b/docs/wandb_integration_spec_v2.md @@ -9,12 +9,24 @@ ## 0. このドキュメントの目的 -`raspi/` 配下の駐車場入出庫カウントシステムに、Weights & Biases(以下 W&B)による実験管理機能を追加する。 +駐車場入出庫カウントシステムに、Weights & Biases(以下 W&B)による実験管理機能を追加する。 本システムには検出ロジックが 2 系統あり、両者の **性能比較** と、本番デバイス上での **処理速度管理** を目的とする。 - `line_detection/` … 2 ライン + 外積法 + ハイブリッド方式(信頼度付き) - `roi-counter/` … ROI 内の進行度 `s` による状態機械 +> **本ドキュメントの位置づけ(2026-09-10 追記)** +> 本仕様は2方式が並存していた時期に書かれたものである。その後2ライン方式の採用が決まり +> ([decisions/0001-two-line-method.md](../decisions/0001-two-line-method.md))、ROI方式 +> (`roi-counter/`)のコードは本ブランチには存在しない。ROI方式に言及する箇所は、 +> 比較設計の経緯を残すための記述として読むこと。現行コードでの対応は次のとおり。 +> +> | 本文中の記述 | 現在の場所 | +> |---|---| +> | `line_detection/main.py` | `scripts/run_detection.py` | +> | `common/*.py` | `src/tracking_parking/common/*.py` | +> | `roi-counter/scripts/*` | `feat/mike/89-bbox-analysis-within-roi` ブランチの `raspi/roi-counter/scripts/*` | + 精度指標(Accuracy / Precision / Recall / F1)は **SAM3 による GT を用いて後日別スクリプトで算出する**ため、本実装では「速度・台数の即時記録」と「精度を後から同一 run へ追記できる仕組み」を分けて作る。 --- @@ -43,7 +55,7 @@ 8. **ネットワークの無い環境(Raspberry Pi 実機)での計測を第一級ユースケースとする。** - `WANDB_MODE` 環境変数(`online` / `offline`)を尊重する。offline 時はローカルに記録され、後日 `wandb sync` でアップロードできる。 - README(または各スクリプトの docstring)に offline 計測 → sync の手順を 3〜4 行で記載すること。 - - **運用は `offline` に固定する(2026-08-26 決定)**。`online` はネットワークへ到達できない環境で `wandb.init()` がハングし、`WANDB_INIT_TIMEOUT` でも `Settings(init_timeout)` でも打ち切れない。実測値と根拠は `raspi/line_detection/VERIFICATION.md` の0章に記載した。 + - **運用は `offline` に固定する(2026-08-26 決定)**。`online` はネットワークへ到達できない環境で `wandb.init()` がハングし、`WANDB_INIT_TIMEOUT` でも `Settings(init_timeout)` でも打ち切れない。実測値と根拠は [verification.md](../verification.md) の0章に記載した。 - **本番運用では `--wandb` / `USE_WANDB` を付けない(2026-08-26 決定)**。ネットワーク断が入出庫カウントの停止に直結する状態を、24/7で動く監視系へ持ち込まないため。ROI方式の `main.py` も同じ理由でW&B非対応のままとする。 --- @@ -204,7 +216,7 @@ W&B では `comparison_key` が一致する ROI / 2 ライン run のみを直 ## 3. 実装するモジュール -### 3.1 共通ユーティリティ `raspi/common/wandb_logger.py`(新規作成) +### 3.1 共通ユーティリティ `src/tracking_parking/common/wandb_logger.py`(新規作成) 両ロジックから共有する薄いラッパを作る。W&B 無効時は no-op になること。 @@ -230,14 +242,14 @@ W&B では `comparison_key` が一致する ROI / 2 ライン run のみを直 - **呼び出し側は `finish()` を必ず `try/finally` で保証する**(§4 各所に明記)。カメラ切断・例外・Ctrl+C でも run が "running" のまま放置されないこと。異常終了時は `exit_code=1` で finish する。 - 長時間のカメラ運用向けに `update_running_summary(self, d: dict)`(処理途中の count_in/out 等を summary へ随時反映するだけの薄いメソッド)を用意する。クラッシュしても直近の集計が残る。 -### 3.1a run識別ユーティリティ `raspi/common/run_identity.py` +### 3.1a run識別ユーティリティ `src/tracking_parking/common/run_identity.py` - `build_condition_key(condition)`は型付きcanonical JSONをSHA-256化する。floatの表示丸めや辞書挿入順に依存しない。 - `build_run_identity()`は同一条件でも毎回異なる`execution_id`を生成する。 - `collect_reproducibility_info()`はGit SHA、dirty状態、Python・主要ライブラリ版を返す。 - `write_run_manifest()`はW&Bの有効・無効に関係なく、ID、config、出力パスをJSONへ保存する。 -### 3.2 統計ユーティリティ `raspi/common/frame_stats.py`(新規作成) +### 3.2 統計ユーティリティ `src/tracking_parking/common/frame_stats.py`(新規作成) - `compute_frame_stats(frame_ms_list: list[float], source_fps: float) -> dict` - min / max / mean / p50 / p95 / p99 / total_ms / effective_fps / realtime_ok を返す。 @@ -246,19 +258,14 @@ W&B では `comparison_key` が一致する ROI / 2 ライン run のみを直 ### 3.3 共通モジュールの import 経路(重要・未指定だと迷う) -両プロジェクトは既に `sys.path.insert(0, <自ルート>)` スタイルなので、これに合わせる。パッケージ化(pip install -e)は今回しない。 +本仕様の策定時は両方式が `sys.path.insert(0, <自ルート>)` スタイルだったが、 +2ライン方式の採用後に `src` レイアウトへ移行し、`uv sync` によるeditable installで解決している。 -- `raspi/common/__init__.py` を作成する。 -- 各スクリプトの既存 `sys.path.insert` の直後に、`raspi/` ディレクトリも追加する: - ```python - # 例: raspi/roi-counter/scripts/02_run_analysis.py の場合 - sys.path.insert(0, str(Path(__file__).parents[1])) # 既存: roi-counter/ - sys.path.insert(0, str(Path(__file__).parents[2])) # 追加: raspi/ - from common.wandb_logger import ExperimentLogger - from common.run_identity import build_condition_key, build_run_identity - from common.frame_stats import compute_frame_stats - ``` -- `line_detection/main.py` は `os.path.dirname` スタイルなのでそれに合わせて親ディレクトリを追加する。 +```python +from tracking_parking.common.wandb_logger import ExperimentLogger +from tracking_parking.common.run_identity import build_condition_key, build_run_identity +from tracking_parking.common.frame_stats import compute_frame_stats +``` --- @@ -291,7 +298,7 @@ W&B では `comparison_key` が一致する ROI / 2 ライン run のみを直 - `detail_rows`(既存CSV)に`wandb_run_id`、`execution_id`、`condition_key`列を追加してCSVとW&Bを相互参照可能にする。 - 既存の `results.csv` / `mae_summary.csv` 出力は維持する。 -### 4.3 `line_detection/main.py` +### 4.3 `scripts/run_detection.py` - `process_video()` に W&B 連携を追加。 - `config`は`logic_name="line_detection"`、Line1・Line2の全座標、駐車場基準点、 @@ -307,7 +314,7 @@ W&B では `comparison_key` が一致する ROI / 2 ライン run のみを直 --- -## 5. 後追い精度評価スクリプト `raspi/eval/update_accuracy_from_sam3.py`(新規作成・雛形のみ) +## 5. 後追い精度評価スクリプト `src/tracking_parking/eval/update_accuracy_from_sam3.py`(新規作成・雛形のみ) SAM3 の GT が揃った後に実行する独立スクリプト。**run の再開ではなく W&B API 経由で既存 run の summary を更新する**方針(実行と評価を分離するため)。 @@ -365,7 +372,7 @@ SAM3 の GT が揃った後に実行する独立スクリプト。**run の再 1. `common/frame_stats.py`、`common/wandb_logger.py`、`common/run_identity.py`(no-op経路・識別子・manifestテスト含む)。 2. `roi-counter/scripts/02_run_analysis.py` への差し込み(最小構成の検証)。 3. `roi-counter/scripts/04_multi_video_mae.py`(スイープ → 複数 run、トラッカーリセット含む)。 -4. `line_detection/main.py`。 +4. `scripts/run_detection.py`。 5. `eval/update_accuracy_from_sam3.py`(雛形 + dry-run)。 6. requirements 更新・README への offline 手順追記。 diff --git a/raspi/common/tests/__init__.py b/models/.gitkeep similarity index 100% rename from raspi/common/tests/__init__.py rename to models/.gitkeep diff --git a/raspi/number/main.py b/plate_recognition/main.py similarity index 94% rename from raspi/number/main.py rename to plate_recognition/main.py index a25eea97..8bd6d48d 100644 --- a/raspi/number/main.py +++ b/plate_recognition/main.py @@ -9,7 +9,7 @@ # パスの設定 HOME_DIR = os.environ["HOME_DIR"] -PATH = HOME_DIR + "/yolo_fine_tuning/runs/detect/l_640_FHD/weights/best.pt" +PATH = HOME_DIR + "/training/runs/detect/l_640_FHD/weights/best.pt" # YOLOモデルのロード model = YOLO(PATH, verbose=False) diff --git a/raspi/number/model.md b/plate_recognition/model.md similarity index 100% rename from raspi/number/model.md rename to plate_recognition/model.md diff --git a/raspi/number/requirements.txt b/plate_recognition/requirements.txt similarity index 100% rename from raspi/number/requirements.txt rename to plate_recognition/requirements.txt diff --git a/pyproject.toml b/pyproject.toml index d4018b65..0ccf6ed7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "tracking-parking" version = "0.1.0" -description = "Add your description here" +description = "2ライン方式による駐車場の入出庫検知" readme = "README.md" requires-python = ">=3.12" dependencies = [ @@ -26,3 +26,13 @@ dependencies = [ dev = [ "pytest>=9.1.1", ] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/tracking_parking"] + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/raspi/detect/demo_data/prediction_visual.png b/raspi/detect/demo_data/prediction_visual.png deleted file mode 100644 index 15ca3de5..00000000 Binary files a/raspi/detect/demo_data/prediction_visual.png and /dev/null differ diff --git a/raspi/detect/demo_data/small-vehicles1.jpeg b/raspi/detect/demo_data/small-vehicles1.jpeg deleted file mode 100644 index 5e258c9d..00000000 Binary files a/raspi/detect/demo_data/small-vehicles1.jpeg and /dev/null differ diff --git a/raspi/detect/demo_data/terrain2.png b/raspi/detect/demo_data/terrain2.png deleted file mode 100644 index 4469c41d..00000000 Binary files a/raspi/detect/demo_data/terrain2.png and /dev/null differ diff --git a/raspi/detect/detect.py b/raspi/detect/detect.py deleted file mode 100644 index 998e6163..00000000 --- a/raspi/detect/detect.py +++ /dev/null @@ -1,38 +0,0 @@ -# 入力:カメラで得た画像 -# 出力:検出台数 - -from sahi.predict import get_sliced_prediction - -def detect(model,data,count_class): - count = 0 - - # 入力データから検出を行う - print("detect start...") - result = get_sliced_prediction( - data, - model, - slice_height=256, - slice_width=256, - overlap_height_ratio=0.2, - overlap_width_ratio=0.2, - ) - - print("detect finish") - - # 検出した結果を画像にする - result.export_visuals(export_dir="result/") - - # 検出結果を辞書型のリストに変換 - detect_list = result.to_coco_predictions() - - # 指定のカテゴリをカウント - for detect_object in detect_list: - if detect_object["category_id"] == count_class: - count += 1 - - print(f'{count_class} is the number of {count}') - - return count - - - diff --git a/raspi/detect/main.py b/raspi/detect/main.py deleted file mode 100644 index 7ca724df..00000000 --- a/raspi/detect/main.py +++ /dev/null @@ -1,57 +0,0 @@ -# yoloで物体認識したものを、日付と車数に変換して送信する - -import cv2 -from sahi import AutoDetectionModel -from sahi.utils.yolov8 import download_yolov8s_model -from send import send_mongo,connect_mongo -from detect import detect -import os -from dotenv import load_dotenv - -load_dotenv() -USER = os.environ["USER_NAME"] -PASS = os.environ["PASS"] -HOST = os.environ["HOST"] -PORT = os.environ["PORT"] -DB_NAME = os.environ["DB_NAME"] -COLLECTION_NAME = os.environ["COLLECTION_NAME"] - -# 人なら0、車なら2 -count_class = 0 - -def main(): - # モデルをダウンロード - yolov8_model_path = "models/yolov8s.pt" - download_yolov8s_model(yolov8_model_path) - - # mongoDBと接続 - collection,_ = connect_mongo(USER,PASS,HOST,PORT,DB_NAME,COLLECTION_NAME) - - - # 画像パスまたはnumpy画像を用いて標準的な推論を行う。 - # YOLOv8 、このようにオブジェクト検出用のモデルをインスタンス化することができる: - detection_model = AutoDetectionModel.from_pretrained( - model_type="yolov8", - model_path=yolov8_model_path, - confidence_threshold=0.3, - device="cpu", # or 'cuda:0' - ) - - # カメラデバイスを開く - cap = cv2.VideoCapture(0) - - while True: - ret, frame = cap.read() - print("isCap",ret) - if not ret: - break - - # detect.pyでカウントした数を取り出す - count = detect(detection_model,frame,count_class) - - # データを送信する - send_mongo(count,collection) - - -if __name__ == "__main__": - main() diff --git a/raspi/detect/models/.gitignore b/raspi/detect/models/.gitignore deleted file mode 100644 index eede66d8..00000000 --- a/raspi/detect/models/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.pt \ No newline at end of file diff --git a/raspi/detect/requirements.txt b/raspi/detect/requirements.txt deleted file mode 100644 index f59b3bf0..00000000 --- a/raspi/detect/requirements.txt +++ /dev/null @@ -1,139 +0,0 @@ -aiofiles==23.2.1 -anyio==4.3.0 -argcomplete==3.3.0 -asttokens==2.4.1 -attrs==23.2.0 -beautifulsoup4==4.12.3 -boto3==1.34.102 -botocore==1.34.102 -Brotli==1.1.0 -cachetools==5.3.3 -certifi==2024.2.2 -charset-normalizer==3.3.2 -click==8.1.7 -contourpy==1.2.1 -cycler==0.12.1 -dacite==1.7.0 -decorator==5.1.1 -Deprecated==1.2.14 -dill==0.3.8 -dnspython==2.6.1 -exceptiongroup==1.2.1 -executing==2.0.1 -fiftyone==0.23.8 -fiftyone-brain==0.16.1 -fiftyone_db==1.1.2 -filelock==3.14.0 -fire==0.6.0 -fonttools==4.51.0 -fsspec==2024.3.1 -ftfy==6.2.0 -future==1.0.0 -glob2==0.7 -graphql-core==3.2.3 -h11==0.14.0 -h2==4.1.0 -hpack==4.0.0 -httpcore==1.0.5 -httpx==0.27.0 -humanize==4.9.0 -Hypercorn==0.16.0 -hyperframe==6.0.1 -idna==3.7 -imageio==2.34.1 -imantics==0.1.12 -inflate64==1.0.0 -ipython==8.24.0 -jedi==0.19.1 -Jinja2==3.1.4 -jmespath==1.0.1 -joblib==1.4.2 -jsonlines==4.0.0 -kaleido==0.2.1 -kiwisolver==1.4.5 -lazy_loader==0.4 -lxml==5.2.1 -MarkupSafe==2.1.5 -matplotlib==3.8.4 -matplotlib-inline==0.1.7 -mongoengine==0.24.2 -motor==3.4.0 -mpmath==1.3.0 -multivolumefile==0.2.3 -networkx==3.3 -numpy==1.26.4 -opencv-python==4.7.0.72 -opencv-python-headless==4.9.0.80 -packaging==24.0 -pandas==2.2.2 -parso==0.8.4 -pexpect==4.9.0 -pillow==10.3.0 -plotly==5.22.0 -pprintpp==0.4.0 -priority==2.0.0 -prompt-toolkit==3.0.43 -psutil==5.9.8 -ptyprocess==0.7.0 -pure-eval==0.2.2 -py-cpuinfo==9.0.0 -py7zr==0.21.0 -pybboxes==0.1.6 -pybcj==1.0.2 -pycryptodomex==3.20.0 -Pygments==2.18.0 -pymongo==4.7.2 -pyparsing==3.1.2 -pyppmd==1.1.0 -python-dateutil==2.9.0.post0 -python-dotenv==1.0.1 -pytz==2024.1 -PyYAML==6.0.1 -pyzstd==0.15.10 -rarfile==4.2 -regex==2024.5.10 -requests==2.31.0 -retrying==1.3.4 -s3transfer==0.10.1 -sahi==0.11.15 -scikit-image==0.23.2 -scikit-learn==1.4.2 -scipy==1.13.0 -seaborn==0.13.2 -shapely==2.0.4 -six==1.16.0 -sniffio==1.3.1 -sortedcontainers==2.4.0 -soupsieve==2.5 -sse-starlette==0.10.3 -sseclient-py==1.8.0 -stack-data==0.6.3 -starlette==0.37.2 -strawberry-graphql==0.138.1 -sympy==1.12 -tabulate==0.9.0 -taskgroup==0.0.0a4 -tenacity==8.3.0 -termcolor==2.4.0 -terminaltables==3.1.10 -texttable==1.7.0 -thop==0.1.1.post2209072238 -threadpoolctl==3.5.0 -tifffile==2024.5.10 -tomli==2.0.1 -torch==2.3.0 -torchvision==0.18.0 -tqdm==4.66.4 -traitlets==5.14.3 -typing_extensions==4.11.0 -tzdata==2024.1 -tzlocal==5.2 -ultralytics==8.2.11 -universal-analytics-python3==1.1.1 -urllib3==2.2.1 -voxel51-eta==0.12.6 -wcwidth==0.2.13 -wrapt==1.16.0 -wsproto==1.2.0 -xmljson==0.2.1 -xmltodict==0.13.0 diff --git a/raspi/detect/runs/predict/exp5/visuals/small-vehicles1.png b/raspi/detect/runs/predict/exp5/visuals/small-vehicles1.png deleted file mode 100644 index 28da1eda..00000000 Binary files a/raspi/detect/runs/predict/exp5/visuals/small-vehicles1.png and /dev/null differ diff --git a/raspi/detect/runs/predict/exp6/visuals/small-vehicles1.png b/raspi/detect/runs/predict/exp6/visuals/small-vehicles1.png deleted file mode 100644 index 28da1eda..00000000 Binary files a/raspi/detect/runs/predict/exp6/visuals/small-vehicles1.png and /dev/null differ diff --git a/raspi/detect/send.py b/raspi/detect/send.py deleted file mode 100644 index 73a4a730..00000000 --- a/raspi/detect/send.py +++ /dev/null @@ -1,44 +0,0 @@ -# 検出数と時刻を送信 -from pymongo import MongoClient -import time -import os -from dotenv import load_dotenv - -def connect_mongo(user,password,host,port,db_name,collection_name): - client = MongoClient(f"mongodb://{user}:{password}@{host}:{port}") - db = client[db_name] - collection = db[collection_name] - return collection,client - -def get_time(): - ut = time.time() * 1000 - return ut - -def make_doc(num,time): - num_int = f"{num}" - document = {"count": int(num_int), "time": time } - return document - -def send_mongo(num,mongo_col): - print("send mongo") - ut = get_time() - doc = make_doc(num,ut) - mongo_col.insert_one(doc) - print("send finish") - #mongo_cl.close() - return - -# print(get_time()) - -if __name__ == "__main__": - load_dotenv() - USER = os.environ["USER_NAME"] - PASS = os.environ["PASS"] - HOST = os.environ["HOST"] - PORT = os.environ["PORT"] - DB_NAME = os.environ["DB_NAME"] - COLLECTION_NAME = os.environ["COLLECTION_NAME"] - collection,client = connect_mongo(USER,PASS,HOST,PORT,DB_NAME,COLLECTION_NAME) - send_mongo("20") - print(collection) - diff --git a/raspi/line_detection/requirements.txt b/raspi/line_detection/requirements.txt deleted file mode 100644 index 105f995e..00000000 --- a/raspi/line_detection/requirements.txt +++ /dev/null @@ -1,21 +0,0 @@ -# YOLOv8とトラッキング -ultralytics==8.2.52 - -# 動画処理 -opencv-python==4.8.1.78 - -# 設定管理 -python-dotenv==1.0.0 - -# 数値演算 -numpy==1.24.4 - -# 深層学習バックエンド -torch==2.2.2 -torchvision==0.17.2 - -# データ処理 -pandas==2.2.1 - -# 実験管理(WANDB_MODE=offline対応) -wandb>=0.28.0 diff --git a/raspi/line_detection/run_gate4_alternating.py b/raspi/line_detection/run_gate4_alternating.py deleted file mode 100644 index 72fe4676..00000000 --- a/raspi/line_detection/run_gate4_alternating.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env python3 -"""ROI方式と2ライン方式を動画ごとに交互実行し、同一セッション内の相対速度を測る。 - -docs.local/gate4-speed-comparison-caveat.html の結論(開発機での速度比較は -機体の熱状態に支配され、別々の時間帯に測った2つのrun同士では比較できない) -を受けた案1の実装。動画ごとにROI→2ライン→ROI→…と交互に呼ぶことで、熱状態の -上がり方を両方式へ均等にかけ、このrun内でのcore_ms_p95の比を相対比較として -読めるようにする。 - -ROI方式のコードは feat/mike/89-bbox-analysis-within-roi にしかなく、本ブランチの -worktreeには存在しない。そのため別ディレクトリにROI方式用のgit worktreeを用意し -(ROI_ROOT)、そちらのscripts/04_multi_video_mae.pyをサブプロセスから動画1本ぶんだけ -呼び出す(scripts/_gate4_single_video_runner.py、worktree内のみに存在するローカル -専用ブートストラップ。04_multi_video_mae.py自体は無変更)。2ライン方式はmain.pyを -run_multi_video.pyと同じ流儀でサブプロセス実行する。 - -各方式を独立サブプロセスにする理由はrun_multi_video.pyと同じ -(.envの上書き問題/YOLOモデルとトラッカーの状態をrun間で持ち越さないため)。 - -出力: - data/outputs/{EXP_NAME}/ - ├── {video_stem}/ # 2ライン方式(main.pyが書く) - ├── timeline.csv # 交互実行の実施順・開始/終了時刻 - └── summary_alternating.csv # 方式別の速度・精度を並べた集約 -""" -import csv -import glob -import json -import os -import subprocess -import sys -import time -from datetime import datetime, timezone -from pathlib import Path - -import pandas as pd - -# ── パラメータ ────────────────────────────────────────────────────────────── -VIDEOS = [ - ("data/inputs/1787008160.558032.mp4", "newcam.env"), - ("data/inputs/1787009706.719727.mp4", "newcam.env"), - ("data/inputs/1787011229.231516.mp4", "newcam.env"), - ("data/inputs/1787012751.179971.mp4", "newcam.env"), - ("data/inputs/1787014266.421887.mp4", "newcam.env"), - ("data/inputs/IMG_2787.MOV", "img2787.env"), -] - -GT_DIR = os.getenv("GT_DIR", "../roi-counter/data/inputs/configs") -EXP_NAME = os.getenv("EXP_NAME", "gate4_alt") - -# ROI方式のコードが実際に存在するworktree(本ブランチには無い。docs.local/ -# gate4-speed-comparison-caveat.htmlの案1採用にあたり `git worktree add` で作成)。 -ROI_ROOT = Path(os.getenv( - "ROI_ROOT", "/Users/ycn/Workspace/NUTMEG/tracking-parking-roi-mike", -)) -ROI_DIR = ROI_ROOT / "raspi" / "roi-counter" -ROI_BOOTSTRAP = ROI_DIR / "scripts" / "_gate4_single_video_runner.py" - -# ROI方式の確定run(exp_adopted_final_gtfix)と同じ採用値。 -S_LOW = os.getenv("S_LOW", "0.26") -S_HIGH = os.getenv("S_HIGH", "0.32") -# ──────────────────────────────────────────────────────────────────────────── - -TIMELINE_COLUMNS = ("seq", "method", "video", "started_at", "ended_at", "duration_sec", "returncode") - -SUMMARY_COLUMNS = ( - "video", "roi_count_in", "roi_gt_in", "roi_count_out", "roi_gt_out", "roi_count_error", - "roi_core_ms_p95", "roi_comparison_key", - "line_count_in", "line_gt_in", "line_count_out", "line_gt_out", "line_count_error", - "line_core_ms_p95", "line_comparison_key", - "speed_ratio_line_over_roi", -) - - -def gt_path_for(video: str) -> Path: - return Path(GT_DIR) / f"{Path(video).stem}_gt.json" - - -def check_inputs() -> None: - missing = [] - for video, env in VIDEOS: - if not Path(video).exists(): - missing.append(f"動画がありません: {video}") - if not Path(env).exists(): - missing.append(f"設定がありません: {env}") - if not gt_path_for(video).exists(): - missing.append(f"GTがありません: {gt_path_for(video)}") - if not ROI_BOOTSTRAP.exists(): - missing.append( - f"ROI方式のブートストラップがありません: {ROI_BOOTSTRAP}\n" - " git worktree add でfeat/mike/89-bbox-analysis-within-roiを展開し、" - "raspi/roi-counter/scripts/_gate4_single_video_runner.py を用意してください。" - ) - if missing: - raise SystemExit("[ERROR] 実行前チェックに失敗しました:\n " + "\n ".join(missing)) - - -def now_iso() -> str: - return datetime.now(timezone.utc).astimezone().isoformat() - - -def run_logged(cmd: list, *, cwd: Path, env: dict, method: str, video: str, seq: int, - timeline_rows: list) -> int: - print(f"\n{'=' * 70}\n[{seq}] {method}: {Path(video).name}\n{'=' * 70}", flush=True) - started = now_iso() - t0 = time.monotonic() - proc = subprocess.run(cmd, cwd=str(cwd), env=env) - duration = time.monotonic() - t0 - ended = now_iso() - timeline_rows.append({ - "seq": seq, "method": method, "video": Path(video).name, - "started_at": started, "ended_at": ended, - "duration_sec": round(duration, 1), "returncode": proc.returncode, - }) - return proc.returncode - - -def run_roi_single(video: str, seq: int, timeline_rows: list) -> dict: - roi_gt_config = (Path.cwd() / gt_path_for(video)).resolve() - before = set(glob.glob(str(ROI_DIR / "data" / "outputs" / EXP_NAME / "mae_*"))) - - env = os.environ.copy() - env.update({ - "S_LOW_LIST": S_LOW, - "S_HIGH_LIST": S_HIGH, - "EXP_NAME": EXP_NAME, - "USE_WANDB": "true", - "WANDB_MODE": "offline", - "WANDB_PROJECT": "tracking-parking", - "WANDB_DIR": "data/outputs", - }) - env.pop("YOLO_DEVICE", None) # 確定runと同じくUltralyticsの自動選択に委ねる - - cmd = [sys.executable, str(ROI_BOOTSTRAP), "--gt-config", str(roi_gt_config)] - code = run_logged(cmd, cwd=ROI_DIR, env=env, method="ROI", video=video, seq=seq, - timeline_rows=timeline_rows) - if code != 0: - print(f"[ERROR] ROI方式が異常終了(code={code}): {video}", flush=True) - return {"video": Path(video).name} - - after = set(glob.glob(str(ROI_DIR / "data" / "outputs" / EXP_NAME / "mae_*"))) - new_dirs = sorted(after - before) - if not new_dirs: - print(f"[ERROR] ROI方式の出力ディレクトリが見つかりません: {video}", flush=True) - return {"video": Path(video).name} - mae_dir = Path(new_dirs[-1]) - return collect_roi(video, mae_dir) - - -def collect_roi(video: str, mae_dir: Path) -> dict: - manifest_paths = sorted(glob.glob(str(mae_dir / "manifests" / "*.json"))) - if not manifest_paths: - return {"video": Path(video).name, "roi_count_error": "収集失敗"} - manifest = json.loads(Path(manifest_paths[-1]).read_text(encoding="utf-8")) - config = manifest.get("config", {}) - - results_csv = mae_dir / "results.csv" - row = {} - if results_csv.exists(): - df = pd.read_csv(results_csv) - if not df.empty: - row = df.iloc[-1].to_dict() - - core_ms_p95 = None - timing_path = mae_dir / "gate4_timing_summary.json" - if timing_path.exists(): - timing_summary = json.loads(timing_path.read_text(encoding="utf-8")) - core_ms_p95 = timing_summary.get("core_ms_p95") - - return { - "video": Path(video).name, - "roi_count_in": row.get("count_in"), - "roi_gt_in": row.get("gt_in"), - "roi_count_out": row.get("count_out"), - "roi_gt_out": row.get("gt_out"), - "roi_count_error": row.get("count_error"), - "roi_core_ms_p95": core_ms_p95, - "roi_comparison_key": config.get("comparison_key"), - } - - -def run_line_single(video: str, env_file: str, seq: int, timeline_rows: list) -> dict: - out_dir = Path("data/outputs") / EXP_NAME / Path(video).stem - cmd = [ - sys.executable, "main.py", - "--input", video, - "--env", env_file, - "--gt", str(gt_path_for(video)), - "--output", str(out_dir), - ] - code = run_logged(cmd, cwd=Path.cwd(), env=os.environ.copy(), method="2ライン", - video=video, seq=seq, timeline_rows=timeline_rows) - if code != 0: - print(f"[ERROR] 2ライン方式が異常終了(code={code}): {video}", flush=True) - return collect_line(video, out_dir) - - -def latest_json(pattern: str) -> dict | None: - paths = sorted(glob.glob(pattern)) - if not paths: - return None - return json.loads(Path(paths[-1]).read_text(encoding="utf-8")) - - -def collect_line(video: str, out_dir: Path) -> dict: - events = latest_json(str(out_dir / "logs" / "events_*.json")) - manifest = latest_json(str(out_dir / "manifests" / "*.json")) - if events is None: - return {"video": Path(video).name, "line_count_error": "収集失敗"} - - summary = events.get("summary", {}) - accuracy = events.get("accuracy", {}) - timing = events.get("timing", {}) - config = (manifest or {}).get("config", {}) - return { - "video": Path(video).name, - "line_count_in": summary.get("total_in"), - "line_gt_in": accuracy.get("gt_in"), - "line_count_out": summary.get("total_out"), - "line_gt_out": accuracy.get("gt_out"), - "line_count_error": accuracy.get("count_error"), - "line_core_ms_p95": timing.get("core_ms_p95"), - "line_comparison_key": config.get("comparison_key"), - } - - -def print_summary(rows: list) -> None: - print(f"\n{'=' * 90}\n交互実行 集約結果\n{'=' * 90}") - header = f"{'video':<24}{'ROI p95':>10}{'2line p95':>11}{'比(2line/ROI)':>16}{'ROI err':>9}{'2line err':>11}" - print(header) - for row in rows: - roi_p95 = row.get("roi_core_ms_p95") - line_p95 = row.get("line_core_ms_p95") - ratio = row.get("speed_ratio_line_over_roi") - roi_p95_s = f"{roi_p95:.1f}" if isinstance(roi_p95, (int, float)) else "-" - line_p95_s = f"{line_p95:.1f}" if isinstance(line_p95, (int, float)) else "-" - ratio_s = f"{ratio:.2f}x" if isinstance(ratio, (int, float)) else "-" - print( - f"{row['video']:<24}{roi_p95_s:>10}{line_p95_s:>11}{ratio_s:>16}" - f"{str(row.get('roi_count_error')):>9}{str(row.get('line_count_error')):>11}" - ) - - -def main() -> int: - check_inputs() - base = Path("data/outputs") / EXP_NAME - base.mkdir(parents=True, exist_ok=True) - (ROI_DIR / "data" / "outputs" / EXP_NAME).mkdir(parents=True, exist_ok=True) - print(f"動画数: {len(VIDEOS)} 出力先(2ライン): {base} 出力先(ROI): " - f"{ROI_DIR / 'data' / 'outputs' / EXP_NAME}") - - timeline_rows: list = [] - rows: list = [] - seq = 0 - for video, env_file in VIDEOS: - seq += 1 - roi_result = run_roi_single(video, seq, timeline_rows) - seq += 1 - line_result = run_line_single(video, env_file, seq, timeline_rows) - - merged = {**roi_result, **{k: v for k, v in line_result.items() if k != "video"}} - roi_p95 = merged.get("roi_core_ms_p95") - line_p95 = merged.get("line_core_ms_p95") - if isinstance(roi_p95, (int, float)) and isinstance(line_p95, (int, float)) and roi_p95 > 0: - merged["speed_ratio_line_over_roi"] = line_p95 / roi_p95 - rows.append(merged) - - # 動画ごとに逐次書き込み(数時間かかるrunなので、失敗時も途中結果を失わない)。 - timeline_path = base / "timeline.csv" - with timeline_path.open("w", newline="", encoding="utf-8") as f: - writer = csv.DictWriter(f, fieldnames=list(TIMELINE_COLUMNS)) - writer.writeheader() - writer.writerows(timeline_rows) - - summary_path = base / "summary_alternating.csv" - with summary_path.open("w", newline="", encoding="utf-8") as f: - writer = csv.DictWriter(f, fieldnames=list(SUMMARY_COLUMNS)) - writer.writeheader() - for r in rows: - writer.writerow({k: r.get(k) for k in SUMMARY_COLUMNS}) - - print_summary(rows) - print(f"\n出力: {base / 'summary_alternating.csv'} {base / 'timeline.csv'}") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/raspi/sensor/ESP_BLE/ESP_BLE.ino b/raspi/sensor/ESP_BLE/ESP_BLE.ino deleted file mode 100644 index 64f9b66f..00000000 --- a/raspi/sensor/ESP_BLE/ESP_BLE.ino +++ /dev/null @@ -1,184 +0,0 @@ -#include -#include -#include -#include - -//--------------------------------------------------------- -// Constants -//--------------------------------------------------------- -#define SERVICE_UUID "55725ac1-066c-48b5-8700-2d9fb3603c5e" -#define CHARACTERISTIC_UUID "69ddb59c-d601-4ea4-ba83-44f679a670ba" -#define BLE_DEVICE_NAME "MyBLEDevice" -#define LED_PIN 22 -#define BUTTON_PIN 23 -#define echoPin 16 // Echo Pin -#define trigPin 17 // Trigger Pin - -//--------------------------------------------------------- -// Variables -//--------------------------------------------------------- -BLEServer *pServer = NULL; -BLECharacteristic *pCharacteristic = NULL; -bool deviceConnected = false; -bool oldDeviceConnected = false; -std::string rxValue; -std::string txValue; -bool bleOn = false; -bool buttonPressed = false; -int carFlag = 0; - -double Duration = 0; //受信した間隔 -double Distance = 0; //距離 -double thresholdDistance = 50; //検出閾値[cm] - -//--------------------------------------------------------- -// Callbacks -//--------------------------------------------------------- -class MyServerCallbacks : public BLEServerCallbacks { - void onConnect(BLEServer *pServer) { - deviceConnected = true; - Serial.println("onConnect"); - }; - void onDisconnect(BLEServer *pServer) { - deviceConnected = false; - Serial.println("onDisconnect"); - } -}; - -class MyCharacteristicCallbacks : public BLECharacteristicCallbacks { - void onWrite(BLECharacteristic *pCharacteristic) { - Serial.println("onWrite"); - std::string rxValue = pCharacteristic->getValue(); - if (rxValue.length() > 0) { - bleOn = rxValue[0] != 0; - Serial.print("Received Value: "); - for (int i = 0; i < rxValue.length(); i++) { - Serial.print(rxValue[i], HEX); - } - Serial.println(); - } - } -}; - -//--------------------------------------------------------- -void setup() { - // - pinMode(LED_PIN, OUTPUT); - pinMode(BUTTON_PIN, INPUT_PULLUP); - - pinMode( echoPin, INPUT ); - pinMode( trigPin, OUTPUT ); - - Serial.begin(115200); - // - BLEDevice::init(BLE_DEVICE_NAME); - // Server - pServer = BLEDevice::createServer(); - pServer->setCallbacks(new MyServerCallbacks()); - // Service - BLEService *pService = pServer->createService(SERVICE_UUID); - // Characteristic - pCharacteristic = pService->createCharacteristic( - CHARACTERISTIC_UUID, - BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY); - pCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); - pCharacteristic->addDescriptor(new BLE2902()); - // - pService->start(); - // Advertising - BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); - pAdvertising->addServiceUUID(SERVICE_UUID); - pAdvertising->setScanResponse(false); - pAdvertising->setMinPreferred(0x0); - BLEDevice::startAdvertising(); - Serial.println("startAdvertising"); -} - -//--------------------------------------------------------- -void loop() { - // disconnecting - if (!deviceConnected && oldDeviceConnected) { - delay(500); // give the bluetooth stack the chance to get things ready - pServer->startAdvertising(); - Serial.println("restartAdvertising"); - oldDeviceConnected = deviceConnected; - } - // connecting - if (deviceConnected && !oldDeviceConnected) { - oldDeviceConnected = deviceConnected; - } - // LED - digitalWrite(LED_PIN, bleOn ? HIGH : LOW); - - //超音波センサ - digitalWrite(trigPin, LOW); - delayMicroseconds(2); - digitalWrite( trigPin, HIGH ); //超音波を出力 - delayMicroseconds( 10 ); // - digitalWrite( trigPin, LOW ); - Duration = pulseIn( echoPin, HIGH ); //センサからの入力 - - if (Duration > 0) { - Duration = Duration/2; //往復距離を半分にする - Distance = Duration*340*100/1000000; // 音速を340m/sに設定 - //Distance[cm] - if (Distance >= thresholdDistance) { - carFlag = 0; - String str = "CarFlag:" + String(carFlag); - Serial.println(str); - // Notify - if (deviceConnected) { - txValue = str.c_str(); - pCharacteristic->setValue(txValue); - pCharacteristic->notify(); - } - // buttonPressed = true; - delay(50); - } - else { - carFlag = 1; - String str = "CarFlag:" + String(carFlag); - Serial.println(str); - // Notify - if (deviceConnected) { - txValue = str.c_str(); - pCharacteristic->setValue(txValue); - pCharacteristic->notify(); - } - // buttonPressed = true; - delay(50); - } - } - else{ - carFlag = 0; - String str = "CarFlag:" + String(carFlag); - Serial.println(str); - // Notify - if (!deviceConnected) { - txValue = str.c_str(); - pCharacteristic->setValue(txValue); - pCharacteristic->notify(); - } - // buttonPressed = false; - delay(50); - } - // Serial.println(Distance); - // if (_hoge = 1) { - - // } - - // _hoge = 1; - - - // Serial.println("hoge"); - // carFlag = 2; - // String str = "CarFlag:" + String(carFlag); - // Serial.println(str); - // // Notify - // if (deviceConnected) { - // txValue = str.c_str(); - // pCharacteristic->setValue(txValue); - // pCharacteristic->notify(); - // } - delay(1000); -} \ No newline at end of file diff --git a/raspi/sensor/handson-sample/LICENSE b/raspi/sensor/handson-sample/LICENSE deleted file mode 100644 index 074b5fd0..00000000 --- a/raspi/sensor/handson-sample/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 sakura.io - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/raspi/sensor/handson-sample/arduino/IBMcloud-part2-LED.ino b/raspi/sensor/handson-sample/arduino/IBMcloud-part2-LED.ino deleted file mode 100644 index a0cb59e7..00000000 --- a/raspi/sensor/handson-sample/arduino/IBMcloud-part2-LED.ino +++ /dev/null @@ -1,83 +0,0 @@ -// ヘッダファイル指定 Including header files -#include - -// LEDの定義 Definition of LED -#define LED_1 7 -#define LED_2 6 -#define LED_3 5 - -// 変数の定義 Definition of variables -SakuraIO_I2C sakuraio; -uint32_t cnt = 0; - -// 起動時に1回だけ実行 Run once at startup -void setup() { - Serial.begin(9600); - Serial.print("Waiting to come online"); - for (;;) { - if ( (sakuraio.getConnectionStatus() & 0x80) == 0x80 ) break; - Serial.print("."); - delay(1000); - } - Serial.println(""); - pinMode(LED_1, OUTPUT); - pinMode(LED_2, OUTPUT); - pinMode(LED_3, OUTPUT); -} - -// 以下ループ実行 Loop execution -void loop() { - - // cnt値のカウントアップ Count up cnt value - cnt++; - Serial.println(cnt); - - // データの即時送信 Immediate transmission of data - if (sakuraio.sendImmediately(0, cnt) != CMD_ERROR_NONE) { - Serial.println("[ERR] enqueue error"); - } - digitalWrite(LED_3, HIGH); - delay(500); - digitalWrite(LED_3, LOW); - delay(500); - - // 利用可能な領域(Available)とデータが格納された領域(Queued)の取得 Get Available and Queued - uint8_t available; - uint8_t queued; - if (sakuraio.getTxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get tx queue length error"); - } - Serial.print("Available :"); - Serial.print(available); - Serial.print(" Queued :"); - Serial.println(queued); - - // sakura.ioからの受信データに応じたLED点灯(Digital7,6) Lights the LED according to received data - available = 0; - if (sakuraio.getRxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get rx queue length error"); - } - if (available > 0) - { - for (int i = 0; i < available; i++) - { - uint8_t ch, type, value[8]; - int64_t offset; - sakuraio.dequeueRx(&ch, &type, value, &offset); - if (ch == 0) { - if (value[0] == 1) { - digitalWrite(LED_1, HIGH); - } else { - digitalWrite(LED_1, LOW); - } - } else if (ch == 1) { - if (value[0] == 1) { - digitalWrite(LED_2, HIGH); - } else { - digitalWrite(LED_2, LOW); - } - } - } - sakuraio.clearRx(); - } -} \ No newline at end of file diff --git a/raspi/sensor/handson-sample/arduino/basic-HDC1000-with-LED.ino b/raspi/sensor/handson-sample/arduino/basic-HDC1000-with-LED.ino deleted file mode 100644 index 61744263..00000000 --- a/raspi/sensor/handson-sample/arduino/basic-HDC1000-with-LED.ino +++ /dev/null @@ -1,118 +0,0 @@ -// ヘッダファイル指定 Including header files -#include -#include - -// LEDの定義 Definition of LED -#define LED_1 7 -#define LED_2 6 -#define LED_3 5 - -// 変数の定義 Definition of variables -SakuraIO_I2C sakuraio; -HDC1000 hdc1000; -uint32_t cnt = 0; - -// 起動時に1回だけ実行 Run once at startup -void setup() { - hdc1000.begin(); - Serial.begin(9600); - Serial.print("Waiting to come online"); - for (;;) { - if ( (sakuraio.getConnectionStatus() & 0x80) == 0x80 ) break; - Serial.print("."); - delay(1000); - } - Serial.println(""); - pinMode(LED_1, OUTPUT); - pinMode(LED_2, OUTPUT); - pinMode(LED_3, OUTPUT); -} - -// 以下ループ実行 Loop execution -void loop() { - - // cnt値のカウントアップ Count up cnt value - cnt++; - Serial.println(cnt); - - // 温度情報の取得 Get temperature - float temp = hdc1000.getTemperature(); - if (temp == HDC1000_ERROR_CODE) { - hdc1000.begin(); // retry once - temp = hdc1000.getTemperature(); - } - Serial.print("Temperature: "); - Serial.println(temp); - - // 湿度情報の取得 Get humidity - float humi = hdc1000.getHumidity(); - if (humi == HDC1000_ERROR_CODE) { - hdc1000.begin(); // retry once - humi = hdc1000.getHumidity(); - } - Serial.print("Humidity: "); - Serial.println(humi); - - // さくらの通信モジュールへの各値のキューイング Queuing each value to module - if(sakuraio.enqueueTx(0,temp) != CMD_ERROR_NONE){ - Serial.println("[ERR] enqueue error"); - } - if(sakuraio.enqueueTx(1,humi) != CMD_ERROR_NONE){ - Serial.println("[ERR] enqueue error"); - } - if(sakuraio.enqueueTx(2,cnt) != CMD_ERROR_NONE){ - Serial.println("[ERR] enqueue error"); - } - - // キューイングされた値の送信 Sending queued values - sakuraio.send(); - delay(100); - - // 利用可能な領域(Available)とデータが格納された領域(Queued)の取得 Get Available and Queued - uint8_t available; - uint8_t queued; - if (sakuraio.getTxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get tx queue length error"); - } - Serial.print("Available :"); - Serial.print(available); - Serial.print(" Queued :"); - Serial.println(queued); - - // sakura.ioからの受信データに応じたLED点灯(Digital 7,6,5) Lights the LED according to received data - available = 0; - if (sakuraio.getRxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get rx queue length error"); - } - if (available > 0) - { - for (int i = 0; i < available; i++) - { - uint8_t ch, type, value[8]; - int64_t offset; - sakuraio.dequeueRx(&ch, &type, value, &offset); - if (ch == 0) { - if (value[0] == 1) { - digitalWrite(LED_1, HIGH); - } else { - digitalWrite(LED_1, LOW); - } - } else if (ch == 1) { - if (value[0] == 1) { - digitalWrite(LED_2, HIGH); - } else { - digitalWrite(LED_2, LOW); - } - } else if (ch == 2) { - if (value[0] == 1) { - digitalWrite(LED_3, HIGH); - } else { - digitalWrite(LED_3, LOW); - } - } - } - sakuraio.clearRx(); - } - - delay(5000); -} \ No newline at end of file diff --git a/raspi/sensor/handson-sample/arduino/basic-SHT31-with-LED.ino b/raspi/sensor/handson-sample/arduino/basic-SHT31-with-LED.ino deleted file mode 100644 index f1191ac7..00000000 --- a/raspi/sensor/handson-sample/arduino/basic-SHT31-with-LED.ino +++ /dev/null @@ -1,124 +0,0 @@ -// ヘッダファイル指定 Including header files -#include "Adafruit_SHT31.h" -#include - -// LEDの定義 Definition of LED -#define LED_1 7 -#define LED_2 6 -#define LED_3 5 - -// 変数の定義 Definition of variables -SakuraIO_I2C sakuraio; -Adafruit_SHT31 sht31 = Adafruit_SHT31(); -uint32_t cnt = 0; - -// 起動時に1回だけ実行 Run once at startup -void setup() { - if (! sht31.begin(0x45)) { // AE-SHT3X default addr = 0x45 - Serial.println("SHT31 initialize error!"); - while(1); - } - Serial.begin(9600); - Serial.print("Waiting to come online"); - for (;;) { - if ( (sakuraio.getConnectionStatus() & 0x80) == 0x80 ) break; - Serial.print("."); - delay(1000); - } - Serial.println(""); - pinMode(LED_1, OUTPUT); - pinMode(LED_2, OUTPUT); - pinMode(LED_3, OUTPUT); -} - -// 以下ループ実行 Loop execution -void loop() { - - // cnt値のカウントアップ Count up cnt value - cnt++; - Serial.println(cnt); - - // 温度情報の取得 Get temperature - float temp = sht31.readTemperature(); - - if (isnan(temp)) { - Serial.println("Failed to read temperature!"); - } else { - Serial.print("Temperature: "); - Serial.println(temp); - } - - // 湿度情報の取得 Get humidity - float humi = sht31.readHumidity(); - - if (isnan(humi)) { - Serial.println("Failed to read humidity!"); - } else { - Serial.print("Humidity: "); - Serial.println(humi); - } - - // さくらの通信モジュールへの各値のキューイング Queuing each value to module - if(sakuraio.enqueueTx(0,temp) != CMD_ERROR_NONE){ - Serial.println("[ERR] enqueue error"); - } - if(sakuraio.enqueueTx(1,humi) != CMD_ERROR_NONE){ - Serial.println("[ERR] enqueue error"); - } - if(sakuraio.enqueueTx(2,cnt) != CMD_ERROR_NONE){ - Serial.println("[ERR] enqueue error"); - } - - // キューイングされた値の送信 Sending queued values - sakuraio.send(); - delay(100); - - // 利用可能な領域(Available)とデータが格納された領域(Queued)の取得 Get Available and Queued - uint8_t available; - uint8_t queued; - if (sakuraio.getTxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get tx queue length error"); - } - Serial.print("Available :"); - Serial.print(available); - Serial.print(" Queued :"); - Serial.println(queued); - - // sakura.ioからの受信データに応じたLED点灯(Digital7,6,5) Lights the LED according to received data - available = 0; - if (sakuraio.getRxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get rx queue length error"); - } - if (available > 0) - { - for (int i = 0; i < available; i++) - { - uint8_t ch, type, value[8]; - int64_t offset; - sakuraio.dequeueRx(&ch, &type, value, &offset); - if (ch == 0) { - if (value[0] == 1) { - digitalWrite(LED_1, HIGH); - } else { - digitalWrite(LED_1, LOW); - } - } else if (ch == 1) { - if (value[0] == 1) { - digitalWrite(LED_2, HIGH); - } else { - digitalWrite(LED_2, LOW); - } - } else if (ch == 2) { - if (value[0] == 1) { - digitalWrite(LED_3, HIGH); - } else { - digitalWrite(LED_3, LOW); - } - } - } - sakuraio.clearRx(); - } - - // 待機時間の調整 Adjust wait time - delay(5000); -} \ No newline at end of file diff --git a/raspi/sensor/handson-sample/arduino/twilio-HDC1000-with-LED.ino b/raspi/sensor/handson-sample/arduino/twilio-HDC1000-with-LED.ino deleted file mode 100644 index ffb37648..00000000 --- a/raspi/sensor/handson-sample/arduino/twilio-HDC1000-with-LED.ino +++ /dev/null @@ -1,113 +0,0 @@ -// ヘッダファイル指定 Including header files -#include -#include - -// LEDの定義 Definition of LED -#define LED_1 7 -#define LED_2 6 -#define LED_3 5 - -// 変数の定義 Definition of variables -SakuraIO_I2C sakuraio; -HDC1000 hdc1000; -uint32_t cnt = 0; - -// 起動時に1回だけ実行 Run once at startup -void setup() { - hdc1000.begin(); - Serial.begin(9600); - Serial.print("Waiting to come online"); - for (;;) { - if ( (sakuraio.getConnectionStatus() & 0x80) == 0x80 ) break; - Serial.print("."); - delay(1000); - } - Serial.println(""); - pinMode(LED_1, OUTPUT); - pinMode(LED_2, OUTPUT); - pinMode(LED_3, OUTPUT); -} - -// 以下ループ実行 Loop execution -void loop() { - - // cnt値のカウントアップ Count up cnt value - cnt++; - Serial.println(cnt); - - // 温度情報の取得 Get temperature - float temp = hdc1000.getTemperature(); - - if (temp == HDC1000_ERROR_CODE) { - hdc1000.begin(); // retry once - temp = hdc1000.getTemperature(); - } - Serial.print("Temperature: "); - Serial.println(temp); - - // さくらの通信モジュールへの各値の閾値判定およびキューイング Threshold determination and queuing of each value to module - if (temp >= 30) { // temp値による判定 - if (sakuraio.enqueueTx(0, cnt) != CMD_ERROR_NONE) { - Serial.println("[ERR] enqueue error"); - } - if (sakuraio.enqueueTx(1, temp) != CMD_ERROR_NONE) { - Serial.println("[ERR] enqueue error"); - } - - // キューイングされた値の送信 Sending queued values - sakuraio.send(); - Serial.println("* Sent to sakura.io *"); - for (int i = 0; i < 10; i++) // 送信時にLED_3を10回点滅 LED_3 blinks 10 times during transmission - { - digitalWrite(LED_3, HIGH); - delay(1000); - digitalWrite(LED_3, LOW); - delay(1000); - } - delay(10000); // 送信後の待機時間 Wait time after transmission - } else { - delay(1000); // 送信を行わない場合の計測周期 Measurement cycle when transmission is not performed - } - - // 利用可能な領域(Available)とデータが格納された領域(Queued)の取得 Get Available and Queued - uint8_t available; - uint8_t queued; - if (sakuraio.getTxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get tx queue length error"); - } - - Serial.print("Available :"); - Serial.print(available); - Serial.print(" Queued :"); - Serial.println(queued); - - // sakura.ioからの受信データに応じたLED点灯(Digital7) Lights the LED according to received data - available = 0; - if (sakuraio.getRxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get rx queue length error"); - } - if (available > 0) - { - for (int i = 0; i < available; i++) - { - uint8_t ch, type, value[8]; - int64_t offset; - sakuraio.dequeueRx(&ch, &type, value, &offset); - if (ch == 0) { // ch0への受信時の動作定義 Definition of operation upon reception on ch0 - if (value[0] == 1) { - digitalWrite(LED_1, HIGH); - } else { - digitalWrite(LED_1, LOW); - } - } else if (ch == 1) { // ch1への受信時の定義 Definition of operation upon reception on ch1 - - if (value[0] == 1) { - digitalWrite(LED_2, HIGH); - } else { - digitalWrite(LED_2, LOW); - } - } - } - sakuraio.clearRx(); - } -} \ No newline at end of file diff --git a/raspi/sensor/handson-sample/arduino/twilio-SHT31-with-LED.ino b/raspi/sensor/handson-sample/arduino/twilio-SHT31-with-LED.ino deleted file mode 100644 index c6a31788..00000000 --- a/raspi/sensor/handson-sample/arduino/twilio-SHT31-with-LED.ino +++ /dev/null @@ -1,115 +0,0 @@ -// ヘッダファイル指定 Including header files -#include -#include - -// LEDの定義 Definition of LED -#define LED_1 7 -#define LED_2 6 -#define LED_3 5 - -// 変数の定義 Definition of variables -SakuraIO_I2C sakuraio; -Adafruit_SHT31 sht31 = Adafruit_SHT31(); -uint32_t cnt = 0; - -// 起動時に1回だけ実行 Run once at startup -void setup() { - if (! sht31.begin(0x45)) { // AE-SHT3X default addr = 0x45 - Serial.println("SHT31 initialize error!"); - while (1); - } - Serial.begin(9600); - Serial.print("Waiting to come online"); - for (;;) { - if ( (sakuraio.getConnectionStatus() & 0x80) == 0x80 ) break; - Serial.print("."); - delay(1000); - } - Serial.println(""); - pinMode(LED_1, OUTPUT); - pinMode(LED_2, OUTPUT); - pinMode(LED_3, OUTPUT); -} - -// 以下ループ実行 Loop execution -void loop() { - - // cnt値のカウントアップ Count up cnt value - cnt++; - Serial.println(cnt); - - // 温度情報の取得 Get temperature - float temp = sht31.readTemperature(); - if (isnan(temp)) { - Serial.println("Failed to read temperature!"); - } else { - Serial.print("Temperature: "); - Serial.println(temp); - } - - // さくらの通信モジュールへの各値の閾値判定およびキューイング Threshold determination and queuing of each value to module - if (temp >= 30) { // temp値による判定 - if (sakuraio.enqueueTx(0, cnt) != CMD_ERROR_NONE) { - Serial.println("[ERR] enqueue error"); - } - if (sakuraio.enqueueTx(1, temp) != CMD_ERROR_NONE) { - Serial.println("[ERR] enqueue error"); - } - - // キューイングされた値の送信 Sending queued values - sakuraio.send(); - Serial.println("* Sent to sakura.io *"); - for (int i = 0; i < 10; i++) // 送信時にLED_3を10回点滅 LED_3 blinks 10 times during transmission - { - digitalWrite(LED_3, HIGH); - delay(1000); - digitalWrite(LED_3, LOW); - delay(1000); - } - delay(10000); // 送信後の待機時間 Wait time after transmission - } else { - delay(1000); // 送信を行わない場合の計測周期 Measurement cycle when transmission is not performed - } - - // 利用可能な領域(Available)とデータが格納された領域(Queued)の取得 Get Available and Queued - uint8_t available; - uint8_t queued; - if (sakuraio.getTxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get tx queue length error"); - } - - Serial.print("Available :"); - Serial.print(available); - Serial.print(" Queued :"); - Serial.println(queued); - - // sakura.ioからの受信データに応じたLED点灯(Digital7) Lights the LED according to received data - available = 0; - if (sakuraio.getRxQueueLength(&available, &queued) != CMD_ERROR_NONE) { - Serial.println("[ERR] get rx queue length error"); - } - if (available > 0) - { - for (int i = 0; i < available; i++) - { - uint8_t ch, type, value[8]; - int64_t offset; - sakuraio.dequeueRx(&ch, &type, value, &offset); - if (ch == 0) { // ch0への受信時の動作定義 Definition of operation upon reception on ch0 - if (value[0] == 1) { - digitalWrite(LED_1, HIGH); - } else { - digitalWrite(LED_1, LOW); - } - } else if (ch == 1) { // ch1への受信時の定義 Definition of operation upon reception on ch1 - - if (value[0] == 1) { - digitalWrite(LED_2, HIGH); - } else { - digitalWrite(LED_2, LOW); - } - } - } - sakuraio.clearRx(); - } -} diff --git a/raspi/sensor/handson-sample/microsoft-azure/stream-analytics-sevice-query b/raspi/sensor/handson-sample/microsoft-azure/stream-analytics-sevice-query deleted file mode 100644 index 08714134..00000000 --- a/raspi/sensor/handson-sample/microsoft-azure/stream-analytics-sevice-query +++ /dev/null @@ -1,15 +0,0 @@ -WITH Tmps AS ( -SELECT - e.INPUT.ConnectionDeviceId as ConnectionDeviceId, e.module as module, - chs.ArrayValue.channel, chs.ArrayValue.type, chs.ArrayValue.value as temp, - chs.ArrayValue.datetime -FROM INPUT AS e -CROSS APPLY GetElements (e.payload.channels) as chs -WHERE chs.ArrayValue.channel = 0 -) -Select avg(Tmps.temp) -Into OUTPUT1 -From Tmps -GROUP BY TumblingWindow(second, 10); - -SELECT * INTO OUTPUT2 FROM INPUT; diff --git a/raspi/sensor/handson-sample/node-red/basic-graph-and-control-with-tweet.json b/raspi/sensor/handson-sample/node-red/basic-graph-and-control-with-tweet.json deleted file mode 100644 index bbe46b09..00000000 --- a/raspi/sensor/handson-sample/node-red/basic-graph-and-control-with-tweet.json +++ /dev/null @@ -1,739 +0,0 @@ -[ - { - "id": "dbab9424.ebe0f8", - "type": "tab", - "label": "グラフ表示と制御", - "disabled": false, - "info": "本フローはsakura.ioハンズオンで利用する、Arduino Unoと温湿度センサ(HDC1000 or SHT31)を用いたグラフ表示とTwitter連携を試すものです。\n\n■グラフ表示&SNS投稿\n\nsakura.ioのWebSocket(`wss://api.sakura.io/ws/v1/.....`)サービスから送信されたJSONデータを用いてダッシュボード上にグラフを描画します。\nまた、グラフは`http://:<指定したWeb UIポート番号>/ui/`で確認することができます。\n\nオプションとして、Twittter IDを設定することで温度情報(Channel0)を取り出し、Twitterにメッセージを投稿します。\n\n以下2点を設定しデプロイすることで動作します。\n\n1. sakuraio_in(WebSocket-inノード)\n\n sakura.ioコンパネで確認できるWebSocketのURL(`wss://api.sakura.io/ws/v1/.....`)を設定します。\n\n2. Tweet(Twitterノード)\n\n  Ch0(temp)_parseとtrigger 1hrをドラッグアンドドロップで接続します。\n\n 認証を行いTwitterのアカウントを連携します。(鍵アカウントでの動作は確認していません)\n \n■デバイス制御\n\nダッシュボード上のボタンを押下することで、モジュールIDで指定したデバイスに制御用のJSONデータを生成し送信します。\n\n以下2点を設定しデプロイすることで動作します。\n\n1. moduleID_set(functionノード)\n\n sakura.ioコンパネで確認できるID(`uXXXXXXXXXXXX`)を設定します。\n \n2. sakuraio_out(WebSocket-outノード)\n\n sakura.ioコンパネで確認できるWebSocketのURL(`wss://api.sakura.io/ws/v1/.....`)を設定します。\n\n\n> [sakura.io コントロールパネル](https://secure.sakura.ad.jp/iot/)\n\n> [利用Arduinoスケッチ1(HDC1000利用時)](https://github.com/sakuraio/handson-sample/blob/master/arduino/basic-HDC1000-with-LED.ino)\n\n> [利用Arduinoスケッチ2(SHT31利用時)](https://github.com/sakuraio/handson-sample/blob/master/arduino/basic-SHT31-with-LED.ino)\n\n> [参考ハンズオン資料](https://speakerdeck.com/sakuraiot/sakuraio-handson-part1)" - }, - { - "id": "6aa91ec7.7c76a", - "type": "websocket in", - "z": "dbab9424.ebe0f8", - "name": "sakuraio_in", - "server": "", - "client": "3efc4ee6.d6fc42", - "x": 110, - "y": 80, - "wires": [ - [ - "4c824085.9e118", - "5d198923.69a608" - ] - ] - }, - { - "id": "4c824085.9e118", - "type": "json", - "z": "dbab9424.ebe0f8", - "name": "String→Object", - "pretty": false, - "x": 340, - "y": 80, - "wires": [ - [ - "c909773.594cd88" - ] - ] - }, - { - "id": "c909773.594cd88", - "type": "switch", - "z": "dbab9424.ebe0f8", - "name": "Type_Check", - "property": "payload.type", - "propertyType": "msg", - "rules": [ - { - "t": "eq", - "v": "channels", - "vt": "str" - } - ], - "checkall": "true", - "outputs": 1, - "x": 150, - "y": 280, - "wires": [ - [ - "a557a3ec.c13b4", - "9fc7185e.35b118" - ] - ] - }, - { - "id": "a557a3ec.c13b4", - "type": "function", - "z": "dbab9424.ebe0f8", - "name": "Ch0(temp)_parse", - "func": "msg.payload = Math.round(msg.payload.payload.channels[0].value*10)/10;return msg;", - "outputs": 1, - "noerr": 0, - "x": 410, - "y": 200, - "wires": [ - [ - "8d91e8af.9d0858", - "d8ec6c7a.f841d", - "57c78451.2c0b9c" - ] - ] - }, - { - "id": "d8ec6c7a.f841d", - "type": "ui_chart", - "z": "dbab9424.ebe0f8", - "name": "Temp_chart", - "group": "4b67594d.a5b328", - "order": 2, - "width": "8", - "height": "5", - "label": "温度 (10分)", - "chartType": "line", - "legend": "false", - "xformat": "HH:mm:ss", - "interpolate": "linear", - "nodata": "", - "dot": false, - "ymin": "0", - "ymax": "40", - "removeOlder": "10", - "removeOlderPoints": "", - "removeOlderUnit": "60", - "cutout": 0, - "colors": [ - "#1f77b4", - "#aec7e8", - "#ff7f0e", - "#2ca02c", - "#98df8a", - "#d62728", - "#ff9896", - "#9467bd", - "#c5b0d5" - ], - "useOldStyle": true, - "x": 610, - "y": 200, - "wires": [ - [], - [] - ] - }, - { - "id": "8d91e8af.9d0858", - "type": "ui_gauge", - "z": "dbab9424.ebe0f8", - "name": "Temp_gauge", - "group": "4b67594d.a5b328", - "order": 1, - "width": "8", - "height": "5", - "gtype": "gage", - "title": "現在の温度", - "label": "℃", - "format": "{{value}}", - "min": 0, - "max": "40", - "colors": [ - "#00b500", - "#e6e600", - "#ca3838" - ], - "seg1": "30", - "seg2": "32", - "x": 610, - "y": 160, - "wires": [] - }, - { - "id": "9fc7185e.35b118", - "type": "function", - "z": "dbab9424.ebe0f8", - "name": "Ch1(humi)_parse", - "func": "msg.payload = Math.round(msg.payload.payload.channels[1].value*10)/10;return msg;", - "outputs": 1, - "noerr": 0, - "x": 410, - "y": 360, - "wires": [ - [ - "24faa6d4.b9547a", - "22b7968a.bfd7ea", - "43cd4220.bc135c" - ] - ] - }, - { - "id": "b512a0b2.e4466", - "type": "debug", - "z": "dbab9424.ebe0f8", - "name": "", - "active": true, - "console": "false", - "complete": "false", - "x": 1170, - "y": 120, - "wires": [] - }, - { - "id": "94666103.72d45", - "type": "function", - "z": "dbab9424.ebe0f8", - "name": "Message", - "func": "msg.payload = `さくらインターネットのハンズオンで温度情報を取得中!ただ今の現地温度は${msg.payload}度だよ。資料は http://bit.ly/hosap101 です!今後ともよろしくお願いします! #sakuraio #さくらインターネット`;return msg;", - "outputs": 1, - "noerr": 0, - "x": 1000, - "y": 80, - "wires": [ - [ - "4876bcac.e67d94", - "b512a0b2.e4466" - ] - ] - }, - { - "id": "134295dc.5808da", - "type": "trigger", - "z": "dbab9424.ebe0f8", - "op1": "", - "op2": "", - "op1type": "pay", - "op2type": "nul", - "duration": "1", - "extend": false, - "units": "hr", - "reset": "", - "bytopic": "all", - "name": "", - "x": 830, - "y": 80, - "wires": [ - [ - "94666103.72d45" - ] - ] - }, - { - "id": "4876bcac.e67d94", - "type": "twitter out", - "z": "dbab9424.ebe0f8", - "twitter": "", - "name": "Tweet", - "x": 1150, - "y": 80, - "wires": [] - }, - { - "id": "24faa6d4.b9547a", - "type": "ui_gauge", - "z": "dbab9424.ebe0f8", - "name": "Humi_gauge", - "group": "4a6401b3.8bf5", - "order": 0, - "width": "8", - "height": "5", - "gtype": "gage", - "title": "現在の湿度", - "label": "%", - "format": "{{value}}", - "min": 0, - "max": "100", - "colors": [ - "#ca3838", - "#00b500", - "#0000d2" - ], - "seg1": "30", - "seg2": "70", - "x": 610, - "y": 320, - "wires": [] - }, - { - "id": "22b7968a.bfd7ea", - "type": "ui_chart", - "z": "dbab9424.ebe0f8", - "name": "Humi_chart", - "group": "4a6401b3.8bf5", - "order": 0, - "width": "8", - "height": "5", - "label": "湿度 (10分)", - "chartType": "line", - "legend": "false", - "xformat": "HH:mm:ss", - "interpolate": "linear", - "nodata": "", - "dot": false, - "ymin": "0", - "ymax": "100", - "removeOlder": "10", - "removeOlderPoints": "", - "removeOlderUnit": "60", - "cutout": 0, - "colors": [ - "#1f77b4", - "#aec7e8", - "#ff7f0e", - "#2ca02c", - "#98df8a", - "#d62728", - "#ff9896", - "#9467bd", - "#c5b0d5" - ], - "useOldStyle": true, - "x": 610, - "y": 360, - "wires": [ - [], - [] - ] - }, - { - "id": "7566db0f.ffb034", - "type": "comment", - "z": "dbab9424.ebe0f8", - "name": "ダッシュボード画面でのグラフ表示", - "info": "", - "x": 160, - "y": 40, - "wires": [] - }, - { - "id": "60fdce21.fe051", - "type": "ui_button", - "z": "dbab9424.ebe0f8", - "name": "LED_RED_on", - "group": "600c4eb9.9c1f6", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "RED on", - "color": "", - "bgcolor": "red", - "icon": "", - "payload": "redon", - "payloadType": "global", - "topic": "", - "x": 140, - "y": 700, - "wires": [ - [ - "d3a687cd.558e78", - "e80f109f.d3463" - ] - ] - }, - { - "id": "d3a687cd.558e78", - "type": "websocket out", - "z": "dbab9424.ebe0f8", - "name": "sakuraio_out", - "server": "", - "client": "fc59ee64.e5928", - "x": 590, - "y": 700, - "wires": [] - }, - { - "id": "e80f109f.d3463", - "type": "debug", - "z": "dbab9424.ebe0f8", - "name": "", - "active": true, - "console": "false", - "complete": "false", - "x": 590, - "y": 760, - "wires": [] - }, - { - "id": "798124d1.aa107c", - "type": "ui_button", - "z": "dbab9424.ebe0f8", - "name": "LED_GREEN_on", - "group": "600c4eb9.9c1f6", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "GREEN on", - "color": "", - "bgcolor": "limegreen", - "icon": "", - "payload": "greenon", - "payloadType": "global", - "topic": "", - "x": 150, - "y": 740, - "wires": [ - [ - "d3a687cd.558e78", - "e80f109f.d3463" - ] - ] - }, - { - "id": "5948cfa.78f5f3", - "type": "ui_button", - "z": "dbab9424.ebe0f8", - "name": "LED_BLUE_on", - "group": "600c4eb9.9c1f6", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "BLUE on", - "color": "", - "bgcolor": "blue", - "icon": "", - "payload": "blueon", - "payloadType": "global", - "topic": "", - "x": 140, - "y": 780, - "wires": [ - [ - "d3a687cd.558e78", - "e80f109f.d3463" - ] - ] - }, - { - "id": "4f3b1581.101c7c", - "type": "ui_button", - "z": "dbab9424.ebe0f8", - "name": "LED_RED_off", - "group": "600c4eb9.9c1f6", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "RED off", - "color": "", - "bgcolor": "tan", - "icon": "", - "payload": "redoff", - "payloadType": "global", - "topic": "", - "x": 140, - "y": 820, - "wires": [ - [ - "d3a687cd.558e78", - "e80f109f.d3463" - ] - ] - }, - { - "id": "3a993dd3.775602", - "type": "ui_button", - "z": "dbab9424.ebe0f8", - "name": "LED_GREEN_off", - "group": "600c4eb9.9c1f6", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "GREEN off", - "color": "", - "bgcolor": "darkseagreen", - "icon": "", - "payload": "greenoff", - "payloadType": "global", - "topic": "", - "x": 150, - "y": 860, - "wires": [ - [ - "d3a687cd.558e78", - "e80f109f.d3463" - ] - ] - }, - { - "id": "77f4aa.c867bb58", - "type": "ui_button", - "z": "dbab9424.ebe0f8", - "name": "LED_BLUE_off", - "group": "600c4eb9.9c1f6", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "BLUE off", - "color": "", - "bgcolor": "lightskyblue", - "icon": "", - "payload": "blueoff", - "payloadType": "global", - "topic": "", - "x": 140, - "y": 900, - "wires": [ - [ - "d3a687cd.558e78", - "e80f109f.d3463" - ] - ] - }, - { - "id": "acb60037.ede2f", - "type": "ui_button", - "z": "dbab9424.ebe0f8", - "name": "LED_ALL_on", - "group": "600c4eb9.9c1f6", - "order": 0, - "width": "4", - "height": "2", - "passthru": true, - "label": "ALL on", - "color": "", - "bgcolor": "gold", - "icon": "", - "payload": "allon", - "payloadType": "global", - "topic": "", - "x": 130, - "y": 940, - "wires": [ - [ - "d3a687cd.558e78", - "e80f109f.d3463" - ] - ] - }, - { - "id": "e5288cf.1ac757", - "type": "ui_button", - "z": "dbab9424.ebe0f8", - "name": "LED_ALL_off", - "group": "600c4eb9.9c1f6", - "order": 0, - "width": "4", - "height": "2", - "passthru": true, - "label": "ALL off", - "color": "", - "bgcolor": "gray", - "icon": "", - "payload": "alloff", - "payloadType": "global", - "topic": "", - "x": 130, - "y": 980, - "wires": [ - [ - "d3a687cd.558e78", - "e80f109f.d3463" - ] - ] - }, - { - "id": "d7fbab5.140a658", - "type": "comment", - "z": "dbab9424.ebe0f8", - "name": "デバイス制御(動作部分)", - "info": "", - "x": 130, - "y": 660, - "wires": [] - }, - { - "id": "bd363cb.334d5c", - "type": "comment", - "z": "dbab9424.ebe0f8", - "name": "デバイス制御(モジュールID指定と制御用JSONデータの定義)", - "info": "", - "x": 250, - "y": 460, - "wires": [] - }, - { - "id": "d4648be7.6583c8", - "type": "function", - "z": "dbab9424.ebe0f8", - "name": "moduleID_set", - "func": "//context.global.moduleに制御対象のモジュールIDを指定\ncontext.global.module = \"%%moduleID%%\";\n\nmsg.payload = context.global.module;\nreturn msg;", - "outputs": "1", - "noerr": 0, - "x": 340, - "y": 560, - "wires": [ - [ - "dbf4b9be.223bc8" - ] - ] - }, - { - "id": "6cf4cf3e.f2fb6", - "type": "inject", - "z": "dbab9424.ebe0f8", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": true, - "x": 130, - "y": 560, - "wires": [ - [ - "d4648be7.6583c8" - ] - ] - }, - { - "id": "282a8aa0.b8d5d6", - "type": "debug", - "z": "dbab9424.ebe0f8", - "name": "", - "active": false, - "console": "false", - "complete": "payload", - "x": 770, - "y": 560, - "wires": [] - }, - { - "id": "dbf4b9be.223bc8", - "type": "function", - "z": "dbab9424.ebe0f8", - "name": "JSON_set", - "func": "context.global.redon = {\n \"module\": context.global.module,\n \"type\": \"channels\",\n \"payload\": {\n \"channels\": [\n {\n \"channel\": 0,\n \"type\": \"I\",\n \"value\": 1\n }\n ]\n }\n};\ncontext.global.greenon = {\n \"module\": context.global.module,\n \"type\": \"channels\",\n \"payload\": {\n \"channels\": [\n {\n \"channel\": 1,\n \"type\": \"I\",\n \"value\": 1\n }\n ]\n }\n};\ncontext.global.blueon = {\n \"module\": context.global.module,\n \"type\": \"channels\",\n \"payload\": {\n \"channels\": [\n {\n \"channel\": 2,\n \"type\": \"I\",\n \"value\": 1\n }\n ]\n }\n};\ncontext.global.redoff = {\n \"module\": context.global.module,\n \"type\": \"channels\",\n \"payload\": {\n \"channels\": [\n {\n \"channel\": 0,\n \"type\": \"I\",\n \"value\": 0\n }\n ]\n }\n};\ncontext.global.greenoff = {\n \"module\": context.global.module,\n \"type\": \"channels\",\n \"payload\": {\n \"channels\": [\n {\n \"channel\": 1,\n \"type\": \"I\",\n \"value\": 0\n }\n ]\n }\n};\ncontext.global.blueoff = {\n \"module\": context.global.module,\n \"type\": \"channels\",\n \"payload\": {\n \"channels\": [\n {\n \"channel\": 2,\n \"type\": \"I\",\n \"value\": 0\n }\n ]\n }\n};\ncontext.global.allon = {\n \"module\": context.global.module,\n \"type\": \"channels\",\n \"payload\": {\n \"channels\": [\n {\n \"channel\": 0,\n \"type\": \"I\",\n \"value\": 1\n },\n {\n \"channel\": 1,\n \"type\": \"I\",\n \"value\": 1\n },\n {\n \"channel\": 2,\n \"type\": \"I\",\n \"value\": 1\n }\n ]\n }\n};\ncontext.global.alloff = {\n \"module\": context.global.module,\n \"type\": \"channels\",\n \"payload\": {\n \"channels\": [\n {\n \"channel\": 0,\n \"type\": \"I\",\n \"value\": 0\n },\n {\n \"channel\": 1,\n \"type\": \"I\",\n \"value\": 0\n },\n {\n \"channel\": 2,\n \"type\": \"I\",\n \"value\": 0\n }\n ]\n }\n};\n\nreturn [\n {payload:context.global.redon},\n {payload:context.global.greenon},\n {payload:context.global.blueon},\n {payload:context.global.redoff},\n {payload:context.global.greenoff},\n {payload:context.global.blueoff},\n {payload:context.global.allon},\n {payload:context.global.alloff}\n];", - "outputs": "8", - "noerr": 0, - "x": 590, - "y": 560, - "wires": [ - [ - "282a8aa0.b8d5d6" - ], - [ - "282a8aa0.b8d5d6" - ], - [ - "282a8aa0.b8d5d6" - ], - [ - "282a8aa0.b8d5d6" - ], - [ - "282a8aa0.b8d5d6" - ], - [ - "282a8aa0.b8d5d6" - ], - [ - "282a8aa0.b8d5d6" - ], - [ - "282a8aa0.b8d5d6" - ] - ] - }, - { - "id": "43cd4220.bc135c", - "type": "debug", - "z": "dbab9424.ebe0f8", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 610, - "y": 400, - "wires": [] - }, - { - "id": "57c78451.2c0b9c", - "type": "debug", - "z": "dbab9424.ebe0f8", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 610, - "y": 240, - "wires": [] - }, - { - "id": "5d198923.69a608", - "type": "debug", - "z": "dbab9424.ebe0f8", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 190, - "y": 140, - "wires": [] - }, - { - "id": "c4a42a0f.f49748", - "type": "comment", - "z": "dbab9424.ebe0f8", - "name": "【オプション】SNS投稿", - "info": "", - "x": 830, - "y": 40, - "wires": [] - }, - { - "id": "3efc4ee6.d6fc42", - "type": "websocket-client", - "z": "", - "path": "wss://api.sakura.io/ws/v1/", - "tls": "", - "wholemsg": "false" - }, - { - "id": "4b67594d.a5b328", - "type": "ui_group", - "z": "", - "name": "温度", - "tab": "34242c41.c92474", - "order": 1, - "disp": true, - "width": "8" - }, - { - "id": "4a6401b3.8bf5", - "type": "ui_group", - "z": "", - "name": "湿度", - "tab": "34242c41.c92474", - "order": 2, - "disp": true, - "width": "8" - }, - { - "id": "600c4eb9.9c1f6", - "type": "ui_group", - "z": "", - "name": "Control", - "tab": "34242c41.c92474", - "disp": true, - "width": "4" - }, - { - "id": "fc59ee64.e5928", - "type": "websocket-client", - "z": "", - "path": "wss://api.sakura.io/ws/v1/", - "tls": "", - "wholemsg": "false" - }, - { - "id": "34242c41.c92474", - "type": "ui_tab", - "z": "", - "name": "Handson", - "icon": "dashboard", - "order": 1 - } -] diff --git a/raspi/sensor/handson-sample/node-red/ekispert-flow-send-message-in-slack.json b/raspi/sensor/handson-sample/node-red/ekispert-flow-send-message-in-slack.json deleted file mode 100644 index 60b4d459..00000000 --- a/raspi/sensor/handson-sample/node-red/ekispert-flow-send-message-in-slack.json +++ /dev/null @@ -1,459 +0,0 @@ -[ - { - "id": "fe1de68f.2d1648", - "type": "tab", - "label": "駅すぱあとWebサービス連携", - "disabled": false, - "info": "本フローは駅すぱあとWebサービス&sakura.ioハンズオンで利用する、Arduino Unoと温湿度センサ(HDC1000 or SHT31)を用いたセンシング情報と駅情報や経路情報の連携を試すものです。\n\n■駅名&駅コードの取得\n\nデプロイ時にStationノードに指定した駅名に基づき駅名および駅コードを取得し、変数に格納します。\n以下を設定しデプロイすることで動作します。\n\n1. Stationノード:name欄に任意の駅名を、key欄に駅すぱあとWebサービスのAPIのアクセスキーを、それぞれ指定します。\n\n■駅すぱあと範囲探索&Slack投稿\n\nsakura.ioのWebSocket(`wss://api.sakura.io/ws/v1/.....`)サービスから送信されたJSONデータに含まれる温度と湿度の2項目が指定範囲を逸脱した場合、その内容を元にSlackの該当チャンネルに対して、センサからの温湿度情報、および指定した駅コードを元に指定時間内に行くことができる経路情報の2つのメッセージを生成し投稿します。\n\n以下3点を設定しデプロイすることで動作します。\n\n1. sakuraio_in(WebSocket-inノード):sakura.ioコンパネで確認できるWebSocketのURL(`wss://api.sakura.io/ws/v1/.....`)を設定します。\n \n2. search rangeノード:key欄に駅すぱあとWebサービスのAPIのアクセスキーを、upperMinute欄に任意の分数を、それぞれ指定します。baselist欄が空欄であるためエラーを示す▲マークが表示されますが、前段のノードでセットしたbaselistが送信されるため、記入は不要です。\n\n3. slackノード:WebHook URL欄に投稿先となるSlackチーム内チャンネルのIncoming WebHooksのURLを指定します。\n\n> [sakura.io コントロールパネル](https://secure.sakura.ad.jp/iot/)\n\n> [利用Arduinoスケッチ1(HDC1000利用時)](https://github.com/sakuraio/handson-sample/blob/master/arduino/basic-HDC1000-with-LED.ino)\n\n> [利用Arduinoスケッチ2(SHT31利用時)](https://github.com/sakuraio/handson-sample/blob/master/arduino/basic-SHT31-with-LED.ino)\n\n> [参考ハンズオン資料](https://www.slideshare.net/sakura_pr/sakuraio-handson-with-val-laboratory)" - }, - { - "id": "1db5f843.e4be68", - "type": "comment", - "z": "fe1de68f.2d1648", - "name": "駅すぱあと範囲探索&Slack投稿", - "info": "", - "x": 170, - "y": 160, - "wires": [] - }, - { - "id": "9cb9259b.294c58", - "type": "function", - "z": "fe1de68f.2d1648", - "name": "Route_Gen", - "func": "var points = msg.payload.ResultSet.Point;\n\nif(!Array.isArray(points)){\n points = [points];\n}\n\nvar displayStations = [];\n\nfor(var i = 0; i < points.length; i++) {\n displayStations.push(\"・\" + points[i].Station.Name + \"駅 \" + points[i].Cost.Minute + \"分 乗換\" + points[i].Cost.TransferCount + \"回\");\n}\n\nvar newMsg = {\n payload: displayStations.join(\"\\n\")\n};\nreturn newMsg;", - "outputs": 1, - "noerr": 0, - "x": 710, - "y": 460, - "wires": [ - [ - "80eee60.e377d18" - ] - ] - }, - { - "id": "59e6f7d.0758508", - "type": "slack", - "z": "fe1de68f.2d1648", - "name": "", - "channelURL": "", - "username": "", - "emojiIcon": "", - "channel": "", - "x": 1050, - "y": 400, - "wires": [] - }, - { - "id": "d6fdb56.fbb3148", - "type": "debug", - "z": "fe1de68f.2d1648", - "name": "", - "active": true, - "console": "false", - "complete": "false", - "x": 1070, - "y": 460, - "wires": [] - }, - { - "id": "c09e5b8e.973308", - "type": "switch", - "z": "fe1de68f.2d1648", - "name": "Humi_Range", - "property": "payload.payload.channels[1].value", - "propertyType": "msg", - "rules": [ - { - "t": "lt", - "v": "10", - "vt": "num" - }, - { - "t": "gt", - "v": "70", - "vt": "str" - } - ], - "checkall": "false", - "repair": false, - "outputs": 2, - "x": 330, - "y": 340, - "wires": [ - [ - "bc2ef34f.17882" - ], - [ - "bc2ef34f.17882" - ] - ] - }, - { - "id": "4cafd691.073fa8", - "type": "switch", - "z": "fe1de68f.2d1648", - "name": "Temp_Range", - "property": "payload.payload.channels[0].value", - "propertyType": "msg", - "rules": [ - { - "t": "lt", - "v": "10", - "vt": "num" - }, - { - "t": "gt", - "v": "25", - "vt": "str" - } - ], - "checkall": "false", - "repair": false, - "outputs": 2, - "x": 150, - "y": 340, - "wires": [ - [ - "c09e5b8e.973308" - ], - [ - "c09e5b8e.973308" - ] - ] - }, - { - "id": "93dbd2e.2c0dc3", - "type": "search range", - "z": "fe1de68f.2d1648", - "name": "", - "accessKey": "", - "baseList": "", - "upperMinute": "10", - "upperTransferCount": "", - "plane": "", - "shinkansen": "", - "limitedExpress": "", - "limit": "", - "x": 330, - "y": 460, - "wires": [ - [ - "e2277656.026a28" - ] - ] - }, - { - "id": "8f729241.3a255", - "type": "template", - "z": "fe1de68f.2d1648", - "name": "Message_1", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "mustache", - "template": "部屋の環境がよろしくないみたい!\n温度:{{global.temp}}度\n湿度:{{global.humi}}%", - "output": "str", - "x": 890, - "y": 340, - "wires": [ - [ - "ccb2137a.1280e", - "59e6f7d.0758508" - ] - ] - }, - { - "id": "80eee60.e377d18", - "type": "template", - "z": "fe1de68f.2d1648", - "name": "Message_2", - "field": "payload", - "fieldType": "msg", - "format": "handlebars", - "syntax": "mustache", - "template": "気分転換にお出かけでもどう?\n{{global.stationName}}から行ける駅を調べたよ!\n{{payload}}", - "output": "str", - "x": 890, - "y": 460, - "wires": [ - [ - "d6fdb56.fbb3148", - "59e6f7d.0758508" - ] - ] - }, - { - "id": "f355b4e9.40ab58", - "type": "websocket in", - "z": "fe1de68f.2d1648", - "name": "sakuraio-in", - "server": "", - "client": "38c42b23.5ea724", - "x": 120, - "y": 220, - "wires": [ - [ - "d7272dd2.4c56b" - ] - ] - }, - { - "id": "d7272dd2.4c56b", - "type": "json", - "z": "fe1de68f.2d1648", - "name": "String -> Object", - "property": "payload", - "action": "", - "pretty": false, - "x": 340, - "y": 220, - "wires": [ - [ - "c46801bc.d0072" - ] - ] - }, - { - "id": "c46801bc.d0072", - "type": "switch", - "z": "fe1de68f.2d1648", - "name": "Type_Check", - "property": "payload.type", - "propertyType": "msg", - "rules": [ - { - "t": "eq", - "v": "channels", - "vt": "str" - } - ], - "checkall": "true", - "repair": false, - "outputs": 1, - "x": 530, - "y": 220, - "wires": [ - [ - "25fa8a73.37fa36" - ] - ] - }, - { - "id": "56263df6.57ad14", - "type": "debug", - "z": "fe1de68f.2d1648", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "payload", - "x": 1070, - "y": 200, - "wires": [] - }, - { - "id": "31f1beb0.58ef02", - "type": "function", - "z": "fe1de68f.2d1648", - "name": "Name&Code_Set", - "func": "var points = msg.payload.ResultSet.Point;\nif(!Array.isArray(points))\n{\n points = [points];}\n\ncontext.global.stationName = points[0].Station.Name;\ncontext.global.stationCode = points[0].Station.code;\n\nreturn {\n payload: {\n stationName: context.global.stationName,\n stationCode: context.global.stationCode\n }\n};", - "outputs": 1, - "noerr": 0, - "x": 730, - "y": 100, - "wires": [ - [ - "9d262a81.a86fa8" - ] - ] - }, - { - "id": "6de2dc9f.57c354", - "type": "inject", - "z": "fe1de68f.2d1648", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": true, - "x": 130, - "y": 100, - "wires": [ - [ - "bd7f2a1e.d2a0f8" - ] - ] - }, - { - "id": "bd7f2a1e.d2a0f8", - "type": "station", - "z": "fe1de68f.2d1648", - "name": "", - "accessKey": "", - "stationName": "高円寺", - "stationCode": "", - "stationOldName": "", - "corporationName": "", - "railName": "", - "operationLineCode": "", - "trafficType": "", - "prefectureCode": "", - "offset": "", - "limit": "", - "direction": "", - "corporationBind": "", - "gcs": "", - "x": 310, - "y": 100, - "wires": [ - [ - "31f1beb0.58ef02" - ] - ] - }, - { - "id": "9d262a81.a86fa8", - "type": "debug", - "z": "fe1de68f.2d1648", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "complete": "false", - "x": 1070, - "y": 100, - "wires": [] - }, - { - "id": "847f9f32.593be", - "type": "template", - "z": "fe1de68f.2d1648", - "name": "Code_Push", - "field": "baseList", - "fieldType": "msg", - "format": "handlebars", - "syntax": "mustache", - "template": "{{global.stationCode}}", - "output": "str", - "x": 150, - "y": 460, - "wires": [ - [ - "93dbd2e.2c0dc3" - ] - ] - }, - { - "id": "4d8ff954.700c68", - "type": "comment", - "z": "fe1de68f.2d1648", - "name": "駅名&駅コードの取得", - "info": "", - "x": 140, - "y": 40, - "wires": [] - }, - { - "id": "bc2ef34f.17882", - "type": "trigger", - "z": "fe1de68f.2d1648", - "op1": "", - "op2": "", - "op1type": "pay", - "op2type": "nul", - "duration": "1", - "extend": false, - "units": "min", - "reset": "", - "bytopic": "all", - "name": "", - "x": 530, - "y": 340, - "wires": [ - [ - "8f729241.3a255", - "847f9f32.593be" - ] - ] - }, - { - "id": "25fa8a73.37fa36", - "type": "function", - "z": "fe1de68f.2d1648", - "name": "Temp&Humi_Set", - "func": "// 温度をグローバル変数に代入\ncontext.global.temp = Math.round(msg.payload.payload.channels[0].value*10)/10;\n\n// 湿度をグローバル変数に代入\ncontext.global.humi = Math.round(msg.payload.payload.channels[1].value*10)/10;\n\ndebug = {\n \"temp\": context.global.temp,\n \"humi\": context.global.humi\n};\n\nreturn [{payload:debug},msg];", - "outputs": 2, - "noerr": 0, - "x": 730, - "y": 220, - "wires": [ - [ - "56263df6.57ad14" - ], - [ - "4cafd691.073fa8", - "a59b867b.fe3748" - ] - ] - }, - { - "id": "ccb2137a.1280e", - "type": "debug", - "z": "fe1de68f.2d1648", - "name": "", - "active": true, - "console": "false", - "complete": "false", - "x": 1070, - "y": 340, - "wires": [] - }, - { - "id": "a59b867b.fe3748", - "type": "debug", - "z": "fe1de68f.2d1648", - "name": "", - "active": false, - "tosidebar": true, - "console": false, - "complete": "payload", - "x": 1070, - "y": 240, - "wires": [] - }, - { - "id": "e2277656.026a28", - "type": "delay", - "z": "fe1de68f.2d1648", - "name": "", - "pauseType": "delay", - "timeout": "5", - "timeoutUnits": "seconds", - "rate": "1", - "nbRateUnits": "1", - "rateUnits": "second", - "randomFirst": "1", - "randomLast": "5", - "randomUnits": "seconds", - "drop": false, - "x": 520, - "y": 460, - "wires": [ - [ - "9cb9259b.294c58" - ] - ] - }, - { - "id": "38c42b23.5ea724", - "type": "websocket-client", - "z": "", - "path": "wss://api.sakura.io/ws/v1/", - "wholemsg": "false" - } -] \ No newline at end of file diff --git a/raspi/sensor/handson-sample/node-red/ekispert-graph-with-tweet.json b/raspi/sensor/handson-sample/node-red/ekispert-graph-with-tweet.json deleted file mode 100644 index afdaa4e3..00000000 --- a/raspi/sensor/handson-sample/node-red/ekispert-graph-with-tweet.json +++ /dev/null @@ -1,376 +0,0 @@ -[ - { - "id": "7cf29ec2.c2e5a", - "type": "tab", - "label": "グラフ表示", - "disabled": false, - "info": "本フローはsakura.ioハンズオンで利用する、Arduino Unoと温湿度センサ(HDC1000 or SHT31)を用いたグラフ表示とTwitter連携を試すものです。\n\n■グラフ表示&SNS投稿\n\nsakura.ioのWebSocket(`wss://api.sakura.io/ws/v1/.....`)サービスから送信されたJSONデータを用いてダッシュボード上にグラフを描画します。\nまた、グラフは`http://:<指定したWeb UIポート番号>/ui/`で確認することができます。\n\nオプションとして、Twittter IDを設定することで温度情報(Channel0)を取り出し、Twitterにメッセージを投稿します。\n\n以下2点を設定しデプロイすることで動作します。\n\n1. sakuraio_in(WebSocket-inノード)\n\n sakura.ioコンパネで確認できるWebSocketのURL(`wss://api.sakura.io/ws/v1/.....`)を設定します。\n\n2. Tweet(Twitterノード)\n\n 認証を行いTwitterのアカウントを連携します。(鍵アカウントでは動作しません)\n \n\n> [sakura.io コントロールパネル](https://secure.sakura.ad.jp/iot/)\n\n> [利用Arduinoスケッチ1(HDC1000利用時)](https://github.com/sakuraio/handson-sample/blob/master/arduino/basic-HDC1000-with-LED.ino)\n\n> [利用Arduinoスケッチ2(SHT31利用時)](https://github.com/sakuraio/handson-sample/blob/master/arduino/basic-SHT31-with-LED.ino)\n\n> [参考ハンズオン資料](https://www.slideshare.net/sakura_pr/sakuraio-handson-with-val-laboratory)" - }, - { - "id": "f20e450.0e38db8", - "type": "websocket in", - "z": "7cf29ec2.c2e5a", - "name": "sakuraio_in", - "server": "", - "client": "8107e288.90ed8", - "x": 110, - "y": 80, - "wires": [ - [ - "75a47006.ad5be", - "90713763.7687f8" - ] - ] - }, - { - "id": "75a47006.ad5be", - "type": "json", - "z": "7cf29ec2.c2e5a", - "name": "String→Object", - "pretty": false, - "x": 340, - "y": 80, - "wires": [ - [ - "49949438.e70b7c" - ] - ] - }, - { - "id": "49949438.e70b7c", - "type": "switch", - "z": "7cf29ec2.c2e5a", - "name": "Type_Check", - "property": "payload.type", - "propertyType": "msg", - "rules": [ - { - "t": "eq", - "v": "channels", - "vt": "str" - } - ], - "checkall": "true", - "outputs": 1, - "x": 150, - "y": 280, - "wires": [ - [ - "9280b077.0777b", - "86ec5192.4757c" - ] - ] - }, - { - "id": "9280b077.0777b", - "type": "function", - "z": "7cf29ec2.c2e5a", - "name": "Ch0(temp)_parse", - "func": "msg.payload = Math.round(msg.payload.payload.channels[0].value*10)/10;return msg;", - "outputs": 1, - "noerr": 0, - "x": 350, - "y": 200, - "wires": [ - [ - "c0ad165f.8a7a68", - "81fe069c.1d53f8", - "917b355e.6f1598", - "5109ea4d.946be4" - ] - ] - }, - { - "id": "81fe069c.1d53f8", - "type": "ui_chart", - "z": "7cf29ec2.c2e5a", - "name": "Temp_chart", - "group": "6d6d4e99.13e86", - "order": 2, - "width": "8", - "height": "5", - "label": "温度 (10分)", - "chartType": "line", - "legend": "false", - "xformat": "HH:mm:ss", - "interpolate": "linear", - "nodata": "", - "dot": false, - "ymin": "0", - "ymax": "40", - "removeOlder": "10", - "removeOlderPoints": "", - "removeOlderUnit": "60", - "cutout": 0, - "colors": [ - "#1f77b4", - "#aec7e8", - "#ff7f0e", - "#2ca02c", - "#98df8a", - "#d62728", - "#ff9896", - "#9467bd", - "#c5b0d5" - ], - "useOldStyle": true, - "x": 590, - "y": 220, - "wires": [ - [], - [] - ] - }, - { - "id": "c0ad165f.8a7a68", - "type": "ui_gauge", - "z": "7cf29ec2.c2e5a", - "name": "Temp_gauge", - "group": "6d6d4e99.13e86", - "order": 1, - "width": "8", - "height": "5", - "gtype": "gage", - "title": "現在の温度", - "label": "℃", - "format": "{{value}}", - "min": 0, - "max": "40", - "colors": [ - "#00b500", - "#e6e600", - "#ca3838" - ], - "seg1": "30", - "seg2": "32", - "x": 590, - "y": 180, - "wires": [] - }, - { - "id": "917b355e.6f1598", - "type": "debug", - "z": "7cf29ec2.c2e5a", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 590, - "y": 140, - "wires": [] - }, - { - "id": "86ec5192.4757c", - "type": "function", - "z": "7cf29ec2.c2e5a", - "name": "Ch1(humi)_parse", - "func": "msg.payload = Math.round(msg.payload.payload.channels[1].value*10)/10;return msg;", - "outputs": 1, - "noerr": 0, - "x": 350, - "y": 360, - "wires": [ - [ - "c1730939.8829e8", - "b88e039b.d5c58", - "b7c50f75.a978" - ] - ] - }, - { - "id": "cccd8faf.fd96d", - "type": "debug", - "z": "7cf29ec2.c2e5a", - "name": "", - "active": true, - "console": "false", - "complete": "false", - "x": 930, - "y": 260, - "wires": [] - }, - { - "id": "9e8e7ad.6159688", - "type": "function", - "z": "7cf29ec2.c2e5a", - "name": "Message", - "func": "msg.payload = `ヴァル研究所とさくらインターネットのコラボハンズオンで温度情報を取得中!ただ今の現地温度は${msg.payload}度だよ。資料は https://goo.gl/qb8QgZ です!どちらもよろしくお願いします! #sakuraio #駅すぱあとWebサービス`;return msg;", - "outputs": 1, - "noerr": 0, - "x": 760, - "y": 260, - "wires": [ - [ - "b5d8091a.081cc8", - "cccd8faf.fd96d" - ] - ] - }, - { - "id": "5109ea4d.946be4", - "type": "trigger", - "z": "7cf29ec2.c2e5a", - "op1": "", - "op2": "", - "op1type": "pay", - "op2type": "nul", - "duration": "10", - "extend": false, - "units": "min", - "reset": "", - "name": "", - "x": 590, - "y": 260, - "wires": [ - [ - "9e8e7ad.6159688" - ] - ] - }, - { - "id": "b5d8091a.081cc8", - "type": "twitter out", - "z": "7cf29ec2.c2e5a", - "twitter": "", - "name": "Tweet", - "x": 910, - "y": 300, - "wires": [] - }, - { - "id": "c1730939.8829e8", - "type": "debug", - "z": "7cf29ec2.c2e5a", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 590, - "y": 320, - "wires": [] - }, - { - "id": "b88e039b.d5c58", - "type": "ui_gauge", - "z": "7cf29ec2.c2e5a", - "name": "Humi_gauge", - "group": "4d07ef3f.f23ea", - "order": 0, - "width": "8", - "height": "5", - "gtype": "gage", - "title": "現在の湿度", - "label": "%", - "format": "{{value}}", - "min": 0, - "max": "100", - "colors": [ - "#ca3838", - "#00b500", - "#0000d2" - ], - "seg1": "30", - "seg2": "70", - "x": 590, - "y": 360, - "wires": [] - }, - { - "id": "b7c50f75.a978", - "type": "ui_chart", - "z": "7cf29ec2.c2e5a", - "name": "Humi_chart", - "group": "4d07ef3f.f23ea", - "order": 0, - "width": "8", - "height": "5", - "label": "湿度 (10分)", - "chartType": "line", - "legend": "false", - "xformat": "HH:mm:ss", - "interpolate": "linear", - "nodata": "", - "dot": false, - "ymin": "0", - "ymax": "100", - "removeOlder": "10", - "removeOlderPoints": "", - "removeOlderUnit": "60", - "cutout": 0, - "colors": [ - "#1f77b4", - "#aec7e8", - "#ff7f0e", - "#2ca02c", - "#98df8a", - "#d62728", - "#ff9896", - "#9467bd", - "#c5b0d5" - ], - "useOldStyle": true, - "x": 590, - "y": 400, - "wires": [ - [], - [] - ] - }, - { - "id": "d78227a8.d34958", - "type": "comment", - "z": "7cf29ec2.c2e5a", - "name": "グラフ表示&SNS投稿", - "info": "", - "x": 120, - "y": 40, - "wires": [] - }, - { - "id": "90713763.7687f8", - "type": "debug", - "z": "7cf29ec2.c2e5a", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 190, - "y": 140, - "wires": [] - }, - { - "id": "8107e288.90ed8", - "type": "websocket-client", - "z": "", - "path": "wss://api.sakura.io/ws/v1/", - "wholemsg": "false" - }, - { - "id": "6d6d4e99.13e86", - "type": "ui_group", - "z": "", - "name": "温度", - "tab": "82da4147.a75f7", - "order": 1, - "disp": true, - "width": "8" - }, - { - "id": "4d07ef3f.f23ea", - "type": "ui_group", - "z": "", - "name": "湿度", - "tab": "82da4147.a75f7", - "order": 2, - "disp": true, - "width": "8" - }, - { - "id": "82da4147.a75f7", - "type": "ui_tab", - "z": "", - "name": "Handson", - "icon": "dashboard", - "order": 1 - } -] \ No newline at end of file diff --git a/raspi/sensor/handson-sample/node-red/ibmcloud-graph-and-control-with-tweet.json b/raspi/sensor/handson-sample/node-red/ibmcloud-graph-and-control-with-tweet.json deleted file mode 100644 index 1b6c8e0b..00000000 --- a/raspi/sensor/handson-sample/node-red/ibmcloud-graph-and-control-with-tweet.json +++ /dev/null @@ -1,778 +0,0 @@ -[ - { - "id": "10370309.20e95d", - "type": "tab", - "label": "IBM Cloud連携", - "disabled": false, - "info": "" - }, - { - "id": "1d88ea3d.5d08c6", - "type": "websocket in", - "z": "10370309.20e95d", - "name": "sakuraio-websocket", - "server": "", - "client": "3cdbf565.42950a", - "x": 130, - "y": 80, - "wires": [ - [ - "477cc9ea.cd5658", - "c4d1188.ef7cee8" - ] - ] - }, - { - "id": "477cc9ea.cd5658", - "type": "json", - "z": "10370309.20e95d", - "name": "String→Objectへの変換", - "pretty": false, - "x": 370, - "y": 80, - "wires": [ - [ - "e145eb59.163f78" - ] - ] - }, - { - "id": "e145eb59.163f78", - "type": "switch", - "z": "10370309.20e95d", - "name": "Type=Channelsだけ通過", - "property": "payload.type", - "propertyType": "msg", - "rules": [ - { - "t": "eq", - "v": "channels", - "vt": "str" - } - ], - "checkall": "true", - "outputs": 1, - "x": 170, - "y": 300, - "wires": [ - [ - "7d026bbf.3dadd4", - "d27ca545.71d028" - ] - ] - }, - { - "id": "7d026bbf.3dadd4", - "type": "function", - "z": "10370309.20e95d", - "name": "Channel0(temp) parse", - "func": "msg.payload = Math.round(msg.payload.payload.channels[0].value*10)/10;return msg;", - "outputs": 1, - "noerr": 0, - "x": 440, - "y": 200, - "wires": [ - [ - "d497cfe6.f6512", - "15ec4700.767349", - "487d1d11.abaaa4", - "61547629.031ce8" - ] - ] - }, - { - "id": "15ec4700.767349", - "type": "ui_chart", - "z": "10370309.20e95d", - "name": "温度推移", - "group": "4a9046c1.bf5cf8", - "order": 0, - "width": "8", - "height": "5", - "label": "温度 (30分)", - "chartType": "line", - "legend": "false", - "xformat": "HH:mm:ss", - "interpolate": "linear", - "nodata": "", - "dot": false, - "ymin": "0", - "ymax": "40", - "removeOlder": "30", - "removeOlderPoints": "", - "removeOlderUnit": "60", - "cutout": 0, - "colors": [ - "#1f77b4", - "#aec7e8", - "#ff7f0e", - "#2ca02c", - "#98df8a", - "#d62728", - "#ff9896", - "#9467bd", - "#c5b0d5" - ], - "useOldStyle": true, - "x": 680, - "y": 280, - "wires": [ - [], - [] - ] - }, - { - "id": "d497cfe6.f6512", - "type": "ui_gauge", - "z": "10370309.20e95d", - "name": "温度", - "group": "4a9046c1.bf5cf8", - "order": 0, - "width": "8", - "height": "5", - "gtype": "gage", - "title": "現在の温度", - "label": "℃", - "format": "{{value}}", - "min": 0, - "max": "40", - "colors": [ - "#00b500", - "#e6e600", - "#ca3838" - ], - "seg1": "30", - "seg2": "32", - "x": 670, - "y": 240, - "wires": [] - }, - { - "id": "487d1d11.abaaa4", - "type": "debug", - "z": "10370309.20e95d", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 690, - "y": 200, - "wires": [] - }, - { - "id": "d27ca545.71d028", - "type": "function", - "z": "10370309.20e95d", - "name": "Channel1(humi) parse", - "func": "msg.payload = Math.round(msg.payload.payload.channels[1].value*10)/10;return msg;", - "outputs": 1, - "noerr": 0, - "x": 440, - "y": 400, - "wires": [ - [ - "d0bf8519.fa9f78", - "da9bc2f5.7e563", - "45812b89.0bd2f4" - ] - ] - }, - { - "id": "7cd854d9.afd6ac", - "type": "debug", - "z": "10370309.20e95d", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 1050, - "y": 340, - "wires": [] - }, - { - "id": "cd3cf3d.2f9631", - "type": "function", - "z": "10370309.20e95d", - "name": "メッセージ化", - "func": "msg.payload = 'BMXUGとさくらインターネットのハンズオンで温度情報を取得中!ただ今の現地温度は'+msg.payload+'度だよ。資料は https://goo.gl/oZ94or です!今後ともよろしくお願いします! #bmxug #bmxgg #sakuraio #さくらインターネット';return msg;", - "outputs": 1, - "noerr": 0, - "x": 860, - "y": 340, - "wires": [ - [ - "7cd854d9.afd6ac", - "ccba4f17.c390d" - ] - ] - }, - { - "id": "61547629.031ce8", - "type": "trigger", - "z": "10370309.20e95d", - "op1": "1", - "op2": "0", - "op1type": "pay", - "op2type": "nul", - "duration": "10", - "extend": false, - "units": "min", - "reset": "", - "name": "", - "x": 690, - "y": 340, - "wires": [ - [ - "cd3cf3d.2f9631" - ] - ] - }, - { - "id": "d0bf8519.fa9f78", - "type": "debug", - "z": "10370309.20e95d", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 690, - "y": 400, - "wires": [] - }, - { - "id": "da9bc2f5.7e563", - "type": "ui_gauge", - "z": "10370309.20e95d", - "name": "湿度", - "group": "681d2ed9.5ec2c", - "order": 0, - "width": "8", - "height": "5", - "gtype": "gage", - "title": "現在の湿度", - "label": "%", - "format": "{{value}}", - "min": 0, - "max": "100", - "colors": [ - "#00b500", - "#e6e600", - "#ca3838" - ], - "seg1": "", - "seg2": "", - "x": 670, - "y": 440, - "wires": [] - }, - { - "id": "45812b89.0bd2f4", - "type": "ui_chart", - "z": "10370309.20e95d", - "name": "湿度推移", - "group": "681d2ed9.5ec2c", - "order": 0, - "width": "8", - "height": "5", - "label": "湿度 (30分)", - "chartType": "line", - "legend": "false", - "xformat": "HH:mm:ss", - "interpolate": "linear", - "nodata": "", - "dot": false, - "ymin": "0", - "ymax": "100", - "removeOlder": "30", - "removeOlderPoints": "", - "removeOlderUnit": "60", - "cutout": 0, - "colors": [ - "#1f77b4", - "#aec7e8", - "#ff7f0e", - "#2ca02c", - "#98df8a", - "#d62728", - "#ff9896", - "#9467bd", - "#c5b0d5" - ], - "useOldStyle": true, - "x": 680, - "y": 480, - "wires": [ - [], - [] - ] - }, - { - "id": "77cca099.deecb", - "type": "comment", - "z": "10370309.20e95d", - "name": "グラフ&Twitter投稿(温度)サンプル", - "info": "本フローはsakura.ioハンズオンで利用する、Arduino Unoと温湿度センサ(HDC1000 or SHT31)を用いたグラフ表示とTwitter連携を試すものです。\n\nsakura.ioのWebSocket`wss://api.sakura.io/ws/v1/.....`サービスから送信されたJSONデータから温度(Channel0)を取り出し、指定したTwittter IDを用いてTwitterにメッセージを投稿します。\n\nまたグラフ表示は`http://:<指定したWeb UIポート番号>/ui/`で確認することができます。\n\nハンズオンでの内容に加えて、本サンプルではボタンによる制御信号の送信を追加しています。\n\n> [sakura.io コントロールパネル](https://secure.sakura.ad.jp/iot/)\n\n> [利用Arduinoスケッチ(温湿度センサHDC1000を利用する場合)](https://github.com/sakuraio/handson-sample/blob/master/arduino/basic-HDC1000-with-LED.ino)\n\n> [利用Arduinoスケッチ(温湿度センサSHT31を利用する場合)](https://github.com/sakuraio/handson-sample/blob/master/arduino/basic-SHT31-with-LED.ino)\n\n> [参考ハンズオン資料](https://www.slideshare.net/sakura_pr/sakuraio-handson)", - "x": 170, - "y": 40, - "wires": [] - }, - { - "id": "516375bf.b5ccec", - "type": "ui_button", - "z": "10370309.20e95d", - "name": "LED-RED-on", - "group": "5fbb3b95.cbef74", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "RED on", - "color": "", - "bgcolor": "red", - "icon": "", - "payload": "{\"module\":\"%%moduleID%%\",\"type\":\"channels\",\"payload\":{\"channels\":[{\"channel\":0,\"type\":\"I\",\"value\":1}]}}", - "payloadType": "json", - "topic": "", - "x": 310, - "y": 580, - "wires": [ - [ - "b30e0c07.e1ed6", - "e03e28a7.0b5b68" - ] - ] - }, - { - "id": "c4d1188.ef7cee8", - "type": "debug", - "z": "10370309.20e95d", - "name": "", - "active": false, - "console": "false", - "complete": "false", - "x": 190, - "y": 158.8888750076294, - "wires": [] - }, - { - "id": "b30e0c07.e1ed6", - "type": "websocket out", - "z": "10370309.20e95d", - "name": "sakuraio-websocket", - "server": "", - "client": "3cdbf565.42950a", - "x": 600, - "y": 580, - "wires": [] - }, - { - "id": "e03e28a7.0b5b68", - "type": "debug", - "z": "10370309.20e95d", - "name": "", - "active": true, - "console": "false", - "complete": "false", - "x": 570, - "y": 640, - "wires": [] - }, - { - "id": "b387736e.12ec9", - "type": "ui_button", - "z": "10370309.20e95d", - "name": "LED-GREEN-on", - "group": "5fbb3b95.cbef74", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "GREEN on", - "color": "", - "bgcolor": "limegreen", - "icon": "", - "payload": "{\"module\":\"%%moduleID%%\",\"type\":\"channels\",\"payload\":{\"channels\":[{\"channel\":1,\"type\":\"I\",\"value\":1}]}}", - "payloadType": "json", - "topic": "", - "x": 320, - "y": 620, - "wires": [ - [ - "b30e0c07.e1ed6", - "e03e28a7.0b5b68" - ] - ] - }, - { - "id": "9da9e39d.5a29b", - "type": "ui_button", - "z": "10370309.20e95d", - "name": "LED-BLUE-on", - "group": "5fbb3b95.cbef74", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "BLUE on", - "color": "", - "bgcolor": "blue", - "icon": "", - "payload": "{\"module\":\"%%moduleID%%\",\"type\":\"channels\",\"payload\":{\"channels\":[{\"channel\":2,\"type\":\"I\",\"value\":1}]}}", - "payloadType": "json", - "topic": "", - "x": 320, - "y": 660, - "wires": [ - [ - "b30e0c07.e1ed6", - "e03e28a7.0b5b68" - ] - ] - }, - { - "id": "855cff4f.fcef1", - "type": "ui_button", - "z": "10370309.20e95d", - "name": "LED-RED-off", - "group": "5fbb3b95.cbef74", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "RED off", - "color": "", - "bgcolor": "tan", - "icon": "", - "payload": "{\"module\":\"%%moduleID%%\",\"type\":\"channels\",\"payload\":{\"channels\":[{\"channel\":0,\"type\":\"I\",\"value\":0}]}}", - "payloadType": "json", - "topic": "", - "x": 310, - "y": 700, - "wires": [ - [ - "b30e0c07.e1ed6", - "e03e28a7.0b5b68" - ] - ] - }, - { - "id": "5b445f1f.9f59b", - "type": "ui_button", - "z": "10370309.20e95d", - "name": "LED-GREEN-off", - "group": "5fbb3b95.cbef74", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "GREEN off", - "color": "", - "bgcolor": "darkseagreen", - "icon": "", - "payload": "{\"module\":\"%%moduleID%%\",\"type\":\"channels\",\"payload\":{\"channels\":[{\"channel\":1,\"type\":\"I\",\"value\":0}]}}", - "payloadType": "json", - "topic": "", - "x": 320, - "y": 740, - "wires": [ - [ - "b30e0c07.e1ed6", - "e03e28a7.0b5b68" - ] - ] - }, - { - "id": "13adb07d.e9a6d", - "type": "ui_button", - "z": "10370309.20e95d", - "name": "LED-BLUE-off", - "group": "5fbb3b95.cbef74", - "order": 0, - "width": 0, - "height": 0, - "passthru": true, - "label": "BLUE off", - "color": "", - "bgcolor": "lightskyblue", - "icon": "", - "payload": "{\"module\":\"%%moduleID%%\",\"type\":\"channels\",\"payload\":{\"channels\":[{\"channel\":2,\"type\":\"I\",\"value\":0}]}}", - "payloadType": "json", - "topic": "", - "x": 320, - "y": 780, - "wires": [ - [ - "b30e0c07.e1ed6", - "e03e28a7.0b5b68" - ] - ] - }, - { - "id": "c07c8ff4.fea2c", - "type": "ui_button", - "z": "10370309.20e95d", - "name": "LED-ALL-on", - "group": "5fbb3b95.cbef74", - "order": 0, - "width": "4", - "height": "2", - "passthru": true, - "label": "ALL on", - "color": "", - "bgcolor": "gold", - "icon": "", - "payload": "{\"module\":\"%%moduleID%%\",\"type\":\"channels\",\"payload\":{\"channels\":[{\"channel\":0,\"type\":\"I\",\"value\":1},{\"channel\":1,\"type\":\"I\",\"value\":1},{\"channel\":2,\"type\":\"I\",\"value\":1}]}}", - "payloadType": "json", - "topic": "", - "x": 310, - "y": 820, - "wires": [ - [ - "b30e0c07.e1ed6", - "e03e28a7.0b5b68" - ] - ] - }, - { - "id": "ae427dfb.1cd43", - "type": "ui_button", - "z": "10370309.20e95d", - "name": "LED-ALL-off", - "group": "5fbb3b95.cbef74", - "order": 0, - "width": "4", - "height": "2", - "passthru": true, - "label": "ALL off", - "color": "", - "bgcolor": "gray", - "icon": "", - "payload": "{\"module\":\"%%moduleID%%\",\"type\":\"channels\",\"payload\":{\"channels\":[{\"channel\":0,\"type\":\"I\",\"value\":0},{\"channel\":1,\"type\":\"I\",\"value\":0},{\"channel\":2,\"type\":\"I\",\"value\":0}]}}", - "payloadType": "json", - "topic": "", - "x": 310, - "y": 860, - "wires": [ - [ - "b30e0c07.e1ed6", - "e03e28a7.0b5b68" - ] - ] - }, - { - "id": "abf2b776.0a46b8", - "type": "comment", - "z": "10370309.20e95d", - "name": "デバイスの制御サンプル", - "info": "", - "x": 130, - "y": 540, - "wires": [] - }, - { - "id": "f94411e4.49889", - "type": "inject", - "z": "10370309.20e95d", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": false, - "x": 120, - "y": 580, - "wires": [ - [ - "516375bf.b5ccec" - ] - ] - }, - { - "id": "875713b6.50f3f", - "type": "inject", - "z": "10370309.20e95d", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": false, - "x": 120, - "y": 620, - "wires": [ - [ - "b387736e.12ec9" - ] - ] - }, - { - "id": "b9e0724e.d443e", - "type": "inject", - "z": "10370309.20e95d", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": false, - "x": 120, - "y": 660, - "wires": [ - [ - "9da9e39d.5a29b" - ] - ] - }, - { - "id": "8fba174b.5dc578", - "type": "inject", - "z": "10370309.20e95d", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": false, - "x": 120, - "y": 700, - "wires": [ - [ - "855cff4f.fcef1" - ] - ] - }, - { - "id": "5ed694b8.2cc97c", - "type": "inject", - "z": "10370309.20e95d", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": false, - "x": 120, - "y": 740, - "wires": [ - [ - "5b445f1f.9f59b" - ] - ] - }, - { - "id": "2e407bbe.8f7154", - "type": "inject", - "z": "10370309.20e95d", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": false, - "x": 120, - "y": 780, - "wires": [ - [ - "13adb07d.e9a6d" - ] - ] - }, - { - "id": "9e5affa4.86b2", - "type": "inject", - "z": "10370309.20e95d", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": false, - "x": 120, - "y": 820, - "wires": [ - [ - "c07c8ff4.fea2c" - ] - ] - }, - { - "id": "ff1d7a64.607aa8", - "type": "inject", - "z": "10370309.20e95d", - "name": "", - "topic": "", - "payload": "", - "payloadType": "date", - "repeat": "", - "crontab": "", - "once": false, - "x": 120, - "y": 860, - "wires": [ - [ - "ae427dfb.1cd43" - ] - ] - }, - { - "id": "ccba4f17.c390d", - "type": "twitter out", - "z": "10370309.20e95d", - "twitter": "", - "name": "Tweet", - "x": 1030, - "y": 380, - "wires": [] - }, - { - "id": "3cdbf565.42950a", - "type": "websocket-client", - "z": "", - "path": "wss://api.sakura.io/ws/v1/", - "wholemsg": "false" - }, - { - "id": "4a9046c1.bf5cf8", - "type": "ui_group", - "z": "", - "name": "温度", - "tab": "a20950a3.209cd", - "order": 1, - "disp": true, - "width": "8" - }, - { - "id": "681d2ed9.5ec2c", - "type": "ui_group", - "z": "", - "name": "湿度", - "tab": "a20950a3.209cd", - "order": 2, - "disp": true, - "width": "8" - }, - { - "id": "5fbb3b95.cbef74", - "type": "ui_group", - "z": "", - "name": "Control", - "tab": "a20950a3.209cd", - "disp": true, - "width": "4" - }, - { - "id": "a20950a3.209cd", - "type": "ui_tab", - "z": "", - "name": "Handson", - "icon": "dashboard", - "order": 1 - } -] \ No newline at end of file diff --git a/raspi/sensor/handson-sample/rpi/LINKS.md b/raspi/sensor/handson-sample/rpi/LINKS.md deleted file mode 100644 index 31138ed5..00000000 --- a/raspi/sensor/handson-sample/rpi/LINKS.md +++ /dev/null @@ -1,42 +0,0 @@ -本資料はsakura.io体験ハンズオンで使用するリンクをまとめたものです。ハンズオンでの進行に合わせてご利用ください。 - -**各リンクは別タブで開くようにして進めるとスムーズです。** - ------ -# Raspberry Pi & sakura.io 体験ハンズオン part1 リンク集 - -## 概要 - -本ハンズオンでは、sakura.ioのコンセプトをより深く体験いただくため、簡単なIoTサービスを構築します。具体的には「温度センサとLEDを搭載するIoTデバイス」および「センサデータの可視化とデバイスの制御を行うサービス」を構築します。IoTデバイス側のコンピュータとしてRaspberry Pi、IoTデバイスとしてFaboを使用します。また、動作確認用にPythonのプログラム、Webサービスの構築にはNode-REDを使用します。 - -## スライド - * [さくらインターネットのIoTソリューション](https://speakerdeck.com/sakuraiot/sakura-iot-introduction) - * [sakura.io 体験ハンズオン(Raspberry Pi編 part1)](https://speakerdeck.com/sakuraiot/sakuraio-handson-rpi-part1) - -## 本編リンク - * さくらインターネット会員登録 https://secure.sakura.ad.jp/signup3/member-register/input.html - * さくらのクラウドコントロールパネル https://secure.sakura.ad.jp/cloud/ - * sakura.ioコントロールパネル https://secure.sakura.ad.jp/iot/ - * ハンズオン用サンプルコード集 https://github.com/sakuraio/handson-sample/ - * Node-REDサンプル用フロー https://github.com/sakuraio/handson-sample/blob/reform/firststep/part2/nodered-firststep-part2.json - -## 【参考】開発者向けページ - * さくらのクラウド ドキュメント https://manual.sakura.ad.jp/cloud/ - * さくらのクラウド API1.1 https://developer.sakura.ad.jp/cloud/api/1.1/ - * sakura.io 開発者向け https://sakura.io/developer/ - * HAT for Raspberry Pi スタートガイド https://sakura.io/docs/pages/guide/startup-guide/rpihat.html - * sakura.io Python ライブラリ https://github.com/sakuraio/python-sakuraio - * sakura.io Python ライブラリのドキュメント http://python-sakuraio.readthedocs.io/ - -## 【参考】モジュール&オプションのご購入 - * 商品の購入 https://sakura.io/product/ - * sakura.ioモジュール(LTE) https://sakura.io/product/module_lte.html - * sakura.io HAT for Raspberry Pi https://sakura.io/product/rpihat.html - -## 【参考】その他物品のご購入 - * FaBo #003 Starter Kit for RaspberryPi https://fabo.store/products/starter-kit-for-arduinoのコピー - * Raspberry Pi Model3 B https://eleshop.jp/shop/g/gGBH312/ - * ACアダプター - https://eleshop.jp/shop/g/gF5T311/ - https://eleshop.jp/shop/g/gE3336C/ - diff --git a/raspi/sensor/handson-sample/rpi/README.md b/raspi/sensor/handson-sample/rpi/README.md deleted file mode 100644 index 5129231e..00000000 --- a/raspi/sensor/handson-sample/rpi/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Raspberry Pi & sakura.io 体験ハンズオン用サンプルプログラム集 - -## 概要 - -「Raspberry Pi & sakura.io 体験ハンズオン」にて使用するサンプルプログラム集です。本ハンズオンでは、sakura.ioのコンセプトをより深く体験いただくため、簡単なIoTサービスを構築します。具体的には「温度センサとLEDを搭載するIoTデバイス」および「センサデータの可視化とデバイスの制御を行うサービス」を構築します。IoTデバイス側のコンピュータとしてRaspberry Pi、IoTデバイスとしてFabo、プログラミング言語はPython 3を使用します。 - -## install-devtools.sh - -* Raspberry Piで動かすプログラムです。 -* Raspberry Piの開発環境をセットアップします。 - -### 使い方 - -* スクリプトを1行ずつコマンドラインに入力して実行します。 -* もしくは下記のコマンドでも実行できます。(sudo apt updateに時間がかかるのでご注意ください) - -% bash install-devtools.sh - - -## rpi-fabo.py - -* Raspberry Piで動かすプログラムです。 -* Raspberry Piとsakura.ioの間で、Faboに接続されたセンサーやLEDのデータを送受信します。 - -### 使い方 - -% python3 rpi-fabo.py - - -## sakuraio-send.py - -* sakura.ioのデータ送信テストプログラムです。 -* Raspberry Piにsakura.ioを接続して実行します。 - -### 使い方 - -* % python3でインタラクティブシェルを起動し、>>> が出てきたら1行ずつ入力してENTERを押します。 -* もしくは、下記のコマンドでも実行できます。 - -% python3 sakuraio-send.py - -* sakuraio.enqueue_tx(0, 1)の1を任意の値に変えると、sakura.ioに送信される値が変わります。 - - -## sakuraio-test.py - -* sakura.ioの動作テストプログラムです。 -* Raspberry Piにsakura.ioを接続して実行します。 - -### 使い方 - -* % python3でインタラクティブシェルを起動し、>>> が出てきたら1行ずつ入力してENTERを押します。 -* もしくは、下記のコマンドでも実行できます。 - -% python3 sakuraio-test.py - -* 出力されるのは個々のsakura.ioモジュールに割り振られた固有のIDです。 - - -## websocket-recv.py - -* PCもしくはサーバで動かすプログラムです。 -* sakura.ioから送られてきたデータを受信するプログラムです。 -* 事前に、Pythonのwebsocketモジュールをインストールしてください。 -* WebSocketのURLを設定してから実行してください。 - -### 使い方 - -% python3 websocket-recv.py - - -## websocket-send.py - -* PCもしくはサーバで動かすプログラムです。 -* Faboに接続されたLEDを操作するデータをsakura.ioに送信するプログラムです。 -* 事前に、Pythonのwebsocketモジュールをインストールしてください。 -* WebSocketのURLと、sakura.ioモジュールのIDを設定してから実行してください。 - -### 使い方 - -% python3 websocket-send.py diff --git a/raspi/sensor/handson-sample/rpi/install-devtools.sh b/raspi/sensor/handson-sample/rpi/install-devtools.sh deleted file mode 100644 index 4d174b28..00000000 --- a/raspi/sensor/handson-sample/rpi/install-devtools.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# install-devtools.sh -# 開発環境をセットアップするプログラム -# (Raspberry Piで実行) - -# Raspberry Piにインストールされているソフトウェアを最新にします。 -sudo apt update - -# Python3およびパッケージ管理ツールをインストールします。 -sudo apt install python3 python3-pip - -# Pythonでハードウェアを制御するためのライブラリをインストールします。 -sudo apt install python3-smbus python3-rpi.gpio i2c-tools - -# pipを使用してsakura.ioライブラリをインストールします。 -sudo pip3 install sakuraio pyserial diff --git a/raspi/sensor/handson-sample/rpi/rpi-fabo.py b/raspi/sensor/handson-sample/rpi/rpi-fabo.py deleted file mode 100644 index 4d3ee3b9..00000000 --- a/raspi/sensor/handson-sample/rpi/rpi-fabo.py +++ /dev/null @@ -1,89 +0,0 @@ -# rpi-fabo.py -# Raspberry Pi + Faboとsakura.ioでデータの送受信をするプログラム -# (Raspberry Pi側で動かすプログラム) -# 使い方: -# % python3 rpi-fabo.py - - -from sakuraio.hardware.rpi import SakuraIOSMBus -from decimal import * -import RPi.GPIO as GPIO -import spidev -import time -import sys - -# A0に温度センサー(#108)を接続 -TEMPPIN = 0 -# GPIO4にLED(#101)を接続 -LEDPIN = 4 - -# 温度センサーからデータを取得 -def readadc(channel): - adc = spi.xfer2([1,(8+channel)<<4,0]) - data = ((adc[1]&3) << 8) + adc[2] - return data - -# 取得した値の変換 -def arduino_map(x, in_min, in_max, out_min, out_max): - a = Decimal(x - in_min) - b = Decimal(out_max - out_min) - c = Decimal(in_max - in_min) - d = Decimal(out_min) - e = Decimal(a*b/c+d).quantize(Decimal('.01')) -# print(a,b,c,d,e) - return e - -# 初期化 -spi = spidev.SpiDev() -spi.open(0,0) -spi.max_speed_hz = 5000 -GPIO.setwarnings(False) -GPIO.setmode( GPIO.BCM ) -GPIO.setup( LEDPIN, GPIO.OUT ) -sakuraio = SakuraIOSMBus() -cnt = 0 -led = 0 - -# Ctrl-Cが押されるまで無限ループ -try: - while True: - # 温度センサーから値を取得し電圧を経て温度に変換 - data = readadc(TEMPPIN) - volt = arduino_map(data, 0, 1023, 0, 5000) - deci_temp = arduino_map(volt, 300, 1600, -30, 100) - temp = float(deci_temp) - # ch0にカウンター、ch1に温度を入れて送信 - sakuraio.enqueue_tx(0, cnt) - sakuraio.enqueue_tx(1, temp) - sakuraio.send() - print("cnt = %i" % (cnt)) - print("temp = %f" % (temp)) - # カウンターを+1 - cnt+=1 - - # sakura.ioのキューにデータがあったら受信し値をledに入れる - if (sakuraio.get_rx_queue_length()).get('queued'): - dict_data = sakuraio.dequeue_rx_raw() - led = dict_data['data'][0] - print("led = %i" % (led)) - else: - print("nothing recv...") - # ledが1ならLEDを点灯、0なら消灯、それ以外なら何もしない - if led == 1: - GPIO.output( LEDPIN, True ) - elif led == 0: - GPIO.output( LEDPIN, False ) - else: - print("no operation...") - # キューを空にする - sakuraio.clear_rx() - - # 5秒待って繰り返し - time.sleep( 5 ) - -# Ctrl-Cが押されたら終了処理 -except KeyboardInterrupt: - GPIO.cleanup() - spi.close() - sys.exit(0) - diff --git a/raspi/sensor/handson-sample/rpi/sakuraio-recv.py b/raspi/sensor/handson-sample/rpi/sakuraio-recv.py deleted file mode 100644 index f322bbf5..00000000 --- a/raspi/sensor/handson-sample/rpi/sakuraio-recv.py +++ /dev/null @@ -1,18 +0,0 @@ -# sakuraio-recv.py -# sakura.ioのデータ受信テストプログラム -# (Raspberry Piでsakura.ioを接続して実行) -# 使い方: -# sakura.ioコントロールパネルのメッセージ送信機能でデータを送ってから、 -# % python3 sakuraio-recv.py - -# ライブラリのインポートと初期化 -from sakuraio.hardware.rpi import SakuraIOSMBus -sakuraio = SakuraIOSMBus() - -# sakura.ioのキューからデータを受信 -dict_data = sakuraio.dequeue_rx_raw() -# 取得したデータ全体を表示 -print(dict_data) -# 0チャンネルにセットされたデータを取得しledに代入して表示 -led = dict_data['data'][0] -print("led = %i" % (led)) diff --git a/raspi/sensor/handson-sample/rpi/sakuraio-send.py b/raspi/sensor/handson-sample/rpi/sakuraio-send.py deleted file mode 100644 index 148b3450..00000000 --- a/raspi/sensor/handson-sample/rpi/sakuraio-send.py +++ /dev/null @@ -1,18 +0,0 @@ -# sakuraio-send.py -# sakura.ioのデータ送信テストプログラム -# (Raspberry Piでsakura.ioを接続して実行) -# 使い方: -# % python3でインタラクティブシェルを起動し、 -# >>> が出てきたら1行ずつ入力してENTER -# もしくは -# % python3 sakuraio-send.py -# -# 参考: -# sakuraio.enqueue_tx(0, 1)の1を任意の値に変えると -# sakura.ioに送信される値が変わる - - -from sakuraio.hardware.rpi import SakuraIOSMBus -sakuraio = SakuraIOSMBus() -sakuraio.enqueue_tx(0, 1) -sakuraio.send() diff --git a/raspi/sensor/handson-sample/rpi/sakuraio-test.py b/raspi/sensor/handson-sample/rpi/sakuraio-test.py deleted file mode 100644 index 4f7f45e0..00000000 --- a/raspi/sensor/handson-sample/rpi/sakuraio-test.py +++ /dev/null @@ -1,14 +0,0 @@ -# sakuraio-test.py -# sakura.ioの動作テストプログラム -# (Raspberry Piでsakura.ioを接続して実行) -# 使い方: -# % python3でインタラクティブシェルを起動し、 -# >>> が出てきたら1行ずつ入力してENTER -# もしくは -# % python3 sakuraio-test.py - - -import sakuraio -from sakuraio.hardware.rpi import SakuraIOSMBus -sakuraio = SakuraIOSMBus() -print(sakuraio.get_unique_id()) diff --git a/raspi/sensor/handson-sample/rpi/websocket-recv.py b/raspi/sensor/handson-sample/rpi/websocket-recv.py deleted file mode 100644 index 59688c1c..00000000 --- a/raspi/sensor/handson-sample/rpi/websocket-recv.py +++ /dev/null @@ -1,49 +0,0 @@ -# websocket-recv.py -# Raspberry Pi + Faboからのデータを受信するプログラム -# (PCで動かすプログラム) -# 使い方: -# % python3 websocket-recv.py - -# 【要設定】WebSocketのURI -uri = "wss://api.sakura.io/ws/v1/xxxxxxx" - - -# ライブラリのインポート -from websocket import create_connection -import time -import sys -import json - -# WebSocketのURIに接続 -ws = create_connection(uri) -print("Receiving...") - -# Ctrl-Cが押されるまで無限ループ -try: - while True: - # WebSocketでデータを受信 - result = ws.recv() -# print("Received '%s'" % result) - # JSONのデータ構造に格納 - json_load = json.loads(result) - # カウンターと温度のデータを取り出して表示 - if json_load['type'] == "channels": - cnt = json_load['payload']['channels'][0]['value'] - temp = json_load['payload']['channels'][1]['value'] - print("cnt = %i" % (cnt)) - print("temp = %f" % (temp)) - - # 1秒待って繰り返し - time.sleep( 1 ) - -# Ctrl-Cが押されたら終了処理 -except KeyboardInterrupt: - ws.close() - sys.exit(0) - - - - - - - diff --git a/raspi/sensor/handson-sample/rpi/websocket-send.py b/raspi/sensor/handson-sample/rpi/websocket-send.py deleted file mode 100644 index 0eefbe60..00000000 --- a/raspi/sensor/handson-sample/rpi/websocket-send.py +++ /dev/null @@ -1,59 +0,0 @@ -# websocket-send.py -# Raspberry Pi + Faboにデータを送信するプログラム -# (PCで動かすプログラム) -# 使い方: -# % python3 websocket-send.py - -# 【要設定】WebSocketのURI -uri = "wss://api.sakura.io/ws/v1/xxxxxxx" - -# 【要設定】sakura.ioモジュールのID -module_id = "uxxxxxxxxxxx" - - -# ライブラリのインポート -from websocket import create_connection -import time -import sys -import json - -# JSONデータ -# channelはArduino版との互換性のため0,1,2を定義しているが -# 本プログラムではchannel 0のみ使用 -json_data = { - "type": "channels", - "module": module_id, - "payload": { - "channels": [ - {"channel":0,"type":"I","value":0}, - {"channel":1,"type":"I","value":0}, - {"channel":2,"type":"I","value":0} - ] - } -} - -# WebSocketのURIに接続 -ws = create_connection(uri) -print("Sending...") - -# Ctrl-Cが押されるまで無限ループ -try: - while True: - # コマンドラインから値を入力 - print('input data: (0=OFF/1=ON/other=NOP)') - input_data = input('>> ') -# print(input_data) - # 値が0か1なら整数値に変換してJSONデータに代入しWebSocketで送信 - if input_data == '0' or input_data == '1': - int_data = int(input_data) - json_data['payload']['channels'][0]['value'] = int_data - ws.send(json.dumps(json_data)) -# print(json.dumps(json_data)) - # それ以外なら何もしない - else: - print("nothing send ...") - -# Ctrl-Cが押されたら終了処理 -except KeyboardInterrupt: - ws.close() - sys.exit(0) diff --git a/raspi/sensor/handson-sample/twilio/ivr.js b/raspi/sensor/handson-sample/twilio/ivr.js deleted file mode 100644 index 99d3bc50..00000000 --- a/raspi/sensor/handson-sample/twilio/ivr.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -このプログラムはsakura.ioとTwilioとのコラボハンズオンにおいて、Twilio Functionsと連携させるためのものです。 -outboundcallプログラムより本プログラムを実行することで、自動音声応答装置(IVR)として音声案内およびプッシュホンによる操作プログラム(turn)を実行させます。 - -本プログラムの動作に必要なパッケージはありません。 - -パラメータは以下のように指定します。 -PATH : /ivr -ACCESS CONTROL : チェックなし -EVENT : 指定なし -*/ -exports.handler = function(context, event, callback) { - let twiml = new Twilio.twiml.VoiceResponse(); - // 音声の指定(声、言語) Audio specification - let voiceParam = {}; - voiceParam.voice = 'alice'; - voiceParam.language = 'ja-JP'; - // ユーザによるキー入力の条件指定(桁数、タイムアウト値) Specify user's key input conditions - let gatherParam = {}; - gatherParam.numDigits = 1; - gatherParam.timeout = 10; - // キー入力による分岐(turn)へのリダイレクトおよび初回の呼び出し内容(default)の指定 Specification of branch contents by key input - switch (event.Digits) { - case '0': - twiml.say(voiceParam, 'エアコンを消します'); - twiml.redirect('https://'+context.DOMAIN_NAME+'/turn?switch=off'); - break; - case '1': - twiml.say(voiceParam, 'エアコンをつけます'); - twiml.redirect('https://'+context.DOMAIN_NAME+'/turn?switch=on'); - break; - default: - twiml.pause({"length": 2}); - twiml.gather(gatherParam) - .say(voiceParam, '室温が上昇しています。エアコンをつける場合は1、消す場合は0を押してください'); - } - callback(null, twiml); -}; \ No newline at end of file diff --git a/raspi/sensor/handson-sample/twilio/outboundcall.js b/raspi/sensor/handson-sample/twilio/outboundcall.js deleted file mode 100644 index e802175d..00000000 --- a/raspi/sensor/handson-sample/twilio/outboundcall.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -このプログラムはsakura.ioとTwilioとのコラボハンズオンにおいて、Twilio Functionsと連携させるためのものです。 -こちらで生成されたURLをsakura.ioのOutgoing Webhookで指定することにより、sakura.ioで取得されたセンサデータはJSONデータとして本プログラムで処理されます。 - -本プログラムの動作に必要なパッケージはありません。 - -パラメータは以下のように指定します。 -PATH : /outboundcall -ACCESS CONTROL : チェックなし -EVENT : 指定なし -*/ -exports.handler = function(context, event, callback) { - console.log(event); - - // モジュールからのメッセージの場合のみ処理を行う(切断・接続メッセージなどは無視) Determining the message type - var messageType = event.type; - if (messageType !== "channels") { - console.log("messageType is not channels"); - callback(null, 'ignored messageType'); - return; - } - - // 受信したJSONデータに含まれるモジュールIDと、Functionsで指定した環境変数(MODULE_ID)を照合 Verification of module ID - var moduleId = event.module || ''; - - // 一致していた場合のみ、環境変数(TO_NUMBER&FROM_NUMBER)で指定した番号に架電 If the ID matches, call to the specified phone number - if (moduleId === context.MODULE_ID) { - var client = context.getTwilioClient(); - console.log("call /ivr"); - client.calls.create({ - // 別途作成するプログラム(ivr)を実行 Execute separately created program - url: 'https://'+context.DOMAIN_NAME+'/ivr', - to: context.TO_NUMBER, - from: context.FROM_NUMBER, - }) - .then((call) => callback(null, call.sid)) - .catch((error)=> callback(error)); - } else { - callback(null, 'moduleId was unmatch'); - } -}; \ No newline at end of file diff --git a/raspi/sensor/handson-sample/twilio/part1/ivr.js b/raspi/sensor/handson-sample/twilio/part1/ivr.js deleted file mode 100644 index 99d3bc50..00000000 --- a/raspi/sensor/handson-sample/twilio/part1/ivr.js +++ /dev/null @@ -1,38 +0,0 @@ -/* -このプログラムはsakura.ioとTwilioとのコラボハンズオンにおいて、Twilio Functionsと連携させるためのものです。 -outboundcallプログラムより本プログラムを実行することで、自動音声応答装置(IVR)として音声案内およびプッシュホンによる操作プログラム(turn)を実行させます。 - -本プログラムの動作に必要なパッケージはありません。 - -パラメータは以下のように指定します。 -PATH : /ivr -ACCESS CONTROL : チェックなし -EVENT : 指定なし -*/ -exports.handler = function(context, event, callback) { - let twiml = new Twilio.twiml.VoiceResponse(); - // 音声の指定(声、言語) Audio specification - let voiceParam = {}; - voiceParam.voice = 'alice'; - voiceParam.language = 'ja-JP'; - // ユーザによるキー入力の条件指定(桁数、タイムアウト値) Specify user's key input conditions - let gatherParam = {}; - gatherParam.numDigits = 1; - gatherParam.timeout = 10; - // キー入力による分岐(turn)へのリダイレクトおよび初回の呼び出し内容(default)の指定 Specification of branch contents by key input - switch (event.Digits) { - case '0': - twiml.say(voiceParam, 'エアコンを消します'); - twiml.redirect('https://'+context.DOMAIN_NAME+'/turn?switch=off'); - break; - case '1': - twiml.say(voiceParam, 'エアコンをつけます'); - twiml.redirect('https://'+context.DOMAIN_NAME+'/turn?switch=on'); - break; - default: - twiml.pause({"length": 2}); - twiml.gather(gatherParam) - .say(voiceParam, '室温が上昇しています。エアコンをつける場合は1、消す場合は0を押してください'); - } - callback(null, twiml); -}; \ No newline at end of file diff --git a/raspi/sensor/handson-sample/twilio/part1/outboundcall.js b/raspi/sensor/handson-sample/twilio/part1/outboundcall.js deleted file mode 100644 index e802175d..00000000 --- a/raspi/sensor/handson-sample/twilio/part1/outboundcall.js +++ /dev/null @@ -1,41 +0,0 @@ -/* -このプログラムはsakura.ioとTwilioとのコラボハンズオンにおいて、Twilio Functionsと連携させるためのものです。 -こちらで生成されたURLをsakura.ioのOutgoing Webhookで指定することにより、sakura.ioで取得されたセンサデータはJSONデータとして本プログラムで処理されます。 - -本プログラムの動作に必要なパッケージはありません。 - -パラメータは以下のように指定します。 -PATH : /outboundcall -ACCESS CONTROL : チェックなし -EVENT : 指定なし -*/ -exports.handler = function(context, event, callback) { - console.log(event); - - // モジュールからのメッセージの場合のみ処理を行う(切断・接続メッセージなどは無視) Determining the message type - var messageType = event.type; - if (messageType !== "channels") { - console.log("messageType is not channels"); - callback(null, 'ignored messageType'); - return; - } - - // 受信したJSONデータに含まれるモジュールIDと、Functionsで指定した環境変数(MODULE_ID)を照合 Verification of module ID - var moduleId = event.module || ''; - - // 一致していた場合のみ、環境変数(TO_NUMBER&FROM_NUMBER)で指定した番号に架電 If the ID matches, call to the specified phone number - if (moduleId === context.MODULE_ID) { - var client = context.getTwilioClient(); - console.log("call /ivr"); - client.calls.create({ - // 別途作成するプログラム(ivr)を実行 Execute separately created program - url: 'https://'+context.DOMAIN_NAME+'/ivr', - to: context.TO_NUMBER, - from: context.FROM_NUMBER, - }) - .then((call) => callback(null, call.sid)) - .catch((error)=> callback(error)); - } else { - callback(null, 'moduleId was unmatch'); - } -}; \ No newline at end of file diff --git a/raspi/sensor/handson-sample/twilio/part1/turn.js b/raspi/sensor/handson-sample/twilio/part1/turn.js deleted file mode 100644 index e334b903..00000000 --- a/raspi/sensor/handson-sample/twilio/part1/turn.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -このプログラムはsakura.ioとTwilioとのコラボハンズオンにおいて、Twilio Functionsと連携させるためのものです。 -ivrプログラムより本プログラムを実行することで、sakura.ioで生成したIncoming WebhookのURL(SAKURA_URL)に対して指定したJSONデータを送信します。 - -本プログラムには以下のパッケージが必要です。Ver.は検証時のものを記載しています。 -got : 6.7.1 - -パラメータは以下のように指定します。 -PATH : /turn -ACCESS CONTROL : チェックなし -EVENT : 指定なし -*/ -exports.handler = function(context, event, callback) { - // switchパラメータで、エアコンのOnとOffを制御 Control on / off of air conditioner with switch parameter - const sw = event.switch || 'off'; - const value = ( sw === 'on' ? 1 : 0 ); - const message = ( sw === 'on' ? 'エアコンをつけました' : 'エアコンを消しました' ); - - // 返送するJSONデータの指定(ヘッダおよびMODULE_IDを含むボディ) Specify JSON data to send back - const got = require('got'); - const headers = JSON.stringify({ - "Content-Type": "application/json", - "Accept": "application/json" - }); - const body = JSON.stringify({ - "type": "channels", - "module": context.MODULE_ID, - "payload": { - "channels": [{ - "channel": 0, - "type": "i", - "value": value - }] - } - }); - - // 音声の指定(声、言語) Audio specification - let twiml = new Twilio.twiml.VoiceResponse(); - let sayParam = {}; - sayParam.language = 'ja-JP'; - sayParam.voice = 'alice'; - - // 送信先URLの指定(SAKURA_URL) Specify th destination URL - got(context.SAKURA_URL, { - json: true, - method: 'POST', - headers: headers, - body: body - }) - // 成功した場合の挙動の指定 Designation of behavior in case of success - .then(response => { - console.log(response); - twiml.say(sayParam, message); - twiml.hangup(); - callback(null, twiml); - }) - // 失敗した場合の挙動の指定 Designation of behavior in case of failure - .catch(error => { - console.log(error); - twiml.say(sayParam, 'エラーが発生しました'); - twiml.hangup(); - callback(null, twiml); - }); -}; \ No newline at end of file diff --git a/raspi/sensor/handson-sample/twilio/part2/callflow.js b/raspi/sensor/handson-sample/twilio/part2/callflow.js deleted file mode 100644 index d7f1b5fe..00000000 --- a/raspi/sensor/handson-sample/twilio/part2/callflow.js +++ /dev/null @@ -1,46 +0,0 @@ -/* -このプログラムはsakura.ioとTwilioとのコラボハンズオンにおいて、Twilio Functionsと連携させるためのものです。 -こちらで生成されたURLをsakura.ioのOutgoing Webhookで指定することにより、sakura.ioで取得されたセンサデータはJSONデータとして本プログラムで処理されます。 - -本プログラムの動作に必要なパッケージはありません。 - -パラメータは以下のように指定します。 -PATH : /callflow -ACCESS CONTROL : チェックなし -EVENT : 指定なし -*/ -exports.handler = function(context, event, callback) { - - // モジュールからのメッセージの場合のみ処理を行う(切断・接続メッセージなどは無視) Determining the message type - const messageType = event.type; - if (messageType !== "channels") { - console.log("messageType is not channels"); - callback(null, 'ignored messageType'); - } - - // JSONデータに含まれるモジュールIDを変数に代入 Substitute the module ID included in the JSON data - const moduleId = event.module || ''; - - // モジュールIDの照合 Verification of module ID - if (moduleId !== context.MODULE_ID) { - console.log("Module ID unmatch"); - callback(null, 'Module id unmatch.'); - } - - const twilioClient = context.getTwilioClient(); - twilioClient.studio.flows('FWから始まるStudioフローのSID').engagements.create({ - to: context.TO_NUMBER, - from: context.FROM_NUMBER, - parameters: JSON.stringify({ - moduleId: moduleId - }) - }) - .then(function(engagement) { - console.log(engagement.sid); - callback(null, engagement.sid); - }) - .catch(error => { - console.error(`problem with request: ${error.message}`); - callback(error.message); - }); -}; \ No newline at end of file diff --git a/raspi/sensor/handson-sample/twilio/part2/turn.js b/raspi/sensor/handson-sample/twilio/part2/turn.js deleted file mode 100644 index 024318db..00000000 --- a/raspi/sensor/handson-sample/twilio/part2/turn.js +++ /dev/null @@ -1,53 +0,0 @@ -/* -このプログラムはsakura.ioとTwilioとのコラボハンズオンにおいて、Twilio Functionsと連携させるためのものです。 -ivrプログラムより本プログラムを実行することで、sakura.ioで生成したIncoming WebhookのURL(SAKURA_URL)に対して指定したJSONデータを送信します。 - -本プログラムには以下のパッケージが必要です。Ver.は検証時のものを記載しています。 -got : 6.7.1 - -パラメータは以下のように指定します。 -PATH : /turn -ACCESS CONTROL : チェックなし -EVENT : 指定なし -*/ -exports.handler = function(context, event, callback) { - // switchパラメータで、エアコンのOnとOffを制御 Control on / off of air conditioner with switch parameter - const sw = event.switch || 'off'; - const value = ( sw === 'on' ? 1 : 0 ); - - // 返送するJSONデータの指定(ヘッダおよびMODULE_IDを含むボディ) Specify JSON data to send back - const got = require('got'); - const headers = JSON.stringify({ - "Content-Type": "application/json", - "Accept": "application/json" - }); - const body = JSON.stringify({ - "type": "channels", - "module": context.MODULE_ID, - "payload": { - "channels": [{ - "channel": 0, - "type": "i", - "value": value - }] - } - }); - - // 送信先URLの指定(SAKURA_URL) Specify the destination URL - got(context.SAKURA_URL, { - json: true, - method: 'POST', - headers: headers, - body: body - }) - // 成功した場合の挙動の指定 Designation of successful behavior - .then(response => { - console.log(response); - callback(null, 'OK'); - }) - // 失敗した場合の挙動の指定 Designation of behavior in case of failure - .catch(error => { - console.log(error); - callback(error); - }); - }; \ No newline at end of file diff --git a/raspi/sensor/handson-sample/twilio/turn.js b/raspi/sensor/handson-sample/twilio/turn.js deleted file mode 100644 index e334b903..00000000 --- a/raspi/sensor/handson-sample/twilio/turn.js +++ /dev/null @@ -1,64 +0,0 @@ -/* -このプログラムはsakura.ioとTwilioとのコラボハンズオンにおいて、Twilio Functionsと連携させるためのものです。 -ivrプログラムより本プログラムを実行することで、sakura.ioで生成したIncoming WebhookのURL(SAKURA_URL)に対して指定したJSONデータを送信します。 - -本プログラムには以下のパッケージが必要です。Ver.は検証時のものを記載しています。 -got : 6.7.1 - -パラメータは以下のように指定します。 -PATH : /turn -ACCESS CONTROL : チェックなし -EVENT : 指定なし -*/ -exports.handler = function(context, event, callback) { - // switchパラメータで、エアコンのOnとOffを制御 Control on / off of air conditioner with switch parameter - const sw = event.switch || 'off'; - const value = ( sw === 'on' ? 1 : 0 ); - const message = ( sw === 'on' ? 'エアコンをつけました' : 'エアコンを消しました' ); - - // 返送するJSONデータの指定(ヘッダおよびMODULE_IDを含むボディ) Specify JSON data to send back - const got = require('got'); - const headers = JSON.stringify({ - "Content-Type": "application/json", - "Accept": "application/json" - }); - const body = JSON.stringify({ - "type": "channels", - "module": context.MODULE_ID, - "payload": { - "channels": [{ - "channel": 0, - "type": "i", - "value": value - }] - } - }); - - // 音声の指定(声、言語) Audio specification - let twiml = new Twilio.twiml.VoiceResponse(); - let sayParam = {}; - sayParam.language = 'ja-JP'; - sayParam.voice = 'alice'; - - // 送信先URLの指定(SAKURA_URL) Specify th destination URL - got(context.SAKURA_URL, { - json: true, - method: 'POST', - headers: headers, - body: body - }) - // 成功した場合の挙動の指定 Designation of behavior in case of success - .then(response => { - console.log(response); - twiml.say(sayParam, message); - twiml.hangup(); - callback(null, twiml); - }) - // 失敗した場合の挙動の指定 Designation of behavior in case of failure - .catch(error => { - console.log(error); - twiml.say(sayParam, 'エラーが発生しました'); - twiml.hangup(); - callback(null, twiml); - }); -}; \ No newline at end of file diff --git a/raspi/sensor/receive.py b/raspi/sensor/receive.py deleted file mode 100644 index b2d53041..00000000 --- a/raspi/sensor/receive.py +++ /dev/null @@ -1,81 +0,0 @@ -import asyncio - -from bleak import BleakClient, BleakError, BleakScanner -from sakuraio.hardware.rpi import SakuraIOSMBus - -# ESP32のデバイスを識別するためのUUID (これはデバイスにより異なります) -ESP32_UUIDs = ["08:B6:1F:B9:4F:FA"] - -# ESP側のサービスのUUID -RX_UUID = "69ddb59c-d601-4ea4-ba83-44f679a670ba" # RX Characteristic UUID (from ESP32 to Computer) - -# 0: chanel -# 1: value -# さくらのモジュールを呼び出し -sakuraio = SakuraIOSMBus() - - -# コールバック関数: データが送信されたときに呼び出されます -# 0: なんもいないよ〜 -# 1: きたよ〜ん -def notification_handler(sender: int, data: bytearray, **_kwargs): - data = data.decode() - print(f"Received: {data} (from {sender})") - # TODO 入力をトリガーにしてナンバーの情報を送信する - # データをチャネル0に入力 - print(data) - sakuraio.enqueue_tx(0, data) - sakuraio.send() - - -async def connect_to_device(device): - client = BleakClient(device) - try: - await client.connect() - print(f"Connected to {device.name} ({device.address})") - - # Characteristicの情報を得るために記述。本番ではコメントアウトしても良い - for service in client.services: - print("---------------------") - print(f"service uuid:{service.uuid}, description:{service.description}") - [print(f"{c.properties},{c.uuid}") for c in service.characteristics] - - await client.start_notify(RX_UUID, notification_handler) - - while True: - await asyncio.sleep(1.0) - - except BleakError as e: - print(f"Connection to {device.address} failed: {e}") - - finally: - if client.is_connected: - # RX_UUIDが存在する場合のみstop_notifyを呼び出す - characteristics = [ - c for service in client.services for c in service.characteristics - ] - if any(c.uuid == RX_UUID for c in characteristics): - await client.stop_notify(RX_UUID) - await client.disconnect() - print(f"Disconnected from {device.address}") - - -async def run(): - while True: - print("Scanning for devices...") - scanner = BleakScanner() - devices = await scanner.discover() - - clients = [] - for device in devices: - if device.address in ESP32_UUIDs: - clients.append(device) - - for device in clients: - await connect_to_device(device) - - # 再接続のために少し待機してから再度スキャンを開始 - await asyncio.sleep(5.0) - - -asyncio.run(run()) diff --git a/raspi/line_detection/main.py b/scripts/run_detection.py similarity index 95% rename from raspi/line_detection/main.py rename to scripts/run_detection.py index d874b3d0..f9a2be7b 100755 --- a/raspi/line_detection/main.py +++ b/scripts/run_detection.py @@ -15,18 +15,16 @@ from pathlib import Path from ultralytics import YOLO -# 自ディレクトリと raspi/ をパスに追加 -sys.path.insert(0, os.path.dirname(__file__)) -sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) - -from detection.config import Config -from detection.line_crossing import LineCrossingDetector, get_vehicle_point -from detection.tracker import VehicleTracker -from result_output.video_writer import VideoAnnotator -from result_output.event_logger import EventLogger -from common.frame_stats import compute_timing_stats -from common.time_windows import frames_from_seconds -from common.frame_timing import ( +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +from tracking_parking.config import Config +from tracking_parking.detection.line_crossing import LineCrossingDetector, get_vehicle_point +from tracking_parking.detection.tracker import VehicleTracker +from tracking_parking.output.video_writer import VideoAnnotator +from tracking_parking.output.event_logger import EventLogger +from tracking_parking.common.frame_stats import compute_timing_stats +from tracking_parking.common.time_windows import frames_from_seconds +from tracking_parking.common.frame_timing import ( DEFAULT_WARMUP_FRAMES, TIMING_SCHEMA_VERSION, FrameTiming, @@ -38,19 +36,19 @@ sha256_file, validate_warmup_frames, ) -from common.wandb_logger import ( +from tracking_parking.common.wandb_logger import ( ExperimentLogger, next_log_boundary, should_log_frame, validate_log_interval_sec, ) -from common.run_identity import ( +from tracking_parking.common.run_identity import ( build_display_name, build_run_identity, collect_reproducibility_info, write_run_manifest, ) -from common.ground_truth import ( +from tracking_parking.common.ground_truth import ( GroundTruth, build_ground_truth_config, build_ground_truth_summary, @@ -603,7 +601,7 @@ def main(): parser.add_argument( "--env", default=None, - help=".envファイルのパス(デフォルト: カレントディレクトリの.env)" + help=".envファイルのパス(デフォルト: リポジトリルートの.env)" ) parser.add_argument( "--display", @@ -636,8 +634,8 @@ def main(): if not args.input and args.camera is None: print("エラー: --inputまたは--cameraを指定してください") print("\n使用例:") - print(" python main.py --input data/inputs/test.mp4") - print(" python main.py --camera 0 --display") + print(" python scripts/run_detection.py --input data/inputs/test.mp4") + print(" python scripts/run_detection.py --camera 0 --display") return 1 # 入力ソースを決定 @@ -651,12 +649,12 @@ def main(): if args.env: config = Config.from_env(args.env) else: - # カレントディレクトリの.envを探す - env_path = os.path.join(os.path.dirname(__file__), ".env") + # リポジトリルートの.envを探す + env_path = os.path.join(REPO_ROOT, ".env") if not os.path.exists(env_path): print(f"エラー: .envファイルが見つかりません: {env_path}") - print("\nまず line_setup/setup_lines.py を実行してライン座標を設定してください:") - print(" python line_setup/setup_lines.py --video data/inputs/test.mp4") + print("\nまず scripts/setup_lines.py を実行してライン座標を設定してください:") + print(" python scripts/setup_lines.py --video data/inputs/test.mp4") return 1 config = Config.from_env(env_path) diff --git a/raspi/line_detection/run_multi_video.py b/scripts/run_multi_video.py similarity index 69% rename from raspi/line_detection/run_multi_video.py rename to scripts/run_multi_video.py index eacb7cfd..073f98b2 100644 --- a/raspi/line_detection/run_multi_video.py +++ b/scripts/run_multi_video.py @@ -1,26 +1,39 @@ #!/usr/bin/env python3 -"""複数動画をmain.pyで順に処理し、方式比較用のサマリーへ集約する。 +"""複数動画をrun_detection.pyで順に処理し、結果をサマリーへ集約する。 -ROI方式の scripts/04_multi_video_mae.py に相当する。2ライン方式のmain.pyは -1動画ずつしか扱わないため、Gate 4の同条件計測には動画ごとの実行と結果の集約が要る。 +run_detection.pyは1動画ずつしか扱わないため、同条件での複数動画計測には +動画ごとの実行と結果の集約が要る。 **各runは独立したサブプロセスで走らせる。** 理由が2つある。 1. `load_dotenv` は既存の環境変数を上書きしない。同一プロセスで2つの.envを 読むと後から読んだほうが無視されるため、画角ごとに設定を切り替えられない。 -2. YOLOモデルとトラッカーの状態をrun間で持ち越さない。ROI方式が - prepare_model_for_run で行っている初期化を、プロセス分離で保証する。 +2. YOLOモデルとトラッカーの状態をrun間で持ち越さない。 -閾値スイープは行わない。2ライン方式には探索対象のパラメータが無いため、 -04_multi_video_mae.py の組み合わせループに相当するものは存在しない。 +閾値スイープは行わない。2ライン方式には探索対象のパラメータが無い。 + +対象動画の一覧は、動画と.envの対応を書いたJSONで与える(既定は +data/inputs/videos.json、`--videos` で変更可)。動画・.env・GTはいずれも +.gitignore対象のローカル資産のため、リポジトリには含まれない。 +書式は data/inputs/videos.example.json を参照。 + + [ + {"video": "data/inputs/sample_a.mp4", "env": "camera_a.env"}, + {"video": "data/inputs/sample_b.mp4", "env": "camera_b.env"} + ] + +.envは画角ごとに用意する。.env.templateをコピーし、画角ごとに +scripts/setup_lines.py でライン座標を設定すればよい。比較目的で走らせるときは、 +比較条件(model/conf/iou/classes/imgsz/tracker/device/warmup)を全.envで揃えること。 出力: data/outputs/{EXP_NAME}/ ├── {video_stem}/ - │ ├── logs/events_.json # main.pyが書く + │ ├── logs/events_.json # run_detection.pyが書く │ └── manifests/.json # 同上 └── summary.csv # 本スクリプトが集約する """ +import argparse import csv import glob import json @@ -29,28 +42,17 @@ import sys from pathlib import Path +REPO_ROOT = Path(__file__).resolve().parents[1] + # ── パラメータ ────────────────────────────────────────────────────────────── -# 画角が異なる動画は設定ファイルを分ける。newcam.envは新画角5本、img2787.envは -# 旧画角。比較条件(model/conf/iou/classes/imgsz/tracker/device/warmup)は両者で -# 同一にしてあり、ROI方式の確定runとcomparison_keyが一致するよう揃えてある。 -VIDEOS = [ - ("data/inputs/1787008160.558032.mp4", "newcam.env"), - ("data/inputs/1787009706.719727.mp4", "newcam.env"), - ("data/inputs/1787011229.231516.mp4", "newcam.env"), - ("data/inputs/1787012751.179971.mp4", "newcam.env"), - ("data/inputs/1787014266.421887.mp4", "newcam.env"), - ("data/inputs/IMG_2787.MOV", "img2787.env"), -] - -# GTはROI方式と同一のファイルを見る。両方式が同じ正解を参照することが比較の前提。 -GT_DIR = os.getenv("GT_DIR", "../roi-counter/data/inputs/configs") +DEFAULT_VIDEO_LIST = "data/inputs/videos.json" + +GT_DIR = os.getenv("GT_DIR", "data/inputs/configs") EXP_NAME = os.getenv("EXP_NAME", "gate4_2line") -# comparison_keyの突合先。空文字を渡すと突合を行わない。 -ROI_RUN_DIR = os.getenv( - "ROI_RUN_DIR", - "../roi-counter/data/outputs/exp_adopted_final_gtfix/mae_20260828_062140", -) +# comparison_keyの突合先。ROI方式は本リポジトリから削除済みのため既定では突合しない。 +# 過去のROI方式runと比較したい場合のみ、そのrunディレクトリを渡す。 +ROI_RUN_DIR = os.getenv("ROI_RUN_DIR", "") # ──────────────────────────────────────────────────────────────────────────── SUMMARY_COLUMNS = ( @@ -61,18 +63,44 @@ ) +def load_videos(list_path: str) -> list[tuple[str, str]]: + """対象動画と.envの対応をJSONから読む。 + + 動画・.env・GTはいずれも.gitignore対象のローカル資産なので、 + リポジトリ内のハードコードではなく実行環境ごとのファイルで与える。 + """ + path = Path(list_path) + if not path.exists(): + raise SystemExit( + f"[ERROR] 動画リストがありません: {path}\n" + f" data/inputs/videos.example.json を {path} へコピーして編集してください。" + ) + entries = json.loads(path.read_text(encoding="utf-8")) + videos = [] + for i, entry in enumerate(entries): + try: + videos.append((entry["video"], entry["env"])) + except (TypeError, KeyError) as exc: + raise SystemExit( + f"[ERROR] {path} の {i} 番目の要素に video/env がありません: {entry!r}" + ) from exc + if not videos: + raise SystemExit(f"[ERROR] {path} に動画が1本もありません。") + return videos + + def gt_path_for(video: str) -> Path: """動画パスから対応するGTファイルのパスを導く。""" return Path(GT_DIR) / f"{Path(video).stem}_gt.json" -def check_inputs() -> None: +def check_inputs(videos: list[tuple[str, str]]) -> None: """動画・設定・GTの存在を先に確認する。 1本目を数十分かけて処理した後に3本目のGTが無いと分かる、という失敗を避ける。 """ missing = [] - for video, env in VIDEOS: + for video, env in videos: if not Path(video).exists(): missing.append(f"動画がありません: {video}") if not Path(env).exists(): @@ -85,9 +113,9 @@ def check_inputs() -> None: def run_one(video: str, env: str, out_dir: Path) -> int: - """main.pyを1動画ぶん実行する。戻り値は終了コード。""" + """run_detection.pyを1動画ぶん実行する。戻り値は終了コード。""" cmd = [ - sys.executable, "main.py", + sys.executable, str(REPO_ROOT / "scripts" / "run_detection.py"), "--input", video, "--env", env, "--gt", str(gt_path_for(video)), @@ -194,13 +222,24 @@ def print_comparison_check(rows: list) -> None: def main() -> int: - check_inputs() + parser = argparse.ArgumentParser( + description="複数動画を順に処理し、比較用サマリーへ集約する" + ) + parser.add_argument( + "--videos", + default=DEFAULT_VIDEO_LIST, + help=f"動画と.envの対応を書いたJSON(既定: {DEFAULT_VIDEO_LIST})", + ) + args = parser.parse_args() + + videos = load_videos(args.videos) + check_inputs(videos) base = Path("data/outputs") / EXP_NAME base.mkdir(parents=True, exist_ok=True) - print(f"動画数: {len(VIDEOS)} 出力先: {base}") + print(f"動画数: {len(videos)} 出力先: {base}") rows, failed = [], [] - for video, env in VIDEOS: + for video, env in videos: out_dir = base / Path(video).stem code = run_one(video, env, out_dir) if code != 0: diff --git a/raspi/line_detection/line_setup/setup_lines.py b/scripts/setup_lines.py similarity index 95% rename from raspi/line_detection/line_setup/setup_lines.py rename to scripts/setup_lines.py index 769976c0..dd53f41d 100755 --- a/raspi/line_detection/line_setup/setup_lines.py +++ b/scripts/setup_lines.py @@ -55,7 +55,7 @@ def apply_line_values(existing: str, values: dict) -> str: if remaining: if lines and lines[-1].strip(): lines.append("") - lines.append("# ライン座標(line_setup/setup_lines.pyが書き込む)") + lines.append("# ライン座標(scripts/setup_lines.pyが書き込む)") for key in LINE_ENV_KEYS: if key in remaining: lines.append(f"{key}={remaining[key]}") @@ -70,11 +70,11 @@ def __init__(self, video_path: str, env_path: str = None): """ Args: video_path: 動画ファイルのパス - env_path: .envファイルのパス(Noneの場合はline_detection/.envを使用) + env_path: .envファイルのパス(Noneの場合はリポジトリルートの.envを使用) """ self.video_path = video_path self.env_path = env_path or os.path.join( - os.path.dirname(os.path.dirname(__file__)), + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), ".env" ) @@ -278,9 +278,8 @@ def run(self) -> bool: def save_to_env(self): """ライン座標だけを.envへ書き戻す。 - 他のキー、コメント、並び順は保持する(roi-counterのroi_config.pyと - 同じ契約)。以前はファイル全体をテンプレートで上書きしており、 - 比較条件のために手で入れた値が消えていた。 + 他のキー、コメント、並び順は保持する。以前はファイル全体をテンプレートで + 上書きしており、比較条件のために手で入れた値が消えていた。 """ values = build_line_env_values(self.points) @@ -314,7 +313,7 @@ def main(): parser.add_argument( "--env", default=None, - help=".envファイルのパス(デフォルト: line_detection/.env)" + help=".envファイルのパス(デフォルト: リポジトリルートの.env)" ) args = parser.parse_args() diff --git a/raspi/line_detection/visualize_lines_and_vehicles.py b/scripts/visualize_lines.py similarity index 90% rename from raspi/line_detection/visualize_lines_and_vehicles.py rename to scripts/visualize_lines.py index b32c3f08..ff2de3f6 100644 --- a/raspi/line_detection/visualize_lines_and_vehicles.py +++ b/scripts/visualize_lines.py @@ -9,10 +9,10 @@ import os from ultralytics import YOLO -sys.path.insert(0, os.path.dirname(__file__)) +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -from detection.config import Config -from detection.line_crossing import get_vehicle_point, side_of_line, signed_distance +from tracking_parking.config import Config +from tracking_parking.detection.line_crossing import get_vehicle_point, side_of_line, signed_distance def visualize(video_path: str, config: Config, output_path: str = None, start_frame: int = 0, end_frame: int = 300): @@ -244,15 +244,16 @@ def visualize(video_path: str, config: Config, output_path: str = None, start_fr description="ライン位置と車両検出位置を可視化", epilog=( "例:\n" - " python visualize_lines_and_vehicles.py data/inputs/IMG_2787.MOV\n" - " python visualize_lines_and_vehicles.py data/inputs/IMG_2787.MOV debug_vis.mp4\n" - " python visualize_lines_and_vehicles.py data/inputs/IMG_2787.MOV debug_vis.mp4 300 600\n" - " python visualize_lines_and_vehicles.py data/inputs/1787011229.231516.mp4 " - "vis.mp4 0 300 --env newcam.env" + " python scripts/visualize_lines.py data/inputs/sample.mp4\n" + " python scripts/visualize_lines.py data/inputs/sample.mp4 debug_vis.mp4\n" + " python scripts/visualize_lines.py data/inputs/sample.mp4 debug_vis.mp4 300 600\n" + " # 画角ごとに.envを分けている場合\n" + " python scripts/visualize_lines.py data/inputs/sample.mp4 " + "debug_vis.mp4 0 300 --env camera_a.env" ), formatter_class=argparse.RawDescriptionHelpFormatter, ) - # 位置引数の並びはVERIFICATION.mdの記載と互換を保つ。 + # 位置引数の並びは docs/verification.md の記載と互換を保つ。 parser.add_argument("video", help="動画ファイルのパス") parser.add_argument("output", nargs="?", default=None, help="出力動画パス(省略時は保存しない)") parser.add_argument("start_frame", nargs="?", type=int, default=0, help="開始フレーム") @@ -260,12 +261,12 @@ def visualize(video_path: str, config: Config, output_path: str = None, start_fr parser.add_argument( "--env", default=None, - help=".envファイルのパス(既定: line_detection/.env)。" + help=".envファイルのパス(既定: リポジトリルートの.env)。" "画角ごとに設定を分けている場合に指定する", ) args = parser.parse_args() - env_path = args.env or os.path.join(os.path.dirname(__file__), ".env") + env_path = args.env or os.path.join(REPO_ROOT, ".env") if not os.path.exists(env_path): print(f"エラー: .envファイルが見つかりません: {env_path}") sys.exit(1) diff --git a/raspi/line_detection/detection/__init__.py b/src/tracking_parking/__init__.py similarity index 100% rename from raspi/line_detection/detection/__init__.py rename to src/tracking_parking/__init__.py diff --git a/raspi/line_detection/result_output/__init__.py b/src/tracking_parking/common/__init__.py similarity index 100% rename from raspi/line_detection/result_output/__init__.py rename to src/tracking_parking/common/__init__.py diff --git a/raspi/common/event_matching.py b/src/tracking_parking/common/event_matching.py similarity index 100% rename from raspi/common/event_matching.py rename to src/tracking_parking/common/event_matching.py diff --git a/raspi/common/frame_stats.py b/src/tracking_parking/common/frame_stats.py similarity index 100% rename from raspi/common/frame_stats.py rename to src/tracking_parking/common/frame_stats.py diff --git a/raspi/common/frame_timing.py b/src/tracking_parking/common/frame_timing.py similarity index 100% rename from raspi/common/frame_timing.py rename to src/tracking_parking/common/frame_timing.py diff --git a/raspi/common/ground_truth.py b/src/tracking_parking/common/ground_truth.py similarity index 99% rename from raspi/common/ground_truth.py rename to src/tracking_parking/common/ground_truth.py index dd91b2b8..ada75d9a 100644 --- a/raspi/common/ground_truth.py +++ b/src/tracking_parking/common/ground_truth.py @@ -8,7 +8,7 @@ from pathlib import Path from typing import Any -from common.frame_timing import sha256_file +from tracking_parking.common.frame_timing import sha256_file @dataclass(frozen=True) diff --git a/raspi/common/run_identity.py b/src/tracking_parking/common/run_identity.py similarity index 100% rename from raspi/common/run_identity.py rename to src/tracking_parking/common/run_identity.py diff --git a/raspi/common/time_windows.py b/src/tracking_parking/common/time_windows.py similarity index 100% rename from raspi/common/time_windows.py rename to src/tracking_parking/common/time_windows.py diff --git a/raspi/common/wandb_logger.py b/src/tracking_parking/common/wandb_logger.py similarity index 100% rename from raspi/common/wandb_logger.py rename to src/tracking_parking/common/wandb_logger.py diff --git a/raspi/line_detection/detection/config.py b/src/tracking_parking/config.py similarity index 100% rename from raspi/line_detection/detection/config.py rename to src/tracking_parking/config.py diff --git a/raspi/line_detection/tests/__init__.py b/src/tracking_parking/detection/__init__.py similarity index 100% rename from raspi/line_detection/tests/__init__.py rename to src/tracking_parking/detection/__init__.py diff --git a/raspi/line_detection/detection/line_crossing.py b/src/tracking_parking/detection/line_crossing.py similarity index 99% rename from raspi/line_detection/detection/line_crossing.py rename to src/tracking_parking/detection/line_crossing.py index 03231aba..b52a5511 100644 --- a/raspi/line_detection/detection/line_crossing.py +++ b/src/tracking_parking/detection/line_crossing.py @@ -6,7 +6,7 @@ import math from dataclasses import dataclass from typing import Tuple, Optional, List -from detection.config import Line +from tracking_parking.config import Line def side_of_line(point: Tuple[float, float], diff --git a/raspi/line_detection/detection/tracker.py b/src/tracking_parking/detection/tracker.py similarity index 99% rename from raspi/line_detection/detection/tracker.py rename to src/tracking_parking/detection/tracker.py index 698fddbf..9ab34739 100644 --- a/raspi/line_detection/detection/tracker.py +++ b/src/tracking_parking/detection/tracker.py @@ -7,7 +7,7 @@ from typing import Dict, Literal, Optional, Tuple, List import time -from detection.line_crossing import LineTransitionState +from tracking_parking.detection.line_crossing import LineTransitionState Confidence = Literal["pending", "high", "normal"] diff --git a/src/tracking_parking/eval/__init__.py b/src/tracking_parking/eval/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/raspi/line_detection/build_accuracy_report.py b/src/tracking_parking/eval/build_accuracy_report.py similarity index 92% rename from raspi/line_detection/build_accuracy_report.py rename to src/tracking_parking/eval/build_accuracy_report.py index 506736bd..5fd8f5e1 100644 --- a/raspi/line_detection/build_accuracy_report.py +++ b/src/tracking_parking/eval/build_accuracy_report.py @@ -9,11 +9,9 @@ from pathlib import Path from typing import Any -sys.path.insert(0, str(Path(__file__).parents[1])) # raspi/(common 共有のため) - -from common.event_matching import PredictedEvent -from common.ground_truth import load_ground_truth -from eval import build_event_accuracy_rows +from tracking_parking.common.event_matching import PredictedEvent +from tracking_parking.common.ground_truth import load_ground_truth +from tracking_parking.eval import build_event_accuracy_rows def collect_eval_rows( diff --git a/raspi/eval/build_event_accuracy_rows.py b/src/tracking_parking/eval/build_event_accuracy_rows.py similarity index 93% rename from raspi/eval/build_event_accuracy_rows.py rename to src/tracking_parking/eval/build_event_accuracy_rows.py index 7acb1951..caf318fb 100644 --- a/raspi/eval/build_event_accuracy_rows.py +++ b/src/tracking_parking/eval/build_event_accuracy_rows.py @@ -7,8 +7,8 @@ from pathlib import Path from typing import Any, Sequence -from common.event_matching import PredictedEvent, match_events -from common.ground_truth import GroundTruth +from tracking_parking.common.event_matching import PredictedEvent, match_events +from tracking_parking.common.ground_truth import GroundTruth def build_eval_row( diff --git a/raspi/eval/update_accuracy_from_sam3.py b/src/tracking_parking/eval/update_accuracy_from_sam3.py similarity index 95% rename from raspi/eval/update_accuracy_from_sam3.py rename to src/tracking_parking/eval/update_accuracy_from_sam3.py index ed976e82..a02f66ff 100644 --- a/raspi/eval/update_accuracy_from_sam3.py +++ b/src/tracking_parking/eval/update_accuracy_from_sam3.py @@ -3,7 +3,7 @@ SAM3 の GT が揃った後に、既存 W&B run の summary に精度指標を後追い書き込むスクリプト(雛形)。 【実行と評価の分離】 -本システムの計測スクリプト(02_run_analysis.py / 04_multi_video_mae.py / line_detection/main.py)は +本システムの計測スクリプト(scripts/run_detection.py)は 速度・台数のみを即時記録し、精度系 summary キーは None で確保している。SAM3 の GT が揃った後に 本スクリプトを独立実行し、W&B API 経由で同一 run の summary を更新する(run の再開はしない)。 @@ -14,17 +14,15 @@ 使い方: # 書き込まずに対象 run と予定値だけ確認 - python update_accuracy_from_sam3.py --input eval_results.csv --dry-run + python -m tracking_parking.eval.update_accuracy_from_sam3 --input eval_results.csv --dry-run # 実際に summary を更新 - python update_accuracy_from_sam3.py --input eval_results.csv + python -m tracking_parking.eval.update_accuracy_from_sam3 --input eval_results.csv """ import argparse import json import sys from pathlib import Path -sys.path.insert(0, str(Path(__file__).parents[1])) # raspi/(common 共有のため) - # ── 設定 ──────────────────────────────────────────────────────────────────── WANDB_PROJECT_PATH = "tracking-parking" # 例: "entity/project" or "project" EVAL_UNIT = "event" # "event" or "detection" diff --git a/src/tracking_parking/output/__init__.py b/src/tracking_parking/output/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/raspi/line_detection/result_output/event_logger.py b/src/tracking_parking/output/event_logger.py similarity index 100% rename from raspi/line_detection/result_output/event_logger.py rename to src/tracking_parking/output/event_logger.py diff --git a/raspi/line_detection/result_output/video_writer.py b/src/tracking_parking/output/video_writer.py similarity index 98% rename from raspi/line_detection/result_output/video_writer.py rename to src/tracking_parking/output/video_writer.py index 1803f13e..764ceb29 100644 --- a/raspi/line_detection/result_output/video_writer.py +++ b/src/tracking_parking/output/video_writer.py @@ -6,8 +6,8 @@ import cv2 import numpy as np from typing import Dict, Tuple -from detection.config import Line -from detection.tracker import VehicleTracker, VehicleState +from tracking_parking.config import Line +from tracking_parking.detection.tracker import VehicleTracker, VehicleState class VideoAnnotator: diff --git a/tests/common/__init__.py b/tests/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/raspi/common/tests/test_event_matching.py b/tests/common/test_event_matching.py similarity index 98% rename from raspi/common/tests/test_event_matching.py rename to tests/common/test_event_matching.py index a2d907d9..6687e246 100644 --- a/raspi/common/tests/test_event_matching.py +++ b/tests/common/test_event_matching.py @@ -1,4 +1,4 @@ -from common.event_matching import GtEvent, PredictedEvent, match_events +from tracking_parking.common.event_matching import GtEvent, PredictedEvent, match_events def test_perfect_match_across_directions(): diff --git a/raspi/common/tests/test_frame_stats.py b/tests/common/test_frame_stats.py similarity index 92% rename from raspi/common/tests/test_frame_stats.py rename to tests/common/test_frame_stats.py index 9a07fceb..f73bc1fc 100644 --- a/raspi/common/tests/test_frame_stats.py +++ b/tests/common/test_frame_stats.py @@ -1,7 +1,7 @@ import numpy as np -from common.frame_stats import compute_frame_stats, compute_timing_stats -from common.frame_timing import FrameTiming +from tracking_parking.common.frame_stats import compute_frame_stats, compute_timing_stats +from tracking_parking.common.frame_timing import FrameTiming def test_basic_stats(): diff --git a/raspi/common/tests/test_frame_timing.py b/tests/common/test_frame_timing.py similarity index 94% rename from raspi/common/tests/test_frame_timing.py rename to tests/common/test_frame_timing.py index 6b2b6f9d..0995b1b9 100644 --- a/raspi/common/tests/test_frame_timing.py +++ b/tests/common/test_frame_timing.py @@ -2,7 +2,7 @@ import pytest -from common.frame_timing import ( +from tracking_parking.common.frame_timing import ( FrameTiming, build_comparison_key, elapsed_timer, @@ -81,7 +81,7 @@ def test_comparison_key_is_stable_and_sensitive_to_conditions(): def test_elapsed_timer_synchronizes_before_and_after(monkeypatch): clock = iter([1.0, 1.025]) - monkeypatch.setattr("common.frame_timing.time.perf_counter", lambda: next(clock)) + monkeypatch.setattr("tracking_parking.common.frame_timing.time.perf_counter", lambda: next(clock)) sync_calls = [] with elapsed_timer(lambda: sync_calls.append("sync")) as elapsed: diff --git a/raspi/common/tests/test_ground_truth.py b/tests/common/test_ground_truth.py similarity index 98% rename from raspi/common/tests/test_ground_truth.py rename to tests/common/test_ground_truth.py index 00f217d0..07c31e98 100644 --- a/raspi/common/tests/test_ground_truth.py +++ b/tests/common/test_ground_truth.py @@ -2,8 +2,8 @@ import pytest -from common.frame_timing import COMPARISON_CONFIG_KEYS -from common.ground_truth import ( +from tracking_parking.common.frame_timing import COMPARISON_CONFIG_KEYS +from tracking_parking.common.ground_truth import ( DEFAULT_TOLERANCE_SEC, GtEvent, GroundTruth, diff --git a/raspi/common/tests/test_run_identity.py b/tests/common/test_run_identity.py similarity index 98% rename from raspi/common/tests/test_run_identity.py rename to tests/common/test_run_identity.py index cbc4fc36..d8ef48bf 100644 --- a/raspi/common/tests/test_run_identity.py +++ b/tests/common/test_run_identity.py @@ -3,7 +3,7 @@ import pytest -from common.run_identity import ( +from tracking_parking.common.run_identity import ( build_condition_key, build_run_identity, canonical_condition_json, diff --git a/raspi/common/tests/test_time_windows.py b/tests/common/test_time_windows.py similarity index 96% rename from raspi/common/tests/test_time_windows.py rename to tests/common/test_time_windows.py index ee21fabf..f9af3597 100644 --- a/raspi/common/tests/test_time_windows.py +++ b/tests/common/test_time_windows.py @@ -2,7 +2,7 @@ import pytest -from common.time_windows import frames_from_seconds +from tracking_parking.common.time_windows import frames_from_seconds @pytest.mark.parametrize( diff --git a/raspi/common/tests/test_wandb_logger.py b/tests/common/test_wandb_logger.py similarity index 99% rename from raspi/common/tests/test_wandb_logger.py rename to tests/common/test_wandb_logger.py index 52a13420..342ef43c 100644 --- a/raspi/common/tests/test_wandb_logger.py +++ b/tests/common/test_wandb_logger.py @@ -5,7 +5,7 @@ import pytest -from common.wandb_logger import ( +from tracking_parking.common.wandb_logger import ( build_exp_key, ExperimentLogger, next_log_boundary, diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..6ac55521 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,13 @@ +"""テストからCLIスクリプトを読めるようにする。 + +src/tracking_parking はeditable installで解決できるが、scripts/ 配下は +パッケージではないため import パスに載らない。個々のテストで sys.path を +いじる代わりに、ここ1箇所へ集約する。 +""" +import sys +from pathlib import Path + +SCRIPTS_DIR = Path(__file__).resolve().parents[1] / "scripts" + +if str(SCRIPTS_DIR) not in sys.path: + sys.path.insert(0, str(SCRIPTS_DIR)) diff --git a/tests/detection/__init__.py b/tests/detection/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/raspi/line_detection/tests/test_confidence_event_wiring.py b/tests/detection/test_confidence_event_wiring.py similarity index 91% rename from raspi/line_detection/tests/test_confidence_event_wiring.py rename to tests/detection/test_confidence_event_wiring.py index 85f01635..d43f320c 100644 --- a/raspi/line_detection/tests/test_confidence_event_wiring.py +++ b/tests/detection/test_confidence_event_wiring.py @@ -1,15 +1,9 @@ import math -import sys -from pathlib import Path - -LINE_ROOT = Path(__file__).parents[1] -sys.path.insert(0, str(LINE_ROOT)) - -from detection.config import Line -from detection.line_crossing import LineCrossingDetector -from detection.tracker import VehicleTracker -from result_output.event_logger import EventLogger +from tracking_parking.config import Line +from tracking_parking.detection.line_crossing import LineCrossingDetector +from tracking_parking.detection.tracker import VehicleTracker +from tracking_parking.output.event_logger import EventLogger LINE1 = Line(start=(0, 0), end=(100, 0)) diff --git a/raspi/line_detection/tests/test_config_time_windows.py b/tests/detection/test_config_time_windows.py similarity index 98% rename from raspi/line_detection/tests/test_config_time_windows.py rename to tests/detection/test_config_time_windows.py index dfef4dfb..94429f3d 100644 --- a/raspi/line_detection/tests/test_config_time_windows.py +++ b/tests/detection/test_config_time_windows.py @@ -5,7 +5,7 @@ """ import pytest -from detection.config import Config +from tracking_parking.config import Config # from_env が必須とする最小限のenv。 REQUIRED = { diff --git a/raspi/line_detection/tests/test_ground_truth_integration.py b/tests/detection/test_ground_truth_integration.py similarity index 80% rename from raspi/line_detection/tests/test_ground_truth_integration.py rename to tests/detection/test_ground_truth_integration.py index 57695da0..7d112c72 100644 --- a/raspi/line_detection/tests/test_ground_truth_integration.py +++ b/tests/detection/test_ground_truth_integration.py @@ -1,23 +1,8 @@ -import importlib.util -import sys -from pathlib import Path - import pytest +import run_detection as line_main -LINE_ROOT = Path(__file__).parents[1] -RASPI_ROOT = LINE_ROOT.parent -sys.path.insert(0, str(LINE_ROOT)) -sys.path.insert(0, str(RASPI_ROOT)) - -spec = importlib.util.spec_from_file_location( - "line_detection_main_ground_truth", LINE_ROOT / "main.py" -) -line_main = importlib.util.module_from_spec(spec) -sys.modules[spec.name] = line_main -spec.loader.exec_module(line_main) - -from common.run_identity import build_condition_key +from tracking_parking.common.run_identity import build_condition_key BASE_RUN_CONFIG = { diff --git a/raspi/line_detection/tests/test_line_crossing.py b/tests/detection/test_line_crossing.py similarity index 97% rename from raspi/line_detection/tests/test_line_crossing.py rename to tests/detection/test_line_crossing.py index 13a502fb..bede44ff 100644 --- a/raspi/line_detection/tests/test_line_crossing.py +++ b/tests/detection/test_line_crossing.py @@ -1,14 +1,7 @@ -import sys -from pathlib import Path - import pytest - -LINE_ROOT = Path(__file__).parents[1] -sys.path.insert(0, str(LINE_ROOT)) - -from detection.config import Line -from detection.line_crossing import ( +from tracking_parking.config import Line +from tracking_parking.detection.line_crossing import ( LineCrossingDetector, LineTransitionState, classify_side, diff --git a/raspi/line_detection/tests/test_run_identity_integration.py b/tests/detection/test_run_identity_integration.py similarity index 76% rename from raspi/line_detection/tests/test_run_identity_integration.py rename to tests/detection/test_run_identity_integration.py index 923da45e..c7a47df8 100644 --- a/raspi/line_detection/tests/test_run_identity_integration.py +++ b/tests/detection/test_run_identity_integration.py @@ -1,23 +1,8 @@ -import importlib.util -import sys -from pathlib import Path - import pytest +import run_detection as line_main -LINE_ROOT = Path(__file__).parents[1] -RASPI_ROOT = LINE_ROOT.parent -sys.path.insert(0, str(LINE_ROOT)) -sys.path.insert(0, str(RASPI_ROOT)) - -spec = importlib.util.spec_from_file_location( - "line_detection_main_identity", LINE_ROOT / "main.py" -) -line_main = importlib.util.module_from_spec(spec) -sys.modules[spec.name] = line_main -spec.loader.exec_module(line_main) - -from common.run_identity import build_condition_key +from tracking_parking.common.run_identity import build_condition_key BASE_RUN_CONFIG = { diff --git a/raspi/line_detection/tests/test_tracker.py b/tests/detection/test_tracker.py similarity index 98% rename from raspi/line_detection/tests/test_tracker.py rename to tests/detection/test_tracker.py index e3b860b1..2c88b839 100644 --- a/raspi/line_detection/tests/test_tracker.py +++ b/tests/detection/test_tracker.py @@ -1,14 +1,8 @@ -import sys import math -from pathlib import Path - -LINE_ROOT = Path(__file__).parents[1] -sys.path.insert(0, str(LINE_ROOT)) - -from detection.config import Line -from detection.line_crossing import LineCrossingDetector -from detection.tracker import VehicleTracker +from tracking_parking.config import Line +from tracking_parking.detection.line_crossing import LineCrossingDetector +from tracking_parking.detection.tracker import VehicleTracker # Line1(入口側、y=0)→Line2(駐車場側、y=50)→駐車場基準点(y=100)の順に並ぶ、 diff --git a/tests/eval/__init__.py b/tests/eval/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/raspi/line_detection/tests/test_build_accuracy_report.py b/tests/eval/test_build_accuracy_report.py similarity index 95% rename from raspi/line_detection/tests/test_build_accuracy_report.py rename to tests/eval/test_build_accuracy_report.py index 3bef4bbf..fd73426b 100644 --- a/raspi/line_detection/tests/test_build_accuracy_report.py +++ b/tests/eval/test_build_accuracy_report.py @@ -1,14 +1,7 @@ import json -import sys from pathlib import Path - -RASPI_ROOT = Path(__file__).parents[2] -LINE_ROOT = RASPI_ROOT / "line_detection" -sys.path.insert(0, str(LINE_ROOT)) -sys.path.insert(0, str(RASPI_ROOT)) - -from build_accuracy_report import build_accuracy_report +from tracking_parking.eval.build_accuracy_report import build_accuracy_report def write_events_file( diff --git a/raspi/eval/tests/test_build_event_accuracy_rows.py b/tests/eval/test_build_event_accuracy_rows.py similarity index 91% rename from raspi/eval/tests/test_build_event_accuracy_rows.py rename to tests/eval/test_build_event_accuracy_rows.py index efa976dc..ae563719 100644 --- a/raspi/eval/tests/test_build_event_accuracy_rows.py +++ b/tests/eval/test_build_event_accuracy_rows.py @@ -1,17 +1,13 @@ import csv import json -import sys from pathlib import Path import pytest -RASPI_ROOT = Path(__file__).parents[2] -sys.path.insert(0, str(RASPI_ROOT)) - -from common.event_matching import PredictedEvent, match_events -from common.ground_truth import GroundTruth, GtEvent -from eval.build_event_accuracy_rows import build_eval_row, write_eval_rows +from tracking_parking.common.event_matching import PredictedEvent, match_events +from tracking_parking.common.ground_truth import GroundTruth, GtEvent +from tracking_parking.eval.build_event_accuracy_rows import build_eval_row, write_eval_rows def make_ground_truth() -> GroundTruth: diff --git a/raspi/eval/tests/test_update_accuracy_from_sam3.py b/tests/eval/test_update_accuracy_from_sam3.py similarity index 91% rename from raspi/eval/tests/test_update_accuracy_from_sam3.py rename to tests/eval/test_update_accuracy_from_sam3.py index 71a75bbb..37388986 100644 --- a/raspi/eval/tests/test_update_accuracy_from_sam3.py +++ b/tests/eval/test_update_accuracy_from_sam3.py @@ -1,12 +1,8 @@ -import sys -from pathlib import Path from types import SimpleNamespace import pytest -sys.path.insert(0, str(Path(__file__).parents[2])) # raspi/ - -from eval.update_accuracy_from_sam3 import AmbiguousRunError, find_run +from tracking_parking.eval.update_accuracy_from_sam3 import AmbiguousRunError, find_run class FakeApi: diff --git a/tests/output/__init__.py b/tests/output/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/raspi/line_detection/tests/test_event_logger.py b/tests/output/test_event_logger.py similarity index 96% rename from raspi/line_detection/tests/test_event_logger.py rename to tests/output/test_event_logger.py index 20a87149..d5aef6bb 100644 --- a/raspi/line_detection/tests/test_event_logger.py +++ b/tests/output/test_event_logger.py @@ -1,12 +1,7 @@ import json -import sys from pathlib import Path - -LINE_ROOT = Path(__file__).parents[1] -sys.path.insert(0, str(LINE_ROOT)) - -from result_output.event_logger import EventLogger +from tracking_parking.output.event_logger import EventLogger TRACKER_SUMMARY = { diff --git a/tests/scripts/__init__.py b/tests/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/raspi/line_detection/tests/test_setup_lines_env.py b/tests/scripts/test_setup_lines_env.py similarity index 98% rename from raspi/line_detection/tests/test_setup_lines_env.py rename to tests/scripts/test_setup_lines_env.py index 13dd0843..f3f1865e 100644 --- a/raspi/line_detection/tests/test_setup_lines_env.py +++ b/tests/scripts/test_setup_lines_env.py @@ -10,7 +10,7 @@ """ import pytest -from line_setup.setup_lines import ( +from setup_lines import ( LINE_ENV_KEYS, apply_line_values, build_line_env_values, diff --git a/yolo_fine_tuning/.gitignore b/training/.gitignore similarity index 100% rename from yolo_fine_tuning/.gitignore rename to training/.gitignore diff --git a/yolo_fine_tuning/requirements.txt b/training/requirements.txt similarity index 100% rename from yolo_fine_tuning/requirements.txt rename to training/requirements.txt diff --git a/yolo_fine_tuning/resorces/create_nuber_plates/.gitignore b/training/resorces/create_nuber_plates/.gitignore similarity index 100% rename from yolo_fine_tuning/resorces/create_nuber_plates/.gitignore rename to training/resorces/create_nuber_plates/.gitignore diff --git a/yolo_fine_tuning/resorces/dataset/background/background_1.png b/training/resorces/dataset/background/background_1.png similarity index 100% rename from yolo_fine_tuning/resorces/dataset/background/background_1.png rename to training/resorces/dataset/background/background_1.png diff --git a/yolo_fine_tuning/resorces/dataset/background/background_2.png b/training/resorces/dataset/background/background_2.png similarity index 100% rename from yolo_fine_tuning/resorces/dataset/background/background_2.png rename to training/resorces/dataset/background/background_2.png diff --git a/yolo_fine_tuning/resorces/dataset/background/background_3.png b/training/resorces/dataset/background/background_3.png similarity index 100% rename from yolo_fine_tuning/resorces/dataset/background/background_3.png rename to training/resorces/dataset/background/background_3.png diff --git a/yolo_fine_tuning/resorces/dataset/background/background_4.png b/training/resorces/dataset/background/background_4.png similarity index 100% rename from yolo_fine_tuning/resorces/dataset/background/background_4.png rename to training/resorces/dataset/background/background_4.png diff --git a/yolo_fine_tuning/resorces/dataset/background/background_5.png b/training/resorces/dataset/background/background_5.png similarity index 100% rename from yolo_fine_tuning/resorces/dataset/background/background_5.png rename to training/resorces/dataset/background/background_5.png diff --git a/yolo_fine_tuning/resorces/dataset/background/background_6.png b/training/resorces/dataset/background/background_6.png similarity index 100% rename from yolo_fine_tuning/resorces/dataset/background/background_6.png rename to training/resorces/dataset/background/background_6.png diff --git a/yolo_fine_tuning/resorces/dataset/background/background_7.png b/training/resorces/dataset/background/background_7.png similarity index 100% rename from yolo_fine_tuning/resorces/dataset/background/background_7.png rename to training/resorces/dataset/background/background_7.png diff --git a/yolo_fine_tuning/resorces/dataset/output_ground_truth/.gitignore b/training/resorces/dataset/output_ground_truth/.gitignore similarity index 100% rename from yolo_fine_tuning/resorces/dataset/output_ground_truth/.gitignore rename to training/resorces/dataset/output_ground_truth/.gitignore diff --git a/yolo_fine_tuning/resorces/dataset/output_png/.gitignore b/training/resorces/dataset/output_png/.gitignore similarity index 100% rename from yolo_fine_tuning/resorces/dataset/output_png/.gitignore rename to training/resorces/dataset/output_png/.gitignore diff --git a/yolo_fine_tuning/resorces/fonts/BIZ-UDGOTHICB.TTC b/training/resorces/fonts/BIZ-UDGOTHICB.TTC similarity index 100% rename from yolo_fine_tuning/resorces/fonts/BIZ-UDGOTHICB.TTC rename to training/resorces/fonts/BIZ-UDGOTHICB.TTC diff --git a/yolo_fine_tuning/resorces/fonts/TrmFontJB.ttf b/training/resorces/fonts/TrmFontJB.ttf similarity index 100% rename from yolo_fine_tuning/resorces/fonts/TrmFontJB.ttf rename to training/resorces/fonts/TrmFontJB.ttf diff --git a/yolo_fine_tuning/src/.gitignore b/training/src/.gitignore similarity index 100% rename from yolo_fine_tuning/src/.gitignore rename to training/src/.gitignore diff --git a/yolo_fine_tuning/src/convert_ground_truth_to_yolo.py b/training/src/convert_ground_truth_to_yolo.py similarity index 98% rename from yolo_fine_tuning/src/convert_ground_truth_to_yolo.py rename to training/src/convert_ground_truth_to_yolo.py index 647de3aa..5cd81dcc 100644 --- a/yolo_fine_tuning/src/convert_ground_truth_to_yolo.py +++ b/training/src/convert_ground_truth_to_yolo.py @@ -8,9 +8,9 @@ HOME_DIR = os.environ["HOME_DIR"] # 定義 -inputPath = HOME_DIR + "/yolo_fine_tuning/resorces/dataset/output_ground_truth" +inputPath = HOME_DIR + "/training/resorces/dataset/output_ground_truth" # outputPath = "./dataset/yolo" -outputPath = HOME_DIR + "/yolo_fine_tuning/yolo" +outputPath = HOME_DIR + "/training/yolo" manifest = "output.manifest" # 学習用と検証用の分割比率 ratio = 0.8 # 8対2に分割する diff --git a/yolo_fine_tuning/src/create_dataset_gpt.py b/training/src/create_dataset_gpt.py similarity index 97% rename from yolo_fine_tuning/src/create_dataset_gpt.py rename to training/src/create_dataset_gpt.py index e183f060..fc0586b7 100644 --- a/yolo_fine_tuning/src/create_dataset_gpt.py +++ b/training/src/create_dataset_gpt.py @@ -25,9 +25,9 @@ COLORS = [(0, 0, 175), (175, 0, 0), (0, 175, 0), (175, 0, 175)] -BACKGROUND_IMAGE_PATH = HOME_DIR + "/yolo_fine_tuning/resorces/dataset/background" -TARGET_IMAGE_PATH = HOME_DIR + "/yolo_fine_tuning/resorces/dataset/output_png" -OUTPUT_PATH = HOME_DIR + "/yolo_fine_tuning/resorces/dataset/output_ground_truth" +BACKGROUND_IMAGE_PATH = HOME_DIR + "/training/resorces/dataset/background" +TARGET_IMAGE_PATH = HOME_DIR + "/training/resorces/dataset/output_png" +OUTPUT_PATH = HOME_DIR + "/training/resorces/dataset/output_ground_truth" S3Bucket = "s3://ground_truth_dataset" manifestFile = "output.manifest" diff --git a/yolo_fine_tuning/src/create_detaset.py b/training/src/create_detaset.py similarity index 98% rename from yolo_fine_tuning/src/create_detaset.py rename to training/src/create_detaset.py index 26769a92..0cd7a952 100644 --- a/yolo_fine_tuning/src/create_detaset.py +++ b/training/src/create_detaset.py @@ -24,9 +24,9 @@ COLORS = [(0, 0, 175), (175, 0, 0), (0, 175, 0), (175, 0, 175)] -BACKGROUND_IMAGE_PATH = HOME_DIR + "/yolo_fine_tuning/resorces/dataset/background" -TARGET_IMAGE_PATH = HOME_DIR + "/yolo_fine_tuning/resorces/dataset/output_png" -OUTPUT_PATH = HOME_DIR + "/yolo_fine_tuning/resorces/dataset/output_ground_truth" +BACKGROUND_IMAGE_PATH = HOME_DIR + "/training/resorces/dataset/background" +TARGET_IMAGE_PATH = HOME_DIR + "/training/resorces/dataset/output_png" +OUTPUT_PATH = HOME_DIR + "/training/resorces/dataset/output_ground_truth" S3Bucket = "s3://ground_truth_dataset" manifestFile = "output.manifest" diff --git a/yolo_fine_tuning/src/create_number_plate.py b/training/src/create_number_plate.py similarity index 78% rename from yolo_fine_tuning/src/create_number_plate.py rename to training/src/create_number_plate.py index 72299e91..779894c3 100644 --- a/yolo_fine_tuning/src/create_number_plate.py +++ b/training/src/create_number_plate.py @@ -2,7 +2,7 @@ import cv2 from dotenv import load_dotenv -from yolo_fine_tuning.src.number_plate import NumberPlate +from training.src.number_plate import NumberPlate load_dotenv() HOME_DIR = os.environ["HOME_DIR"] @@ -23,10 +23,10 @@ category = i for j in range(10000): img = number_plate.generate(category) - # print(HOME_DIR +'/yolo_fine_tuning/resorces/create_nuber_plates/img{i}.{j}.jpg') + # print(HOME_DIR +'/training/resorces/create_nuber_plates/img{i}.{j}.jpg') cv2.imwrite( HOME_DIR - + "/yolo_fine_tuning/resorces/create_nuber_plates/img" + + "/training/resorces/create_nuber_plates/img" + str(i) + "_" + str(j) diff --git a/yolo_fine_tuning/src/fine_tuning.py b/training/src/fine_tuning.py similarity index 89% rename from yolo_fine_tuning/src/fine_tuning.py rename to training/src/fine_tuning.py index b0c3c19d..c2b2a2d4 100644 --- a/yolo_fine_tuning/src/fine_tuning.py +++ b/training/src/fine_tuning.py @@ -13,7 +13,7 @@ model = YOLO("yolov8m.pt") model.train( - data=HOME_DIR + "/yolo_fine_tuning/yolo/dataset.yaml", + data=HOME_DIR + "/training/yolo/dataset.yaml", epochs=5, batch=8, workers=4, diff --git a/yolo_fine_tuning/src/image_upload.py b/training/src/image_upload.py similarity index 100% rename from yolo_fine_tuning/src/image_upload.py rename to training/src/image_upload.py diff --git a/yolo_fine_tuning/src/number_plate.py b/training/src/number_plate.py similarity index 97% rename from yolo_fine_tuning/src/number_plate.py rename to training/src/number_plate.py index e4994827..53884a3e 100644 --- a/yolo_fine_tuning/src/number_plate.py +++ b/training/src/number_plate.py @@ -26,9 +26,9 @@ class NumberPlate: def __init__(self): - self.__fontFace_0 = HOME_DIR + "/yolo_fine_tuning/resorces/fonts/TrmFontJB.ttf" + self.__fontFace_0 = HOME_DIR + "/training/resorces/fonts/TrmFontJB.ttf" self.__fontFace_1 = ( - HOME_DIR + "/yolo_fine_tuning/resorces/fonts/BIZ-UDGOTHICB.TTC" + HOME_DIR + "/training/resorces/fonts/BIZ-UDGOTHICB.TTC" ) black = (0, 0, 0) diff --git a/yolo_fine_tuning/src/testVideo/IMG_6047.jpg b/training/src/testVideo/IMG_6047.jpg similarity index 100% rename from yolo_fine_tuning/src/testVideo/IMG_6047.jpg rename to training/src/testVideo/IMG_6047.jpg diff --git a/yolo_fine_tuning/src/testVideo/IMG_6049.jpg b/training/src/testVideo/IMG_6049.jpg similarity index 100% rename from yolo_fine_tuning/src/testVideo/IMG_6049.jpg rename to training/src/testVideo/IMG_6049.jpg diff --git a/yolo_fine_tuning/src/testVideo/IMG_6058.jpg b/training/src/testVideo/IMG_6058.jpg similarity index 100% rename from yolo_fine_tuning/src/testVideo/IMG_6058.jpg rename to training/src/testVideo/IMG_6058.jpg diff --git a/yolo_fine_tuning/src/testVideo/ocr_test2.mp4 b/training/src/testVideo/ocr_test2.mp4 similarity index 100% rename from yolo_fine_tuning/src/testVideo/ocr_test2.mp4 rename to training/src/testVideo/ocr_test2.mp4 diff --git a/yolo_fine_tuning/src/testVideo/ocr_test_video.mp4 b/training/src/testVideo/ocr_test_video.mp4 similarity index 100% rename from yolo_fine_tuning/src/testVideo/ocr_test_video.mp4 rename to training/src/testVideo/ocr_test_video.mp4 diff --git a/yolo_fine_tuning/src/testVideo/tra-pa_motion_test.mp4 b/training/src/testVideo/tra-pa_motion_test.mp4 similarity index 100% rename from yolo_fine_tuning/src/testVideo/tra-pa_motion_test.mp4 rename to training/src/testVideo/tra-pa_motion_test.mp4 diff --git a/yolo_fine_tuning/src/testVideo/tra-pa_motion_test_multi.mp4 b/training/src/testVideo/tra-pa_motion_test_multi.mp4 similarity index 100% rename from yolo_fine_tuning/src/testVideo/tra-pa_motion_test_multi.mp4 rename to training/src/testVideo/tra-pa_motion_test_multi.mp4 diff --git a/yolo_fine_tuning/src/yolo_motion_tracking.py b/training/src/yolo_motion_tracking.py similarity index 91% rename from yolo_fine_tuning/src/yolo_motion_tracking.py rename to training/src/yolo_motion_tracking.py index f1e4813a..5afcf417 100644 --- a/yolo_fine_tuning/src/yolo_motion_tracking.py +++ b/training/src/yolo_motion_tracking.py @@ -7,8 +7,8 @@ # パス設定 load_dotenv() HOME_DIR = os.environ["HOME_DIR"] -PATH = os.path.join(HOME_DIR, "yolo_fine_tuning/runs/detect/train14/weights/best.pt") -video_path = os.path.join(HOME_DIR, "yolo_fine_tuning/src/testVideo/ocr_test_video.mp4") +PATH = os.path.join(HOME_DIR, "training/runs/detect/train14/weights/best.pt") +video_path = os.path.join(HOME_DIR, "training/src/testVideo/ocr_test_video.mp4") camera_path = 0 # Yoloモデルのロードとビデオキャプチャのセットアップ @@ -17,7 +17,7 @@ # cap = cv2.VideoCapture(0) # 出力動画の設定 -output_path = os.path.join(HOME_DIR, 'yolo_fine_tuning/src/testVideo/output.mp4') +output_path = os.path.join(HOME_DIR, 'training/src/testVideo/output.mp4') fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter(output_path, fourcc, 30.0, (int(cap.get(3)), int(cap.get(4)))) diff --git a/yolo_fine_tuning/src/yolo_motion_tracking_func.py b/training/src/yolo_motion_tracking_func.py similarity index 95% rename from yolo_fine_tuning/src/yolo_motion_tracking_func.py rename to training/src/yolo_motion_tracking_func.py index c6067f9f..c0cc58b6 100644 --- a/yolo_fine_tuning/src/yolo_motion_tracking_func.py +++ b/training/src/yolo_motion_tracking_func.py @@ -9,8 +9,8 @@ def setup_paths(): load_dotenv() HOME_DIR = os.environ["HOME_DIR"] - PATH = os.path.join(HOME_DIR, "yolo_fine_tuning/runs/detect/train14/weights/best.pt") - video_path = os.path.join(HOME_DIR, "yolo_fine_tuning/src/testVideo/tra-pa_motion_test_multi.mp4") + PATH = os.path.join(HOME_DIR, "training/runs/detect/train14/weights/best.pt") + video_path = os.path.join(HOME_DIR, "training/src/testVideo/tra-pa_motion_test_multi.mp4") camera_path = 0 return PATH, camera_path diff --git a/yolo_fine_tuning/src/yolo_motion_tracking_tmp.py b/training/src/yolo_motion_tracking_tmp.py similarity index 93% rename from yolo_fine_tuning/src/yolo_motion_tracking_tmp.py rename to training/src/yolo_motion_tracking_tmp.py index c59d3bbc..5f4d18d7 100644 --- a/yolo_fine_tuning/src/yolo_motion_tracking_tmp.py +++ b/training/src/yolo_motion_tracking_tmp.py @@ -8,10 +8,10 @@ # パス設定 load_dotenv() HOME_DIR = os.environ["HOME_DIR"] -PATH = os.path.join(HOME_DIR, "yolo_fine_tuning/runs/detect/train14/weights/best.pt") -# PATH = "/Users/ycn/Workspace/NUTMEG/tracking-parking/yolo_fine_tuning/test/yolov8l.pt" -# video_path = os.path.join(HOME_DIR, "yolo_fine_tuning/src/testVideo/ocr_test2.mp4") -video_path = os.path.join(HOME_DIR, "yolo_fine_tuning/src/testVideo/tra-pa_motion_test_multi.mp4") +PATH = os.path.join(HOME_DIR, "training/runs/detect/train14/weights/best.pt") +# PATH = "/Users/ycn/Workspace/NUTMEG/tracking-parking/training/test/yolov8l.pt" +# video_path = os.path.join(HOME_DIR, "training/src/testVideo/ocr_test2.mp4") +video_path = os.path.join(HOME_DIR, "training/src/testVideo/tra-pa_motion_test_multi.mp4") camera_path = 0 @@ -24,7 +24,7 @@ cap.set(cv2.CAP_PROP_FPS, 60) # 出力動画の設定 -# output_path = os.path.join(HOME_DIR, 'yolo_fine_tuning/src/testVideo/output.mp4') +# output_path = os.path.join(HOME_DIR, 'training/src/testVideo/output.mp4') # fourcc = cv2.VideoWriter_fourcc(*'mp4v') # out = cv2.VideoWriter(output_path, fourcc, 30.0, (int(cap.get(3)), int(cap.get(4)))) diff --git a/yolo_fine_tuning/test/plate_0.jpg b/training/test/plate_0.jpg similarity index 100% rename from yolo_fine_tuning/test/plate_0.jpg rename to training/test/plate_0.jpg diff --git a/yolo_fine_tuning/test/test_code.py b/training/test/test_code.py similarity index 93% rename from yolo_fine_tuning/test/test_code.py rename to training/test/test_code.py index bf1a2804..2ff111f0 100644 --- a/yolo_fine_tuning/test/test_code.py +++ b/training/test/test_code.py @@ -8,8 +8,8 @@ HOME_DIR = os.environ["HOME_DIR"] # パス設定 -PATH = HOME_DIR + "/yolo_fine_tuning/runs/detect/train14/weights/best.pt" -TEST_FILE = HOME_DIR + "/yolo_fine_tuning/test/test_img_1.jpg" +PATH = HOME_DIR + "/training/runs/detect/train14/weights/best.pt" +TEST_FILE = HOME_DIR + "/training/test/test_img_1.jpg" # 設定 confidence = 0.8 diff --git a/yolo_fine_tuning/test/test_img_1.jpg b/training/test/test_img_1.jpg similarity index 100% rename from yolo_fine_tuning/test/test_img_1.jpg rename to training/test/test_img_1.jpg diff --git a/yolo_fine_tuning/yolo/.gitignore b/training/yolo/.gitignore similarity index 100% rename from yolo_fine_tuning/yolo/.gitignore rename to training/yolo/.gitignore diff --git a/training/yolo/dataset.yaml b/training/yolo/dataset.yaml new file mode 100644 index 00000000..f10458d1 --- /dev/null +++ b/training/yolo/dataset.yaml @@ -0,0 +1,4 @@ +train: /home/ichinose/workspace/tracking-parking/training/yolo/train/ # 訓練データセットのディレクトリ +val: /home/ichinose/workspace/tracking-parking/training/yolo/valid/ # 検証データセットのディレクトリ +nc: 1 # クラス数(例えば、ナンバープレートが1種類のみの場合) +names: ['number_plate'] # クラス名のリスト diff --git a/uv.lock b/uv.lock index e3afa85b..739e40e4 100644 --- a/uv.lock +++ b/uv.lock @@ -1509,7 +1509,7 @@ wheels = [ [[package]] name = "tracking-parking" version = "0.1.0" -source = { virtual = "." } +source = { editable = "." } dependencies = [ { name = "lap" }, { name = "numpy" }, diff --git a/yolo_fine_tuning/hoge.txt b/yolo_fine_tuning/hoge.txt deleted file mode 100644 index 557db03d..00000000 --- a/yolo_fine_tuning/hoge.txt +++ /dev/null @@ -1 +0,0 @@ -Hello World diff --git a/yolo_fine_tuning/yolo/dataset.yaml b/yolo_fine_tuning/yolo/dataset.yaml deleted file mode 100644 index ba7406b4..00000000 --- a/yolo_fine_tuning/yolo/dataset.yaml +++ /dev/null @@ -1,4 +0,0 @@ -train: /home/ichinose/workspace/tracking-parking/yolo_fine_tuning/yolo/train/ # 訓練データセットのディレクトリ -val: /home/ichinose/workspace/tracking-parking/yolo_fine_tuning/yolo/valid/ # 検証データセットのディレクトリ -nc: 1 # クラス数(例えば、ナンバープレートが1種類のみの場合) -names: ['number_plate'] # クラス名のリスト