見出し画像

『FEELCYCLE 次世代AI実装大全』――Python・Rust・TypeScript・Swift・SQL・AWS・Hardwareで、人・音楽・身体・空間・会社を接続する(第6回)(第Ⅴ部 Physical Reality――コードを音・光・身体・空間へ変換する 第6章 Physical Runtime――音響・照明・センサー・機材を動かす)

第Ⅴ部 Physical Reality

――コードを音・光・身体・空間へ変換する

ここまでFEELCYCLE AIは、Softwareとして大きく完成へ近づいてきた。

Python Intelligenceは、Music、Program、身体運動、Member、Instructor、Sessionを理解する。

Rust Runtimeは、それらを45分の時間の中で低遅延かつ安全に実行する。

TypeScript Interfaceは、Machine StateをInstructor、Studio、本部が理解できるMeaningへ変換する。

Swift Mobileは、MemberとInstructorの日常を45分の前後へ接続する。

SQLとAWSは、一回のRealityをMemoryとして保持し、次の45分へLearningを継承する。

しかし、ここまでのSystemには決定的な境界が残されている。

CodeとRealityの境界である。

Databaseに、

lighting_state = peak

と記録されても、Studioが明るくなるわけではない。

Rust Runtimeが、

audio_command = play

を生成しても、Speakerから音が出るわけではない。

Python Intelligenceが、

collective_state = synchronized

と推定しても、実際の空間を観測するSensorが存在しなければ、その推定をRealityと接続できない。

SoftwareだけではPhysical Experienceは成立しない。

必要なのは、

Code

Signal

Device

Physical Change

というTranslationである。

同時に逆方向も必要になる。

Physical Reality

Sensor

Signal

Data

Runtime / Intelligence

である。

つまりPhysical Runtimeは、Softwareから物理世界へ命令を送る一方向のControllerではない。

Software

Physical Reality

を接続する双方向の実行層である。

第Ⅴ部では、この境界を実装する。

Audio System。

Lighting System。

Microphone。

Sensor。

Edge Device。

Network。

Controller。

Protocol。

そしてStudio Integration。

これらを一つのPhysical Runtimeへ接続する。

最終的には、

Python

Rust

Physical Runtime

音・光・空間

だけではなく、

音・光・身体・空間

Sensor

Rust

Python

まで一周させる。

1 Physical Realityとは何か

本書でいうPhysical Realityとは、単にHardwareの集合ではない。

FEELCYCLEの45分が実際に成立する場である。

その中心には、

Music。

身体。

暗闇。

光。

Instructor。

Member。

他者。

空間。

時間。

が存在する。

Technologyは、このRealityを置き換えるものではない。

Realityを支える。

観測する。

必要な変化を実行する。

そして、なるべく人間のAttentionを奪わず背景へ退く。

したがってPhysical Runtimeの設計原則は、

Technology First

ではなく、

Experience First

である。

どのHardwareが最先端かから始めない。

45分に何が必要かから始める。

2 Software CommandからPhysical Actionへ

Rust RuntimeではSemantic Commandを扱ってきた。

たとえばLightingなら、

Dark
Focus
Build
Peak
Release

である。

これはHardware Commandではない。

実際のLighting Systemは、

DMX。

Art-Net。

sACN。

Vendor API。

など具体的なProtocolを要求する可能性がある。

そこでTranslation Layerを置く。

Rust Runtime

Semantic Command

Physical Adapter

Hardware Protocol

Controller

Lighting Fixture

重要なのは、Rust Runtime自身に特定メーカーのHardware仕様を埋め込まないことである。

Runtimeは、

Peakにしたい

と指示する。

Physical Adapterが、

どのFixtureを。

どのChannelへ。

どの値で。

動かすかを決定する。

これによってHardware交換時にもIntelligenceやRuntimeの中心を維持できる。

3 Audioも同じである

Music Playbackを考える。

Runtimeに必要なのは、

Trackを開始する。

停止する。

Positionを確認する。

同期する。

Volume Stateを変更する。

などの意味操作である。

一方、Physical Audio Systemには、

Audio Interface。

Mixer。

DSP。

Amplifier。

Speaker。

Playback Computer。

Network Audio。

などが存在する。

したがって、

Music Runtime

Playback Command

Audio Adapter

Playback Engine

Audio Interface / DSP

Amplifier

Speaker

Studio

と分離する。

Music Fileの再生と、Studio全体の音響を同一Componentにしない。

4 Physical Runtimeの中心はEdgeに置く

StudioのAudioやLightingを、毎回AWSへ送ってから制御する設計にはしない。

Cloudへの往復にはNetwork DelayとFailureがある。

Physical RealityはStudio内で完結できなければならない。

そこでEdge Deviceを置く。

概念的には、

AWS Cloud
  ↕
Studio Edge
  ├── Rust Runtime
  ├── Audio Adapter
  ├── Lighting Adapter
  ├── Sensor Gateway
  └── Local Buffer
         ↓
   Physical Devices

となる。

CloudはMemoryとLearningを担当する。

EdgeはRealityを担当する。

この境界は重要である。

Cloud Intelligenceが高度になっても、

Physical Execution Authority

はLocalへ残す。

5 Edge Deviceの役割

Edge Deviceには最低限、

Rust Runtime。

Device Adapter。

Network Client。

Local Event Buffer。

Health Monitor。

Fallback Configuration。

を持たせる。

Edge Runtime

├── Clock
├── Event Engine
├── Safety Manager
├── Audio Adapter
├── Lighting Adapter
├── Sensor Gateway
├── Device Health
└── Cloud Sync

これによってStudioはCloudから一時的に切り離されても継続できる。

Cloud Failure

Physical Failure

である。

6 Audio System

第6章では、まずAudio Systemを設計する。

扱う対象はMusic Playerだけではない。

Playback。

Mixing。

Signal Routing。

Output。

Monitoring。

Synchronization。

Fallback。

である。

FEELCYCLE AIがMusicを理解しても、Physical Audioが不安定ならExperienceは崩れる。

したがってAudio Runtimeでは、

音質

だけでなく、

Latency。

Continuity。

Synchronization。

Failure Recovery。

を扱う。

MusicはFEELCYCLEの時間基準でもある。

Audio SystemはPhysical RuntimeのClockと密接に接続される。

7 Lighting System

LightingはFEELCYCLEの空間状態を変える。

しかしAIがFixtureを一つずつ直接操作するのではない。

Semantic Stateを使う。

AI / Program

Lighting Intent

Semantic Scene

Physical Adapter

Lighting Controller

Fixtures

たとえば、

Peak

という一つのStateが、

複数Fixture。

Intensity。

Position。

Timing。

Transition。

へ展開される。

これによってProgram CreatorはHardware ParameterではなくExperience Meaningを設計できる。

8 MicrophoneとSensor

次にRealityを観測する。

Microphone。

Environmental Sensor。

Device Telemetry。

Optional Wearable Aggregate。

などである。

しかしSensor Architectureでも、

More Sensors

Better Reality

である。

必要な観測だけを使う。

特にMicrophoneは、音響状態やCollective Featureを分析できる可能性がある一方、音声を扱うためPrivacy上の慎重な設計が必要になる。

目的が音響Featureなら、

Raw Audio

Edge Processing

Feature

Raw Audio破棄

という設計を優先できる。

人の会話内容をCloudへ記録する必要はない。

9 SensorはRealityではない

Sensorが示した値はObservationである。

Realityそのものではない。

Microphoneが音圧を測る。

Motion SensorがMovementを推定する。

WearableがHeart Rateを測る。

しかし、

Sensor Value

Human Experience

である。

したがって、

Observed。

Estimated。

Reported。

を分離するMemory PrincipleをPhysical Runtimeでも維持する。

10 Protocol

Physical Runtimeには複数のProtocolが存在する可能性がある。

Audio Network。

Lighting Control。

Bluetooth。

OSC。

MIDI。

HTTP。

WebSocket。

Vendor Protocol。

しかしFEELCYCLE Runtimeがそれらを直接すべて知る必要はない。

Adapter Patternを使う。

trait PhysicalAdapter {
   fn health(
       &self
   ) -> DeviceHealth;
   fn safe_state(
       &mut self
   ) -> Result<(), DeviceError>;
}

Lighting Adapter。

Audio Adapter。

Sensor Adapter。

がそれぞれ実装する。

この共通境界によって、Physical DeviceをRuntimeへ統合できる。

11 Device Identity

HardwareにもIdentityを持たせる。

device_id
device_type
studio_id
hardware_reference
firmware_version
configuration_version
health_state

これによって、

何が故障したのか。

どのFirmwareだったのか。

どのSessionへ影響したのか。

をMemoryへ戻せる。

Physical DeviceもFEELCYCLE AIのVersioned Systemの一部になる。

12 Physical State

Runtime Stateとは別にPhysical Stateを持つ。

Desired State

Command

Physical Device

Observed State

たとえば、

Desired Lighting = Peak

でも、

Actual Lighting Controller = Unavailable

かもしれない。

したがって、

Desired

Observed

を分ける。

SoftwareがCommandを送ったからといって、Physical Realityが変化したと仮定してはいけない。

Device ResponseをVerificationへ戻す。

13 Command Acknowledgement

Physical Commandには結果を返す。

概念的には、

struct DeviceCommandResult {
   command_id: uuid::Uuid,
   accepted: bool,
   applied: bool,
   message: Option<String>,
}

とする。

これによって、

Command Sent。

Command Accepted。

Command Applied。

を分離できる。

Human IntentとPhysical Executionを分離したのと同じである。

14 Latency Budget

Physical SystemではLatencyがより重要になる。

MusicとLightingのTiming。

SensorからRuntimeまでのDelay。

Network ControllerのResponse。

それぞれにLatencyが存在する。

したがって、

Detection Latency
+
Processing Latency
+
Network Latency
+
Device Latency
=
End-to-End Latency

として測定する。

低遅延とはRustの処理速度だけではない。

Physical Chain全体で決まる。

15 Synchronization

第Ⅱ部ではSoftware上でMusic、Movement、Lightingを同期した。

第Ⅴ部ではPhysical Deviceまで含めて同期する。

Music Timeline
     ↓
Rust Runtime
     ↓
┌─────┼─────┐
↓     ↓     ↓
Audio Light Cue
↓     ↓
Physical Output

しかしAudio DeviceとLighting DeviceのResponse Timeは異なる。

そこでDeviceごとのLatency Compensationを持てる。

Target Physical Time
-
Expected Device Latency
=
Command Dispatch Time

Program TimeとCommand Timeを分離する。

これによってPhysical Outputを同じMomentへ近づける。

16 Fallback

Physical RuntimeではFallbackがより具体的になる。

Lighting Controllerが落ちる。

Sensorが切れる。

Network Audioが失われる。

Edge Deviceの一部が異常になる。

その場合、

Safe Static Lighting。

Manual Audio Control。

Sensor-independent Runtime。

Local Program Execution。

などへ移る。

つまり、

Physical Failure

Fallback

Reduced Experience

とし、
Physical Failure

Uncontrolled State
にはしない。
17 Manual Override
Physical RealityではHuman Overrideが最も重要になる。
Instructor。
Studio Operator。
設備側Operator。
がAutomationを切り離せる。
AIが、
Lighting = Peak
を提案していても、人間がManualへ切り替えたらAutomationは停止する。
Human AuthorityをSoftwareだけでなくPhysical Controlにも保持する。
理想的には、重要なSafety ControlはSoftware UIだけに依存しない。
Physical Control Surfaceや設備側Safety Mechanismも残す。
18 Safety Boundary
FEELCYCLE AIはPhysical Equipmentを制御する以上、安全設計を最優先にする。
特に、
過度な音量。
危険なLighting State。
予期しない急変。
Device暴走。
Network異常。
Software Failure。
などを考慮する。
AIの出力を直接Hardwareへ無制限に流さない。
```text
AI Suggestion

Policy

Runtime Validation

Safety Constraint

Physical Adapter

Hardware

とする。

Safety Constraintを最後の境界として置く。

19 Physical ConfigurationはVersion化する

Studio Configurationも変化する。

Speaker。

Mixer。

Lighting Fixture。

Sensor。

Network。

Edge Device。

Firmware。

だからConfiguration Versionを記憶する。

Session Memoryには、

Program Version。

AI Model Version。

Studio Configuration Version。

を対応させる。

すると後から、

Program v12
AI Model v4.3
Studio Configuration v8

という実行条件を復元できる。

これはPhysical Realityの再現性に重要である。

20 SimulationからPhysical Realityへ

Program BuilderではSimulationを行った。

しかしSimulationはRealityではない。

次の段階は、

Simulation

Staging Hardware

Controlled Test

Production Studio

である。

いきなりProduction Sessionへ新しいPhysical Control AlgorithmをDeployしない。

Softwareと同様に、

Develop

Test

Verify

Deploy

をHardware Integrationでも維持する。

21 Digital TwinではなくPhysical Verification

Studioの状態をSoftware上でModel化することは有用である。

しかしSoftware Modelが一致しているからPhysical Realityも一致しているとは限らない。

最終的にはDevice Response、Sensor、Human Verificationによって確認する。

Model

Reality

という本書の最初からの原則をPhysical Runtimeでも保持する。

22 Studio Integration

Audio Systemだけ正常でもFEELCYCLEは成立しない。

Lightingだけでも成立しない。

Sensorだけでも成立しない。

最終的には、

Audio。

Lighting。

Sensor。

Network。

Edge。

Rust Runtime。

Instructor Console。

Human Operation。

を同じStudioへ統合する。

                  Studio
       Python Intelligence
                ↕
           Rust Runtime
                ↓
      ┌─────────┼─────────┐
      ↓         ↓         ↓
    Audio    Lighting   Sensor
      ↓         ↓         ↑
     音         光       空間
      └─────────┼─────────┘
                ↓
      Member / Instructor
                ↓
             Reality

そしてRealityから再びDataが戻る。

23 Physical RuntimeのMemory

Physical Realityも記憶する。

Command。

Device Response。

Health State。

Fallback。

Manual Override。

Latency。

Configuration Version。

これらをSession IDへ接続する。

これによって、

「AIは正しかったがHardwareが実行しなかった」

「Programは正しかったがNetworkにDelayがあった」

という原因分解が可能になる。

Learningの精度が上がる。

24 Physical Runtimeと会社

Physical Runtime DataはCompany Dashboardへ集約できる。

しかしCompany側にRaw Sensorや詳細なRealtime Controlを必要以上に見せない。

Studioごとの、

Runtime Availability。

Device Failure。

Fallback Rate。

Configuration。

Maintenance Need。

などにAggregationする。

Companyの目的はStudioを遠隔から細かく支配することではない。

Network全体のResilienceを理解することである。

25 Physical Intelligence

ここまで進むと、FEELCYCLE AIは単なるSoftware AIではなくなる。

Musicを解析する。

Runtimeを実行する。

人間へ意味を返す。

Memoryから学ぶ。

そしてPhysical Spaceへ作用する。

しかし、これを「AIがStudioを支配する」と理解してはいけない。

正確には、

AI
+
Runtime
+
Hardware
+
Human
+
Reality

が一つのSystemになる。

HumanをSystemから外さない。

26 FEELCYCLE Physical Runtimeの最小条件

第Ⅴ部で完成させるPhysical Runtime v0.1には、少なくとも次の条件が必要になる。

AudioへCommandを送れる。

LightingへSemantic Stateを送れる。

SensorからObservationを受け取れる。

Device Healthを取得できる。

EdgeでLocal Runtimeを動かせる。

Cloud Failure時にも継続できる。

Device Failure時にFallbackできる。

HumanがManual Overrideできる。

Physical Execution ResultをAuditできる。

Session Memoryへ戻せる。

この一周が成立すれば、SoftwareとPhysical Realityの境界は接続されたと言える。

27 CodeからRealityへ

ここまで本書は長い距離を移動してきた。

最初は二冊の言語化だった。

そこからDataへ変換した。

Pythonによって理解した。

Rustによって時間の中で実行した。

TypeScriptとSwiftで人間へ接続した。

SQLとAWSでMemoryにした。

そしてここから、

Data

Code

Signal

Hardware

Physical Reality

へ進む。

同時に、

Physical Reality

Sensor

Data

Memory

Learning

という逆方向を成立させる。

28 Physical Realityは最後ではない

Hardwareへ出たから実装が完了するわけではない。

Physical RealityはVerificationの入口である。

Softwareが考えたことが、Realityではどうなったのか。

予定したTimingは成立したか。

Lightingは意図した空間を生成したか。

Sensorは何を観測したか。

Instructorはどう判断したか。

Memberはどう経験したか。

Realityが答えを返す。

したがって、

Implementation

Reality

Verification

である。

29 第Ⅴ部の役割

第Ⅴ部では、このPhysical Translationを具体化する。

まずAudio Systemを実装する。

次にLighting Systemを実装する。

Microphone、Sensor、Edge Deviceを接続する。

Network、Controller、Protocolを統合する。

そしてStudio Integrationを行う。

最後に、

FEELCYCLE Physical Runtime v0.1

として、

Code

Physical Reality

の双方向循環を完成させる。

30 Realityへ出る

FEELCYCLE AIは、ここから画面の中だけには存在しない。

Speakerから音が出る。

光が変化する。

Sensorが空間を観測する。

Edge Runtimeが時間を保持する。

人間が判断する。

身体が動く。

45分がRealityになる。

AIの価値は、モデルの精度だけでは決まらない。

現実へ出たときに、

人間のExperienceを壊さず。

安全性を損なわず。

創造性を奪わず。

必要な瞬間だけ支援し。

そしてRealityから再び学べるか。

によって決まる。

第Ⅴ部では、その最終境界へ進む。

SoftwareをRealityへ。

Codeを音へ。

Stateを光へ。

Dataを空間へ。

そして再びRealityをDataへ。

FEELCYCLE AIを、実際の45分が生まれるStudioへ実装する。
第6章 Physical Runtime

――音響・照明・センサー・機材を動かす

第1節 AIから物理空間へ

ここまでFEELCYCLE AIは、Softwareの内部では一つのSystemとして動き始めている。

Python IntelligenceがMusicや45分を理解する。

Rust Runtimeが時間を保持し、Eventを処理する。

TypeScriptとSwiftが人間とのInterfaceを形成する。

SQLとAWSが、一回の45分をMemoryとして次へ接続する。

しかし、FEELCYCLEのRealityはSoftwareの中には存在しない。

最終的に存在するのはStudioである。

Speakerから音が出る。

Lightingが空間を変える。

Instructorの声が届く。

Memberの身体が動く。

複数の人間が一つの時間を共有する。

そして45分がRealityとして成立する。

したがってFEELCYCLE AIを完成させるには、

Digital State

Physical State

へのTranslationが必要になる。

本章では、この変換を担当する実行系をPhysical Runtimeと呼ぶ。

Physical RuntimeとはHardwareそのものではない。

AI、Software、Network、Controller、Device、Sensor、人間を、Physical Realityの中で安全に接続するExecution Layerである。

基本構造は、

Python Intelligence
       ↓
    Intent
       ↓
Rust Runtime
       ↓
Semantic Command
       ↓
Physical Runtime
       ↓
Device Protocol
       ↓
Hardware
       ↓
Physical Reality

となる。

しかし、これは半分でしかない。

Physical RealityからSoftwareへ戻る経路も必要である。

Physical Reality
       ↓
Sensor / Device
       ↓
Observation
       ↓
Physical Runtime
       ↓
Rust Runtime
       ↓
Python Intelligence

したがって全体は、

AI

Runtime

Physical Reality

Observation

AI


という循環になる。

この双方向性が重要である。

AIがCommandを出しただけではRealityが変わったとは言えない。

Hardwareが実際に応答したか。

音が出ているか。

Lighting ControllerがStateを受け取ったか。

Sensorが何を観測しているか。

それを再びSystemへ返さなければならない。

つまり、

Command

Reality

である。

1 Physical Runtimeの責任

Physical Runtimeには、少なくとも五つの責任がある。

第一に、Semantic CommandをHardware Commandへ翻訳する。

第二に、複数Deviceを同じ時間上で同期する。

第三に、Physical Deviceの状態を観測する。

第四に、Failure時に安全な状態へ縮退する。

第五に、実行結果をRuntimeとMemoryへ返す。

この五つを一つにすると、

Translate
Synchronize
Observe
Protect
Verify

となる。

Physical Runtimeは、単なるDevice Driverの集合ではない。

Reality Execution Systemである。

2 Semantic Command

FEELCYCLE AIでは、AIにHardware固有の値を直接生成させない。

たとえばLightingを考える。

AIが、

「DMX Channel 17を183に変更し、Channel 42を221にする」

と直接出力するArchitectureは避ける。

AIやProgram Builderが扱うのは、

Dark
Focus
Build
Peak
Release

のようなSemantic Stateである。

Rust側では、

#[derive(
   Debug,
   Clone,
   serde::Serialize,
   serde::Deserialize,
)]
enum LightingIntent {
   Dark,
   Focus,
   Build,
   Peak,
   Release,
}

と表現できる。

これをPhysical Adapterへ渡す。

struct LightingCommand {
   intent: LightingIntent,
   target_time_seconds: f64,
}

Physical Adapterが、そのStudioの設備に応じて具体的なHardware Commandへ変換する。

つまり、

Meaning

Adapter

Protocol

Device

である。

これによってIntelligenceとHardwareを分離できる。

3 Hardware Abstraction

StudioによってEquipmentが異なる可能性がある。

Lighting Controller A。

Lighting Controller B。

異なるAudio Interface。

異なるSensor Gateway。

それでもRust Runtimeそのものを書き換え続ける必要はない。

共通Interfaceを作る。

#[async_trait::async_trait]
trait LightingAdapter:
   Send + Sync
{
   async fn apply(
       &self,
       command: LightingCommand,
   ) -> Result<
       DeviceExecutionResult,
       PhysicalError,
   >;
   async fn health(
       &self,
   ) -> DeviceHealth;
}

Audioにも同じ考え方を使う。

#[async_trait::async_trait]
trait AudioAdapter:
   Send + Sync
{
   async fn play(
       &self,
       command: AudioCommand,
   ) -> Result<
       DeviceExecutionResult,
       PhysicalError,
   >;
   async fn stop(
       &self,
   ) -> Result<
       DeviceExecutionResult,
       PhysicalError,
   >;
   async fn health(
       &self,
   ) -> DeviceHealth;
}

Physical Runtimeが共通Interfaceを見る。

Vendor固有ImplementationはAdapterの裏側へ隠す。

4 Physical Command

Hardwareへ送るCommandにはIdentityを持たせる。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
struct PhysicalCommand {
   command_id:
       uuid::Uuid,
   session_id:
       uuid::Uuid,
   device_id:
       uuid::Uuid,
   command_type:
       String,
   target_time_seconds:
       f64,
   payload:
       serde_json::Value,
}

なぜcommand_idが必要なのか。

同じCommandを重複実行しないため。

Auditするため。

Device Responseと対応させるため。

Latencyを測るため。

である。

Physical RealityでもIdentityが必要になる。

5 Commandと結果を分離する

Commandを送信したらResultを受け取る。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
struct DeviceExecutionResult {
   command_id:
       uuid::Uuid,
   accepted:
       bool,
   applied:
       bool,
   applied_at_seconds:
       Option<f64>,
   message:
       Option<String>,
}

ここで、

accepted

と、

applied

を分ける。

ControllerがCommandを受け取った。

しかしFixtureが実際に変化したかは別である。

したがって、

Sent

Accepted

Applied

Observed

という段階を持つ。

Physical Verificationを強くするならSensor Observationまで接続する。

6 Desired StateとObserved State

Physical Runtimeでは二種類のStateを持つ。

Desired State。

Observed State。

である。

#[derive(Debug, Clone)]
struct PhysicalState {
   desired_lighting:
       Option<LightingIntent>,
   observed_lighting:
       Option<String>,
   audio_active:
       Option<bool>,
   sensor_health:
       DeviceHealth,
}

例えば、

Desired Lighting
=
Peak
Observed Device State
=
Unavailable

という状態があり得る。

Software上のStateだけをRealityとみなさない。

これは本書全体で繰り返してきた、

Reality

Representation

という境界のPhysical版である。

7 Edge Runtime

Physical Executionの中心はStudio側に置く。

AWSへCommandを送り、Cloudから返ってきた答えでLightingを制御するArchitectureにはしない。

Studio内のEdge DeviceでRust Runtimeを動かす。

Studio Edge

├── Rust Runtime
├── Physical Adapters
├── Local Program
├── Local State
├── Safety Policy
├── Local Event Buffer
└── Cloud Synchronizer

これによってCloudが失われても45分を継続できる。

Cloud Connected
→ Normal
Cloud Disconnected
→ Local Runtime Continues

Physical Realityの時間はCloud Availabilityに従属させない。

8 Edge Deviceの候補

実装機材は導入規模によって変わる。

概念上は、

産業用小型PC。

Intel / AMD系Mini PC。

ARM系Edge Computer。

Studio Rack内Computer。

などを利用できる。

重要なのは製品名より要件である。

必要なのは、

Rust Runtimeを安定稼働できる。

有線Networkを利用できる。

必要なUSB / Audio / Control Interfaceへ接続できる。

Local Storageを持つ。

自動起動できる。

Watchdogを持てる。

遠隔Monitoring可能。

という条件である。

Physical RuntimeではConsumer DeviceをそのままSafety-Critical Coreにするのではなく、運用継続性を含めて選択する。

9 Real-TimeとSoft Real-Time

FEELCYCLE Runtimeは、工場RobotのEmergency ControlのようなHard Real-Time Systemとは目的が異なる。

しかしMusicとLightingの同期では、人間が違和感を感じない程度の時間精度が必要になる。

したがって、

Soft Real-Time

として設計する。

目標時刻を明示する。

struct ScheduledPhysicalCommand {
   target_time_seconds:
       f64,
   dispatch_time_seconds:
       f64,
   command:
       PhysicalCommand,
}

Device Latencyを予測してDispatch Timeを調整する。

Target Physical Time
-
Estimated Device Latency
=
Dispatch Time

である。

10 Latency Profile

DeviceごとにLatency Profileを持つ。

#[derive(
   Debug,
   Clone,
)]
struct DeviceLatencyProfile {
   device_id:
       uuid::Uuid,
   expected_ms:
       f64,
   p95_ms:
       f64,
   maximum_ms:
       f64,
}

たとえばLighting Controllerが平均12msで反応し、Audio Commandが平均4msなら、同じPhysical Momentを狙ってDispatch Timeを変える。

Software上で同時送信することと、Physical Realityで同時になることは同じではない。

11 End-to-End Latency

Physical Runtimeで測定すべきLatencyは一つではない。

AI / Program Decision

Runtime Processing

Command Dispatch

Network

Controller

Device

Physical Output

全体のDelayを見る。

概念的には、

End-to-End Latency

Runtime Processing
+
Network
+
Controller
+
Device Response

である。

この値をSession Memoryへ戻せば、StudioごとのTiming Differenceを学習できる。

12 Clock

複数Deviceを同期するにはClockも重要になる。

Runtime側にSession Clockがある。

Device側にもInternal Clockがある場合がある。

Network上の複数Computerを使うならClock Synchronizationが必要になる。

概念上、

Master Session Clock

Edge Runtime

Timestamped Command

Device

とする。

重要なのは、

「Commandを受け取った瞬間に実行」

だけに依存しないことである。

ProtocolやDeviceがTimestamp Executionを支援するなら利用できる。

支援しないDeviceではEdge側のDispatch Precisionを高める。

13 Physical Event Engine

Software EventをPhysical Commandへ変換する。

async fn handle_physical_event(
   event: RuntimeEvent,
   lighting:
       &dyn LightingAdapter,
   audio:
       &dyn AudioAdapter,
) -> Result<(), PhysicalError> {
   match event.event_type {
       EventType::LightingCue => {
           let command =
               translate_lighting_event(
                   event
               )?;
           lighting
               .apply(command)
               .await?;
           Ok(())
       }
       EventType::MusicSectionChange => {
           let command =
               translate_audio_event(
                   event
               )?;
           audio
               .play(command)
               .await?;
           Ok(())
       }
       _ => Ok(()),
   }
}

Runtime EventとDevice Protocolの間にTranslation Functionを置く。

14 AIを直接Hardwareへ接続しない

Python IntelligenceからDeviceへ直接Commandを送らせない。

基本経路は、

Python Intelligence

Prediction / Suggestion

Human / Policy

Rust Runtime

Safety Validation

Physical Runtime

Device

とする。

Programにあらかじめ定義されたAutomationであっても、

Rust Runtime

Safety

Physical Adapter

を通す。

AI Outputの自由度とPhysical Hardwareの自由度を同一にしない。

15 Safety Envelope

Physical Commandには許容範囲を設定する。

LightingのTransition速度。

Audio Level。

Device Operation。

などである。

概念的に、

struct SafetyEnvelope {
   max_audio_level:
       f64,
   max_lighting_change_rate:
       f64,
   allowed_devices:
       std::collections::HashSet<
           uuid::Uuid
       >,
}

とする。

Validationする。

fn validate_physical_command(
   command:
       &PhysicalCommand,
   safety:
       &SafetyEnvelope,
) -> Result<(), PhysicalError> {
   if !safety
       .allowed_devices
       .contains(
           &command.device_id
       )
   {
       return Err(
           PhysicalError::
               DeviceNotAllowed
       );
   }
   Ok(())
}

実Studioでは音量・照明・設備の安全基準を専門家と設備仕様に基づいて具体化する。

16 Manual Authority

AutomationがPhysical Realityへ入るほど、人間のOverride経路を明確にする。

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
)]
enum PhysicalControlMode {
   Automatic,
   Assisted,
   Manual,
   Safe,
}

Manualへ切り替えた場合、

AI Execution
=
OFF
Automatic Physical Commands
=
OFF
Human Control
=
ON

とする。

AIやRuntimeはObservationを継続してもよい。

しかしExecution Authorityは人間へ移る。

17 Physical Safe State

異常時に「何もしない」ことが安全とは限らない。

Lighting ControllerがFailureしたとき、最後の極端なSceneのまま固定するよりSafe Static Sceneへ移した方がよい場合がある。

Audioも安全なFallback Operationを持つ。

したがってDeviceごとにSafe Stateを定義する。

#[async_trait::async_trait]
trait SafeDevice {
   async fn enter_safe_state(
       &self,
   ) -> Result<
       DeviceExecutionResult,
       PhysicalError,
   >;
}

Emergency時にはPhysical Runtimeが全AdapterへSafe Stateを要求する。

18 Health Monitoring

Physical RuntimeはCommandだけでなくHealthを監視する。

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
)]
enum DeviceHealth {
   Healthy,
   Degraded,
   Unavailable,
   Unknown,
}

DeviceごとのHealthをまとめる。

struct PhysicalHealth {
   audio:
       DeviceHealth,
   lighting:
       DeviceHealth,
   sensor:
       DeviceHealth,
   network:
       DeviceHealth,
}

UnknownをHealthyとして扱わない。

Physical Realityでも同じ原則を守る。

19 Heartbeat

Network DeviceではHeartbeatを使える。

struct DeviceHeartbeat {
   last_seen:
       std::time::Instant,
   timeout:
       std::time::Duration,
}
impl DeviceHeartbeat {
   fn healthy(
       &self,
   ) -> bool {
       self.last_seen
           .elapsed()
           <= self.timeout
   }
}

一定時間応答がなければHealthをUnknownまたはUnavailableへ移す。

20 Sensor Gateway

Physical RuntimeにはInput側もある。

Sensor Gatewayは複数SensorからObservationを受け取る。

#[derive(
   Debug,
   Clone,
)]
struct SensorObservation {
   sensor_id:
       uuid::Uuid,
   session_id:
       uuid::Uuid,
   observed_at_seconds:
       f64,
   feature_type:
       String,
   value:
       f64,
   confidence:
       Option<f64>,
}

ここでもRaw SignalとMeaningful Featureを分ける。

Microphone Raw AudioをそのままRust Runtimeへ大量送信するのではなく、Edge ProcessingしたFeatureを渡す構造を基本にする。

21 Physical Realityを観測する

Sensor ObservationをRuntime Eventへ変換する。

fn observation_to_event(
   observation:
       SensorObservation,
) -> RuntimeEvent {
   RuntimeEvent::new(
       observation
           .session_id
           .to_string(),
       EventType::SensorUpdate,
       observation
           .observed_at_seconds,
       EventPriority::Normal,
       EventPayload::Sensor {
           sensor_id:
               observation
                   .sensor_id
                   .to_string(),
           value:
               observation
                   .value,
       },
   )
}

これによって、

Physical

Sensor

Runtime

の逆方向が成立する。

22 Physical Execution Record

CommandとResultをMemoryへ残す。

#[derive(
   Debug,
   serde::Serialize,
)]
struct PhysicalExecutionRecord {
   command_id:
       uuid::Uuid,
   session_id:
       uuid::Uuid,
   device_id:
       uuid::Uuid,
   target_seconds:
       f64,
   applied_seconds:
       Option<f64>,
   result:
       String,
}

SQL側では例えば、

CREATE TABLE physical_execution_events (
   execution_id UUID PRIMARY KEY,
   command_id UUID NOT NULL,
   session_id UUID NOT NULL,
   device_id UUID NOT NULL,
   target_seconds
       DOUBLE PRECISION,
   applied_seconds
       DOUBLE PRECISION,
   result TEXT NOT NULL,
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

とできる。

これでSoftware EventとPhysical Executionを区別して記憶できる。

23 Planned・Commanded・Observed

ここでMemoryは三段階になる。

Planned

Commanded

Observed

Programでは、

Lighting Peakを120秒に予定。

Physical Runtimeでは、

119.987秒にCommand Dispatch。

Deviceでは、

120.011秒にApplied。

Sensorでは、

120.020秒に状態変化をObserved。

というように追跡できる。

これによってPhysical Synchronizationを検証できる。

24 Device Configuration

Device ControlにはConfigurationが必要である。

#[derive(
   Debug,
   serde::Deserialize,
)]
struct DeviceConfig {
   device_id:
       uuid::Uuid,
   device_type:
       String,
   protocol:
       String,
   address:
       String,
   enabled:
       bool,
}

ConfigurationはCodeへHardcodeしない。

Studio Configuration VersionとしてMemoryへ保持する。

Session開始時に正しいConfigurationを読み込む。

25 Configuration Validation

Session開始前にDevice Configurationを検証する。

fn validate_device_config(
   config:
       &DeviceConfig,
) -> Result<(), PhysicalError> {
   if !config.enabled {
       return Err(
           PhysicalError::
               DeviceDisabled
       );
   }
   if config.address
       .trim()
       .is_empty()
   {
       return Err(
           PhysicalError::
               InvalidConfiguration
       );
   }
   Ok(())
}

Session開始後に初めてConfiguration Errorを発見するのでは遅い。

Preparation Stageで検出する。

26 Preflight Check

Session開始前にPhysical Runtimeを確認する。

Runtime
Audio
Lighting
Sensor
Network
Manual Control
Safe State

を検証する。

struct PreflightReport {
   runtime_ready:
       bool,
   audio_ready:
       bool,
   lighting_ready:
       bool,
   sensor_ready:
       bool,
   network_ready:
       bool,
}

Critical ComponentがUnavailableなら開始判断へ返す。

SensorのようなOptional ComponentならDegraded Startを許容できる。

27 Physical Runtimeの優先順位

FEELCYCLE Physical Runtimeでは、優先順位を明確にする。

Safety
>
Human Authority
>
Audio Continuity
>
Program Timing
>
Lighting Automation
>
Sensor Enhancement
>
AI Optimization

AI Optimizationが失われても45分は成立する。

Lighting Automationが失われてもFallbackできる。

しかしSafetyやHuman AuthorityをAI Optimizationのために犠牲にしない。

このPriorityは設計全体を貫く。

28 AIから物理空間へ、という意味

ここで本節のタイトルに戻る。

「AIから物理空間へ」とは、AIが直接Studioを動かすという意味ではない。

AIが理解する。

Runtimeが判断可能なStateへ変換する。

HumanとPolicyがAuthorityを与える。

Physical RuntimeがHardwareの言語へTranslationする。

DeviceがRealityを変える。

RealityがObservationを返す。

という複数の境界を通過することである。

AI

Meaning

Authority

Runtime

Safety

Protocol

Hardware

Reality

Observation

AI

一段も飛ばさない。

この分離が安全性、交換可能性、検証可能性を生む。

29 Physical Runtime v0.1の起点

本節で、Physical Runtimeの基本Architectureは成立した。

Semantic Command。

Adapter。

Edge Runtime。

Command Identity。

Desired / Observed State。

Latency。

Safety Envelope。

Manual Authority。

Health Monitoring。

Sensor Gateway。

Physical Execution Memory。

ここから各Systemを具体化する。

最初はAudioである。

FEELCYCLEにおいてMusicは単なるBackground Soundではない。

45分の時間構造を形成する主要な基準であり、身体運動、Lighting、Instructor、Member Experienceを接続する軸でもある。

したがって次節では、

Audio System

を実装する。

Music Fileがどこから再生されるのか。

Playback Engineをどう構成するのか。

Mixer、DSP、Audio Interface、Amplifier、Speakerをどう接続するのか。

Rust Runtimeとどこで同期するのか。

障害時にどうFallbackするのか。

そしてAIがMusicを理解した結果を、音として安全にStudioへ出すにはどうすればよいのか。

Codeが最初にPhysical Realityへ変わる場所として、音響系を構築する。
第2節 Audio System

FEELCYCLEのPhysical Runtimeにおいて、Audio Systemは単なる「音を再生する装置」ではない。

Musicは45分の時間そのものを形成する。

身体運動がMusicへ接続する。

InstructorのTimingがMusicへ接続する。

LightingがMusicへ接続する。

Memberの注意と身体感覚もMusicによって変化する。

したがってAudio Systemは、

Playback System

であると同時に、

Physical Time Reference

でもある。

Python IntelligenceがMusicを解析し、Rust Runtimeが45分のTimelineを保持していても、実際のStudioで音が遅延したり、停止したり、意図しない音量へ変化すれば、全体の同期は崩れる。

本節では、

Music Data

Playback Engine

Audio Interface

Mixer / DSP

Amplifier

Speaker

Studio

というPhysical Audio Chainを構築し、Rust Runtimeから安全に制御できるFEELCYCLE Audio Runtime v0.1を実装する。

1 Audio Systemの全体構造

最初に役割を分ける。

Python Intelligence
       ↓
Music Analysis
       ↓
Program Timeline
       ↓
  Rust Runtime
       ↓
  Audio Adapter
       ↓
Playback Engine
       ↓
Audio Interface
       ↓
  Mixer / DSP
       ↓
   Amplifier
       ↓
    Speaker
       ↓
     Studio

逆方向には、

Audio Device

Playback State

Device Health

Rust Runtime

Memory

が存在する。

つまりAudio Systemも双方向である。

Commandだけではなく、

現在再生しているTrack。

現在Position。

Device Health。

Playback Error。

Latency。

をRuntimeへ返す。

2 Audio AssetとPlaybackを分離する

Music Metadataと実際のAudio Assetを同じものとして扱わない。

SQLには、

Track ID。

Title。

Artist。

Duration。

Analysis。

を持つ。

実際のPlayback Assetは、権利処理された配信・再生基盤や適切なAsset Managementの下で扱う。

Rust Runtimeが知るべきなのは、

track_id
asset_reference
start_position
target_start_time

である。

Audio Fileそのものの保存場所や権利条件をRuntimeへ埋め込まない。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
   serde::Deserialize,
)]
struct AudioAssetReference {
   track_id: uuid::Uuid,
   asset_reference: String,
   duration_seconds: f64,
}

これによって、

Music Identity

と、

Physical Playback Asset

を分離できる。

3 Audio Command

RuntimeからAudio Systemへ送るCommandを定義する。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
enum AudioAction {
   Prepare,
   Play,
   Pause,
   Stop,
   Seek,
}
#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
struct AudioCommand {
   command_id: uuid::Uuid,
   session_id: uuid::Uuid,
   track_id: uuid::Uuid,
   action: AudioAction,
   target_position_seconds:
       Option<f64>,
   target_session_seconds:
       f64,
}

ここで重要なのは、

「今すぐPlay」

だけではなく、

「Session Clock上の何秒で開始するか」

を持つことである。

これによってLightingやMovementとの同期基準を作れる。

4 PrepareとPlayを分ける

Track開始時刻になってからFileを読み込み始めてはいけない。

先にPrepareする。

Upcoming Track

Prepare

Decode / Buffer

Ready

Target Time

Play

Audio AdapterにはPrepareを持たせる。

#[async_trait::async_trait]
trait AudioAdapter:
   Send + Sync
{
   async fn prepare(
       &self,
       asset:
           &AudioAssetReference,
   ) -> Result<
       AudioPrepareResult,
       AudioError,
   >;
   async fn execute(
       &self,
       command:
           &AudioCommand,
   ) -> Result<
       AudioExecutionResult,
       AudioError,
   >;
   async fn state(
       &self,
   ) -> Result<
       AudioPlaybackState,
       AudioError,
   >;
   async fn health(
       &self,
   ) -> DeviceHealth;
}

Resultを定義する。

#[derive(Debug)]
struct AudioPrepareResult {
   track_id: uuid::Uuid,
   ready: bool,
}
#[derive(Debug)]
struct AudioExecutionResult {
   command_id: uuid::Uuid,
   accepted: bool,
   applied: bool,
   applied_at_seconds:
       Option<f64>,
   message:
       Option<String>,
}

Prepareに失敗すれば、Track開始前にFallback判断ができる。

5 Playback State

CommandとActual Playbackを分離する。

#[derive(
   Debug,
   Clone,
)]
enum PlaybackStatus {
   Idle,
   Prepared,
   Playing,
   Paused,
   Stopped,
   Failed,
}
#[derive(
   Debug,
   Clone,
)]
struct AudioPlaybackState {
   track_id:
       Option<uuid::Uuid>,
   status:
       PlaybackStatus,
   position_seconds:
       f64,
   updated_at_session_seconds:
       f64,
}

Runtimeは定期的にPlayback Stateを確認する。

Program上ではTrack Aが再生中でも、

Actual Playback State

がFailedなら、Realityは異なる。

6 Playback Positionを検証する

Session ClockとPlayback Positionを比較する。

概念的には、

Expected Position
=
Session Time
-
Track Start Time

である。

Rustでは、

fn expected_position(
   session_seconds: f64,
   track_start_seconds: f64,
) -> f64 {
   (
       session_seconds
       - track_start_seconds
   )
   .max(0.0)
}

Differenceを計算する。

fn playback_drift_ms(
   expected_seconds: f64,
   actual_seconds: f64,
) -> f64 {
   (
       actual_seconds
       - expected_seconds
   )
   * 1000.0
}

これによってAudio Driftを観測できる。

ただし、常に細かな補正を行えばよいわけではない。

補正そのものが音の不自然さを生む可能性がある。

許容範囲を設定する。

7 Audio Drift Policy

#[derive(Debug)]
struct AudioSyncPolicy {
   warning_drift_ms:
       f64,
   critical_drift_ms:
       f64,
}

判定する。

#[derive(Debug)]
enum AudioSyncState {
   Synchronized,
   Warning,
   Critical,
}
fn classify_drift(
   drift_ms: f64,
   policy:
       &AudioSyncPolicy,
) -> AudioSyncState {
   let value =
       drift_ms.abs();
   if value
       >= policy
           .critical_drift_ms
   {
       AudioSyncState::Critical
   } else if value
       >= policy
           .warning_drift_ms
   {
       AudioSyncState::Warning
   } else {
       AudioSyncState::Synchronized
   }
}

具体的なThresholdは、実Studioでの聴覚評価、Device特性、Program要件に基づいて決定する。

8 Audio Interface

Playback EngineからStudio設備へ音を出すにはAudio Interfaceが必要になる。

概念構造は、

Playback Computer

Digital Audio

Audio Interface

Balanced Audio / Network Audio

Mixer / DSP

となる。

Audio Interface選定では、

必要Channel数。

Driverの安定性。

低Latency。

Sample Rate。

Output Format。

Redundancy。

運用実績。

を重視する。

製品名よりも、

Studio Runtimeとして安定運用できること

を要件の中心にする。

9 Mixer

FEELCYCLEのStudio AudioにはMusicだけではなく、Instructor側の音声も存在する可能性がある。

したがってMixerでは、

Music。

Instructor Microphone。

必要なSystem Audio。

を分離する。

Music Playback ─────┐
                   │
Instructor Mic ─────┼→ Mixer → DSP → Speakers
                   │
System Audio ───────┘

AIがMixer全体を自由に操作する設計にはしない。

特にInstructor VoiceやMaster Outputは、Physical SafetyとOperational Controlを持つ。

10 Signal Model

SemanticなAudio Stateを定義できる。

#[derive(
   Debug,
   Clone,
)]
struct AudioMixIntent {
   music_active:
       bool,
   instructor_voice_active:
       bool,
   scene:
       AudioScene,
}
#[derive(
   Debug,
   Clone,
)]
enum AudioScene {
   Normal,
   Transition,
   Recovery,
   Safe,
}

このSemantic StateをMixer Adapterが実設備のRoutingやPresetへ変換する。

AIが個別Gain値を直接生成する構造より安全である。

11 DSP

DSPは音響処理を担当する。

たとえば、

Routing。

EQ。

Dynamics。

Delay Compensation。

Limiter。

などである。

ここで重要なのは、AIがPhysical Safety Limiterを上書きできないようにすることである。

AI / Runtime

Mix Intent

DSP Scene

Safety Processing

Physical Output

Safety LayerはAutomationより下位に置く。

つまりAIがどのようなAudio Intentを生成しても、安全上設定された限界を越えられない。

12 音量のAuthority

音量はFEELCYCLEのExperienceに大きく影響する。

しかし、音量最大化をAI Optimization Objectiveにしない。

また本書のコードだけで具体的な安全音圧値を固定しない。

Studioの物理特性、設備仕様、関連する安全基準、専門的な測定を基に定める必要があるためである。

Architectureとしては、

Program Intent

Runtime

Allowed Range

DSP / Limiter

Output

とする。

最大許容値はConfigurationとして管理する。

#[derive(Debug)]
struct AudioSafetyConfig {
   maximum_output_level:
       f64,
   allow_runtime_gain_change:
       bool,
}

実際の単位・値は設備設計時に定義する。

13 Speaker System

Speaker SystemはPhysical Outputの最終段である。

ここでは、

Coverage。

Placement。

Frequency Response。

Headroom。

Reliability。

Room Acoustics。

を考える。

AIが高度でも、Physical Acoustic Designが悪ければExperienceは改善されない。

Audio Intelligence

Acoustic Engineering

である。

Softwareは、物理音響設計を代替しない。

むしろ適切に設計されたPhysical Systemを再現性高く運用する。

14 StudioごとのAudio Configuration

Studioによって、

Speaker。

Amplifier。

DSP。

Mixer。

Audio Interface。

が異なる場合がある。

そこでAudio ConfigurationをVersion化する。

{
 "configuration_version": 4,
 "audio_interface": {
   "device_id": "audio-interface-01"
 },
 "mixer": {
   "device_id": "mixer-01",
   "scene": "feelcycle-default"
 },
 "dsp": {
   "device_id": "dsp-01",
   "preset": "studio-a-v4"
 }
}

Sessionには、

Studio Configuration Version

を紐付ける。

これによって後から、

「同じProgramなのにStudio AだけExperienceが違った」

原因を物理設備側まで追跡できる。

15 Audio Device Identity

DatabaseにもDeviceを記録する。

CREATE TABLE audio_devices (
   audio_device_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   studio_id UUID NOT NULL,
   device_type TEXT NOT NULL,
   device_name TEXT NOT NULL,
   hardware_reference TEXT,
   firmware_version TEXT,
   active BOOLEAN NOT NULL
       DEFAULT TRUE,
   installed_at TIMESTAMPTZ,
   FOREIGN KEY (
       studio_id
   )
   REFERENCES studios(
       studio_id
   )
);

これによりPhysical Memoryが成立する。

16 Health Monitoring

Audio DeviceのHealthを定期的に確認する。

#[derive(
   Debug,
   Clone,
)]
struct AudioHealth {
   playback_engine:
       DeviceHealth,
   audio_interface:
       DeviceHealth,
   mixer:
       DeviceHealth,
   dsp:
       DeviceHealth,
   output_path:
       DeviceHealth,
}

全体を判定する。

fn overall_audio_health(
   health:
       &AudioHealth,
) -> DeviceHealth {
   let states = [
       health.playback_engine,
       health.audio_interface,
       health.mixer,
       health.dsp,
       health.output_path,
   ];
   if states
       .contains(
           &DeviceHealth::Unavailable
       )
   {
       DeviceHealth::Unavailable
   } else if states
       .contains(
           &DeviceHealth::Degraded
       )
   {
       DeviceHealth::Degraded
   } else if states
       .contains(
           &DeviceHealth::Unknown
       )
   {
       DeviceHealth::Unknown
   } else {
       DeviceHealth::Healthy
   }
}

17 Preflight Check

Session開始前にAudio Systemを確認する。

#[derive(Debug)]
struct AudioPreflightReport {
   playback_ready:
       bool,
   output_ready:
       bool,
   mixer_ready:
       bool,
   dsp_ready:
       bool,
   fallback_ready:
       bool,
}

判定する。

impl AudioPreflightReport {
   fn session_ready(
       &self,
   ) -> bool {
       self.playback_ready
           && self.output_ready
           && self.mixer_ready
           && self.dsp_ready
   }
}

重要なのは、Session開始後に初めて音が出ないことへ気づくのを防ぐことである。

18 Preload

次のTrackを先読みする。

struct PreparedTrack {
   track_id:
       uuid::Uuid,
   ready:
       bool,
   prepared_at:
       std::time::Instant,
}

Program Timelineから次のTrackを取得し、余裕を持ってPrepareする。

Current Track

Next Track Lookup

Preload

Ready

Scheduled Transition

19 Track Transition

Track TransitionをRuntime Eventとして扱う。

#[derive(
   Debug,
   Clone,
)]
struct TrackTransition {
   from_track:
       uuid::Uuid,
   to_track:
       uuid::Uuid,
   target_session_seconds:
       f64,
}

Transition時には、

次TrackがReadyか。

Audio DeviceはHealthyか。

Runtime Clockは正常か。

を確認する。

fn can_transition(
   next_ready: bool,
   health:
       DeviceHealth,
) -> bool {
   next_ready
       && health
           == DeviceHealth::Healthy
}

20 Fallback Playback

Audioは45分の主要基盤であるため、Fallbackが重要になる。

通常Playback EngineがFailureした場合、

Secondary Playback。

Manual Playback。

Preloaded Local Fallback。

などの設計が考えられる。

概念的には、

Primary Playback
↓ failure
Secondary Playback
↓ failure
Manual Fallback

とする。

実装規模に応じて段階を決める。

重要なのは、

Audio Failure

Immediate Session Collapse

だけを唯一の状態にしないことである。

21 Fallback State

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
)]
enum AudioRuntimeMode {
   Primary,
   Secondary,
   Manual,
   SafeStop,
}

Fallback Managerを作る。

fn next_audio_mode(
   current:
       AudioRuntimeMode,
) -> AudioRuntimeMode {
   match current {
       AudioRuntimeMode::Primary =>
           AudioRuntimeMode::Secondary,
       AudioRuntimeMode::Secondary =>
           AudioRuntimeMode::Manual,
       AudioRuntimeMode::Manual =>
           AudioRuntimeMode::SafeStop,
       AudioRuntimeMode::SafeStop =>
           AudioRuntimeMode::SafeStop,
   }
}

Productionでは人間判断や設備構成を含むState Machineにする。

22 Manual Control

Audio Automationを切り離せる必要がある。

Manual Modeへ移れば、

Rust Runtimeからの自動Track Change。

自動Scene Change。

AI由来のAudio Suggestion Execution。

を停止する。

struct AudioAuthority {
   automatic_execution:
       bool,
   human_control:
       bool,
}

Manualへ移す。

fn enter_manual_mode(
   authority:
       &mut AudioAuthority,
) {
   authority
       .automatic_execution =
       false;
   authority
       .human_control =
       true;
}

ただしSafety LimiterなどはManual Modeでも残す。

Human Authority

Safety Bypass

である。

23 Instructor Microphone

Instructor MicrophoneはMusic Playbackとは別系統として扱う。

FEELCYCLE AIがInstructorの声そのものを常時記録する必要はない。

Physical Audioでは、

Mic Input

Mixer / DSP

Speaker

として動く。

AIが必要とするのが、

Voice Activity。

Level。

Signal Health。

などのFeatureだけなら、EdgeでFeature抽出してRaw Voiceを破棄する設計を優先できる。

Microphone

Edge Audio Analysis

Feature

Raw Audio Discard

PrivacyとOperational Utilityを両立する。

24 Microphone Feature

たとえば、

#[derive(
   Debug,
   Clone,
)]
struct VoiceFeature {
   active:
       bool,
   level:
       Option<f64>,
   confidence:
       f64,
}

程度のFeatureで十分な用途もある。

Speech Contentを保存しなくても、

「現在Voice Activityがある」

ことは観測できる。

Need Meaning

Need Recording

である。

25 Audio Sensor

Studio Audioを検証するためのMeasurement Microphoneを別に用意することもできる。

これはInstructor Microphoneとは役割が異なる。

Instructor Mic
=
Human Voice Input
Measurement Mic
=
Studio Audio Observation

両者を混同しない。

Measurement側では、

音響Level。

Frequency Feature。

Output Presence。

などをEdgeで抽出できる。

26 Output Verification

Playback EngineがPlayingでも、Speakerから実際に音が出ているとは限らない。

したがってより完全なPhysical Verificationでは、

Playback State

Audio Interface

Mixer / DSP

Speaker

Measurement

まで確認する。

Software ObservationとPhysical Observationを分離する。

struct AudioVerification {
   playback_reported:
       bool,
   physical_output_detected:
       bool,
}

これによって、

Software Playing
+
Physical Silence

というFailureを検出できる可能性が高まる。

27 Audio EventをMemoryへ戻す

Audio Systemで起きたEventを記録する。

CREATE TABLE audio_runtime_events (
   audio_event_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   session_id UUID NOT NULL,
   track_id UUID,
   event_type TEXT NOT NULL,
   target_seconds DOUBLE PRECISION,
   actual_seconds DOUBLE PRECISION,
   runtime_mode TEXT,
   result TEXT NOT NULL,
   metadata JSONB NOT NULL
       DEFAULT '{}'::jsonb,
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

これによってAudioのPhysical Memoryが成立する。

28 Audio DriftをMemoryへ保存する

CREATE TABLE audio_sync_measurements (
   measurement_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   session_id UUID NOT NULL,
   track_id UUID,
   session_seconds
       DOUBLE PRECISION NOT NULL,
   expected_position_seconds
       DOUBLE PRECISION NOT NULL,
   actual_position_seconds
       DOUBLE PRECISION NOT NULL,
   drift_ms
       DOUBLE PRECISION NOT NULL,
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

すべてのSampleを永久保存する必要はない。

必要なSamplingやSummaryへ圧縮する。

29 Session Audio Summary

Session終了後にSummaryへ変換する。

CREATE TABLE audio_session_summaries (
   session_id UUID PRIMARY KEY,
   availability
       DOUBLE PRECISION,
   average_drift_ms
       DOUBLE PRECISION,
   p95_drift_ms
       DOUBLE PRECISION,
   fallback_count
       INTEGER NOT NULL
       DEFAULT 0,
   failure_count
       INTEGER NOT NULL
       DEFAULT 0,
   generated_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

Company DashboardはRaw Audio Eventではなく、このSummaryを中心に利用する。

30 Audio Runtime Loop

Audio Runtimeの中心を簡略化すると、

async fn audio_runtime_loop(
   adapter:
       &dyn AudioAdapter,
   commands:
       &mut tokio::sync::mpsc::Receiver<
           AudioCommand
       >,
) {
   while let Some(command) =
       commands.recv().await
   {
       let health =
           adapter.health().await;
       if health
           == DeviceHealth::Unavailable
       {
           continue;
       }
       let result =
           adapter
               .execute(
                   &command
               )
               .await;
       match result {
           Ok(result) => {
               record_audio_result(
                   result
               ).await;
           }
           Err(error) => {
               handle_audio_failure(
                   error
               ).await;
           }
       }
   }
}

となる。

実際にはPriority、Timing、Fallback、Safety、Auditを統合する。

31 Audio CommandをRuntimeへ接続する

Music Transition EventからAudio Commandを生成する。

fn to_audio_command(
   session_id:
       uuid::Uuid,
   track_id:
       uuid::Uuid,
   target_seconds:
       f64,
) -> AudioCommand {
   AudioCommand {
       command_id:
           uuid::Uuid::new_v4(),
       session_id,
       track_id,
       action:
           AudioAction::Play,
       target_position_seconds:
           Some(0.0),
       target_session_seconds:
           target_seconds,
   }
}

これでProgram TimelineからPhysical Audioへつながる。

32 Python Intelligenceとの境界

Python IntelligenceはMusic Structureを解析する。

しかしPlayback Engineを直接操作しない。

Pythonが返すのは、

Section。

Energy。

Transition Candidate。

Prediction。

Suggestion。

である。

その情報をProgramやRuntimeが解釈し、Audio Commandへ変える。

Python

Music Understanding

Program / Suggestion

Human / Runtime

Audio Command

知性とPhysical Authorityを分離する。

33 TypeScript Interfaceとの接続

Instructor ConsoleではAudioの詳細Device情報を大量に表示しない。

必要なのは、

Playing。

Fallback。

Manual。

Failure。

などである。

Studio Monitorではさらに詳しく、

Playback Engine。

Interface。

Mixer。

DSP。

Output。

を表示できる。

同じAudio RealityをObserver Positionによって異なるRepresentationへする。

34 Swiftとの接続

Member AppがStudio Audioを制御することはない。

Instructor MobileもPrimary Audio Controlにはしない。

Mobileから見せるとしても、

Session Ready。

Audio System Ready。

程度でよい。

Physical Control AuthorityをPersonal Mobile Deviceへ分散させすぎない。

35 Audio SystemのSafety Boundary

Physical Audioでは、Software Errorが実空間へ直接影響する。

したがって、

Program。

AI。

Runtime。

Adapter。

Mixer。

DSP。

Physical Safety。

を一つのLayerにまとめない。

AI

Runtime

Audio Adapter

Mixer / DSP

Independent Safety Constraint

Speaker

複数の防衛境界を持つ。

36 Audio v0.1のPreflight Flow

Session開始前には、

Playback Engine
↓ check
Audio Asset
↓ check
Audio Interface
↓ check
Mixer
↓ check
DSP
↓ check
Speaker Path
↓ check
Fallback
↓ check
Ready

を確認する。

結果をStudio Readinessへ返す。

37 Session Start

Session開始時、

最初のTrackをPrepare。

Session Clock開始。

Target TimeにPlay。

Playback Stateを確認。

Physical Outputを必要に応じて確認。

Timing Differenceを記録する。

Session Start

Audio Ready

Play

Verify

Continue

38 Session End

Session終了時には、

Playback停止。

Runtime State更新。

Audio Summary生成。

Memory同期。

Fallback History記録。

を行う。

45 Minutes

Audio Stop

Verification

Summary

Memory

一つのAudio Realityが次のLearningへ変わる。

39 Audio System v0.1の完成条件

FEELCYCLE Audio System v0.1は、最低限次を満たす。

Music AssetをTrack Identityと分離できる。

次Trackを事前Prepareできる。

Session Clockに基づいて再生できる。

Actual Playback Positionを取得できる。

Audio Driftを測定できる。

Audio Interface、Mixer、DSP、OutputのHealthを観測できる。

Primary Failure時にFallbackへ移れる。

HumanがManual Controlを引き取れる。

Safety ConstraintをAutomationから独立させられる。

Audio EventをSession Memoryへ戻せる。

この一周が成立すれば、Pythonで解析されたMusicはPhysical Realityの「音」へ変換されたことになる。

40 Musicを時間へ戻す

ここでAudio Systemの意味をもう一度整理する。

FEELCYCLEにおいてMusicは、単に流れるContentではない。

45分の構造を支える時間軸である。

ProgramはMusicへ身体運動を接続する。

Rust RuntimeはMusicへEventを接続する。

LightingはMusicへ空間変化を接続する。

InstructorはMusicを読みながらRealityを生成する。

MemberはMusicを身体として経験する。

だからAudio Systemが安定することは、単に「音が途切れない」以上の意味を持つ。

Music

Time

Body

Space

Experience

へつながる。

Python IntelligenceはMusicを理解した。

Rust Runtimeはその時間を保持した。

Audio Systemによって、その時間がStudioに鳴り始める。

しかし、FEELCYCLEのPhysical Realityは音だけでは完成しない。

同じMusicでも、空間が完全な暗闇なのか、Focusなのか、Buildなのか、PeakなのかによってExperienceは変わる。

次節では、時間を空間へ展開する。

Lighting Systemを実装する。

Semantic Lighting StateをSceneへ変換し、ControllerとFixtureへ安全に送る。

Musicと同期する。

Failure時にはSafe Sceneへ戻る。

Human Overrideを保持する。

そしてRust RuntimeのStateを、実際のStudioの「光」へ変換する。
第3節 Lighting System

Audio Systemによって、FEELCYCLE AIの時間はStudioの「音」へ変換された。

次に、その時間を空間へ展開する。

Lightingである。

FEELCYCLEにおけるLightingは、単なる装飾ではない。

暗闇をつくる。

身体への注意を変える。

InstructorとMemberのRelationを変える。

MusicのTransitionを空間として可視化する。

集中を深める。

開放する。

45分の時間構造を、光と影によってPhysical Spaceへ翻訳する。

したがってLighting Systemの役割は、

「AIが照明を操作すること」

ではない。

Program、Music、Runtimeが持つ意味を、

安全で再現可能なPhysical Lighting State

へTranslationすることである。

基本構造は、

Program / AI

Lighting Intent

Rust Runtime

Lighting Adapter

Scene

Protocol

Controller

Fixture

Physical Space

となる。

そしてAudioと同じように逆方向も必要である。

Controller / Fixture

Device State

Lighting Adapter

Rust Runtime

Studio Monitor

Memory

つまりLighting Systemも、

Command

Observation

を持つPhysical Runtimeとして設計する。

1 LightingをMeaningから設計する

Lighting Systemで最も重要なのは、AIやProgram CreatorへHardware Channelを直接操作させないことである。

Program Builderでは、

Dark。

Focus。

Build。

Peak。

Release。

というSemantic Lighting Stateを扱ってきた。

この思想をPhysical Runtimeでも維持する。

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
   serde::Serialize,
   serde::Deserialize,
)]
enum LightingIntent {
   Dark,
   Focus,
   Build,
   Peak,
   Release,
   Safe,
}

Programには、

120.0 sec
→ Build
138.5 sec
→ Peak

と記述する。

それをRust RuntimeがLighting Adapterへ渡す。

AIは、

「Channel 12を値210へ」

とは言わない。

Meaningを出す。

Hardware Layerが具体値へ翻訳する。

これによって、

Creative Meaning

Hardware Implementation

を維持できる。

2 Sceneを第一級Entityにする

Semantic Intentを、StudioごとのLighting Sceneへ変換する。

#[derive(
   Debug,
   Clone,
   serde::Deserialize,
)]
struct LightingScene {
   scene_id: String,
   intent:
       LightingIntent,
   transition_ms:
       u64,
   parameters:
       serde_json::Value,
}

たとえばStudio AのPeakとStudio BのPeakは、Fixture構成が違っても同じ意味を持てる。

Studio Aでは、

Peak

Fixture A
Fixture B
Fixture C

Studio Bでは、

Peak

Fixture X
Fixture Y
Fixture Z

へTranslationする。

Runtimeは違いを知らなくてよい。

これがHardware Abstractionの価値である。

3 Scene Configuration

Sceneの具体値はConfigurationとして保持する。

概念例は、

{
 "scene_id": "peak_v3",
 "intent": "Peak",
 "transition_ms": 300,
 "fixtures": [
   {
     "fixture_id": "front_01",
     "intensity": 0.75
   },
   {
     "fixture_id": "side_01",
     "intensity": 0.60
   }
 ]
}

である。

ここで具体的な光量・演出値は実Studioで設計・検証する。

重要なのはCodeへHardcodeしないことである。

Lighting SceneもVersion化する。

4 Lighting Scene Version

SQLでは例えば、

CREATE TABLE lighting_scenes (
   scene_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   studio_id UUID NOT NULL,
   scene_name TEXT NOT NULL,
   semantic_intent TEXT NOT NULL,
   version_number INTEGER NOT NULL,
   scene_definition JSONB NOT NULL,
   active BOOLEAN NOT NULL
       DEFAULT TRUE,
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       studio_id
   )
   REFERENCES studios(
       studio_id
   ),
   UNIQUE (
       studio_id,
       scene_name,
       version_number
   )
);

とする。

これによって過去Sessionで、

Program Version。

Studio Configuration Version。

Lighting Scene Version。

を対応させられる。

5 ProgramとSceneを分離する

Program BuilderのLightingIntent::Peakを、そのまま特定Scene IDへ永久固定する必要はない。

Translation Layerを置く。

Program
Peak

Studio Configuration

peak_v3

Physical Scene

これにより設備更新後もProgramそのものを書き換えずに済む。

同じProgramが複数Studioで実行可能になる。

6 Lighting Command

Physical Commandを定義する。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
struct LightingCommand {
   command_id:
       uuid::Uuid,
   session_id:
       uuid::Uuid,
   intent:
       LightingIntent,
   scene_id:
       String,
   target_session_seconds:
       f64,
   transition_ms:
       u64,
}

ここでもtarget_session_secondsを持つ。

AudioとLightingを同じSession Clockへ接続するためである。

7 Lighting Adapter

共通Interfaceを実装する。

#[async_trait::async_trait]
trait LightingAdapter:
   Send + Sync
{
   async fn prepare_scene(
       &self,
       scene:
           &LightingScene,
   ) -> Result<
       (),
       LightingError,
   >;
   async fn apply(
       &self,
       command:
           &LightingCommand,
   ) -> Result<
       LightingExecutionResult,
       LightingError,
   >;
   async fn state(
       &self,
   ) -> Result<
       LightingObservedState,
       LightingError,
   >;
   async fn health(
       &self,
   ) -> DeviceHealth;
   async fn safe_scene(
       &self,
   ) -> Result<
       LightingExecutionResult,
       LightingError,
   >;
}

これによってDMX、Art-Net、sACN、Vendor APIなどをAdapterの後ろへ隠せる。

8 Lighting Protocol

Studio設備では複数のLighting Control Protocolが利用され得る。

代表的には、

DMX512。

Art-Net。

sACN。

専用Controller API。

などである。

FEELCYCLE RuntimeはProtocolを一つへ固定しない。

Rust Runtime

Lighting Adapter
├── DMX Adapter
├── Art-Net Adapter
├── sACN Adapter
└── Vendor Adapter

とする。

Hardware交換時にRuntime Coreを書き換えない。

9 DMX Mapping

DMX系設備なら、FixtureとChannel Mappingが必要になる。

#[derive(
   Debug,
   Clone,
   serde::Deserialize,
)]
struct DmxFixtureMapping {
   fixture_id:
       String,
   universe:
       u16,
   start_channel:
       u16,
   channel_count:
       u16,
}

しかしProgram CreatorやAIはこの値を意識しない。

Physical Adapterだけが利用する。

10 Semantic SceneからDevice Valuesへ

Scene Translationを実装する。

fn resolve_scene(
   intent:
       LightingIntent,
   scenes:
       &[LightingScene],
) -> Option<
   LightingScene
> {
   scenes
       .iter()
       .find(
           |scene|
               scene.intent
                   == intent
       )
       .cloned()
}

さらにScene DefinitionをProtocol Packetへ変換する。

概念的には、

LightingIntent::Peak

LightingScene

Fixture Values

Protocol Packet

となる。

意味と物理値の間に明確なTranslation Boundaryを置く。

11 Lightingの時間

Lightingは単にStateを切り替えるだけではない。

TransitionそのものがExperienceを形成する。

DarkからFocus。

FocusからBuild。

BuildからPeak。

PeakからRelease。

同じ二つのStateでも、

瞬時に変わるのか。

1秒かけるのか。

数秒かけるのか。

によってExperienceは異なる。

したがってLighting Commandは、

Target State

だけでなく、

Transition Duration

も持つ。

struct LightingTransition {
   from:
       LightingIntent,
   to:
       LightingIntent,
   duration_ms:
       u64,
}

12 Musicとの同期

Audio SystemとLighting Systemは同じSession Clockへ接続する。

たとえば、

Music Peak
= 138.500 sec
Lighting Peak
= 138.500 sec

を目標にする。

しかしDevice Latencyは異なる。

Audio Response = 4ms。

Lighting Controller Response = 18ms。

と仮定するなら、

Audio Dispatch
≈ 138.496
Lighting Dispatch
≈ 138.482

のように先行送信する発想になる。

一般化すると、

Dispatch Time
=
Target Physical Time
-
Estimated Device Latency

である。

13 Lighting Latency Profile

#[derive(
   Debug,
   Clone,
)]
struct LightingLatencyProfile {
   controller_id:
       uuid::Uuid,
   expected_ms:
       f64,
   p95_ms:
       f64,
   maximum_ms:
       f64,
}

Sessionごとに測定結果をMemoryへ戻せば、Studio固有の補正値を更新できる。

ただしリアルタイム自動補正を過度に行い、Lightingが不安定になるのは避ける。

Stable Timingを優先する。

14 Lighting Scheduler

Lighting EventをDispatchする。

async fn schedule_lighting(
   command:
       LightingCommand,
   latency:
       &LightingLatencyProfile,
   session_clock:
       &SessionClock,
   adapter:
       &dyn LightingAdapter,
) -> Result<
   LightingExecutionResult,
   LightingError,
> {
   let dispatch_seconds =
       command
           .target_session_seconds
       -
       latency.expected_ms
           / 1000.0;
   session_clock
       .wait_until(
           dispatch_seconds
       )
       .await;
   adapter
       .apply(
           &command
       )
       .await
}

これは概念実装である。

実際にはCancellation、Priority、Clock Drift、Safe Stateなどを追加する。

15 Lighting Execution Result

#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
struct LightingExecutionResult {
   command_id:
       uuid::Uuid,
   accepted:
       bool,
   applied:
       bool,
   applied_at_seconds:
       Option<f64>,
   controller_state:
       Option<String>,
   message:
       Option<String>,
}

Commandを出した時刻ではなく、可能ならApplied Stateまで取得する。

16 DesiredとObserved

Lighting Stateも二重化する。

#[derive(
   Debug,
   Clone,
)]
struct LightingRuntimeState {
   desired:
       LightingIntent,
   commanded_scene:
       Option<String>,
   observed_scene:
       Option<String>,
   health:
       DeviceHealth,
}

例えば、

Desired
=
Peak
Commanded
=
peak_v3
Observed
=
build_v2

なら、Physical RealityはまだPeakへ移行していない。

このDifferenceを明示する。

17 Fixture State

ControllerからFixture個別Stateを取得できる設備なら利用する。

#[derive(
   Debug,
   Clone,
)]
struct FixtureState {
   fixture_id:
       String,
   health:
       DeviceHealth,
   reported_state:
       Option<
           serde_json::Value
       >,
}

しかしすべてのFixtureが双方向Telemetryを返すとは限らない。

その場合、

Controller Accepted

と、

Physical Verified

を混同しない。

18 Physical Verification

より高度なVerificationでは、照度SensorやCameraを利用する可能性もある。

しかしCameraはPrivacy Impactが大きい。

照明確認だけが目的なら、映像を保存するより、

光量Sensor。

Controller Telemetry。

限定的なEdge Feature。

など、よりPrivacy Costの低い方法を優先する。

Need Lighting Verification

Need Continuous Video Recording

である。

19 暗闇を安全に扱う

FEELCYCLEの重要な空間要素として暗闇がある。

しかしPhysical Runtimeで「暗闇」を単なるintensity = 0として扱うだけでは不十分である。

実Studioには、

避難。

設備安全。

運用。

法令。

非常時照明。

などAutomationとは独立したSafety Layerが必要になる。

したがって、

FEELCYCLE Lighting Runtime

が制御する演出照明と、

施設安全のための照明

を分離する。

Experience Lighting

AI / Runtime Control
Safety / Emergency Lighting

Independent Safety System

AIが後者を無効化できない構造にする。

20 Safety Envelope

Lightingにも許容範囲を置く。

#[derive(
   Debug,
   Clone,
)]
struct LightingSafetyConfig {
   allowed_scene_ids:
       std::collections::HashSet<
           String
       >,
   minimum_transition_ms:
       u64,
   allow_dynamic_effects:
       bool,
}

Commandを検証する。

fn validate_lighting_command(
   command:
       &LightingCommand,
   safety:
       &LightingSafetyConfig,
) -> Result<
   (),
   LightingError,
> {
   if !safety
       .allowed_scene_ids
       .contains(
           &command.scene_id
       )
   {
       return Err(
           LightingError::
               SceneNotAllowed
       );
   }
   if command.transition_ms
       < safety.minimum_transition_ms
   {
       return Err(
           LightingError::
               UnsafeTransition
       );
   }
   Ok(())
}

実際の安全要件は照明設備、演出内容、専門家の検証に基づいて具体化する。

21 Dynamic Lightingと注意

高速な点滅や急激な光変化は、人によって安全上の問題を生じ得る。

したがってAIに自由なEffect Generationを許可する設計にはしない。

利用可能なSceneやEffectを事前検証済みのAllowlistへ限定する。

AI

Semantic Intent

Approved Scene Library

Safety Validation

Physical Output

AIは安全範囲の外へ新しいHardware Patternを直接生成しない。

22 Scene Library

CREATE TABLE approved_lighting_scenes (
   scene_id UUID PRIMARY KEY,
   studio_id UUID NOT NULL,
   scene_name TEXT NOT NULL,
   semantic_intent TEXT NOT NULL,
   safety_status TEXT NOT NULL,
   approved_at TIMESTAMPTZ,
   approved_by UUID,
   active BOOLEAN NOT NULL
       DEFAULT FALSE
);

Production Runtimeでは、

safety_status = 'approved'

かつ、

active = TRUE

のSceneだけを実行対象にする。

23 AIとLighting

Python Intelligenceができるのは、

Music Energyを解析する。

Sectionを認識する。

Collective Stateを推定する。

Lighting Candidateを生成する。

ことである。

例えば、

Music Energy rising
+
Transition approaching
+
Program Context

Suggestion:
Build → Peak

とする。

しかし直接Fixture Valueにはしない。

SuggestionがProgram PolicyやHuman Approvalを必要とする場合は、Instructor Consoleへ送る。

Python

Lighting Suggestion

Instructor

Accept / Modify / Ignore

Rust Runtime

Approved Scene

Physical Lighting

とする。

24 Program-defined Lighting

一方、Program Creatorがあらかじめ設計し、承認済みProgram Versionに含まれるLighting CueはAutomationとして実行できる。

ただしそれでも、

Rust Runtime。

Safety Validation。

Lighting Adapter。

を通す。

Published Program

Scheduled Lighting Cue

Runtime

Safety

Adapter

Scene

Program Published

Safety Bypass

である。

25 Manual Override

Lighting Automationは人間が切り離せる。

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
)]
enum LightingControlMode {
   Automatic,
   Assisted,
   Manual,
   Safe,
}

Manualへ移行する。

fn enter_lighting_manual(
   mode:
       &mut LightingControlMode,
) {
   *mode =
       LightingControlMode::Manual;
}

Manual ModeではRuntimeからのAutomation Commandを停止する。

ただしSafety Systemは維持する。

26 Safe Scene

Lighting Controller FailureやRuntime Errorが発生した場合に、可能なら事前定義したSafe Sceneへ移行する。

async fn enter_safe_lighting(
   adapter:
       &dyn LightingAdapter,
) -> Result<
   LightingExecutionResult,
   LightingError,
> {
   adapter
       .safe_scene()
       .await
}

Safe Sceneは派手な演出ではない。

人間が状況を理解し、安全に運用を継続または停止できる状態である。

27 Fallback

Lighting Failureが起きても、Audioまで止める必要がない場合がある。

Lighting Failure

Fallback Scene

Audio Continues

Instructor Continues

つまりSubsystem FailureをSession全体のFailureへ直結させない。

LightingはDegraded Modeへ移る。

28 Lighting Runtime Mode

#[derive(
   Debug,
   Clone,
   Copy,
)]
enum LightingRuntimeMode {
   Normal,
   Fallback,
   Manual,
   Safe,
   Unavailable,
}

Studio MonitorではこのStateを表示する。

Instructor Consoleではより簡潔に、

通常。

代替照明。

手動。

安全状態。

程度へTranslationする。

29 Device Health

#[derive(
   Debug,
   Clone,
)]
struct LightingHealth {
   edge_adapter:
       DeviceHealth,
   controller:
       DeviceHealth,
   network:
       DeviceHealth,
   fixtures:
       DeviceHealth,
}

全体Healthを計算する。

fn overall_lighting_health(
   health:
       &LightingHealth,
) -> DeviceHealth {
   let states = [
       health.edge_adapter,
       health.controller,
       health.network,
       health.fixtures,
   ];
   if states
       .contains(
           &DeviceHealth::Unavailable
       )
   {
       DeviceHealth::Unavailable
   } else if states
       .contains(
           &DeviceHealth::Degraded
       )
   {
       DeviceHealth::Degraded
   } else if states
       .contains(
           &DeviceHealth::Unknown
       )
   {
       DeviceHealth::Unknown
   } else {
       DeviceHealth::Healthy
   }
}

30 Preflight

Session開始前にはLightingを確認する。

#[derive(Debug)]
struct LightingPreflight {
   controller_ready:
       bool,
   scene_library_ready:
       bool,
   network_ready:
       bool,
   safe_scene_ready:
       bool,
   manual_control_ready:
       bool,
}

Critical要素が利用不能なら、開始前にOperatorへ返す。

31 Preflightの意味

確認するのは、

Controllerへ接続できるか。

Programで利用するSceneが存在するか。

Sceneが承認済みか。

Safe Sceneが利用できるか。

Manual Controlが使えるか。

Network Pathが正常か。

である。

Lighting Systemが「動くか」だけではなく、

安全に失敗できるか

まで確認する。

32 Lighting Event Memory

実行結果をDatabaseへ戻す。

CREATE TABLE lighting_runtime_events (
   lighting_event_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   session_id UUID NOT NULL,
   command_id UUID NOT NULL,
   scene_id TEXT NOT NULL,
   semantic_intent TEXT NOT NULL,
   target_seconds
       DOUBLE PRECISION,
   dispatched_seconds
       DOUBLE PRECISION,
   applied_seconds
       DOUBLE PRECISION,
   runtime_mode TEXT NOT NULL,
   result TEXT NOT NULL,
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

これによってPhysical LightingのTimelineを再構成できる。

33 Lighting Timing Difference

Session終了後に、

SELECT
   semantic_intent,
   target_seconds,
   applied_seconds,
   (
       applied_seconds
       - target_seconds
   ) * 1000
       AS difference_ms
FROM lighting_runtime_events
WHERE session_id = $1
 AND applied_seconds
       IS NOT NULL
ORDER BY
   target_seconds;

とすれば、Physical Timing Differenceを検証できる。

34 Lighting Session Summary

CREATE TABLE lighting_session_summaries (
   session_id UUID PRIMARY KEY,
   availability
       DOUBLE PRECISION,
   average_timing_difference_ms
       DOUBLE PRECISION,
   p95_timing_difference_ms
       DOUBLE PRECISION,
   fallback_count
       INTEGER NOT NULL
       DEFAULT 0,
   manual_override_count
       INTEGER NOT NULL
       DEFAULT 0,
   failure_count
       INTEGER NOT NULL
       DEFAULT 0,
   generated_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

Company DashboardではこのSummaryを使える。

35 AudioとLightingを比較する

同じSession IDとTarget Timeによって、AudioとLightingを比較できる。

Target Transition
138.500 sec
Audio Applied
138.504 sec
Lighting Applied
138.518 sec

であれば、Physical Differenceを測定できる。

しかし、常に0msにすることが目的ではない。

人間が知覚するExperienceとHardware特性を踏まえ、必要十分な同期を目指す。

36 Synchronization Memory

CREATE TABLE physical_sync_measurements (
   measurement_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   session_id UUID NOT NULL,
   reference_type TEXT NOT NULL,
   reference_seconds
       DOUBLE PRECISION NOT NULL,
   audio_seconds
       DOUBLE PRECISION,
   lighting_seconds
       DOUBLE PRECISION,
   difference_ms
       DOUBLE PRECISION,
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW()
);

こうしたDataからStudioごとのLatency Characteristicを学習できる。

37 LightingとInstructor

Lighting Systemが高度化してもInstructorをLighting Operatorにしてはいけない。

Instructor Consoleでは必要な場合のみ、

Current Scene。

Next Lighting Transition。

AI Suggestion。

Manual State。

を表示する。

複雑なFixtureやUniverse情報はStudio Monitor側に置く。

InstructorのAttentionはPhysical Realityへ残す。

38 LightingとMember

Member AppへRealtime Lighting Dataを流す必要は基本的にない。

MemberはLightingをDataとして見るのではなくExperienceとして受け取る。

Technologyを意識させないこと自体がUI Designになる。

39 LightingとProgram Creator

Program BuilderではLighting TimelineをMusic、Movementと同じ時間軸上で設計する。

Music
──────Build────────Peak────Release
Movement
────Build──────────Peak────Recovery
Lighting
Dark──Focus──Build──Peak────Release

Program CreatorはHardware ChannelではなくRelationを見る。

これがLighting IntelligenceのHuman Interfaceである。

40 Lighting System v0.1の完成条件

FEELCYCLE Lighting System v0.1は、最低限次を満たす。

Semantic Lighting Intentを定義できる。

Studio固有SceneへTranslationできる。

SceneをVersion管理できる。

承認済みSceneだけをProduction実行できる。

Rust RuntimeからTarget Time付きCommandを送れる。

Device Latencyを考慮できる。

Controller Stateを観測できる。

Desired StateとObserved Stateを分離できる。

Human Manual Overrideを保持できる。

Failure時にFallbackまたはSafe Sceneへ移行できる。

施設Safety LightingをAI制御から独立させられる。

Lighting EventをSession Memoryへ戻せる。

AudioとのPhysical Synchronizationを検証できる。

ここまででFEELCYCLE AIは、

Musicを「音」へ。

Program Stateを「光」へ。

変換できるようになった。

音と光が同じSession Clockへ接続され、45分の時間構造がPhysical Spaceへ展開される。

しかし、ここまでのSystemはまだRealityへ作用する能力の方が強い。

次に必要なのは、Realityを見る能力である。

Studioでは何が起きているのか。

Audio Systemは実際に音を出しているのか。

空間の状態はどう変化しているのか。

Deviceは正常か。

人間を監視することなく、Physical Runtimeに必要なObservationをどう取得するのか。

次節では、

Microphone・Sensor・Edge Device

を実装する。

Physical Realityを無差別に記録するのではなく、必要なFeatureだけをEdgeで抽出し、RealityからAIへの安全な帰路を構築する。
第4節 Microphone・Sensor・Edge Device

Audio Systemによって音がPhysical Realityへ出力され、Lighting Systemによって光が空間へ展開された。

次に必要なのは、Physical RealityからFEELCYCLE AIへ戻る経路である。

Studioでは、SoftwareがCommandを送るだけでは十分ではない。

実際に音は出ているのか。

Lighting Controllerは正常なのか。

Networkは安定しているのか。

Physical Deviceは応答しているのか。

Studioの環境状態にはどのような変化が生じているのか。

こうしたRealityを観測しなければ、AIは自分が生成したCommandの結果を知ることができない。

そこで本節では、

Microphone。

Sensor。

Edge Device。

をPhysical Runtimeへ接続する。

ただし、最初に境界を置く。

FEELCYCLE AIのSensor Systemは、人間を無差別に監視するSystemではない。

目的は、

Physical Runtimeに必要なRealityを、必要最小限のDataによって観測すること

である。

したがって基本構造は、

Physical Reality

Sensor

Raw Signal

Edge Processing

Feature

Rust Runtime

Python Intelligence / Memory

となる。

重要なのは、

Raw Signal

Meaning

である。

Raw Audioを保存することと、Studioの音響状態を理解することは同じではない。

Camera映像を記録することと、空間状態を理解することも同じではない。

可能な限りEdgeで必要なFeatureへ変換し、不要なRaw Dataは残さない。

1 Observation Layer

FEELCYCLE Physical RuntimeにObservation Layerを追加する。

Physical Studio

├── Audio Output
├── Lighting Output
├── Instructor Voice
├── Device State
├── Network State
└── Environmental State
         ↓
       Sensors
         ↓
     Edge Device
         ↓
 Feature Extraction
         ↓
    Observation
         ↓
     Rust Runtime
         ↓
Python Intelligence
         ↓
      Memory

ここでSensorはRealityを完全に記述するものではない。

Realityの一部分を観測する。

複数のObservationを統合しながら、AIが推定する。

したがって、

Observation

Interpretation

Estimate

を分離する。

2 Microphoneの役割を分ける

FEELCYCLE Studioには、少なくとも二種類のMicrophoneの役割が考えられる。

Instructor Microphone。

Measurement Microphone。

である。

Instructor Microphone
=
声をPhysical Audio Systemへ届ける
Measurement Microphone
=
Studioの音響Realityを観測する

二つを同じData Pipelineへ入れない。

Instructor MicrophoneはAudio Inputである。

Measurement MicrophoneはSensorである。

目的が違う。

3 Instructor Microphone

Instructor Microphoneの主要経路は、

Instructor

Microphone

Mixer

DSP

Speaker

Studio

である。

AIが必要とする場合でも、必ずしも音声内容そのものを記録する必要はない。

必要なのが、

Voice Activity。

Signal Level。

Microphone Health。

程度なら、EdgeでFeatureだけを抽出する。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
struct VoiceActivityFeature {
   active: bool,
   signal_level:
       Option<f64>,
   confidence:
       f64,
   observed_at_seconds:
       f64,
}

これだけでも、

現在Instructor Voiceが存在するか。

Signalが途切れていないか。

を観測できる。

Speech Recordingをしなくても成立する。

4 Measurement Microphone

Studio AudioをPhysical Realityとして確認したい場合には、Measurement Microphoneを利用できる。

目的は、

Playback EngineがPlayingと言っている。

しかしStudioでは本当に音が出ているのか。

を確認することである。

Playback Engine

Audio Chain

Speaker

Studio

Measurement Microphone

Feature Extraction

Output Presence

例えばFeatureを、

#[derive(
   Debug,
   Clone,
)]
struct AudioEnvironmentFeature {
   output_detected:
       bool,
   relative_level:
       Option<f64>,
   spectral_summary:
       Option<Vec<f64>>,
   confidence:
       f64,
}

とする。

ここでもRaw RecordingをDefault Memoryにしない。

5 Raw Audioを保存しない設計

音響状態の検証が目的なら、

Raw Audio

Edge Memory

Feature Extraction

Feature

Raw Audio Discard

とできる。

つまり、

Need Audio Feature

Need Audio Archive

である。

これはPrivacyだけでなくStorage、Security、Governanceの負荷も下げる。

Raw Audioが必要になる特別なTestingやCalibrationでは、別の明示的なModeとPolicyを使う。

6 Sensor Type

StudioにはMicrophone以外にも複数Sensorを配置できる。

ただしv0.1では、目的ごとに限定する。

#[derive(
   Debug,
   Clone,
   Copy,
   serde::Serialize,
   serde::Deserialize,
)]
enum SensorType {
   AudioMeasurement,
   Environmental,
   DeviceTelemetry,
   LightingMeasurement,
   NetworkTelemetry,
}

将来のSensorを追加できるが、何でも収集するArchitectureにはしない。

7 Environmental Sensor

Physical Environmentの安定性を見る場合、

温度。

湿度。

機材Rackの環境。

などを設備保守目的で扱える。

#[derive(
   Debug,
   Clone,
)]
struct EnvironmentObservation {
   temperature_c:
       Option<f64>,
   humidity_ratio:
       Option<f64>,
   observed_at:
       chrono::DateTime<
           chrono::Utc
       >,
}

ただしMemberの身体状態をEnvironmental Sensor Dataから勝手に推定するような利用へ拡大しない。

Sensor DataのPurposeを守る。

8 Lighting Measurement

Lighting ControllerがScene Appliedと返しても、Physical Outputが完全に一致しているとは限らない。

必要なら簡易的な光Sensorを使える。

#[derive(
   Debug,
   Clone,
)]
struct LightingObservation {
   relative_light_level:
       f64,
   sensor_id:
       uuid::Uuid,
   observed_at_seconds:
       f64,
   confidence:
       f64,
}

ただし、この値をそのまま、

LightingIntent::Peak

と断定しない。

Observed LevelはMeasurement。

Scene IdentityはInterpretationである。

9 Device Telemetry

多くの場合、Physical Runtimeで最も有用なSensorは専用の人体Sensorではなく、Device自身が返すTelemetryである。

Audio Interface。

Mixer。

DSP。

Lighting Controller。

Edge Computer。

Network Switch。

などの状態を取得する。

#[derive(
   Debug,
   Clone,
)]
struct DeviceTelemetry {
   device_id:
       uuid::Uuid,
   online:
       bool,
   latency_ms:
       Option<f64>,
   error_code:
       Option<String>,
   observed_at:
       chrono::DateTime<
           chrono::Utc
       >,
}

これによって、人間を観測せずにSystem Healthの多くを理解できる。

10 Network Telemetry

Physical Deviceの多くがNetworkへ接続されるなら、Network自身を観測する。

#[derive(
   Debug,
   Clone,
)]
struct NetworkObservation {
   target:
       String,
   latency_ms:
       Option<f64>,
   packet_loss_ratio:
       Option<f64>,
   reachable:
       bool,
   observed_at:
       chrono::DateTime<
           chrono::Utc
       >,
}

Network FailureをLighting FailureやAudio Failureと誤認しないために重要である。

11 Sensor Observationの共通型

異なるSensorをRuntimeへ接続するため、共通Envelopeを作る。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
struct SensorObservation {
   observation_id:
       uuid::Uuid,
   session_id:
       Option<uuid::Uuid>,
   sensor_id:
       uuid::Uuid,
   sensor_type:
       SensorType,
   observed_at_seconds:
       Option<f64>,
   evidence_type:
       EvidenceType,
   quality:
       SensorQuality,
   payload:
       serde_json::Value,
}

Evidence Typeを定義する。

#[derive(
   Debug,
   Clone,
   Copy,
   serde::Serialize,
)]
enum EvidenceType {
   Observed,
   Estimated,
   Derived,
}

Sensor Dataは原則Observedである。

Sensorを組み合わせてAIが生成したCollective StateなどはEstimatedになる。

この区別を失わない。

12 Sensor Quality

すべてのObservationへQualityを付ける。

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
   serde::Serialize,
)]
enum SensorQuality {
   Good,
   Partial,
   Poor,
   Unavailable,
}

例えばMicrophone Featureが、

value = 0

なのか、

Sensor unavailable

なのかを区別する。

0

No Data

である。

13 Sensor Health

#[derive(
   Debug,
   Clone,
)]
struct SensorHealth {
   sensor_id:
       uuid::Uuid,
   device_health:
       DeviceHealth,
   last_observation:
       Option<
           std::time::Instant
       >,
   sample_rate_hz:
       Option<f64>,
}

一定時間Observationが来なければ、UnknownまたはUnavailableへ移す。

14 Edge Deviceとは何か

ここでEdge Deviceの役割を明確にする。

Edge Deviceは単なるStudio PCではない。

CloudとPhysical Realityの境界に置かれるLocal Execution Nodeである。

最低限、

Rust Runtime。

Audio Adapter。

Lighting Adapter。

Sensor Gateway。

Local Memory。

Health Monitoring。

Cloud Synchronization。

を動かす。

FEELCYCLE Edge

├── Runtime Core
├── Audio
├── Lighting
├── Sensor
├── Local Database / Buffer
├── Health
├── Security
└── Cloud Sync

これがStudio単位のPhysical Computer Coreになる。

15 Edge Hardwareの要件

具体的な製品名より、Requirementを先に定める。

必要なのは、

安定したCPU。

十分なMemory。

SSD。

複数のNetwork Interface。

必要なUSB / Audio Interface。

自動再起動。

Watchdog。

長時間連続稼働。

適切な冷却。

遠隔保守可能性。

である。

必要に応じてGPUやNPUを追加できる。

しかし、v0.1のRealtime Runtime自体に巨大GPUは必須ではない。

重いAI InferenceはCloudまたは別Computeへ分離できる。

16 Edgeで何を処理するか

Edgeへ何でも詰め込む必要はない。

Edgeで処理すべきものは、

低Latencyが必要。

Cloud Failure時にも必要。

Privacy上Local Processingが望ましい。

というものを優先する。

Edge
=
Low Latency
+
Local Continuity
+
Privacy-sensitive Feature Extraction

具体的には、

Runtime Clock。

Audio / Lighting Control。

Sensor Feature Extraction。

Device Health。

Local Event Buffer。

Fallback。

などである。

17 Cloudで何を処理するか

一方Cloudへ置くのは、

長期Memory。

Cross-Studio Analytics。

Model Training。

Company Dashboard。

Global Program Distribution。

などである。

Edge
=
Current Reality
Cloud
=
Shared Long-Term Memory

と整理できる。

18 EdgeとCloudの通信

EdgeからCloudへは、直接Database Connectionを持たせるよりAPI / Queue Layerを通す。

Edge

Authenticated Connection

Ingestion API

Queue

Cloud Memory

CloudからEdgeへは、

Program Configuration。

Approved Model。

Studio Configuration。

などを配信する。

しかしCloudから任意のPhysical Commandを直接Pushして即時実行するArchitectureには慎重である。

19 Command Authority

CloudがPhysical Runtimeへ何かを送る場合でも、

Cloud Request

Edge Validation

Authority Check

Safety Policy

Local Runtime

Device

とする。

Edgeが最後のAuthority Boundaryになる。

Cloud

Unrestricted Remote Control

である。

20 Edge Service Architecture

Rust側のProcessを分割する。

feelcycle-edge

├── runtime-core
├── audio-service
├── lighting-service
├── sensor-service
├── health-service
├── persistence-service
└── cloud-sync-service

すべてを一つの巨大なFunctionへ書かない。

ProcessまたはModule Boundaryを持たせる。

21 Sensor Gateway

Sensor Gatewayは複数Sensor Inputを受け取る。

#[async_trait::async_trait]
trait SensorAdapter:
   Send + Sync
{
   async fn read(
       &self,
   ) -> Result<
       SensorObservation,
       SensorError,
   >;
   async fn health(
       &self,
   ) -> DeviceHealth;
}

Microphone Feature Adapter。

Environment Adapter。

Network Adapter。

などがこれを実装する。

22 Sensor Polling

一定周期でSensorを読むものなら、

async fn sensor_loop(
   adapter:
       &dyn SensorAdapter,
   tx:
       tokio::sync::mpsc::Sender<
           SensorObservation
       >,
   interval:
       std::time::Duration,
) {
   let mut timer =
       tokio::time::interval(
           interval
       );
   loop {
       timer.tick().await;
       match adapter.read().await {
           Ok(observation) => {
               let _ =
                   tx.send(
                       observation
                   )
                   .await;
           }
           Err(error) => {
               record_sensor_error(
                   error
               )
               .await;
           }
       }
   }
}

とできる。

必要以上のSampling Rateにしない。

Purposeに応じたFrequencyを選ぶ。

23 Streaming Sensor

Microphone FeatureのようにContinuous Streamが必要なら、Audio Processing ThreadからFeatureだけを送る。

Audio Stream

Windowing

Feature Extraction

Feature Queue

Sensor Gateway

Raw SamplesをRuntime Event Queueへ流し続けるのではない。

Feature Levelまで圧縮する。

24 Feature Window

例えば一定時間WindowでFeatureを生成する。

#[derive(Debug)]
struct FeatureWindow {
   started_at_seconds:
       f64,
   ended_at_seconds:
       f64,
   sample_count:
       usize,
}

これによって、

「この100ms / 500ms / 1秒間で何が観測されたか」

として扱える。

具体的Windowは用途ごとに設定する。

25 Edge AI

将来的にはEdge上で軽量Machine Learning Modelを動かせる。

例えば、

Audio Feature Classification。

Device Anomaly Detection。

Sensor Quality Estimation。

などである。

しかしEdge AIのOutputは、

Observed

ではなく、

Estimated

として扱う。

#[derive(
   Debug,
   Clone,
)]
struct EdgeEstimate {
   estimate_type:
       String,
   value:
       serde_json::Value,
   confidence:
       f64,
   model_version:
       String,
}

Model Versionも残す。

26 Edge Model Distribution

Cloudで検証済みModelをEdgeへ配布する。

Training

Evaluation

Approved Model

Signed Artifact

Edge Download

Verification

Activate

Model Fileを取得しただけで即Activateしない。

ChecksumやSignatureを検証する。

27 Model Rollback

新しいEdge Modelに問題があれば前Versionへ戻せるようにする。

#[derive(
   Debug,
   Clone,
)]
struct EdgeModelState {
   active_version:
       String,
   previous_version:
       Option<String>,
}

AI ModelもHardware Configurationと同様にVersioned Runtime Componentである。

28 Edge Configuration

Edge Device自身のConfigurationを持つ。

#[derive(
   Debug,
   Clone,
   serde::Deserialize,
)]
struct EdgeConfig {
   studio_id:
       uuid::Uuid,
   device_id:
       uuid::Uuid,
   environment:
       String,
   cloud_endpoint:
       String,
   local_data_path:
       String,
   sensor_enabled:
       bool,
}

SecretはConfiguration Fileへ平文保存しない。

Credential StoreやOS-level Secret Mechanismを使う。

29 Boot Sequence

Edge Deviceが起動したら、いきなりHardware Controlを開始しない。

Boot

Configuration Load

Identity Verify

Local Memory Check

Adapter Initialize

Device Health Check

Safe State

Cloud Connect

Ready

という順序にする。

30 Boot State

#[derive(
   Debug,
   Clone,
   Copy,
)]
enum EdgeBootState {
   Starting,
   LoadingConfig,
   InitializingDevices,
   Verifying,
   Ready,
   Degraded,
   Failed,
}

Studio Monitorから確認できるようにする。

31 Watchdog

Edge Runtimeが停止した場合、自動的に検出できる仕組みを持つ。

Software WatchdogやOS / Hardware Watchdogを利用できる。

概念的には、

Runtime Heartbeat

Watchdog
├── Healthy → Continue
└── Missing → Restart / Safe State

とする。

ただしRestart Loopに陥らないよう制御する。

32 Local Persistence

Cloudが切れてもObservationや重要Eventを失わないため、EdgeへLocal Persistenceを持つ。

v0.1ではSQLiteやLocal Append-only Logなどを利用できる。

例えばSQLiteなら、

CREATE TABLE local_events (
   event_id TEXT PRIMARY KEY,
   session_id TEXT,
   event_type TEXT NOT NULL,
   payload TEXT NOT NULL,
   synced INTEGER NOT NULL DEFAULT 0,
   created_at TEXT NOT NULL
);

Cloud Sync後にsynced = 1へする。

33 Append-only Log

Physical RuntimeではAppend-only Event Logも有効である。

Event 001
Event 002
Event 003
...

Current Stateだけでなく、

何が起きたか

を保持する。

Edge Failure後にもSessionを再構成しやすい。

34 Cloud Reconciliation

Cloud復旧後、

Local Event ID。

Cloud Event ID。

を比較する。

未同期だけ送る。

async fn sync_pending_events(
   events:
       Vec<LocalEvent>,
) {
   for event in events {
       if cloud_has_event(
           event.event_id
       )
       .await
       {
           mark_synced(
               event.event_id
           )
           .await;
           continue;
       }
       upload_event(
           &event
       )
       .await;
   }
}

Event IDによって重複Memoryを防ぐ。

35 Edge Security

Edge DeviceはPhysical Studio内にあるため、Cloudとは別のSecurity Riskを持つ。

機材へのPhysical Access。

USB Device。

Network接続。

Local Storage。

Remote Maintenance。

を考える。

最低限、

OS Update。

Firewall。

Disk Encryption。

Service Account。

Secure Bootが可能なら利用。

Unused Portの無効化。

Remote Accessの制限。

を行う。

36 Device Identity

Edge DeviceもIdentityを持つ。

CREATE TABLE edge_devices (
   edge_device_id UUID PRIMARY KEY,
   studio_id UUID NOT NULL,
   device_name TEXT NOT NULL,
   hardware_reference TEXT,
   software_version TEXT,
   status TEXT NOT NULL,
   installed_at TIMESTAMPTZ,
   last_seen_at TIMESTAMPTZ,
   FOREIGN KEY (
       studio_id
   )
   REFERENCES studios(
       studio_id
   )
);

Cloudは、

「Studio Aを名乗っている何か」

ではなく、

認証されたEdge Device

として扱う。

37 Certificate-based Identity

EdgeとCloud間では、Device Certificateなどを用いたMachine Identityを利用できる。

概念構造は、

Edge Private Key

Certificate

Mutual Authentication

Cloud

とする。

固定API Keyを全Studioで共有する設計は避ける。

一台がCompromiseされても全Studioへ広がらないようにする。

38 ObservationをRuntimeへ送る

Sensor ObservationをRust Runtimeへ流す。

async fn observation_router(
   mut rx:
       tokio::sync::mpsc::Receiver<
           SensorObservation
       >,
   runtime_tx:
       tokio::sync::mpsc::Sender<
           RuntimeEvent
       >,
) {
   while let Some(
       observation
   ) = rx.recv().await
   {
       if let Some(event) =
           observation_to_runtime_event(
               observation
           )
       {
           let _ =
               runtime_tx
                   .send(event)
                   .await;
       }
   }
}

すべてのObservationをRuntime Eventへ変換する必要はない。

意味のあるState Changeだけを流す。

39 Noiseを抑える

SensorはNoiseを持つ。

一回だけNetwork Latencyが上がった。

一瞬Measurement MicのFeatureが欠落した。

それだけでRuntime Stateを頻繁に切り替えるとSystemが不安定になる。

そこでDebounceやHysteresisを使う。

概念的には、

One abnormal sample

Observe
Repeated abnormal samples

Degraded State

とする。

40 Simple Threshold State

struct FailureCounter {
   consecutive_failures:
       u32,
   threshold:
       u32,
}
impl FailureCounter {
   fn report_failure(
       &mut self,
   ) -> bool {
       self.consecutive_failures
           += 1;
       self.consecutive_failures
           >= self.threshold
   }
   fn report_success(
       &mut self,
   ) {
       self.consecutive_failures =
           0;
   }
}

これによって一時的NoiseでPhysical Runtime全体がFallbackへ入るのを減らす。

41 Sensor Fusion

複数Observationを統合できる。

例えばAudio Output確認では、

Playback Engine State。

Mixer State。

Measurement Microphone。

を一緒に見る。

Playback says Playing
+
Mixer says Healthy
+
Measurement says Output Present

High Confidence Audio Output

一方、

Playback says Playing
+
Measurement says Silence

Possible Physical Failure

となる。

Single SensorだけでRealityを決めない。

42 Observation Confidence

統合結果にはConfidenceを持たせる。

#[derive(
   Debug,
   Clone,
)]
struct RealityEstimate {
   estimate_type:
       String,
   confidence:
       f64,
   evidence_ids:
       Vec<uuid::Uuid>,
}

Evidence IDから元Observationへ遡れる。

43 Collective Stateとの境界

将来的にStudio全体のCollective StateをAIが推定する場合も、

Microphone。

Wearable Aggregate。

Program Context。

Instructor Observation。

など複数Sourceを使う。

しかし、

Collective State

Sensor Fact

ではない。

AI Estimateである。

そのためMemoryにはEstimatedとして保存する。

44 Raw Human RecordingをDefaultにしない

MicrophoneやCameraを使えば大量のHuman Dataを収集できる。

しかしFEELCYCLE AIの価値はData量に比例しない。

必要なのは、

音が存在する。

Deviceが正常。

Lightingが反応した。

Networkが遅延している。

といった必要なPhysical Featureである場合が多い。

したがって、

More Recording

Better Intelligence

とする。

45 CameraをDefault Sensorにしない

身体運動解析にはCameraが使える可能性がある。

しかしPrivacy ImpactはMicrophone以上に大きい。

v0.1ではCameraを必須Architectureから外す。

将来利用する場合も、

明確なPurpose。

Consent。

Edge Processing。

Data Retention。

No Raw Recording Option。

などを先に設計する。

46 Sensor Privacy Boundary

Physical Sensorについて、

Operational Sensor

System Health
Optional Human Sensor

Explicit Consent

を分離する。

Network Switch TelemetryにMember Consentは通常必要ない。

Wearable DataやHuman Recordingは別である。

Data Categoryを同一に扱わない。

47 Sensor Memory

Cloudへ保存するのは意味のあるObservationやSummaryに限定する。

CREATE TABLE sensor_observations (
   observation_id UUID PRIMARY KEY,
   session_id UUID,
   sensor_id UUID NOT NULL,
   sensor_type TEXT NOT NULL,
   evidence_type TEXT NOT NULL,
   quality TEXT NOT NULL,
   observed_at_seconds
       DOUBLE PRECISION,
   payload JSONB NOT NULL,
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

高頻度Raw SampleはObject Storageまたは短期Local Storageへ分離するか、保存しない。

48 Sensor Summary

Session終了後にはSummaryへ圧縮する。

CREATE TABLE sensor_session_summaries (
   sensor_summary_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   session_id UUID NOT NULL,
   sensor_type TEXT NOT NULL,
   availability
       DOUBLE PRECISION,
   observation_count
       INTEGER NOT NULL,
   quality_summary TEXT,
   summary JSONB NOT NULL
       DEFAULT '{}'::jsonb,
   generated_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

Company Dashboardでは原則Summaryを利用する。

49 Edge Health Summary

Edge Device自身もSessionごとに評価する。

CREATE TABLE edge_session_summaries (
   session_id UUID PRIMARY KEY,
   edge_device_id UUID NOT NULL,
   runtime_availability
       DOUBLE PRECISION,
   restart_count INTEGER NOT NULL
       DEFAULT 0,
   local_buffer_peak
       INTEGER NOT NULL
       DEFAULT 0,
   cloud_disconnect_count
       INTEGER NOT NULL
       DEFAULT 0,
   generated_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

これによってEdge RuntimeのOperational Qualityも学習できる。

50 Preflight

Session開始前にSensorとEdgeも確認する。

#[derive(Debug)]
struct SensorEdgePreflight {
   edge_runtime_ready:
       bool,
   local_storage_ready:
       bool,
   audio_measurement_ready:
       bool,
   network_monitor_ready:
       bool,
   cloud_sync_ready:
       bool,
}

Cloud Syncだけ失われているならSessionは開始可能かもしれない。

Edge Runtimeそのものが利用不能ならCriticalである。

Componentごとの重要度を分ける。

51 Priority

Physical RealityにおけるSensorのPriorityを整理する。

Safety
>
Human Authority
>
Edge Runtime
>
Audio
>
Lighting
>
Operational Sensor
>
Optional Sensor
>
AI Optimization

Sensor Enhancementが失われてもSessionは継続する。

Sensor Failure

Session Failure

である。

52 Fallback

Measurement Microphoneが失われた。

その場合、

Output Verification機能は失われる。

しかしAudio Playback自体は継続できる。

Measurement Failure

Verification Degraded

Audio Continues

Network Telemetryが失われた場合も同様である。

SensorのFailureによって不要にPhysical Outputを止めない。

53 Edge Failure

一方、Edge DeviceそのものがFailureした場合は影響が大きい。

そこで、

Primary Edge。

Manual Physical Control。

Safe State。

などのFallbackを設計する。

規模によってはSecondary Edgeも検討できる。

しかしまずv0.1では、

Edge Failureを検出する。

Automationを安全停止する。

Manualへ移行できる。

ことをCompletion Conditionとする。

54 Manual Physical Operation

Edgeが停止しても、

Audio Mixer。

Lighting Console。

設備Safety。

などのPhysical Control Pathが完全に失われないArchitectureが望ましい。

AI / Edge Automation
↓ failure
Physical Manual Control

Human Operation

Digital AutomationがPhysical Realityの唯一の入口にならない。

55 Edge Update

Edge Softwareの更新も慎重に行う。

Build

Test

Staging

Signed Release

Edge Download

Verify

Install

Health Check

Activate

Session中に無条件でUpdateしない。

Maintenance Windowを利用する。

56 Rollback

#[derive(
   Debug,
   Clone,
)]
struct EdgeSoftwareVersion {
   current:
       String,
   previous:
       Option<String>,
}

Update後のPreflightに失敗したらRollbackする。

Software DeploymentもPhysical Safetyへ影響するからである。

57 Observability

Edge Runtime自身のMetricsを収集する。

CPU。

Memory。

Disk。

Queue Depth。

Device Connection。

Sensor Health。

Cloud Sync Delay。

などである。

しかしMetricsを高頻度でCloudへ送りすぎてNetwork負荷を増やさない。

適切にAggregationする。

58 Edge Diagnostic Event

#[derive(
   Debug,
   serde::Serialize,
)]
struct EdgeDiagnosticEvent {
   edge_device_id:
       uuid::Uuid,
   category:
       String,
   code:
       String,
   occurred_at:
       chrono::DateTime<
           chrono::Utc
       >,
}

Diagnostic LogへRaw AudioやPersonal Dataを不用意に混ぜない。

Observability

Personal Data Collection

である。

59 Studio Monitorとの接続

TypeScript Studio Monitorには、

Edge。

Microphone。

Sensor。

Network。

のHealthを表示する。

しかしRaw Dataを大量表示しない。

Edge
Healthy
Audio Measurement
Healthy
Network
Degraded
Optional Sensor
Unavailable

という意味レベルで表示する。

詳細はDrill Downする。

60 Instructor Consoleとの接続

Instructor Consoleにはさらに少なく表示する。

通常時は何も表示しない。

必要な場合のみ、

「一部Sensorを利用できません。Sessionは継続できます」

と表示する。

Technology FailureによってInstructorのAttentionを不必要に奪わない。

61 Company Dashboardとの接続

Company側には、

Edge Availability。

Sensor Availability。

Network Failure傾向。

Configuration Version。

などをAggregationする。

Raw Audio Featureや個別ObservationをCompany Dashboardの中心には置かない。

Company Observer PositionではStructureを見る。

62 Python Intelligenceとの接続

PythonはSensor Featureを受け取る。

例えば、

def build_runtime_context(
   observations: list[dict],
) -> dict:
   return {
       "audio_output_present":
           latest_feature(
               observations,
               "audio_output"
           ),
       "network_health":
           latest_feature(
               observations,
               "network"
           ),
       "lighting_observed":
           latest_feature(
               observations,
               "lighting"
           ),
   }

ここからAIがReality Estimateを生成する。

しかしSensor Missing時にはUnknownを返す。

推測で埋めない。

63 AIはMissing Dataを知る

def confidence_from_quality(
   quality: str,
) -> float:
   return {
       "good": 1.0,
       "partial": 0.6,
       "poor": 0.3,
       "unavailable": 0.0,
   }.get(
       quality,
       0.0,
   )

これによってAI自身が、

何を知っていて。

何を知らないか。

を扱える。

64 Physical Observation Loop

ここまでを一つにまとめる。

Physical Reality

Microphone / Sensor / Telemetry

Edge Device

Feature Extraction

Quality / Evidence

Rust Runtime

Python Intelligence

Estimate / Suggestion

Human / Policy

Physical Runtime

Physical Reality


これでPhysical Runtimeは初めて閉循環になる。

65 Microphone・Sensor・Edge Device v0.1の完成条件

v0.1では、最低限次を満たす。

Instructor MicrophoneとMeasurement Microphoneの役割を分離できる。

Raw Audioを保存せずFeature抽出できる。

複数Sensorを共通Observation Modelへ変換できる。

Sensor Qualityを保持できる。

ObservedとEstimatedを区別できる。

Device Telemetryを取得できる。

Network Telemetryを取得できる。

Edge上でRust Runtimeを継続実行できる。

Cloud断時にLocal Bufferへ保存できる。

Cloud復旧後に同期できる。

Edge DeviceをMachine Identityとして認証できる。

Sensor Failure時にSessionを継続できる。

Edge Failure時にManual / Safe Stateへ移行できる。

ObservationをSession Memoryへ戻せる。

ここまで成立すれば、FEELCYCLE AIは単にPhysical Spaceへ作用するSystemではなくなる。

Realityから返答を受け取れるSystemになる。

66 見るために記録しすぎない

Physical AIでは、「見えるDataを増やすこと」が進化だと考えやすい。

しかしFEELCYCLE AIでは逆の方向を取る。

Raw Audioをすべて保存しなくてもよい。

全員をCameraで追跡しなくてもよい。

すべての身体をWearableで測定しなくてもよい。

必要なObservationを、必要な瞬間に、必要な粒度で取得する。

それでも、

Audioが出たか。

Lightingが反応したか。

Deviceが健康か。

Networkが安定しているか。

Physical CommandがRealityになったか。

を理解できる。

これはSensorを増やす設計ではなく、

Observationを最小化しながらRealityを理解する設計

である。

67 Physical Realityが返事をする

第6章第1節では、AIからPhysical Spaceへ進んだ。

第2節では音を出した。

第3節では光を出した。

本節では、RealityからAIへ戻る道を作った。

これによって、

AI

音・光

Studio

Sensor

AI

という循環が成立する。

しかしStudioには、まだ複数のDeviceを接続するCommunication Layerが必要である。

Audio。

Lighting。

Sensor。

Edge。

Cloud。

Instructor Console。

これらが別々のProtocolで無秩序につながれば、System全体は脆弱になる。

次節では、

Network・Controller・Protocol

を実装する。

Studio内部のNetworkをどう分離するのか。

Audio、Lighting、Sensorをどう接続するのか。

どのProtocolをどのLayerで使うのか。

Command、Telemetry、Realtime Eventをどう分けるのか。

そしてNetwork Failure時にも45分を継続できるPhysical Communication Architectureを構築する。
第5節 Network・Controller・Protocol

ここまでPhysical Runtimeには、実際に音を出すAudio System、空間を変えるLighting System、Realityを観測するMicrophone・Sensor、そしてStudio内でそれらを処理するEdge Deviceが実装された。

しかし、これらが存在するだけでは一つのSystemにはならない。

Rust Runtime。

Audio Playback Engine。

Mixer。

DSP。

Lighting Controller。

Sensor Gateway。

Edge Device。

Instructor Console。

Studio Monitor。

Cloud。

これらの間では、絶えずDataとCommandが移動している。

したがってPhysical Runtimeには、

何を接続するのか。

どこへ接続するのか。

どのProtocolを使うのか。

どの通信を優先するのか。

Networkが切れたとき何を残すのか。

を定義するCommunication Architectureが必要になる。

本節では、

Network。

Controller。

Protocol。

を一つのLayerとして実装する。

目的は、すべてのDeviceを一つの巨大Networkへつなぐことではない。

異なる性質を持つ通信を分離しながら、45分に必要な情報だけを確実に通すことである。

基本構造は、

Intelligence

Runtime

Command

Network

Controller

Protocol

Device

Physical Reality

である。

逆方向には、

Physical Device

Telemetry

Protocol

Network

Edge Runtime

Observation

Memory / Intelligence

が存在する。

つまりNetworkは単なる配線ではない。

Physical RealityとSoftware Realityを接続する神経系である。

1 通信を一種類にしない

FEELCYCLE Studioには、性質の異なる通信が存在する。

Audio Stream。

Lighting Command。

Sensor Observation。

Device Telemetry。

Human Interface。

Cloud Synchronization。

これらを同じPriorityで扱うべきではない。

まずCommunication Classを定義する。

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
)]
enum CommunicationClass {
   Safety,
   RuntimeControl,
   RealtimeTelemetry,
   HumanInterface,
   MemorySync,
   BackgroundAnalytics,
}

Priorityは、

Safety
>
Runtime Control
>
Realtime Telemetry
>
Human Interface
>
Memory Sync
>
Background Analytics

とする。

Cloud AnalyticsのUploadが増えたことでLighting Commandが遅れるArchitectureにはしない。

2 Studio Networkを分離する

一つのFlat Networkへすべてを置くと、Failure Domainが大きくなる。

概念的には、

Studio Network

├── Control Network
│   ├── Edge Runtime
│   ├── Lighting Controller
│   ├── Audio Controller
│   └── Sensor Gateway

├── Audio Network
│   ├── Playback
│   ├── DSP
│   └── Network Audio

├── Operations Network
│   ├── Instructor Console
│   └── Studio Monitor

└── Cloud Uplink
   └── AWS

と分ける。

実際にはVLANなどを使って論理分離できる。

重要なのは、

Member向けWi-Fi。

社内業務Network。

Physical Control Network。

を無秩序に同じBroadcast Domainへ置かないことである。

3 Network Zone

Network ZoneをDomainとして定義する。

#[derive(
   Debug,
   Clone,
   Copy,
)]
enum NetworkZone {
   PhysicalControl,
   Audio,
   Operations,
   Cloud,
}

各DeviceがどのZoneに属するかConfigurationへ持たせる。

#[derive(
   Debug,
   Clone,
   serde::Deserialize,
)]
struct NetworkDeviceConfig {
   device_id:
       uuid::Uuid,
   zone:
       NetworkZone,
   address:
       String,
   protocol:
       String,
}

これによって、Physical RuntimeがNetwork Structureを明示的に理解できる。

4 有線を中心にする

Physical Controlの中心をWi-Fiだけへ依存させない。

Lighting。

Audio。

Edge Runtime。

主要Controller。

など、固定設備間の通信では有線Networkを基本にする。

Wirelessは、

Mobile。

補助Sensor。

一部Wearable。

などに利用する。

Fixed Critical Device
→ Wired
Personal / Mobile
→ Wireless when appropriate

とする。

Wireless Failure

Core Physical Failure

へ近づける。

5 Controllerとは何か

RuntimeとPhysical Deviceの間にはControllerが存在する。

Lightingでは、

Lighting Console。

DMX / Art-Net Node。

Fixture Controller。

Audioでは、

Playback Engine。

Mixer。

DSP。

Audio Interface。

がControllerとして働く。

したがって、

Runtime

Controller

Physical Device

という一段を明示する。

AIが直接FixtureやSpeakerへ命令を送るのではない。

ControllerがDevice固有の責任を持つ。

6 Controller Identity

ControllerにもIdentityを持たせる。

#[derive(
   Debug,
   Clone,
)]
struct ControllerIdentity {
   controller_id:
       uuid::Uuid,
   controller_type:
       ControllerType,
   studio_id:
       uuid::Uuid,
}
#[derive(
   Debug,
   Clone,
)]
enum ControllerType {
   Audio,
   Lighting,
   Sensor,
   Network,
}

Databaseにも保存する。

CREATE TABLE physical_controllers (
   controller_id UUID PRIMARY KEY,
   studio_id UUID NOT NULL,
   controller_type TEXT NOT NULL,
   name TEXT NOT NULL,
   protocol TEXT NOT NULL,
   hardware_reference TEXT,
   firmware_version TEXT,
   active BOOLEAN NOT NULL
       DEFAULT TRUE,
   FOREIGN KEY (
       studio_id
   )
   REFERENCES studios(
       studio_id
   )
);

これでSession時のPhysical Configurationを追跡できる。

7 Protocol Adapter

FEELCYCLE Runtimeは、各Protocolを直接Coreへ埋め込まない。

共通Adapterを定義する。

#[async_trait::async_trait]
trait ProtocolAdapter:
   Send + Sync
{
   async fn send(
       &self,
       command:
           ProtocolCommand,
   ) -> Result<
       ProtocolResult,
       ProtocolError,
   >;
   async fn health(
       &self,
   ) -> DeviceHealth;
}

Commandを定義する。

#[derive(
   Debug,
   Clone,
)]
struct ProtocolCommand {
   command_id:
       uuid::Uuid,
   target_id:
       uuid::Uuid,
   payload:
       Vec<u8>,
   target_time_seconds:
       Option<f64>,
}

これによってProtocol固有実装を交換できる。

8 Lighting Protocol

Lightingでは、

DMX512。

Art-Net。

sACN。

Vendor API。

などが候補になる。

概念構造は、

Lighting Intent

Scene

Lighting Adapter

Protocol Adapter
├── DMX
├── Art-Net
└── sACN

Controller

Fixture

となる。

ProgramやAIはProtocolを知らない。

9 Audio Protocol

Audio側では用途によって、

Local Audio Driver。

MIDI。

OSC。

Network Audio。

Vendor Control API。

などが存在し得る。

ここでも、

Playback Command

と、

Device Protocol

を分離する。

Play Track

Audio Adapter

Playback Protocol

Audio Engine

DSP Scene変更なら、

Audio Scene

DSP Adapter

Control Protocol

DSP

となる。

一つのAudio Adapterの中でさらにSubsystemを分けてもよい。

10 OSC

OSCのようなMessage-oriented Protocolは、Semantic Controlとの相性がよい。

概念的なMessageは、

/feelcycle/lighting/scene peak
/feelcycle/audio/track/play track-123
/feelcycle/runtime/mode manual

のように設計できる。

ただし文字列だけを自由に送り合うのではなく、Message Schemaを固定する。

11 Protocol Version

Network MessageにもVersionを持たせる。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
   serde::Deserialize,
)]
struct MessageEnvelope<T> {
   schema_version:
       u32,
   message_id:
       uuid::Uuid,
   session_id:
       Option<uuid::Uuid>,
   sent_at:
       chrono::DateTime<
           chrono::Utc
       >,
   payload:
       T,
}

これによってSoftware Update時に旧Messageと新Messageを区別できる。

Network ProtocolもVersioned Systemである。

12 CommandとTelemetryを分離する

同じChannelで無秩序に双方向Messageを流さない。

概念的には、

Command Channel
Runtime → Device
Telemetry Channel
Device → Runtime
Event Channel
Runtime ↔ Interfaces
Memory Channel
Edge → Cloud

と分ける。

これによって通信の意味を明確にできる。

13 CommandにはAcknowledgementを返す

Command送信を成功とみなさない。

#[derive(
   Debug,
   Clone,
)]
struct CommandAcknowledgement {
   command_id:
       uuid::Uuid,
   accepted:
       bool,
   controller_received_at:
       chrono::DateTime<
           chrono::Utc
       >,
   message:
       Option<String>,
}

ここからさらにApplied Resultへ進む。

Sent

Acknowledged

Applied

Observed

の段階を保持する。

14 Timeout

Controllerが返事をしない場合に無限に待たない。

async fn send_with_timeout(
   adapter:
       &dyn ProtocolAdapter,
   command:
       ProtocolCommand,
   timeout:
       std::time::Duration,
) -> Result<
   ProtocolResult,
   ProtocolError,
> {
   match tokio::time::timeout(
       timeout,
       adapter.send(command),
   )
   .await
   {
       Ok(result) =>
           result,
       Err(_) =>
           Err(
               ProtocolError::Timeout
           ),
   }
}

Timeout後のActionはCommand Classによって変える。

15 Retry

すべてのCommandを同じようにRetryしてはいけない。

たとえば、

Telemetry Request

はRetryしてよい。

しかし、

Scene Advance

を無条件に再送すると二重実行の可能性がある。

そこでCommandにIdempotencyを持たせる。

#[derive(
   Debug,
   Clone,
   Copy,
)]
enum RetryPolicy {
   Never,
   Idempotent,
   Bounded(u8),
}

Commandの意味に応じて設定する。

16 Command Identity

重複実行を避けるためcommand_idをController側でも記憶できると強い。

command_id = X
First delivery
→ Execute
Second delivery
→ Already executed
→ Return previous result

これによってNetwork RetryとPhysical Duplicationを分離できる。

17 Network Time

AudioとLightingを同期するにはNetwork上のClockも重要である。

複数Edge / Controllerが独立Clockを持つ場合、

時刻同期。

Clock Drift。

を監視する。

#[derive(
   Debug,
   Clone,
)]
struct ClockHealth {
   offset_ms:
       f64,
   drift_ms_per_minute:
       f64,
   synchronized:
       bool,
}

Session ClockをPhysical Runtimeの基準として保持し、必要なControllerへTimestamp付きCommandを送る。

18 Clock Drift Alert

fn clock_state(
   offset_ms: f64,
   warning_ms: f64,
) -> DeviceHealth {
   if offset_ms.abs()
       <= warning_ms
   {
       DeviceHealth::Healthy
   } else {
       DeviceHealth::Degraded
   }
}

Thresholdは実設備要件に応じて決める。

Network上で「同じ時刻」の意味を確認できることが重要である。

19 Quality of Service

可能なNetwork EquipmentではTraffic Priorityを設定できる。

考え方として、

Physical Control
High Priority
Audio Transport
High Priority
Telemetry
Medium Priority
Cloud Sync
Low Priority

とする。

AWSへのBackup UploadによってPhysical Control Packetが詰まることを避ける。

20 Bandwidth Budget

Studio NetworkでもBandwidthを無限とみなさない。

特に、

Raw Audio Upload。

Video。

High-frequency Sensor。

Cloud Backup。

は大量Trafficになり得る。

そのため、

Raw Sensor

Edge Processing

Feature

Small Network Payload

を優先する。

これはPrivacyだけでなくNetwork Resilienceにも効く。

21 Network Health

EdgeからNetwork状態を監視する。

#[derive(
   Debug,
   Clone,
)]
struct NetworkHealth {
   control_network:
       DeviceHealth,
   audio_network:
       DeviceHealth,
   operations_network:
       DeviceHealth,
   cloud_uplink:
       DeviceHealth,
}

Cloud Uplinkだけが失われても、Control NetworkがHealthyならSessionを継続する。

22 Network Failureを分解する

「Network Down」という一つの状態にしない。

例えば、

Cloud Uplink Down
→ Session continues
→ Memory buffered locally
Operations Network Down
→ Studio Monitor unavailable
→ Runtime continues
Lighting Network Down
→ Lighting fallback
Audio Network Down
→ Audio fallback / manual

と影響範囲を分ける。

これがNetwork Segmentationの大きな価値である。

23 Failure Domain

一つの障害がSystem全体へ波及しないようにする。

Cloud Failure

Audio Failure
Lighting Failure

Runtime Failure
Member Wi-Fi Failure

Studio Control Failure

をArchitectureで実現する。

24 Controller Failure

Controllerが落ちた場合はAdapterがHealthをUnavailableへする。

async fn controller_health_loop(
   adapter:
       &dyn ProtocolAdapter,
) {
   loop {
       let health =
           adapter.health().await;
       publish_controller_health(
           health
       )
       .await;
       tokio::time::sleep(
           std::time::Duration::
               from_secs(1)
       )
       .await;
   }
}

実際のPoll IntervalはDevice特性に合わせる。

25 Controller Fallback

Lighting ControllerがFailureしたら、

Secondary Controller。

Static Safe Scene。

Manual Console。

などへ移る。

Audio Controllerも同様である。

Primary Controller
↓ failure
Secondary / Local
↓ failure
Manual

Safe State

Physical Layerでは「代替経路」の存在が重要になる。

26 Human Control Pathを独立させる

AI / Edge Runtimeが停止したときにも、人間が設備を操作できる経路を残す。

Automation Path
Edge → Controller → Device
Manual Path
Human Console → Controller → Device

二つを可能な範囲で独立させる。

Automation Failure

Loss of Human Control

である。

27 Network Security

Physical Control NetworkはInternetへ無制限に開かない。

最低限、

Firewall。

Network Segmentation。

Device Authentication。

Encrypted Management Channel。

Remote Access制限。

を持つ。

ControllerにDefault Passwordを残さない。

不要なServiceを無効化する。

Firmwareを管理する。

Physical RuntimeもCybersecurityの対象である。

28 Remote Access

保守のためRemote Accessが必要でも、常時無制限Accessにしない。

概念構造は、

Authorized Operator

Authentication

Controlled Remote Access

Audit

Edge / Controller

とする。

誰がいつどのStudioへ接続したかをAuditできるようにする。

29 CloudからのCommand

CloudからStudioへ直接Realtime Physical Commandを送る設計は最小化する。

Cloudは、

Program配布。

Configuration配布。

Model配布。

Operation Request。

を行える。

しかし実行前にEdgeでValidationする。

Cloud Request

Authenticated

Edge

Current Session Check

Permission / Policy

Safety Validation

Apply or Reject

Cloud側がPhysical Realityの最終Authorityにはならない。

30 Configuration Distribution

Studio ConfigurationをCloudから配布する場合もVersionを持つ。

#[derive(
   Debug,
   Clone,
   serde::Deserialize,
)]
struct StudioNetworkConfiguration {
   configuration_version:
       u64,
   studio_id:
       uuid::Uuid,
   devices:
       Vec<
           NetworkDeviceConfig
       >,
}

Edge側では現在Versionと比較する。

31 Configuration Activation

取得したConfigurationを即座に本番適用しない。

Download

Signature / Integrity Check

Schema Validation

Device Validation

Staging

Activation

Session中のNetwork Configuration変更は原則避ける。

32 Configuration Rollback

struct ConfigurationState {
   active_version:
       u64,
   previous_version:
       Option<u64>,
}

Activation後にPreflightが失敗すれば前Versionへ戻す。

Physical InfrastructureにもRollbackが必要である。

33 Protocol Translation

一つのSemantic Commandを、Studioごとに異なるProtocolへ翻訳できる。

enum PhysicalIntent {
   Lighting(
       LightingIntent
   ),
   Audio(
       AudioAction
   ),
}

Translation Layerを置く。

fn route_intent(
   intent:
       PhysicalIntent,
) -> ControllerRoute {
   match intent {
       PhysicalIntent::Lighting(_) =>
           ControllerRoute::Lighting,
       PhysicalIntent::Audio(_) =>
           ControllerRoute::Audio,
   }
}

ここからStudio Configurationに従ってAdapterを選択する。

34 Protocol Registry

struct ProtocolRegistry {
   adapters:
       std::collections::HashMap<
           uuid::Uuid,
           std::sync::Arc<
               dyn ProtocolAdapter
           >,
       >,
}

Controller IDから正しいAdapterを取得する。

impl ProtocolRegistry {
   fn adapter(
       &self,
       controller_id:
           &uuid::Uuid,
   ) -> Option<
       std::sync::Arc<
           dyn ProtocolAdapter
       >
   > {
       self.adapters
           .get(controller_id)
           .cloned()
   }
}

これによりRuntime CoreからProtocol固有実装を分離できる。

35 Protocol Error

#[derive(
   Debug,
   thiserror::Error,
)]
enum ProtocolError {
   #[error(
       "timeout"
   )]
   Timeout,
   #[error(
       "connection lost"
   )]
   ConnectionLost,
   #[error(
       "invalid response"
   )]
   InvalidResponse,
   #[error(
       "unsupported command"
   )]
   UnsupportedCommand,
}

Error Typeを明示することで、Fallback判断を分けられる。

36 Event Bus

Studio内部で複数Serviceを接続する場合、Internal Event Busを利用できる。

Runtime Core

Event Bus
├── Audio Service
├── Lighting Service
├── Sensor Service
├── Interface Gateway
└── Memory Service

ただしv0.1では過剰なDistributed Systemにしない。

一台のEdge Device内なら、Tokio ChannelなどのIn-process Communicationで十分な場合がある。

37 In-process Message

#[derive(
   Debug,
   Clone,
)]
enum EdgeMessage {
   Runtime(
       RuntimeEvent
   ),
   Audio(
       AudioCommand
   ),
   Lighting(
       LightingCommand
   ),
   Sensor(
       SensorObservation
   ),
   Health(
       DeviceHealthEvent
   ),
}

ChannelでService間を接続する。

let (
   tx,
   mut rx
) =
   tokio::sync::mpsc::
       channel::<EdgeMessage>(
           1024
       );

必要になるまでExternal Brokerを増やさない。

38 Backpressure

Messageが処理能力を超えて増えた場合を考える。

特にSensor Eventが大量に来た場合、重要なControl Commandを押し流してはいけない。

そこでQueueを分ける。

Critical Queue
→ Safety / Control
Realtime Queue
→ Audio / Lighting / Telemetry
Background Queue
→ Cloud / Analytics

とする。

39 Priority Channel

概念的には、

struct EdgeChannels {
   critical:
       tokio::sync::mpsc::
           Sender<EdgeMessage>,
   realtime:
       tokio::sync::mpsc::
           Sender<EdgeMessage>,
   background:
       tokio::sync::mpsc::
           Sender<EdgeMessage>,
}

のように分けられる。

一つの巨大Queueにしない。

40 Sensor Flood

Sensorが異常になって毎ミリ秒大量Eventを送る場合、Runtimeを保護する。

Rate Limitを設ける。

struct SensorRateLimit {
   max_events_per_second:
       u32,
}

Raw Eventを全部処理しない。

最新StateやWindow Summaryへ圧縮する。

41 StateとEventを分ける

Network上で、

State。

Event。

を区別する。

Stateは、

「現在LightingはPeak」

である。

Eventは、

「120秒でPeakへ変化した」

である。

#[derive(Debug, Clone)]
struct StateMessage {
   resource_id:
       String,
   version:
       u64,
   value:
       serde_json::Value,
}
#[derive(Debug, Clone)]
struct EventMessage {
   event_id:
       uuid::Uuid,
   event_type:
       String,
   occurred_at:
       chrono::DateTime<
           chrono::Utc
       >,
   payload:
       serde_json::Value,
}

現在状態の同期とHistorical Memoryを混同しない。

42 Authoritative State

Physical RuntimeのAuthoritative StateはEdge Runtimeへ置く。

TypeScript Studio MonitorやCloud DashboardはRepresentationを受け取る。

Edge Authoritative State

State Update

Web / Cloud Representation

Webから古いStateを送り返してRuntimeを上書きしない。

Action Requestとして送る。

43 Sequence Number

Realtime State MessageにはSequence Numberを持たせる。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
)]
struct RuntimeStateMessage {
   session_id:
       uuid::Uuid,
   sequence:
       u64,
   state:
       serde_json::Value,
}

Frontendは古いMessageを無視できる。

Network遅延によってStateが逆順になるのを防ぐ。

44 Studio Monitor通信

Studio MonitorとはWebSocketなどでRealtime Stateを共有できる。

Edge

Operations Gateway

WebSocket

Studio Monitor

ただしStudio Monitorが切れてもRuntimeは継続する。

Interface Connection

Execution Connection

である。

45 Instructor Console通信

Instructor Consoleではさらに小さなMessageだけを送る。

Current Program。

Current Section。

System Mode。

AI Suggestion。

重要Alert。

である。

Device Telemetryの全量を送らない。

Machine ComplexityをHuman Meaningへ圧縮する。

46 Cloud Synchronization

Cloudへ送るDataはBackground Priorityにする。

Runtime Events

Local Buffer

Cloud Sync

Network Congestion時にはCloud Syncを遅らせてもよい。

Physical Controlを優先する。

47 Offline State

Cloudが切断されたら、

Physical Control Network
Healthy
Cloud Uplink
Unavailable

というStateになる。

EdgeはLocal Operationを続ける。

Studio Monitorには、

「Cloud同期停止・Session継続中」

と表示する。

Failureの意味を正しく伝える。

48 Network Reconnect

Cloud復旧時には、一気に大量UploadしてControl Networkを圧迫しない。

Rate Limitを設けて徐々にSyncする。

struct SyncPolicy {
   max_events_per_batch:
       usize,
   pause_between_batches:
       std::time::Duration,
}

Current Realityを過去DataのUploadより優先する。

49 Controller Preflight

Session開始前にはControllerを検証する。

#[derive(Debug)]
struct ControllerPreflight {
   audio_controller:
       bool,
   lighting_controller:
       bool,
   sensor_gateway:
       bool,
   manual_control:
       bool,
   clock_sync:
       bool,
}

必要ControllerがReadyか確認する。

50 Network Preflight

さらに、

Control Network。

Audio Network。

Operations Network。

Cloud Uplink。

を確認する。

#[derive(Debug)]
struct NetworkPreflight {
   control_ready:
       bool,
   audio_ready:
       bool,
   operations_ready:
       bool,
   cloud_ready:
       bool,
}

cloud_ready = falseでもSession開始可能な設計にする。

51 Preflight判定

impl NetworkPreflight {
   fn physical_runtime_ready(
       &self,
   ) -> bool {
       self.control_ready
           && self.audio_ready
   }
}

CloudやOperations Interfaceは別評価にする。

CoreとOptionalを分離する。

52 Protocol Audit

Physical CommandをAuditする。

CREATE TABLE protocol_command_events (
   protocol_event_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   session_id UUID,
   command_id UUID NOT NULL,
   controller_id UUID NOT NULL,
   protocol TEXT NOT NULL,
   command_type TEXT NOT NULL,
   result TEXT NOT NULL,
   latency_ms DOUBLE PRECISION,
   occurred_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW()
);

ただしRaw Packetをすべて永久保存する必要はない。

意味のあるCommand Resultを残す。

53 Network Event Memory

CREATE TABLE network_runtime_events (
   network_event_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   session_id UUID,
   zone TEXT NOT NULL,
   event_type TEXT NOT NULL,
   latency_ms DOUBLE PRECISION,
   packet_loss_ratio
       DOUBLE PRECISION,
   result TEXT,
   occurred_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW()
);

Session終了後にSummaryへ圧縮する。

54 Network Session Summary

CREATE TABLE network_session_summaries (
   session_id UUID PRIMARY KEY,
   control_availability
       DOUBLE PRECISION,
   audio_network_availability
       DOUBLE PRECISION,
   cloud_availability
       DOUBLE PRECISION,
   max_control_latency_ms
       DOUBLE PRECISION,
   reconnect_count INTEGER NOT NULL
       DEFAULT 0,
   generated_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

Company DashboardではこのようなSummaryを使う。

55 Network Learning

複数Sessionを分析すると、

特定Studioで毎晩Latencyが増える。

特定Controller FirmwareでDisconnectが多い。

Cloud Sync後にNetwork Loadが上がる。

などのPatternが分かる。

Python IntelligenceはPhysical Networkについても学習できる。

ただしAIがNetwork Routingを無制限に自動変更するのではない。

まずInsightとしてOperationへ返す。

56 Self-healingの境界

Network異常を検出したら自動RestartできるDeviceもある。

しかし何でも自動再起動すればよいわけではない。

Observe

Classify

Approved Recovery Action

Execute

Verify

とする。

許可されたRecoveryだけをAutomationする。

57 Recovery Policy

#[derive(
   Debug,
   Clone,
)]
enum RecoveryAction {
   Reconnect,
   RestartAdapter,
   SwitchFallback,
   RequireHuman,
}

Network Eventに応じてPolicyで選ぶ。

fn recovery_for(
   error:
       &ProtocolError,
) -> RecoveryAction {
   match error {
       ProtocolError::Timeout =>
           RecoveryAction::Reconnect,
       ProtocolError::ConnectionLost =>
           RecoveryAction::
               SwitchFallback,
       ProtocolError::InvalidResponse =>
           RecoveryAction::
               RequireHuman,
       ProtocolError::UnsupportedCommand =>
           RecoveryAction::
               RequireHuman,
   }
}

58 人間へのEscalation

Automationで解決できない場合はStudio Operatorへ返す。

Alertには、

何が壊れたか。

Sessionは継続可能か。

現在のFallback。

必要なAction。

を表示する。

単なるError Codeだけを人間へ渡さない。

59 Network・Controller・Protocolの統合

ここまでをまとめる。

                   Studio Network
Python Intelligence
       ↓
Rust Runtime
       ↓
 Semantic Intent
       ↓
  Safety / Policy
       ↓
Controller Routing
       ↓
Protocol Adapter
       ↓
┌────────┼─────────┐
↓        ↓         ↓
Audio  Lighting   Sensor
↓        ↓         ↑
Sound   Light   Observation
└────────┼─────────┘
        ↓
     Reality
        ↓
  Device Telemetry
        ↓
     Edge Runtime
        ↓
Local Memory / Cloud

通信系はRealityの外部ではない。

Physical Runtimeの成立条件である。

60 Protocolを目的化しない

DMX。

Art-Net。

sACN。

OSC。

MIDI。

WebSocket。

HTTP。

これらは目的ではない。

FEELCYCLEが必要とするMeaningをDeviceへ届けるためのTranslation Methodである。

したがって、

Best Protocol

を一つ決めるのではなく、

適切なLayerに適切なProtocol

を置く。

Music StreamingとLighting Cueに同じProtocolを強制する必要はない。

61 Minimum Complete Communication

FEELCYCLE AIに必要な最小Communicationは、

RuntimeからAudioへCommandが届く。

RuntimeからLightingへCommandが届く。

SensorからObservationが戻る。

Device Healthが戻る。

Human InterfaceへCurrent Stateが届く。

CloudへMemoryが同期される。

Cloud断でもLocal Realityが継続する。

ということである。

それ以上のComplexityは必要になってから追加する。

62 Network・Controller・Protocol v0.1の完成条件

v0.1では最低限、次を満たす。

Physical Control Networkを他Networkから分離できる。

有線を主要Physical Controlの中心にできる。

ControllerへIdentityを持たせられる。

Protocol AdapterでHardware固有通信を分離できる。

CommandとTelemetryを分離できる。

Message SchemaをVersion管理できる。

Command IDによって重複を抑制できる。

AcknowledgementとApplied Resultを区別できる。

Clock Syncを観測できる。

Critical TrafficをBackground Syncより優先できる。

Cloud Failure時にもPhysical Controlを継続できる。

Controller Failure時にFallbackできる。

Human Manual Control Pathを残せる。

Network EventをSession Memoryへ戻せる。

これによってAudio、Lighting、Sensor、Edgeは、個別の機器ではなく、一つのCommunication Architectureの上で動くようになる。

63 Studioは一つのNetworked Physical Systemになる

ここまでPhysical Runtimeには、

音。

光。

Sensor。

Edge。

Network。

Controller。

Protocol。

が揃った。

それぞれは異なるTechnologyである。

しかしFEELCYCLEの45分では、その違いを人間が意識し続ける必要はない。

Instructorが見るのは、

今の45分。

Memberが経験するのは、

音・身体・暗闇・他者・空間。

Studio Operatorが見るのは、

Systemが正常か。

である。

Machine内部では複雑に分離する。

Human Experienceでは一つへ統合する。

Machine Side
Audio
Lighting
Sensor
Network
Controller
Protocol

Physical Runtime

Human Side
One Studio
One Session
One 45 Minutes

これがPhysical RuntimeのArchitectureである。

64 次はStudio全体を統合する

ここまで各Physical Componentを個別に実装してきた。

しかし、本番環境では、

AudioだけをDeployする。

LightingだけをDeployする。

SensorだけをDeployする。

のではない。

一つのStudioへ全部を入れる。

ProgramをLoadする。

Edgeを起動する。

DeviceをPreflightする。

Instructor Consoleを接続する。

Memberが入る。

Sessionを開始する。

45分を実行する。

終了後にMemoryへ戻す。

次節では、このEnd-to-End Integrationを実装する。

Studio Integration。

SoftwareとHardwareを一つのStudioへ統合し、

Program

Preflight

Session

Physical Reality

Verification

Memory

を一周させる。

ここで初めて、FEELCYCLE AIは「機材が動くSystem」から「45分が実際に成立するSystem」へ進む。
第6節 Studio Integration

ここまで第6章では、Physical Runtimeを構成する主要な要素を個別に実装してきた。

Audio System。

Lighting System。

Microphone・Sensor・Edge Device。

Network・Controller・Protocol。

しかし、実際のFEELCYCLEでは、これらが別々に動いても45分は成立しない。

一つのStudio。

一つのProgram。

一人のInstructor。

複数のMember。

一つのSession Clock。

その中で、音、光、身体、空間、AI、Hardware、人間が同時に動く必要がある。

本節では、ここまで構築したすべてのPhysical Componentを一つのStudioへ統合する。

Studio Integrationである。

Studio Integrationの目的は、機材を接続することではない。

Programという設計を、45分のPhysical Realityとして最初から最後まで安全に成立させ、その結果をMemoryへ戻すことである。

最小構造は、

Program

Studio Configuration

Preflight

Session Start

Physical Runtime

Human + AI + Hardware

Session End

Verification

Memory

となる。

ここまで一周して初めて、一つのStudioがFEELCYCLE AIの実行環境になる。

1 Studioを一つのExecution Unitとして定義する

StudioはLocationではない。

一つのPhysical Runtime Unitである。

#[derive(
   Debug,
   Clone,
)]
struct StudioRuntimeIdentity {
   studio_id:
       uuid::Uuid,
   edge_device_id:
       uuid::Uuid,
   configuration_version:
       u64,
}

Studioごとに、

Edge Device。

Audio Configuration。

Lighting Configuration。

Sensor Configuration。

Network Configuration。

Fallback。

Manual Control。

を持つ。

概念的には、

Studio

├── Edge Runtime
├── Audio System
├── Lighting System
├── Sensor System
├── Network
├── Instructor Console
├── Manual Control
└── Safety System

となる。

これを一つのRuntimeとして扱う。

2 Studio Configuration

Studio Integrationの中心にConfigurationを置く。

#[derive(
   Debug,
   Clone,
   serde::Deserialize,
)]
struct StudioConfiguration {
   studio_id:
       uuid::Uuid,
   version:
       u64,
   audio:
       AudioStudioConfig,
   lighting:
       LightingStudioConfig,
   sensors:
       SensorStudioConfig,
   network:
       NetworkStudioConfig,
   safety:
       StudioSafetyConfig,
}

このConfigurationによって、同じProgramを異なるStudioへTranslationできる。

Programは、

Peak。

Build。

Track A。

Movement B。

という意味を持つ。

Studio Configurationが、それを具体的なDeviceへ変換する。

3 ConfigurationをVersion化する

Studio設備は変化する。

Speakerを交換する。

Lighting Controllerを更新する。

Firmwareを更新する。

Sensorを追加する。

Network構成を変更する。

したがってConfigurationを上書きしない。

CREATE TABLE studio_runtime_configurations (
   configuration_id UUID PRIMARY KEY
       DEFAULT gen_random_uuid(),
   studio_id UUID NOT NULL,
   version_number INTEGER NOT NULL,
   configuration JSONB NOT NULL,
   status TEXT NOT NULL
       DEFAULT 'draft',
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   activated_at TIMESTAMPTZ,
   FOREIGN KEY (
       studio_id
   )
   REFERENCES studios(
       studio_id
   ),
   UNIQUE (
       studio_id,
       version_number
   )
);

Sessionには、実際に使用したConfiguration Versionを記録する。

これによって、

Program Version。

AI Model Version。

Studio Configuration Version。

を一組として残せる。

4 Session Execution Context

Session開始前に、実行条件を一つのContextへ固定する。

#[derive(
   Debug,
   Clone,
)]
struct SessionExecutionContext {
   session_id:
       uuid::Uuid,
   program_version_id:
       uuid::Uuid,
   studio_id:
       uuid::Uuid,
   instructor_id:
       uuid::Uuid,
   studio_configuration_version:
       u64,
   ai_model_version:
       Option<String>,
}

これが一回の45分のExecution Identityになる。

途中でCloud側のCurrent Program Versionが変わっても、このSessionには影響させない。

Running RealityをCurrent Configurationで上書きしない。

5 Studio Boot

Session以前にStudio Runtimeそのものを正常に起動する必要がある。

Boot Sequenceは、

Edge Boot

Configuration Load

Device Initialization

Network Check

Safe State

Cloud Sync

Idle Ready

とする。

RustではState Machineとして扱える。

#[derive(
   Debug,
   Clone,
   Copy,
)]
enum StudioBootState {
   Starting,
   Loading,
   Initializing,
   Verifying,
   Ready,
   Degraded,
   Failed,
}

ReadyになるまではAutomatic Session Startを許可しない。

6 Idle State

StudioはSessionを実行していない時間にもRuntimeとして存在する。

#[derive(
   Debug,
   Clone,
   Copy,
)]
enum StudioOperationalState {
   Offline,
   Booting,
   Idle,
   Preparing,
   Ready,
   Running,
   Degraded,
   Manual,
   Safe,
   Closing,
}

このStateをStudio Monitorへ返す。

「Sessionがない」

と、

「Studio Runtimeが停止している」

を分離する。

7 Session Preparation

Session開始予定が近づいたらPreparationへ入る。

Scheduled Session

Program Download / Verify

Studio Configuration Verify

Asset Prepare

Preflight

Ready

まずProgram Versionを取得する。

struct PreparedProgram {
   program_version_id:
       uuid::Uuid,
   duration_seconds:
       f64,
   tracks:
       Vec<PreparedTrack>,
   lighting:
       Vec<LightingCommand>,
   movements:
       Vec<MovementBlock>,
}

Session開始前に必要なDataをLocalへ準備する。

Cloud ConnectionがSession開始直前に失われても実行できるようにする。

8 Asset Validation

Audio Asset。

Lighting Scene。

Configuration。

すべてが存在するか確認する。

#[derive(Debug)]
struct AssetValidation {
   audio_assets_ready:
       bool,
   lighting_scenes_ready:
       bool,
   program_valid:
       bool,
}
impl AssetValidation {
   fn ready(
       &self,
   ) -> bool {
       self.audio_assets_ready
           && self.lighting_scenes_ready
           && self.program_valid
   }
}

必要なAssetがない状態でSessionを開始しない。

9 Unified Preflight

ここまで各Subsystemで作ったPreflightを一つへ統合する。

#[derive(Debug)]
struct StudioPreflight {
   edge:
       EdgePreflight,
   audio:
       AudioPreflightReport,
   lighting:
       LightingPreflight,
   sensor:
       SensorEdgePreflight,
   network:
       NetworkPreflight,
   controller:
       ControllerPreflight,
   assets:
       AssetValidation,
}

これがStudio Integrationの重要な境界になる。

10 CriticalとOptionalを分離する

すべてが100%正常でなければ開始できない設計にはしない。

ComponentをCritical / Optionalに分類する。

たとえば概念上、

Critical
────────────
Edge Runtime
Audio Core
Control Network
Manual Control
Safety
Degradable
────────────
Lighting Automation
Measurement Sensor
Cloud Connection
AI Intelligence
Company Monitoring

とする。

具体的な分類は実設備と安全要件に合わせて確定する。

11 Preflight Decision

#[derive(
   Debug,
   Clone,
   Copy,
)]
enum PreflightDecision {
   Ready,
   ReadyDegraded,
   RequireHumanCheck,
   CannotStart,
}

判定する。

fn evaluate_preflight(
   report:
       &StudioPreflight,
) -> PreflightDecision {
   if !report
       .edge
       .runtime_ready
       || !report
           .audio
           .playback_ready
       || !report
           .network
           .control_ready
   {
       return
           PreflightDecision::
               CannotStart;
   }
   if !report
       .lighting
       .controller_ready
       || !report
           .sensor
           .audio_measurement_ready
       || !report
           .network
           .cloud_ready
   {
       return
           PreflightDecision::
               ReadyDegraded;
   }
   PreflightDecision::Ready
}

AIが自動的に開始判断するのではなく、必要に応じてStudio OperatorやInstructorへ判断を返す。

12 PreflightをHuman Meaningへ変換する

Studio MonitorではTechnical Detailを表示できる。

Instructor Consoleでは圧縮する。

たとえば、

Studio Ready
Lighting sensor unavailable.
Session can continue.

程度でよい。

人間が判断するために必要な意味だけを返す。

13 Session Ready State

Preflight完了後、

struct SessionReadyState {
   session_id:
       uuid::Uuid,
   decision:
       PreflightDecision,
   prepared_at:
       chrono::DateTime<
           chrono::Utc
       >,
}

を作る。

このStateが成立して初めてSession Startを許可する。

14 Session Start Authority

Session Startは重要なState Transitionである。

Automatic Scheduleだけで勝手に開始するのではなく、運用設計に応じてHuman Startを要求できる。

Scheduled

Prepared

Human Ready

Start

InstructorまたはOperatorからStart Intentを受け取る。

struct StartSessionIntent {
   session_id:
       uuid::Uuid,
   actor_id:
       uuid::Uuid,
   requested_at:
       chrono::DateTime<
           chrono::Utc
       >,
}

Backend / EdgeでAuthorityを確認する。

15 Session Start Transaction

Local Runtimeでは、

Session Clock。

Program State。

Audio。

Lighting。

Memory。

を一貫して開始する。

概念的には、

Validate

Create Runtime State

Start Session Clock

Start Audio

Apply Initial Lighting

Record session_start

Running

となる。

16 Studio Runtime State

統合Stateを定義する。

#[derive(
   Debug,
   Clone,
)]
struct StudioRuntimeState {
   operational_state:
       StudioOperationalState,
   session_id:
       Option<uuid::Uuid>,
   session_seconds:
       f64,
   program_state:
       ProgramRuntimeState,
   audio_state:
       AudioPlaybackState,
   lighting_state:
       LightingRuntimeState,
   physical_health:
       PhysicalHealth,
   control_mode:
       PhysicalControlMode,
   cloud_connected:
       bool,
}

これがStudio側のAuthoritative Runtime Stateになる。

17 One Clock

Studio Integrationで特に重要なのは、一つのSession Clockを共有することである。

Audio。

Lighting。

Program。

Runtime Event。

Sensor Timestamp。

すべてをこのClockへ対応させる。

                Session Clock
                     │
       ┌─────────────┼─────────────┐
       ↓             ↓             ↓
     Audio        Lighting       Program
       ↓             ↓             ↓
    Physical      Physical      Movement
       │             │             │
       └─────────────┼─────────────┘
                     ↓
                 Reality

各Subsystemが独立した「現在時刻」を持って勝手に動かない。

18 Runtime Orchestrator

Subsystemを統合するOrchestratorを作る。

struct StudioOrchestrator {
   audio:
       std::sync::Arc<
           dyn AudioAdapter
       >,
   lighting:
       std::sync::Arc<
           dyn LightingAdapter
       >,
   runtime:
       RuntimeEngine,
   sensors:
       SensorGateway,
   memory:
       LocalMemory,
   safety:
       SafetyManager,
}

Orchestratorは全処理を一つのThreadで直列化するものではない。

Subsystemを一つのState Machineとして調整する。

19 Main Loop

概念的なMain Loopは、

async fn run_session(
   orchestrator:
       &mut StudioOrchestrator,
) -> Result<
   (),
   StudioRuntimeError,
> {
   loop {
       let event =
           orchestrator
               .runtime
               .next_event()
               .await?;
       orchestrator
           .handle_event(
               event
           )
           .await?;
       if orchestrator
           .runtime
           .session_finished()
       {
           break;
       }
   }
   Ok(())
}

となる。

実際にはAudio、Lighting、Sensorを並列で動かし、Eventによって同期する。

20 Event Routing

Runtime Eventを適切なSubsystemへ送る。

async fn route_event(
   event:
       RuntimeEvent,
   audio:
       &dyn AudioAdapter,
   lighting:
       &dyn LightingAdapter,
) -> Result<
   (),
   StudioRuntimeError,
> {
   match event.event_type {
       EventType::TrackStart => {
           let command =
               audio_command_from(
                   &event
               )?;
           audio
               .execute(
                   &command
               )
               .await?;
       }
       EventType::LightingCue => {
           let command =
               lighting_command_from(
                   &event
               )?;
           lighting
               .apply(
                   &command
               )
               .await?;
       }
       _ => {}
   }
   Ok(())
}

一つのProgram Timelineから複数Physical Outputが生成される。

21 Human Eventも同じRuntimeへ入れる

Physical Runtimeで起きるEventはProgramだけではない。

InstructorのManual Override。

Studio OperatorのFallback。

Human Approval。

もある。

enum StudioEvent {
   Program(
       RuntimeEvent
   ),
   Human(
       HumanRuntimeEvent
   ),
   Sensor(
       SensorObservation
   ),
   Device(
       DeviceHealthEvent
   ),
}

ProgramとHumanとSensorを一つのEvent Architecture上で扱う。

22 AuthorityはEventごとに異なる

Program Eventは自動実行可能。

AI SuggestionはHuman Approvalが必要な場合がある。

Safety Eventは即時Safe Stateへ移行する。

Manual EventはHuman Authorityを優先する。

したがって、

Event

Classify

Authority

Safety

Execute

の順序で処理する。

23 Runtime Mode

Session中のModeを統合する。

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
)]
enum IntegratedRuntimeMode {
   Normal,
   Assisted,
   Degraded,
   Manual,
   Safe,
   Stopping,
   Ended,
}

このModeによってAutomationの範囲が変わる。

24 Normal Mode

Normalでは、

Program Timeline。

Approved Automation。

AI Suggestion。

Sensor Observation。

が通常どおり動く。

しかしAI Suggestionは必要なAuthorityを通す。

25 Degraded Mode

一部Subsystemが失われた場合、

AI unavailable
→ Program continues
Sensor unavailable
→ Sensor-dependent function stops
Lighting automation unavailable
→ Safe / Manual lighting
Cloud unavailable
→ Local memory buffers

というようにCapabilitiesを落とす。

Degraded

Failed

である。

26 Capability Model

#[derive(
   Debug,
   Clone,
)]
struct StudioCapabilities {
   audio:
       bool,
   automated_lighting:
       bool,
   sensor_observation:
       bool,
   ai_assistance:
       bool,
   cloud_sync:
       bool,
   manual_control:
       bool,
}

Runtime Modeだけでなく、何が利用可能かを明示する。

27 Manual Mode

HumanがManualを選択すれば、Automation Authorityを移す。

Automatic Audio Control
→ Depending on operation policy
Automatic Lighting
→ OFF
AI Physical Execution
→ OFF
Human Control
→ ON

Runtimeは観測とMemoryを続けられる。

Manual

System Blind

である。

28 Safe Mode

Critical Failure時はSafe Modeへ移る。

async fn enter_safe_mode(
   orchestrator:
       &StudioOrchestrator,
) {
   let _ =
       orchestrator
           .lighting
           .safe_scene()
           .await;
   orchestrator
       .safety
       .disable_automation()
       .await;
}

Audioの安全な扱いは設備・運用条件に応じたSafe Policyを適用する。

Safetyを一つのGeneric Actionに固定しない。

29 Failure Isolation

Studio Integrationの大きな目的は、一つのFailureを全体へ広げないことである。

Measurement Sensor Failure

Audio Failure
Cloud Failure

Lighting Failure
Company Dashboard Failure

Studio Runtime Failure
AI Failure

Program Failure

Subsystem Boundaryを守る。

30 Dependency Graph

ただし本当に依存しているものは明確にする。

例えば、

Audio Playback
depends on
Audio Asset
+
Playback Engine
+
Audio Output Path
Lighting Automation
depends on
Runtime
+
Control Network
+
Lighting Controller
Cloud Memory
depends on
Cloud Uplink
but
Session Runtime does not

このDependency Graphを設計時に明示する。

31 Studio Integration Test Environment

Production Studioへいきなり統合しない。

最低でも、

Simulation。

Bench Test。

Empty Studio Test。

Controlled Session。

Production。

の段階を踏む。

Software Simulation

Hardware Bench

Empty Studio

Staff-only Test

Controlled Operation

Production

Physical IntegrationではRealityによる検証が不可欠である。

32 Hardware-in-the-Loop Test

Rust Runtimeに実際のControllerを接続する。

Runtime

Real Controller

Test Fixture / Audio Path

Measurement

Verification

これによってSimulationでは見えないDevice LatencyやProtocol Behaviorを確認する。

33 Empty Studio Test

実StudioでMemberなしの45分を一周させる。

確認するのは、

Audio。

Lighting。

Transition。

Network。

Sensor。

Fallback。

Manual Override。

Memory。

である。

Program全体をEnd-to-Endで流す。

34 Golden Session

v0.1では基準となるTest Program / Sessionを一つ作るとよい。

例えば、

Golden Session v0.1
Duration:
45 minutes
Includes:
Audio transitions
Lighting transitions
Sensor observations
AI suggestion
Manual override
Fallback simulation
Cloud disconnect
Cloud reconnect

これをReleaseごとに実行する。

一つのE2E Referenceになる。

35 Integration Test Event

Test中に意図的にFailureを入れる。

例えば、

10分:Cloud Disconnect。

15分:Measurement Sensor停止。

20分:Lighting Controller一時切断。

30分:Manual Override。

35分:Automation Restore。

というようにする。

Systemが安全にState Transitionできるか確認する。

36 Fault Injection

概念的なTest Eventを定義する。

enum FaultInjection {
   CloudDisconnect,
   SensorFailure,
   LightingDisconnect,
   AudioAdapterFailure,
   EdgeServiceRestart,
}

ProductionではなくTest Environmentで使う。

Failureを経験していないSystemを、Failureに強いとは評価できない。

37 RestoreもTestする

Failure後に、

正常へ戻る。

ManualからAutomaticへ戻る。

Cloud Syncを再開する。

Local EventをReconcileする。

ことも確認する。

Fail Safeだけでなく、

Recover Safe

が必要である。

38 Human Handoff

AutomationからManualへの移行と、ManualからAutomationへの復帰を明確にする。

Automation

Handoff Request

Human Acknowledgement

Manual
Manual

System Ready

Human Approval

Automation

勝手にAutomationへ復帰しない。

人間がManualで操作している最中にAIが突然Controlを奪わない。

39 Instructor Console Integration

Instructor Consoleへ必要な情報を送る。

Current Track
Current Section
Session Time
Current Mode
Next Major Transition
AI Suggestion
Critical Alert

これ以上のTechnical Detailは極力出さない。

40 Studio Monitor Integration

Studio Monitorでは、

Edge。

Audio。

Lighting。

Sensor。

Network。

Cloud。

Device。

を詳細に表示できる。

さらに、

Current Runtime Mode。

Fallback。

Queue Depth。

Latency。

を確認する。

Operator Observer Positionを維持する。

41 Company Dashboard Integration

Company DashboardへはReal-Time ControlではなくAggregationを送る。

Studio
Normal / Degraded
Runtime Availability
99.x%
Fallback Count
Device Health Summary
Cloud Sync State

会社全体を見るためのRepresentationへ圧縮する。

42 Swift Mobile Integration

Member AppはPhysical Runtimeを直接操作しない。

Instructor AppもPrimary Live Controlにはしない。

Mobile側では、

Session Ready。

Studio Information。

Preparation。

Post-session Review。

を中心にする。

Physical RuntimeのAuthorityをiPhoneへ依存させない。

43 Member ExperienceをTechnologyから守る

Studio Integrationが成功しているとき、Memberは大量のTechnologyを意識しない。

Edge Computer。

Rust。

AWS。

SQS。

Sensor。

Protocol。

AI Model。

これらが存在していても、MemberがExperienceするのは、

音楽。

身体。

暗闇。

光。

Instructor。

他者。

45分。

である。

System Complexity

Invisible Infrastructure

Human Experience

へ変換する。

44 Session終了

45分終了時には、単にAudioを止めるだけではない。

End Sequenceを行う。

Program End

Final Physical State

Audio Stop

Lighting End / Safe Scene

Session Clock Stop

Runtime Close

Local Event Flush

Summary Generation

Memory Sync

Session Endも一つのState Transitionとして扱う。

45 Ending State

async fn close_session(
   orchestrator:
       &mut StudioOrchestrator,
) -> Result<
   (),
   StudioRuntimeError,
> {
   orchestrator
       .runtime
       .set_mode(
           IntegratedRuntimeMode::
               Stopping
       );
   orchestrator
       .runtime
       .finish_program()
       .await?;
   orchestrator
       .memory
       .record_session_end()
       .await?;
   orchestrator
       .runtime
       .set_mode(
           IntegratedRuntimeMode::
               Ended
       );
   Ok(())
}

実際には各Physical Subsystemの終了処理を加える。

46 Session Summary

終了後にSubsystem Summaryを統合する。

struct IntegratedSessionSummary {
   session_id:
       uuid::Uuid,
   runtime_availability:
       f64,
   audio:
       AudioSessionSummary,
   lighting:
       LightingSessionSummary,
   network:
       NetworkSessionSummary,
   sensors:
       SensorSessionSummary,
   fallback_count:
       u32,
   manual_override_count:
       u32,
}

これがPhysical Session Memoryになる。

47 Reality Verification

予定と実際を比較する。

Program Planned

Runtime Commanded

Physical Applied

Sensor Observed

Human Experienced

複数層を比較する。

一つのLayerだけで成功を判定しない。

48 Physical Completion

例えばLighting Cueについて、

ProgramはPeakを要求した。

RuntimeはCommandを送った。

ControllerはAcceptedした。

Physical Observationが変化を検出した。

ここまで揃えばPhysical Completion Confidenceは高い。

一方、

Command Applied

だけなら、まだPhysical Verificationは限定的である。

49 Difference Record

struct PhysicalDifference {
   domain:
       String,
   planned_seconds:
       Option<f64>,
   commanded_seconds:
       Option<f64>,
   applied_seconds:
       Option<f64>,
   observed_seconds:
       Option<f64>,
}

Session終了後にDifferenceをまとめる。

50 Physical MemoryをCloudへ送る

終了後SummaryとEventをCloudへ同期する。

ただしCloudが利用不能ならLocalへ残す。

Session End

Local Memory Complete

Cloud Available?
├── Yes → Sync
└── No → Pending

Session CompletionをCloud Sync Completionへ依存させない。

51 Studio Memory Status

CREATE TABLE studio_memory_status (
   session_id UUID PRIMARY KEY,
   local_memory_complete
       BOOLEAN NOT NULL,
   cloud_sync_complete
       BOOLEAN NOT NULL,
   physical_summary_complete
       BOOLEAN NOT NULL,
   verified_at TIMESTAMPTZ,
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

これでMemoryの状態を追跡できる。

52 Post-session Review

Instructor Appでは、

ProgramとのDifference。

AI Suggestion。

Manual Override。

重要なPhysical Event。

を人間が理解できる形へ変換する。

例えば、

Session completed
Lighting fallback:
1
Manual override:
1
Cloud interruption:
8 min
No impact on session execution

のようにする。

Logそのものを読ませない。

53 Operational Review

Studio OperatorはTechnical DetailをReviewできる。

Audio Drift。

Lighting Timing Difference。

Network Reconnect。

Sensor Availability。

Queue Depth。

などである。

これをMaintenanceへ接続する。

54 Learningへの接続

Python IntelligenceはSession Memoryから、

Program。

Actual Runtime。

Physical Difference。

Human Decision。

Reflection。

を読む。

Program
+
Physical Reality
+
Human Reality

Learning Record

これによって次回の、

Timing Compensation。

Program Design。

AI Suggestion。

Maintenance。

を改善できる。

55 Physical LearningとCreative Learningを分ける

重要なのは、すべてを同じOptimizationへしないことである。

例えばLighting Timingが18ms遅い。

これはTechnical Optimization対象になり得る。

しかしInstructorが意図的にProgramからDeviationした。

これはCreative Differenceかもしれない。

Technical Difference
→ Reduce unnecessary error
Creative Difference
→ Understand, preserve, learn

と分ける。

AIがCreative Differenceまで「誤差」として0へ収束させない。

56 Studio ConfigurationへのFeedback

Physical Learningから、

Device Latency Profile。

Fallback Policy。

Network Configuration。

Maintenance Priority。

を更新できる。

しかしCurrent Configurationを直接自動変更するのではなく、

Candidate Configuration

Test

Approve

Activate

とする。

57 ProgramへのFeedback

Physical Runtime側からProgram Builderへ、

特定TransitionでLighting Loadが高い。

特定SceneのDevice Latencyが大きい。

Program Cueが密集しすぎている。

などを返せる。

ProgramはCreative DesignだけでなくPhysical Executabilityも持つ。

58 Maintenanceへの接続

Device Health Historyから予防保守へ接続できる。

例えば、

特定ControllerのDisconnect増加。

Audio InterfaceのError増加。

Network Latencyの徐々な悪化。

Edge Storage使用量増加。

を検出する。

AIはMaintenance Candidateを提示できる。

ただし自動交換ではなく、人間の運用判断へ返す。

59 Studio IntegrationとResilience

統合Systemほど、一箇所のFailureで全体が崩れる危険がある。

したがってIntegrationの目的は「全部を密結合すること」ではない。

意味的には統合する。

実装上は疎結合にする。

One Experience
+
Multiple Independent Failure Domains

が理想である。

60 One Studio Runtime

ここまでをまとめると、一Studioは次の構造になる。

                    FEELCYCLE Studio
                  Program Version
                        ↓
                  Session Runtime
                        ↓
               ┌────────┴────────┐
               ↓                 ↓
          Human Authority      AI
               ↓                 ↓
               └────────┬────────┘
                        ↓
                    Rust Edge
                        ↓
      ┌─────────────────┼─────────────────┐
      ↓                 ↓                 ↓
    Audio            Lighting           Sensor
      ↓                 ↓                 ↑
     音                 光               観測
      └─────────────────┼─────────────────┘
                        ↓
            Instructor + Members
                        ↓
                   45 Minutes
                        ↓
                    Reality
                        ↓
                     Memory
                        ↓
                    Learning

これがStudio Integrationの完成形である。

61 Studio Integration v0.1のCompletion Criteria

v0.1では、少なくとも次を満たす。

Studio ConfigurationをVersion管理できる。

SessionごとのExecution Contextを固定できる。

ProgramとAssetを事前にLocalへ準備できる。

Audio、Lighting、Sensor、Network、Edgeを一つのPreflightで検証できる。

Critical / Optional Componentを分離できる。

Human Start Authorityを持てる。

一つのSession ClockでPhysical Runtimeを動かせる。

Program Event、Human Event、Sensor Eventを一つのRuntimeへ統合できる。

Subsystem Failure時にDegraded Modeへ移行できる。

Human Manual Overrideを保持できる。

Critical Failure時にSafe Stateへ移行できる。

Cloud Failure時にも45分を継続できる。

Session終了後にPhysical Summaryを生成できる。

Local Memoryを完成させた後Cloudへ同期できる。

Planned / Commanded / Applied / Observedを比較できる。

LearningをProgram、AI、Studio Operationへ戻せる。

ここまで成立すれば、一つのStudioでFEELCYCLE AI v0.1をEnd-to-Endで実行できる。

62 「統合」の意味

Studio Integrationは、すべてを一つのSoftwareへ押し込むことではない。

AudioにはAudioの責任がある。

LightingにはLightingの責任がある。

SensorにはSensorの責任がある。

EdgeにはEdgeの責任がある。

HumanにはHumanのAuthorityがある。

AIにはIntelligenceの役割がある。

差異を保持したまま、一つの45分へ接続する。

それがIntegrationである。

63 Physical Runtimeの完成直前

ここまでで、第6章の主要Physical Componentはすべて接続された。

AIからPhysical Spaceへ。

Audio。

Lighting。

Microphone・Sensor・Edge。

Network・Controller・Protocol。

そしてStudio Integration。

残るのは、このPhysical Runtime全体を一つのv0.1として固定することである。

次節では、

FEELCYCLE Physical Runtime v0.1

を完成させる。

どのHardwareを最低限用意するのか。

どのSoftwareをどこへ配置するのか。

起動からPreflight、45分、Fallback、終了、Memoryまでをどう一周させるのか。

そして、一つのStudioで完成したPhysical Runtimeを、次の第Ⅵ部でPython、Rust、TypeScript、Swift、SQL、AWSと完全に統合するための境界を確定する。
第7節 FEELCYCLE Physical Runtime v0.1

ここまで、第6章ではFEELCYCLE AIをSoftwareの外へ出してきた。

AIから物理空間へ。

Audio System。

Lighting System。

Microphone・Sensor・Edge Device。

Network・Controller・Protocol。

Studio Integration。

Pythonが理解し、Rustが計算していた45分は、ここで初めて、

音。

光。

機材。

Network。

Sensor。

身体。

空間。

へ接続された。

本節では、それらを一つの実行可能なPhysical Runtimeとして固定する。

FEELCYCLE Physical Runtime v0.1である。

v0.1の目的は、AIがStudioのすべてを自動制御することではない。

一つのStudioにおいて、

Programを読み込み。

Session開始前にPhysical Systemを検証し。

45分を一つのClockで動かし。

AudioとLightingを安全に実行し。

Physical RealityをSensorで必要最小限観測し。

Failure時には縮退し。

HumanがいつでもAuthorityを引き取れ。

終了後にRealityをMemoryへ戻せる。

この一周を完成させることである。

最小構造は、

Program

Preflight

Human Start

Rust Runtime

Physical Runtime

Audio・Lighting・Sensor・Network

45 Minutes

Verification

Memory

Learning

となる。

1 Physical Runtimeの最終Architecture

一Studioの全体構造を確定する。

                     FEELCYCLE Physical Runtime v0.1
                              AWS Cloud
                                 ↕
                         Memory / Configuration
                                 ↕
                       ┌─────────────────┐
                       │   Edge Device   │
                       │                 │
                       │  Rust Runtime   │
                       │  Session Clock  │
                       │  Safety Manager │
                       │  Local Memory   │
                       │  Cloud Sync     │
                       └────────┬────────┘
                                │
          ┌─────────────────────┼─────────────────────┐
          │                     │                     │
          ↓                     ↓                     ↓
     Audio System        Lighting System        Sensor Gateway
          ↓                     ↓                     ↑
Playback / Mixer         Controller / Fixture    Microphone
DSP / Amplifier                                 Telemetry
Speaker                                          Environment
          ↓                     ↓                     ↑
          └─────────────────────┼─────────────────────┘
                                ↓
                            Studio
                                ↓
                    Instructor + Members
                                ↓
                          45 Minutes

そしてHuman Interfaceとして、

Instructor Console
Studio Monitor
Physical Manual Control

を接続する。

Physical Manual ControlはAutomationとは独立して残す。

2 最小Hardware構成

v0.1に必要なHardwareを役割別に整理する。

第一に、Edge Computer。

第二に、Audio Playback環境。

第三に、Audio Interface。

第四に、Mixer / DSP。

第五に、Amplifier / Speaker System。

第六に、Lighting Controller。

第七に、Lighting Fixture。

第八に、Measurement用Microphoneまたは必要なOperational Sensor。

第九に、有線Network Switch。

第十に、Instructor / Operator用Control Interface。

さらに設備側の独立したSafety Systemを保持する。

概念的には、

Edge Computer

├── Ethernet
├── Audio Interface
├── Lighting Controller
└── Sensor Gateway
Audio Interface

Mixer / DSP

Amplifier

Speakers
Lighting Controller

Fixtures
Sensor Gateway

Measurement Microphone
Device Telemetry
Network Telemetry

となる。

具体的な製品選定は、既存Studio設備、必要Channel数、設置条件、安全要件、保守体制によって決定する。

本書のArchitectureは特定Vendorに固定しない。

3 最小Software構成

Edge Deviceには最低限、次を配置する。

feelcycle-edge/

├── runtime-core
├── session-clock
├── safety-manager
├── audio-service
├── lighting-service
├── sensor-service
├── network-service
├── local-memory
├── health-service
└── cloud-sync

Rustを中心にする。

Python Intelligenceは重い解析や推論を担当し、Physical Critical Pathから分離する。

TypeScriptはStudio MonitorとInstructor Consoleを担当する。

SwiftはStudioのPrimary Physical Runtimeにはしない。

SQL・AWSはLong-Term Memoryを担当する。

4 責任分離

最終的な役割は、

Python
=
理解・推定・提案
Rust
=
時間・実行・安全・Physical Control
TypeScript
=
Live Human Interface
Swift
=
Personal Mobile Interface
SQL
=
Relational Memory
AWS
=
Shared Memory / Distribution

である。

Physical Runtimeの中心はRust Edgeになる。

5 Authoritative Physical State

Studio内のAuthoritative StateをEdge Runtimeへ置く。

#[derive(
   Debug,
   Clone,
)]
struct AuthoritativePhysicalState {
   session_id:
       Option<uuid::Uuid>,
   mode:
       IntegratedRuntimeMode,
   session_seconds:
       f64,
   audio:
       AudioPlaybackState,
   lighting:
       LightingRuntimeState,
   health:
       PhysicalHealth,
   capabilities:
       StudioCapabilities,
   cloud_connected:
       bool,
}

TypeScriptやCloudは、このStateのRepresentationを受け取る。

Web DashboardをAuthoritative Realityにはしない。

6 Runtime Mode

Physical RuntimeのModeを最終確定する。

#[derive(
   Debug,
   Clone,
   Copy,
   PartialEq,
   Eq,
)]
enum IntegratedRuntimeMode {
   Idle,
   Preparing,
   Ready,
   Normal,
   Assisted,
   Degraded,
   Manual,
   Safe,
   Stopping,
   Ended,
}

State Transitionは無制限に行わせない。

概念的には、

Idle

Preparing

Ready

Normal
├→ Assisted
├→ Degraded
├→ Manual
└→ Safe
      ↓
   Stopping
      ↓
     Ended

となる。

7 State Transition

fn can_transition(
   from:
       IntegratedRuntimeMode,
   to:
       IntegratedRuntimeMode,
) -> bool {
   use IntegratedRuntimeMode::*;
   matches!(
       (from, to),
       (Idle, Preparing)
       | (Preparing, Ready)
       | (Ready, Normal)
       | (Normal, Assisted)
       | (Normal, Degraded)
       | (Normal, Manual)
       | (Normal, Safe)
       | (Assisted, Normal)
       | (Assisted, Manual)
       | (Assisted, Safe)
       | (Degraded, Manual)
       | (Degraded, Safe)
       | (Degraded, Normal)
       | (Manual, Normal)
       | (Manual, Safe)
       | (Normal, Stopping)
       | (Assisted, Stopping)
       | (Degraded, Stopping)
       | (Manual, Stopping)
       | (Safe, Stopping)
       | (Stopping, Ended)
   )
}

Productionではより厳密なGuard Conditionを加える。

8 起動

一日のStudio Runtime起動を考える。

Power On

Edge Boot

Runtime Start

Configuration Load

Device Discovery

Network Check

Safe Physical State

Cloud Connection

Idle

起動直後からLightingやAudioを自由に操作しない。

最初はSafe Stateへ置く。

9 Configuration Load

async fn initialize_studio(
   config:
       StudioConfiguration,
) -> Result<
   StudioRuntime,
   StudioRuntimeError,
> {
   validate_configuration(
       &config
   )?;
   let runtime =
       StudioRuntime::new(
           config
       )
       .await?;
   runtime
       .enter_initial_safe_state()
       .await?;
   Ok(runtime)
}

Configurationが不正ならReadyへ進まない。

10 Session Preparation

予定されたSessionを取得する。

Program Versionを取得する。

Audio AssetをLocalに準備する。

Lighting Sceneを確認する。

Studio Configurationを固定する。

必要なAI Model Versionを確認する。

Session
+
Program Version
+
Studio Configuration
+
Approved Assets
+
Approved Model
=
Execution Context

このContextをSession開始前に固定する。

11 Execution Manifest

v0.1では一つのManifestへまとめるとよい。

#[derive(
   Debug,
   Clone,
   serde::Serialize,
   serde::Deserialize,
)]
struct SessionExecutionManifest {
   session_id:
       uuid::Uuid,
   program_version_id:
       uuid::Uuid,
   studio_id:
       uuid::Uuid,
   studio_configuration_version:
       u64,
   instructor_id:
       uuid::Uuid,
   ai_model_version:
       Option<String>,
   created_at:
       chrono::DateTime<
           chrono::Utc
       >,
}

これがその45分の実行条件の最小証明になる。

12 Manifestを保存する

CREATE TABLE session_execution_manifests (
   session_id UUID PRIMARY KEY,
   program_version_id UUID NOT NULL,
   studio_id UUID NOT NULL,
   studio_configuration_version
       INTEGER NOT NULL,
   instructor_id UUID NOT NULL,
   ai_model_version TEXT,
   manifest JSONB NOT NULL,
   created_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

後からRealityを再構成できる。

13 Unified Preflight

Session開始前に全Physical Systemを一回で検査する。

Program
Asset
Edge
Audio
Lighting
Sensor
Control Network
Manual Control
Safety
Local Storage
Cloud

である。

CloudはOptional。

SafetyやAudio CoreはCritical。

このDifferenceを保持する。

14 Preflight Result

#[derive(
   Debug,
   Clone,
)]
struct PhysicalRuntimePreflight {
   program_ready:
       bool,
   edge_ready:
       bool,
   audio_ready:
       bool,
   lighting_ready:
       bool,
   sensor_ready:
       bool,
   control_network_ready:
       bool,
   manual_control_ready:
       bool,
   safety_ready:
       bool,
   local_memory_ready:
       bool,
   cloud_ready:
       bool,
}

15 開始可否

fn start_decision(
   p:
       &PhysicalRuntimePreflight,
) -> PreflightDecision {
   if !p.program_ready
       || !p.edge_ready
       || !p.audio_ready
       || !p.control_network_ready
       || !p.manual_control_ready
       || !p.safety_ready
       || !p.local_memory_ready
   {
       return
           PreflightDecision::
               CannotStart;
   }
   if !p.lighting_ready
       || !p.sensor_ready
       || !p.cloud_ready
   {
       return
           PreflightDecision::
               ReadyDegraded;
   }
   PreflightDecision::Ready
}

ただし実運用のCritical分類は安全検証によって確定する。

16 Human Start

Physical RuntimeがReadyでも、Human Start Authorityを残す。

InstructorまたはOperatorが、

「このStudio、このProgram、この状態で開始する」

と決定する。

Machine Ready
+
Human Ready
=
Session Start

である。

17 Session Clock

Startと同時に一つのSession Clockを開始する。

struct SessionClock {
   started_at:
       std::time::Instant,
}
impl SessionClock {
   fn seconds(
       &self,
   ) -> f64 {
       self.started_at
           .elapsed()
           .as_secs_f64()
   }
}

Audio。

Lighting。

Runtime Event。

Sensor。

すべてをこのClockへ対応させる。

18 Physical Timeline

Programから実行予定を生成する。

#[derive(
   Debug,
   Clone,
)]
enum PhysicalTimelineEvent {
   Audio(
       AudioCommand
   ),
   Lighting(
       LightingCommand
   ),
   Program(
       RuntimeEvent
   ),
}

時間順にSchedulerへ入れる。

19 Execution Scheduler

async fn execute_timeline(
   clock:
       &SessionClock,
   mut events:
       Vec<
           ScheduledPhysicalEvent
       >,
   tx:
       tokio::sync::mpsc::Sender<
           PhysicalTimelineEvent
       >,
) {
   events.sort_by(
       |a, b|
           a.target_seconds
               .partial_cmp(
                   &b.target_seconds
               )
               .unwrap()
   );
   for event in events {
       wait_until(
           clock,
           event.dispatch_seconds,
       )
       .await;
       let _ =
           tx.send(
               event.payload
           )
           .await;
   }
}

Device Latencyを考慮したDispatch Timeを利用する。

20 Audio + Lighting

同じTransitionを一つのLogical Eventとして扱える。

Logical Transition
138.500 sec

Audio target
138.500

Lighting target
138.500

Different dispatch times

One Physical Moment

ここがPhysical Runtimeの中心になる。

21 Sensor Loop

Physical Outputと並行してSensorを動かす。

tokio::select! {
   event =
       runtime_events.recv() =>
   {
       handle_runtime_event(
           event
       ).await;
   }
   observation =
       sensor_events.recv() =>
   {
       handle_observation(
           observation
       ).await;
   }
   health =
       health_events.recv() =>
   {
       handle_health(
           health
       ).await;
   }
}

Physical Realityを観測しながら実行する。

22 AI Connection

Python IntelligenceはPhysical RuntimeのCritical Loopへ直接置かない。

AIからは、

Prediction。

Suggestion。

Estimate。

が届く。

#[derive(
   Debug,
   Clone,
)]
struct AiRuntimeSuggestion {
   suggestion_id:
       uuid::Uuid,
   session_id:
       uuid::Uuid,
   action:
       serde_json::Value,
   confidence:
       f64,
   requires_human_approval:
       bool,
}

RuntimeがAuthority Policyを確認する。

23 AI実行境界

AI Suggestion

Valid?

Current?

Approved Action Type?

Human Approval if required

Safety Validation

Runtime

Physical Device

期限切れSuggestionを後から実行しない。

24 Human Override

InstructorまたはOperatorはAutomationをOverrideできる。

Override Eventを第一級Eventとして扱う。

#[derive(
   Debug,
   Clone,
)]
struct ManualOverrideEvent {
   override_id:
       uuid::Uuid,
   session_id:
       uuid::Uuid,
   actor_id:
       uuid::Uuid,
   target:
       String,
   reason:
       Option<String>,
   occurred_at_seconds:
       f64,
}

これをMemoryへ残す。

25 Override時のAuthority

Manual Modeに入ると、

AI Physical Executionを停止する。

対象Automationを停止する。

Human Control Pathを有効にする。

SensorとMemoryは継続する。

つまり、

Automation OFF
Observation ON
Memory ON
Human Authority ON

である。

26 安全の優先順位

Physical Runtime v0.1のPriorityを最終確定する。

1 Safety
2 Human Authority
3 Audio Continuity
4 Core Runtime
5 Program Timing
6 Lighting
7 Operational Observation
8 AI Assistance
9 Cloud Synchronization
10 Background Analytics

上位を下位のために犠牲にしない。

27 AI Failure

Python Intelligenceが停止した場合、

AI
Unavailable

Program Runtime continues

Instructor continues

Physical Session continues

とする。

AIはEnhancementであり、45分の唯一の成立条件ではない。

28 Cloud Failure

AWSとのConnectionが切れた場合、

Cloud
Unavailable

Rust Runtime continues

Local Memory

Local Buffer

Session continues

とする。

Physical RealityをCloudへ従属させない。

29 Sensor Failure

Sensor
Unavailable

Sensor-dependent AI disabled

Physical Output continues

Human remains authority

である。

30 Lighting Failure

Lighting ControllerにFailureが起きた場合、

Lighting Automation
↓ failure
Fallback / Safe / Manual Lighting

Audio continues if safe

Session continues when permitted

とする。

31 Audio Failure

Audioはより重要なDependencyである。

Primary Playback Failureなら、

Secondary。

Manual。

必要ならSafe Stop。

へ移る。

Primary

Secondary

Manual

Safe Stop

v0.1では少なくともManual Fallbackへの明確な移行を用意する。

32 Edge Failure

Edge Runtimeそのものが落ちた場合はAutomation Coreを失う。

そのため、

Watchdog。

Restart。

Physical Manual Control。

Safe State。

を持つ。

Edge Failure

Automation Lost

Physical Safety Layer remains

Human Manual Control

を成立させる。

33 Failure Matrix

Physical RuntimeのFailureを整理する。

AI Failure
→ Continue without AI
Cloud Failure
→ Continue locally
Sensor Failure
→ Continue without sensing enhancement
Lighting Failure
→ Fallback / Manual
Audio Failure
→ Secondary / Manual / Stop
Edge Failure
→ Manual / Safe
Safety Failure
→ Do not continue automatically

System全体を一つの「正常/異常」だけで扱わない。

34 Health Aggregation

#[derive(Debug)]
struct PhysicalRuntimeHealth {
   edge:
       DeviceHealth,
   audio:
       DeviceHealth,
   lighting:
       DeviceHealth,
   sensors:
       DeviceHealth,
   network:
       DeviceHealth,
   cloud:
       DeviceHealth,
}

Human Interfaceへは意味として圧縮する。

35 Instructor Console

InstructorがSession中に見る情報は最小化する。

12:34
Track
Current Section
Mode
NORMAL
System
OK
AI Suggestion
1

Critical Failure時だけ強く表示する。

Technical DataはStudio Monitorへ任せる。

36 Studio Monitor

Operator側には、

Edge Runtime。

Audio Chain。

Lighting Controller。

Sensor。

Network。

Cloud。

Queue。

Latency。

Fallback。

を表示する。

Physical Runtimeの全体Observer Positionになる。

37 Physical Manual Control

重要な操作をWeb Browserだけに依存させない。

設備条件に応じて、

Audio Mixer。

Lighting Console。

Physical Switch。

Safety System。

などの手動経路を保持する。

Software UI Failure

Loss of Control

である。

38 Session中のLocal Memory

重要EventはまずLocalへ記録する。

Runtime Event

Local Append Log

Physical Execution

Async Cloud Sync

Cloudへ書けたことを確認してからRealityを実行する構造にはしない。

39 Physical Event Log

Local Eventには、

Program Event。

Audio Result。

Lighting Result。

Human Override。

Device Failure。

Sensor Observation。

Mode Transition。

を残す。

#[derive(
   Debug,
   serde::Serialize,
)]
struct PhysicalRuntimeRecord {
   record_id:
       uuid::Uuid,
   session_id:
       uuid::Uuid,
   record_type:
       String,
   session_seconds:
       f64,
   payload:
       serde_json::Value,
}

40 Planned / Commanded / Applied / Observed

Physical Runtime v0.1では、同じRealityを最低四層に分ける。

Planned

Commanded

Applied

Observed

例を挙げる。

Lighting Peak
Planned:
138.500
Commanded:
138.482
Applied:
138.517
Observed:
138.525

このDifferenceがPhysical Learningになる。

41 Human Experienceを第五層にする

さらに、

Experienced

がある。

SensorやHardwareが完全でも、Member Experienceが自動的に良いとは限らない。

したがって最終構造は、

Planned
Commanded
Applied
Observed
Experienced

である。

Physical AIは最後の一つを完全には機械計測できない。

Human Reflectionが必要になる。

42 Session End

45分終了後の順序を確定する。

Program Reaches End

Final Transition

Audio End

Lighting End / Defined Final Scene

Stop New Automation

Session Clock Freeze

Local Memory Finalize

Physical Summary

Cloud Sync

Idle

Cloud Syncが終わらなくてもStudioは次の準備へ進める設計にする。

43 Physical Summary

#[derive(
   Debug,
   serde::Serialize,
)]
struct PhysicalRuntimeSummary {
   session_id:
       uuid::Uuid,
   runtime_mode_end:
       IntegratedRuntimeMode,
   audio_availability:
       f64,
   lighting_availability:
       f64,
   sensor_availability:
       f64,
   control_network_availability:
       f64,
   cloud_availability:
       f64,
   fallback_count:
       u32,
   manual_override_count:
       u32,
   critical_failure_count:
       u32,
}

このSummaryをLong-Term Memoryへ戻す。

44 SQLへの統合

CREATE TABLE physical_runtime_summaries (
   session_id UUID PRIMARY KEY,
   runtime_mode_end TEXT NOT NULL,
   audio_availability
       DOUBLE PRECISION,
   lighting_availability
       DOUBLE PRECISION,
   sensor_availability
       DOUBLE PRECISION,
   control_network_availability
       DOUBLE PRECISION,
   cloud_availability
       DOUBLE PRECISION,
   fallback_count INTEGER NOT NULL
       DEFAULT 0,
   manual_override_count INTEGER NOT NULL
       DEFAULT 0,
   critical_failure_count INTEGER NOT NULL
       DEFAULT 0,
   generated_at TIMESTAMPTZ NOT NULL
       DEFAULT NOW(),
   FOREIGN KEY (
       session_id
   )
   REFERENCES sessions(
       session_id
   )
);

45 End-to-End Verification

Session終了後に、自動Verificationを行う。

最低限、

Session Start / Endがある。

Program Versionが固定されている。

Audio Executionが存在する。

Lighting Executionが存在する。

Runtime Eventが保存されている。

Critical Eventが記録されている。

Local MemoryがCompleteである。

ことを確認する。

46 Verification Result

#[derive(
   Debug,
   Clone,
)]
struct PhysicalVerification {
   execution_complete:
       bool,
   memory_complete:
       bool,
   critical_failure:
       bool,
   unresolved_events:
       u32,
}

問題があればOperation Reviewへ送る。

47 Memory Sync

Local Physical MemoryをAWSへ送る。

Physical Events

Local Buffer

Ingestion

SQS

Persistence Worker

RDS / S3

第5章のMemory Architectureとここで完全に接続する。

48 Recovery

Cloud復旧後には未同期Eventを再送する。

Edge再起動後には未完了Session Memoryを検出する。

Boot

Local Recovery Scan

Unsynced Session Found

Integrity Check

Resume Sync

Runtime RecoveryとMemory Recoveryを同時に考える。

49 Physical Runtime Release

Softwareとしてv0.1をVersion化する。

FEELCYCLE Physical Runtime
v0.1.0

Edge Software。

Studio Configuration。

Protocol Schema。

Database Migration。

をRelease Manifestで対応させる。

#[derive(
   Debug,
   serde::Serialize,
)]
struct PhysicalRuntimeRelease {
   runtime_version:
       String,
   protocol_schema_version:
       u32,
   configuration_schema_version:
       u32,
   database_schema_version:
       u32,
}

Version Compatibilityを明示する。

50 Release Manifest

{
 "runtime_version": "0.1.0",
 "protocol_schema_version": 1,
 "configuration_schema_version": 1,
 "database_schema_version": 1
}

一部だけ無秩序に更新してCompatibilityを壊さない。

51 Deployment

Production Studioへの展開順序は、

Build

Unit Test

Integration Test

Hardware-in-the-Loop

Golden Session

Empty Studio

Staff Test

Controlled Production

Production

とする。

コードを書いたからPhysical Runtimeが完成したとはみなさない。

RealityによるVerificationを必ず通す。

52 Golden Session v0.1

Physical Runtime全体を検証する基準Sessionを持つ。

0 min
Session Start
5 min
Lighting Transition
10 min
Cloud Disconnect
15 min
Sensor Failure
20 min
Sensor Recovery
25 min
Lighting Fallback
30 min
Manual Override
35 min
Automation Restore
40 min
Cloud Recovery
45 min
Session End

このようなTest TimelineによってFailureまで含めて検証する。

53 Testするのは成功だけではない

通常状態だけを45分動かして終わりにしない。

必ず、

断線。

遅延。

Device Failure。

Cloud Failure。

Manual Transition。

Recovery。

をTestする。

Resilienceは正常時には見えない。

54 Physical Runtime v0.1の完成条件

ここでCompletion Criteriaを確定する。

一つのStudio ConfigurationをVersion管理できる。

一つのProgram VersionをLocalへ準備できる。

一つのExecution Manifestを生成できる。

Unified Preflightを実行できる。

Critical / Optional Componentを分離できる。

Human Start Authorityを持てる。

Session Clockを開始できる。

AudioをPhysical Outputできる。

LightingをPhysical Outputできる。

Sensor Observationを取得できる。

Device Healthを取得できる。

Physical Control NetworkでDeviceを接続できる。

Cloud断時にLocalで継続できる。

AI断時にProgramを継続できる。

Sensor断時にDegraded運転できる。

Lighting断時にFallbackできる。

HumanがManual Authorityを取得できる。

Critical Failure時にSafe Stateへ移行できる。

Planned / Commanded / Applied / Observedを区別できる。

Physical EventをLocal Memoryへ保存できる。

Session終了後にSummaryを作れる。

Cloud復旧後にMemoryを同期できる。

Golden SessionをEnd-to-Endで完走できる。

この条件が満たされれば、

FEELCYCLE Physical Runtime v0.1

は成立する。

55 Hardwareが主役なのではない

ここで注意したい。

Physical Runtimeの完成とは、Studioを大量の最新機材で埋めることではない。

Hardwareの数が増えるほど良いわけではない。

Sensorの数が増えるほどAIが良くなるわけでもない。

必要なのは、

一つの45分に本当に必要なPhysical Capabilityを選び、

そのCapabilityが安全に動き、

人間のExperienceを壊さず、

Failureしても縮退でき、

Realityから学べる

ことである。

Minimum Hardware
+
Maximum Meaning

を目指す。

56 AIが目立たないPhysical AI

FEELCYCLE Physical Runtimeが高度に完成したとき、MemberはAIを強く意識しない可能性がある。

むしろ、それが望ましい。

Musicが自然に続く。

Lightingが自然に変化する。

InstructorはTechnologyではなくMemberを見る。

System Failureは背景で吸収される。

AIは必要な時だけSuggestionを出す。

Session中、iPhoneを見る必要はない。

つまり、

More Intelligence

Less Interface Friction

へ進む。

57 Instructorを中心から外さない

Physical Automationが高度化しても、InstructorはRuntimeの末端ではない。

AIが作った45分を再生するOperatorでもない。

その場のRealityを、人間として観測し、判断し、必要ならProgramから逸脱し、新しいExperienceを生成する主体である。

AIは、

Programを理解する。

Differenceを示す。

Timingを支える。

Physical Failureを吸収する。

Memoryを作る。

しかし、

「今この場で何をするか」

についてHuman Authorityを残す。

58 MemberをData Pointにしない

MemberもSensor入力の集合ではない。

Heart Rate。

Participation。

History。

Recommendation Click。

これらはMemberそのものではない。

Physical Runtimeが最終的に守るべきなのは、

測定精度

ではなく、

Experienceが成立する空間

である。

AIがMemberをより細かく測るほど進化するとは限らない。

必要なDataだけで、より良いRealityを成立させる方が重要である。

59 Physical Runtimeは翻訳機である

FEELCYCLE Physical Runtimeの本質を一語で表すなら、Translationである。

Programを音へ翻訳する。

Stateを光へ翻訳する。

Human IntentをCommandへ翻訳する。

Physical RealityをSensor Featureへ翻訳する。

FailureをFallbackへ翻訳する。

RealityをMemoryへ翻訳する。

Meaning

Code

Signal

Device

Reality

この往復を45分間止めずに継続する。

60 第Ⅴ部で完成したもの

第Ⅴ部の開始時点で、FEELCYCLE AIはまだSoftware中心だった。

ここまでで、

Audio。

Lighting。

Microphone。

Sensor。

Edge Device。

Network。

Controller。

Protocol。

Studio。

が接続された。

つまり、

Software

Hardware

Physical Reality

という境界を越えた。

同時に、

Physical Reality

Observation

Software

という帰路も完成した。

これによってFEELCYCLE AIは、一方向のAutomation Systemではなくなった。

Physical Realityと継続的に往復するRuntimeになった。

61 ここまでの全Stack

ここまで本書で実装したものを積み上げる。

Python
Intelligence

Rust
Runtime

TypeScript
Interface

Swift
Mobile

SQL
Memory

AWS
Cloud

Hardware
Physical Runtime

FEELCYCLE Reality

しかし、これは本当は一直線ではない。

Realityから再び上へ戻る。

Python Intelligence
      ↓
Rust Runtime
      ↓
Human Interface
      ↓
Physical Runtime
      ↓
Reality
      ↓
Memory
      ↓
Python Intelligence
      ↺

という循環である。

62 残る最後の課題

個々のLayerはここまでで成立した。

Pythonは理解できる。

Rustは実行できる。

TypeScriptは見せられる。

Swiftは個人へ接続できる。

SQLとAWSは記憶できる。

HardwareはRealityを動かせる。

しかし、まだこれらは「七つの完成したLayer」である。

最後に必要なのは、

一つのFEELCYCLE AI

として接続することである。

同じIdentity。

同じSession。

同じProgram Version。

同じState。

同じEvent。

同じMemory。

同じAuthority。

を全Layerで共有する必要がある。

63 次はIntegrationへ

次の第Ⅵ部では、すべてを統合する。

PythonとRustを接続する。

Web、Mobile、Cloudを接続する。

AIとStudioを接続する。

Member、Instructor、Companyを接続する。

そして、

Programを一つ選ぶ。

Sessionを一つ生成する。

StudioをPreflightする。

45分を開始する。

AIが理解する。

Rustが実行する。

人間が判断する。

Physical Spaceが変化する。

RealityをMemoryへ戻す。

次の45分へ学習する。

このEnd-to-End Loopを一つのSystemとして実装する。

FEELCYCLE Physical Runtime v0.1は、ここで完成した。

次に完成させるのは、そのPhysical Runtimeを含む全Stackである。

FEELCYCLE AI v0.1。

人・音楽・身体・空間・会社を、一つのRuntimeとして接続する。

愛と敬意を込めてmandala

いいなと思ったら応援しよう!