MQTT_SCHEMA COMPANION_ROBOT - zero1ne-26/doc GitHub Wiki
λ°λ €λ‘λ΄ μ μ΄ MQTT μ€ν€λ§
μΉ΄μ΄μ€νΈν(REX) μμ±. λͺ¨λ λ©μμ§λ CloudEvents 1.0 envelopeμΌλ‘ κ°μΌλ€.
λͺ©μ°¨
κ°μ
| νλͺ© | λ΄μ© |
|---|---|
| μ κ³΅κ° | session_id (μ€μΌμ€νΈλ μ΄μ
μλ²μμ λ°κΈ) |
| μμ± μ£Όμ²΄ | μΉ΄μ΄μ€νΈν |
| μ΅μ’ μμ μΌ | 2026-09-29 |
| robot_id | REX |
| envelope | CloudEvents 1.0 (specversion, type, source, id, time, subject, data) |
2026-09-29 λ³κ²½ μμ½: λ‘λ΄μ λ°ν΄ ꡬλ(μ£Όν) μΌλ‘ λ³κ²½νκ³ REXκ° μ€μ μννλ capabilityλ§ μ μ§νλλ‘ μ 리νλ€. (1) capabilityλ₯Ό
sayΒ·gotoΒ·gesture(+μμ stop)λ‘ μ ν(λλ¨Έμ§ μμ ), (2) μ΄μ‘±λ³΄ν μ μ μλlocomotionμ±λμ νκΈ°νκ³ λ¬Όλ¦¬μ μΌλ‘ λΆλ¦¬λbase(λ°ν΄ μ£Όν)Β·body(머리·μ체 μ μ€μ²)Β·audio(λ°ν) 3μ±λλ‘ μ¬μ μ β μ±λ κ° λ³λ ¬ μ€ν (gotoΒ·gestureΒ·sayλμ κ°λ₯), (3) μ΄μ‘±λ³΄ν μ μ© νλͺ©(unstable_postureμ¬μ , "μ μ RL" μμ ) μμ , (4)sayparamsμμemotionΒ·interruptμ κ±°, (5) λ°ν νμλ₯Ό μΈμ AI νλ₯΄μλ κΈ°μ€μΌλ‘ κ²°μ (μλ²κ°voice_idμ£Όμ ). λΉ/λ―Έμ§μvoice_idλ κ±°μ μμ΄ κΈ°λ³Έ νμsoheeλ‘ λ°ννλ€.
ν ν½ λͺ©λ‘
| κ΅¬λΆ | Topic | λ°©ν₯ | μ€λͺ |
|---|---|---|---|
| REQUEST | robot/REX/command |
μλ² β λ°λ €λ‘λ΄ | λ°λ €λ‘λ΄ μ μ΄ λͺ λ Ή μ μ‘ |
| RESPONSE | robot/REX/ack |
λ°λ €λ‘λ΄ β μλ² | λ°λ €λ‘λ΄ μ μ΄ κ²°κ³Ό μμ |
μ±λ λͺ¨λΈ
λ°λ €λ‘λ΄μ 물리 μμμ 3κ° μ±λλ‘ λλμ΄ κ΄λ¦¬νλ€. capability κ°μ λ°λΌ μ μ
μ±λμ΄ κ²°μ λλ©°, REQUEST νμ΄λ‘λμ μ±λμ λͺ
μνμ§ μλλ€.
| μ±λ | 물리 μμ | μ¬μ© capability | λμ μ€ν |
|---|---|---|---|
base |
λ°ν΄ μ£ΌνλΆ | goto |
μ±λ λ΄ 1κ°μ©(μμ°¨) |
body |
머리·μ체(μ μ€μ² λͺ¨ν°) | gesture |
μ±λ λ΄ 1κ°μ©(μμ°¨) |
audio |
μ€νΌμ»€ | say |
λ³λ ¬ |
μλ‘ λ€λ₯Έ μ±λμ λ³λ ¬λ‘ μ€νλλ€. μ¦ μ£Όν(goto)Β·μ μ€μ²(gesture)Β·λ°ν(say)λ
λμμ μνν μ μκ³ , κ°μ μ±λμ λ€μ΄μ¨ λͺ
λ Ήλ§ μμ°¨ μ²λ¦¬λλ€.
μ±λλ³ κ·μΉ
base β μ£Όν (μ±λ λ΄ μμ°¨)
λ°ν΄ μ£ΌνλΆλ ν λ²μ ν λͺ©μ μ§λ‘ μ΄λνλ€. μλ²λ μ§μ gotoμ completed λ₯Ό νμΈν λ€
λ€μ gotoλ₯Ό 보λ΄λ κ²μ κΈ°λ³Έ νλ¦μΌλ‘ νλ€. μν μ€ λμ°©ν gotoλ κ±°μ νμ§ μκ³ νμ
보κ΄νλ€κ° μμ°¨ μ€ννλ©°, expires_at μ΄ κ²½κ³Όνλ©΄ νμμ κΊΌλΌ λ rejected(expired) λ‘
νμ νλ€. priority: safety λͺ
λ Ήκ³Ό stop μ μ§ν μ€ μ£Όνμ μ¦μ μ μ νλ€.
body β μ μ€μ² (μ±λ λ΄ μμ°¨)
머리·μ체 μ μ€μ²λ ν λ²μ νλμ© μμ°¨ μ€ννλ€. base μ£Όνκ³Όλ λ
립 μ±λμ΄λ―λ‘
μ΄λ μ€μλ μ μ€μ²λ₯Ό λ³λ ¬λ‘ μνν μ μλ€.
audio β λ°ν (λ³λ ¬)
say λ μ£ΌνΒ·μ μ€μ² μ€μλ λ³λ ¬ μν κ°λ₯νλ€. κ°μ μ±λμ say κ° κ²Ήμ³ λμ°©νλ©΄
λ‘λ΄ νν κ³μΈ΅μ΄ κ²°ν© λ°©μ(μμ°¨Β·μλ΅)μ μμ¨ νλ¨ν΄ μ€ννλ€.
REQUEST
Topic: robot/REX/command
Payload
{
"session_id": "string",
"command_id": "string",
"robot_id": "REX",
"capability": "string",
"params": {},
"priority": "operator",
"expires_at": "2026-08-22T10:17:30.000Z",
"context": {
"scenario_id": "string",
"step_index": 0,
"reason": "string",
"utterance_hint": "string",
"must_complete": true
}
}
| νλ | νμ | νμ | μ€λͺ |
|---|---|---|---|
session_id |
string | Y | μ€μΌμ€νΈλ μ΄μ μλ²μμ λ°κΈνλ μΈμ μλ³μ |
command_id |
string | Y | μλ²κ° λΆμ¬νλ λͺ λ Ή μλ³μ(ULID κΆμ₯). RESPONSEμ κ·Έλλ‘ echo |
robot_id |
string | Y | λμ λ‘λ΄. REX κ³ μ . λΆμΌμΉ μ rejected(robot_id_mismatch) |
capability |
string | Y | μλ whitelist κ°. λ―Έλ±λ‘ μ rejected(capability_not_whitelisted:{name}) |
params |
object | N | capabilityλ³ νλΌλ―Έν° |
priority |
string | N | safety | operator (κΈ°λ³Έ operator). safetyλ μ§ν μ€ λμμ μ μ |
expires_at |
string | N | RFC3339 UTC. νμμ κΊΌλΌ λ κ²½κ³Ό μ rejected(expired) |
context.scenario_id |
string | N | μλλ¦¬μ€ μλ³μ |
context.step_index |
number | N | μλλ¦¬μ€ λ΄ μ€ν μλ² |
context.reason |
string | N | λͺ λ Ή μλ(λ‘λ΄μ΄ ννμ μ¬ν΄μνλ κ·Όκ±°) |
context.utterance_hint |
string | N | λ°ν μ μ(say). λ‘λ΄μ΄ κ·Έλλ‘ μ½μ§ μκ³ μκΈ° λ§ν¬λ‘ μ¬ν΄μ |
context.must_complete |
boolean | N | trueλ©΄ RESPONSEμ κ±°μ μ¬μ νμμ νμ©λ μ¬μ μΈμλ λ°λμ μν |
capability whitelist
REXκ° μ§μνλ capabilityλ μλ 4κ°λ€. μ€μ νμ± λͺ©λ‘μ robot/REX/capabilities(retained)μ
μ μΈλλ©°, μλ리μ€λ ν΄λΉ ν ν½μ μ μΈλ κ°λ§ μ¬μ©νλ€.
| capability | μ±λ | params | μ€λͺ |
|---|---|---|---|
stop |
μ μ±λ | channel, command_id, scenario_id (λͺ¨λ μ ν) |
μ μ§Β·μ·¨μ. λ―Έμ§μ μ μ μ±λ μ€λ¨ (μμ μ νμ μ ν¨) |
say |
audio |
text, voice_id |
λ°ν. νμλ voice_idλ‘ μ ν(Β§say voice μ ν) |
goto |
base |
x,y,yaw,velocity_max λλ zone_id |
μ§μ μμΉλ‘ μ£Όν |
gesture |
body |
name, repeat |
μ μ€μ² μ¬μ |
stopμchannelμbase|body|audioμ€ νλλ₯Ό μ§μ ν μ μκ³ , μλ΅ μ μ μ±λμ μ€λ¨νλ€.
say voice μ ν
say λͺ
λ Ήμ params.voice_idλ‘ λ°ν νμλ₯Ό μ ννλ€.
voice_idλ say capabilityμλ§ μ μ©νλ€.
| voice_id | TTS speaker | μ€λͺ |
|---|---|---|
sohee |
Sohee |
Qwen CustomVoice Sohee (REX νμ¬ κΈ°λ³Έ νμ) |
serena |
Serena |
Qwen CustomVoice Serena |
vivian |
Vivian |
Qwen CustomVoice Vivian |
eric |
Eric |
Qwen CustomVoice Eric |
νμ κ²°μ λ°©μ β μΈμ
AI νλ₯΄μλ κΈ°μ€. ν μΈμ
μλ νλμ AI νλ₯΄μλ
(orchestration/{sessionId}/me μ ai_companion)κ° λ§€νλλ©°, λ°ν νμλ κ·Έ νλ₯΄μλμ
λ°λΌ κ²°μ λλ€. μ€μΌμ€νΈλ μ΄μ
μλ²λ say λͺ
λ Ήμ μμ±ν λ μΈμ
νλ₯΄μλμ νμλ₯Ό
params.voice_id μ μ±μ μ μ‘νλ€.
voice_id μ°μ μμ:
- λͺ
λ Ήμ λͺ
μλ
params.voice_id - μΈμ
νλ₯΄μλμ κΈ°λ³Έ
voice_id - REX κΈ°λ³Έ νμ (νμ¬
sohee)
REXλ λ°μ voice_idλ₯Ό ν΄λΉ say λͺ
λ Ήμλ§ μ μ©ν΄ μ¬μνλ©°, νλ₯΄μλβνμ λ§€νμ
μ체 보κ΄νμ§ μλλ€(무μν). voice_idλ μ ν νλμ΄κ³ μλ¬Έμλ‘ λ³΄λΈλ€. λΉμ΄ μκ±°λ
λ―Έμ§μ κ°μ΄λ©΄ κ±°μ νμ§ μκ³ κΈ°λ³Έ νμ(sohee)λ‘ λ°ννλ€(κ°μ μλ¬Έμλ‘ μ κ·ν ν λΉκ΅).
νμ μ νμ μ΄ν λͺ
λ Ήμ κΈ°λ³Έ νμλ₯Ό λ°κΎΈμ§ μλλ€.
νλ₯΄μλλ³
voice_idλ§€νκ°κ³Ό μλ² μ£Όμ λ‘μ§μ μ€μΌμ€νΈλ μ΄μ μλ²/νλ₯΄μλ μ€ν€λ§ μμ κ΄λ¦¬νλ€(Β§νμΈ νμ μ¬ν). νμ¬ νλ₯΄μλ κ°μ²΄(orchestration/ai-companion/list)μλvoice_idνλκ° μμ΄ μΆκ°κ° νμνλ€.
μμ
{
"session_id": "abc123",
"command_id": "01K3QF8XW2N4V7B0YJ5R6T8ZAB",
"robot_id": "REX",
"capability": "gesture",
"params": { "name": "κ°ν λλμ", "repeat": 1 },
"priority": "operator",
"expires_at": "2026-08-22T10:17:30.000Z",
"context": {
"scenario_id": "SCN-WELCOME-03",
"step_index": 2,
"reason": "κ΄λκ° λΆμ€ μ§μ
",
"must_complete": true
}
}
say + voice_id μμ
{
"session_id": "abc123",
"command_id": "01KVOICE00000000000000001",
"robot_id": "REX",
"capability": "say",
"params": {
"text": "μλ
νμΈμ. μ λ REXμμ!",
"voice_id": "sohee"
},
"priority": "operator",
"expires_at": "2026-09-20T07:00:00.000Z",
"context": {
"scenario_id": "SCN-VOICE-TEST-01",
"step_index": 1,
"reason": "REX μμ± νμ μ ν ν
μ€νΈ",
"must_complete": true
}
}
RESPONSE
Topic: robot/REX/ack
λͺ λ Ή 1κ±΄λΉ μ΅μ 2ν λ°ννλ€(μ μ + μ’ λ£).
Payload
{
"session_id": "string",
"command_id": "string",
"robot_id": "REX",
"status": "accepted",
"reason": null,
"result": null,
"timestamp": "2026-08-22T10:16:00.220Z"
}
| νλ | νμ | νμ | μ€λͺ |
|---|---|---|---|
session_id |
string | Y | REQUESTμ κ°μ κ·Έλλ‘ echo |
command_id |
string | Y | REQUESTμ κ°μ κ·Έλλ‘ echo |
robot_id |
string | Y | REX |
status |
string | Y | accepted | queued | started | completed | failed | rejected |
reason |
string | null | N | failed / rejected μ μ¬μ μ½λ |
result |
object | null | N | completed μ κ²°κ³Ό (μ: {"duration_s": 4.1}) |
timestamp |
string | Y | RFC3339 UTC, λ°λ¦¬μ΄ |
status
| κ° | μλ―Έ |
|---|---|
accepted |
λͺ λ Ή μμ Β·ν ν¬μ (μμ ν 200 ms μ΄λ΄ λ°ν) |
queued |
λμ μ±λ(baseΒ·body)μ΄ μ¬μ© μ€μ΄λΌ λκΈ°μ΄μ 보κ΄. μ λμ μ’
λ£ ν started λ°ν |
started |
μ€μ μ€ν μμ |
completed |
μ μ μλ£. κ°μ μ±λμ λ€μ λͺ λ Ή μ μ‘ νΈλ¦¬κ±°λ‘ μ¬μ© |
failed |
μ€ν μ€ μ€ν¨ |
rejected |
μννμ§ μμ |
λμ μ±λμ΄ λΉμ΄ μμΌλ©΄
accepted β started β completed, μ¬μ© μ€μ΄λ©΄accepted β queued β started β completedμμΌλ‘ λ°νλλ€.
reason (failed / rejected)
| κ° | μν© | must_complete λͺ
λ Ήμ μ¬μ© |
|---|---|---|
schema_invalid |
νμ΄λ‘λ κ·κ²© μλ° | κ°λ₯ |
capability_not_whitelisted:{name} |
λ―Έμ§μ capability | κ°λ₯ |
robot_id_mismatch |
λμ λ‘λ΄ λΆμΌμΉ | κ°λ₯ |
expired |
expires_at κ²½κ³Ό (λκΈ° μ€ λ§λ£ ν¬ν¨) |
κ°λ₯ |
queue_full:{channel} |
ν΄λΉ μ±λ λκΈ°μ΄ μ΄κ³Ό | κ°λ₯ |
estop_engaged / estop_fence |
λΉμμ μ§ μν | κ°λ₯ |
unsafe_proximity |
μ¬λμ κ·Όμ ν΄ μ΄λ λΆκ° | κ°λ₯ |
obstacle_blocked |
κ²½λ‘ λ¬Όλ¦¬μ μ°¨λ¨ | κ°λ₯ |
low_battery |
λ°°ν°λ¦¬ μκ³ μ΄ν | κ°λ₯ |
preempted_by_safety |
priority: safety λͺ
λ Ήμ μ μ λ¨ |
β |
cancelled / cancelled_by_stop |
stopμΌλ‘ μ·¨μλ¨ |
β |
must_complete: trueλͺ λ Ήμ μ νμμ "κ°λ₯"μΌλ‘ νμλ μ¬μ λ‘λ§ κ±°μ νλ€. κ΄λκ°κ³Ό κ΅κ° μ€μ΄λΌλ μ΄μ λ‘λ κ±°μ νμ§ μμΌλ©°, νμ¬ λ°νλ₯Ό λ¬Έμ₯ λ¨μλ‘ λ§λ¬΄λ¦¬ν λ€(μ΅λ 3 s)startedλ₯Ό νμ νκ³ μννλ€.
μμ
{
"session_id": "abc123",
"command_id": "01K3QF8XW2N4V7B0YJ5R6T8ZAB",
"robot_id": "REX",
"status": "completed",
"reason": null,
"result": { "duration_s": 4.1 },
"timestamp": "2026-08-22T10:16:04.310Z"
}
QoS / κΈ°ν μ€μ
| νλͺ© | λ΄μ© |
|---|---|
| QoS | REQUEST 1 / RESPONSE 1 |
| Retain | REQUEST false / RESPONSE false |
| μΈμ | clean_session=false, client_id = rex-agent-REX (μ°κ²° λ¨μ μ€ λͺ
λ Ήμ λΈλ‘μ»€κ° λ³΄κ΄) |
| μλ΅ μκ° | command μμ β accepted 200 ms μ΄λ΄ |
μ€λ³΅ command_id |
μ¬μ€ννμ§ μκ³ νμ¬ statusλ₯Ό ackλ‘ μ¬λ°ν |
μ€λ³΅ envelope id |
μ μ‘ κ³μΈ΅ μ€λ³΅μΌλ‘ κ°μ£Όνκ³ λ¬΄μ (μ΅κ·Ό 1024κ° μΊμ) |
| λͺ λ Ή ν | μ±λλ³ λ
립 ν. κ° μ±λ(baseΒ·bodyΒ·audio)μ μ±λ λ΄ μμ°¨, μ±λ κ° λ³λ ¬. κ° ν μμμ safety > operator |
| μκ° νκΈ° | RFC3339 UTC, λ°λ¦¬μ΄, λͺ
μμ Z |
| λΉ-envelope νμ΄λ‘λ | νμ± μ€ν¨λ‘ drop, ack νμ§ μμ |
| μΈμ¦ | MQTT_USERNAME / MQTT_PASSWORD |
λΉκ³
CloudEvents envelope
REQUESTΒ·RESPONSE λͺ¨λ μλ ννλ‘ κ°μΌλ€. ꡬλ
μΈ‘μ data.*λ‘ μ½λλ€.
{
"specversion": "1.0",
"type": "robot.command",
"source": "/services/orchestrator",
"id": "01K3QF8XW2N4V7B0YJ5R6T8ZAC",
"time": "2026-08-22T10:15:03.412Z",
"subject": "robot/REX",
"datacontenttype": "application/json",
"data": { }
}
type |
λμ ν ν½ |
|---|---|
robot.command |
robot/REX/command |
robot.ack |
robot/REX/ack |
μ°κ΄ ν ν½
| ν ν½ | λ°©ν₯ | QoS | Retain | μ€λͺ |
|---|---|---|---|---|
robot/REX/state |
λ°ν | 0 | true | 1 Hz μν μ€λ
μ·. μ±λλ³(baseΒ·bodyΒ·audio) μ μ μν(data.channels) ν¬ν¨. availabilityλ data.connection(online/offline) + LWTλ‘ νν |
robot/REX/fault |
λ°ν | 1 | true | κ³ μ₯ λ°μ/ν΄μ. critical λ°μ μ μ§νΒ·λκΈ° λͺ
λ Ήμ μ λ¦¬ν΄ νμ |
robot/REX/event |
λ°ν | 1 | false | λ‘λ΄μ΄ κ°μ§ν μ΄λ²€νΈ(ν°μΉΒ·μ¬μ©μ κ°μ§ λ±) |
robot/REX/capabilities |
λ°ν | 1 | true | λΆν μ 1ν, μ§μ capabilityμ μ±λ λ§€ν μ μΈ |
safety/estop |
ꡬλ | 2 | true | μ μ λΉμμ μ§. κ±°μ λΆκ° |
system/heartbeat/rex-agent |
λ°ν | 0 | true | 10 s μ£ΌκΈ° |
νμΈ νμ μ¬ν
- νλ₯΄μλλ³ νμ(
voice_id) λ§€νμ μ€μΌμ€νΈλ μ΄μ μλ²μμ μ§μν μ§ νμ . νμ μμ : (1) νλ₯΄μλ κ°μ²΄(orchestration/ai-companion/list, μΈμ /meμai_companion)μvoice_idνλ μΆκ°(sohee|serena|vivian, λ―Έμ§μ μnullβ REX κΈ°λ³Έ νμ), (2) μλ²κ°sayμμ± μ μΈμ νλ₯΄μλμvoice_idλ₯Όparams.voice_idμ μ£Όμ (μ°μ μμ: μ‘μ μ§μ κ° > νλ₯΄μλvoice_id> REX κΈ°λ³Έ), (3) admin νλ₯΄μλ νΈμ§ νλ©΄μ νμ μ ν UI μΆκ°. μ΄κΈ° λ§€νμ:vivianβ μ₯λμ€λ¬μ΄μΉκ΅¬Β·νΌμΉ΄μΈΒ·μμλ±κ°μΌ,soheeβ μμνμ°μΈ,serenaβ μ΄λ¦΄μ ν€μ°λ κ°μμ§Β·λλν λΉμ. (μ€μ μ£Όμ μ νλ₯΄μλid(UUIDv7) κΈ°μ€) robot/{id}/commandν ν½μ λ°©ν₯(μ€μ β λ‘λ΄)μ΄ μ€μΌμ€νΈλ μ΄μ μλ² μ€κ³μ μΌμΉνλμ§ νμΈ. λΆμΌμΉ μrobot/REX/inboxλ‘ λΆλ¦¬ μ μzone_idλͺ λͺ κ·μΉ νμ βgotoparamsμ λΆμ€ μ‘΄ μ΄λ¦μ΄ κ°μ μ΄νλ₯Ό μ¬μ©ν΄μΌ ν¨robot_idλ₯Ό λλ¬ΈμREXλ‘ λ±λ‘ κ°λ₯νμ§ νμΈ- νλ‘λμ λΈλ‘컀 μ£ΌμΒ·μΈμ¦ λ°©μ νμ