using System.Collections.Generic; using UnityEngine; namespace ServiceSim { /// /// 回す条件と、書き出す場所。実験に関する数字と場所はすべてこの .asset に置く。 /// 条件を順に回すのは ExperimentRunner(段階2で作る)。 /// [CreateAssetMenu(menuName = "ServiceSim/Experiment Plan", fileName = "ExperimentPlan")] public class ExperimentPlan : ScriptableObject { public struct Condition { public string id; public int aisleWidthIndex; public float aisleWidthM; public CrowdLevel crowd; public int robots; } [Header("この回の名前")] public string runName = "family_v0"; [Header("回す条件")] [Tooltip("細い通路の幅(StoreLayout.aisleWidthOptions の番号)")] public int[] aisleWidthIndices = { 0, 1, 2 }; [Tooltip("混み具合")] public CrowdLevel[] crowdLevels = { CrowdLevel.Empty, CrowdLevel.Normal, CrowdLevel.Busy }; [Tooltip("ロボットの台数")] public int[] robotCounts = { 1, 2 }; [Header("1条件で回す量")] [Tooltip("1条件・1つの種で回す往復の数。台数を合わせた合計")] public int tripsPerCondition = 100; [Tooltip("乱数の種")] public int[] seeds = { 0, 1, 2, 3, 4 }; [Tooltip("時間の進みを速める倍率")] public float timeScale = 1f; [Header("書き出す場所")] [Tooltip("KIOXIA がつながっているかを確かめる場所")] public string kioxiaVolumePath = "/Volumes/KIOXIA"; [Tooltip("測ったデータの置き場所。この下に {YYYYMMDD}_{run_name}/ を作る")] public string kioxiaOutputRoot = "/Volumes/KIOXIA/r2r2r-data/service_sim/unity"; [Header("段階2:1条件だけ試す")] [Tooltip("細い通路の幅(StoreLayout.aisleWidthOptions の番号)")] public int trialAisleWidthIndex = 1; [Tooltip("混み具合")] public CrowdLevel trialCrowd = CrowdLevel.Normal; [Tooltip("ロボットの台数")] public int trialRobots = 2; [Tooltip("乱数の種")] public int trialSeed = 0; [Tooltip("回す往復の数(台数を合わせた合計)。timeScale 1 で20分程度に収まる数にする")] public int trialTrips = 20; [Tooltip("速めるときの倍率")] public float trialTimeScaleFast = 10f; [Tooltip("1コマの時間を固定して回すときの、1コマの長さ(秒)")] public float trialCaptureDeltaTime = 0.02f; [Tooltip("実時間でこれを超えたら打ち切る(秒)。保険")] public float trialMaxWallSeconds = 2400f; [Tooltip("段階2の置き場所。Plastic のワークスペースと git の外にする(測定データとして残さない)")] public string trialOutputRoot = "/Users/shimazueiichi/ServiceSim_tmp"; [Header("v2:1回の実行の区切り")] [Tooltip("1回の実行の長さ(店の中の分)。試算の peak_min と同じ")] public float measureMinutes = 120f; [Tooltip("慣らしの長さ(店の中の分)。この分は計測に数えない")] public float warmupMinutes = 15f; [Header("v2:速く回す")] [Tooltip("段階3で使う1コマの長さ(秒)。0.05 で約24倍速")] public float measureCaptureDeltaTime = 0.05f; [Tooltip("確かめ用に使う1コマの長さ(秒)。検査1・2でこちらと比べる")] public float referenceCaptureDeltaTime = 0.02f; [Tooltip("回している間、カメラを切って描画を止める。計測を速くするため初期値は ON")] public bool disableRenderingWhileRunning = true; [Tooltip("回している間も頭の上の字幕を出す(B11 を一時的に外す)。" + "人がロボットをよける様子を見て絵を撮るときだけ ON にする。計測は遅くなる")] public bool showLabelsWhileRunning; [Tooltip("キッチンの屋根のモニターを出す(2026-10-23)。" + "板1枚と文字だけなので、測る回でも出す。倍速が落ちるなど問題があれば切る")] public bool showKitchenMonitor = true; [Header("v2:段階3(18条件 × 種)")] [Tooltip("段階3の置き場所。この下に {YYYYMMDD}_{run_name}/{condition}_s{seed}/ を作る")] public string stage3OutputRoot = "/Users/shimazueiichi/ServiceSim_tmp/stage3"; [Tooltip("すでに書き出してある条件は飛ばす(途中から再開する)")] public bool stage3Resume = true; [Header("スクリーンショット")] [Tooltip("段階1の報告に使う画面の置き場所")] public string screenshotDir = "/Users/shimazueiichi/Library/Mobile Documents/com~apple~CloudDocs/デスクトップ/ADAPT DESIGN/じぶんの/PixelstoMotors/デジタルツイン学習R2R2R/サービスロボット導入シミュレーション/unity/screens"; public int screenshotWidth = 1920; public int screenshotHeight = 1080; /// 条件の数(通路の幅 × 混み具合 × 台数) public int ConditionCount { get { int w = aisleWidthIndices != null ? aisleWidthIndices.Length : 0; int c = crowdLevels != null ? crowdLevels.Length : 0; int r = robotCounts != null ? robotCounts.Length : 0; return w * c * r; } } /// 18条件を順に並べる public List BuildConditions(StoreLayout layout) { List list = new List(); if (aisleWidthIndices == null || crowdLevels == null || robotCounts == null) return list; for (int wi = 0; wi < aisleWidthIndices.Length; wi++) { int widthIndex = aisleWidthIndices[wi]; float widthM = 0f; if (layout != null && layout.aisleWidthOptions != null && widthIndex >= 0 && widthIndex < layout.aisleWidthOptions.Length) widthM = layout.aisleWidthOptions[widthIndex]; for (int ci = 0; ci < crowdLevels.Length; ci++) { for (int ri = 0; ri < robotCounts.Length; ri++) { Condition c = new Condition(); c.aisleWidthIndex = widthIndex; c.aisleWidthM = widthM; c.crowd = crowdLevels[ci]; c.robots = robotCounts[ri]; c.id = ConditionId(widthM, c.crowd, c.robots); list.Add(c); } } } return list; } public static string ConditionId(float aisleWidthM, CrowdLevel crowd, int robots) { int mm = Mathf.RoundToInt(aisleWidthM * 100f); return "w" + mm.ToString("000") + "_" + CrowdName(crowd) + "_r" + robots.ToString(); } public static string CrowdName(CrowdLevel crowd) { switch (crowd) { case CrowdLevel.Empty: return "empty"; case CrowdLevel.Busy: return "busy"; default: return "normal"; } } } }