REST API & WebSocket 接口文档 | 多用户隔离架构
每个模拟器页面生成唯一的 clientId(格式 car-xxxxxxxxx),
通过 WebSocket 的 Socket.IO Room 机制实现隔离。指令和状态只在同一个 Room 内广播,不同 clientId 的小车互不干扰。
┌─────────────────────────────────┐
│ 浏览器 A │
│ clientId: car-abc123 │
│ Room "car-abc123" ─── 小车 A │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 浏览器 B │
│ clientId: car-xyz789 │
│ Room "car-xyz789" ─── 小车 B │
└─────────────────────────────────┘
REST API / WebSocket 通过 clientId 指定目标:
?clientId=car-abc123 → 只操控小车 A
?clientId=car-xyz789 → 只操控小车 B
不填 clientId → 指令不会到达任何小车
| Name | Type | Required | Description |
|---|---|---|---|
| clientId | string | required | 目标小车 ID(从模拟器页面复制),不填则指令不会到达任何小车 |
| action | string | required | 动作类型:up / down / left / right / stop / grab / release |
| speed | number | optional | 速度 (0.01 ~ 0.5) |
| distance | number | optional | 移动距离(厘米),适用于 up/down。参考:小车长 20cm,distance=20 约一个车身位 |
| angle | number | optional | 转动角度(度),适用于 left/right |
| time | number | optional | 持续时间(毫秒) |
# 控制 car-abc123 前进 20cm(约一个车身位) GET /api/control?clientId=car-abc123&action=up&speed=0.1&distance=20 # 控制 car-abc123 左转 90 度 GET /api/control?clientId=car-abc123&action=left&speed=0.05&angle=90 # 停止 GET /api/control?clientId=car-abc123&action=stop # 抓起物品 GET /api/control?clientId=car-abc123&action=grab
{
"success": true,
"target": "car-abc123",
"command": {
"action": "up",
"speed": 0.1,
"distance": 20
}
}
| Name | Type | Required | Description |
|---|---|---|---|
| clientId | string | optional | 目标小车 ID,默认 "default" |
{
"x": 0.5,
"z": -1.2,
"rotation": 3.14,
"velocity": 0.1,
"angularVelocity": 0,
"armState": "idle",
"hasBall": false,
"isColliding": false,
"grabAvailable": true,
"ballPosition": { "x": 0, "z": -5 }
}
clientId 指定目标小车{
"clientId": "car-abc123",
"action": "up",
"speed": 0.1,
"distance": 20,
"angle": 90
}
| Name | Type | Required | Description |
|---|---|---|---|
| clientId | string | optional | 目标小车 ID,默认 "default" |
{
"speed": 0.1,
"turnSpeed": 0.05
}
{
"speed": 0.15,
"turnSpeed": 0.08
}
Namespace: /car |
Transport: WebSocket / Polling
注意:所有消息基于 Room 隔离。必须先发送 join(带 clientId)加入 Room,否则 command/state 不会到达目标。
// 模拟器(自动) socket.emit('join', { role: 'simulator', clientId: 'car-abc123' }) // 外部控制器(指定要控制的小车) socket.emit('join', { role: 'controller', clientId: 'car-abc123' })
socket.emit('command', { action: 'up', speed: 0.1, distance: 20, angle: 90 })
socket.on('command', (data) => { // data: { action, value?, speed?, distance?, angle?, time? } // 只有同一 clientId 的客户端能收到 })
socket.emit('state', { x: 0.5, z: -1.2, rotation: 3.14, velocity: 0.1, angularVelocity: 0, armState: 'idle', hasBall: false, isColliding: false, grabAvailable: true, ballPosition: { x: 0, z: -5 } })
socket.on('state', (state) => { // state: RobotState object // 只收到同一 clientId 的小车状态 })
{
action: 'up' | 'down' | 'left' | 'right' | 'stop' | 'grab' | 'release' | 'setSpeed' | 'setTurnSpeed' | 'turnAngle',
value?: number,
distance?: number, // 厘米
angle?: number, // 度
speed?: number,
time?: number // 毫秒
}
{
x: number,
z: number,
rotation: number,
velocity: number,
angularVelocity: number,
armState: 'idle' | 'picking_down' | 'picking_up' | 'holding' | 'dropping_down' | 'dropping_up',
hasBall: boolean,
isColliding: boolean,
grabAvailable: boolean,
ballPosition: { x: number, z: number } | null
}
{
speed: number,
turnSpeed: number
}
| 实物 | 仿真单位 | 换算 |
|---|---|---|
| 小车长度 20cm | 1.8 单位 | 1cm = 0.09 单位 |
| 1 米 | 9 单位 | 1 单位 ≈ 11.1cm |
| 地面尺寸 | 160×160 单位 | ≈ 17.8m × 17.8m |
| 网格间距 | 9 单位 | = 1m(16×16 格) |
distance 参数单位为厘米。例如 distance=20 表示前进 20cm,即 20 × 0.09 = 1.8 单位 ≈ 一个车身位。