Repository navigation
MQTT_SCHEMA COMPANION_ROBOT
카이스트팀(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 가 겹쳐 도착하면
로봇 표현 계층이 결합 방식(순차·생략)을 자율 판단해 실행한다.
{
"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의 거절 사유표에서 허용된 사유 외에는 반드시 수행 |
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 명령은 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
}
}{
"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
}
}명령 1건당 최소 2회 발행한다(접수 + 종료).
{
"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, 밀리초 |
| 값 | 의미 |
|---|---|
accepted |
명령 수신·큐 투입 (수신 후 200 ms 이내 발행) |
queued |
대상 채널(base·body)이 사용 중이라 대기열에 보관. 앞 동작 종료 후 started 발행 |
started |
실제 실행 시작 |
completed |
정상 완료. 같은 채널의 다음 명령 전송 트리거로 사용 |
failed |
실행 중 실패 |
rejected |
수행하지 않음 |
대상 채널이 비어 있으면
accepted → started → completed, 사용 중이면accepted → queued → started → completed순으로 발행된다.
| 값 | 상황 |
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 | 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
|
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로 등록 가능한지 확인 - 프로덕션 브로커 주소·인증 방식 확정
- Google ADK 검토 (프레임워크·모델 비교)
- 페르소나 + 상황 카드 모델 (2차 시나리오 구동 방식)
- Mac Studio 로컬 LLM + Claude Opus 하이브리드 라우팅 전략
- 개발 일정·할 일 점검표 (단계별 진행 점검)
- 로봇 Adapter 개발 온보딩 (HA·MQTT 계정 등록, 토픽 적용, 테스트 송수신, 상태 확인)
- LG ThinQ 가전 연동 (HA 통합 · REST 데이터 취득 · 제어)
- 장소 도착 자동화 (태스크/이벤트/장소 규칙 → SPOT·로봇팔·반려로봇·Hub 자동 명령)
- 엔딩 화면 데이터 출처 점검 (차량 3필드 · 세션 종료 엔딩 5화면)