Files
Migu2.0/SimpleLite/model/agv_glb/docs/接入教程.md
T
ArtoriasWu 15405ba114 新增 WMS 搬运规则与任务管理
支持搬运规则维护、候选预览、任务生成、预占、下发、取消和完成,并完善仓储管理前端交互。
2026-06-25 11:02:22 +08:00

8.7 KiB
Raw Blame History

AGV GLB 接入教程

面向 MES、数字孪生大屏、调度平台等外部系统的集成指南。
API 协议细节见 AGV播放API.md


架构

┌─────────────────┐     HTTP POST/GET      ┌──────────────┐     SSE 推送      ┌─────────────────┐
│  你的业务系统    │ ──────────────────────▶│  serve.py    │ ───────────────▶│  play.html      │
│  (任意语言)      │   /api/catalog         │  :8765       │   /api/agv/events│  (3D 播放页)    │
│                 │   /api/agv             │              │                  │                 │
└─────────────────┘                        └──────────────┘                  └─────────────────┘
组件 职责
业务系统 调用 HTTP 接口,无需了解 Three.js
serve.py 协议校验、命令缓存与转发
play.html 3D 渲染;须保持打开才会执行动作

播放页未打开时,命令会缓存;打开 play.html 后自动同步执行。


快速开始

1. 启动服务

Windows(推荐): 双击项目根目录 start.bat

# 或命令行
.\start.ps1

手动启动:

python serve.py
# 默认端口 8765;可通过环境变量 PORT 修改

首次部署且需使用 analyze_agv.py 扫描 GLB 时:

.\start.ps1 -Setup

2. 打开播放页

服务启动后,在浏览器中保持以下页面打开:

http://127.0.0.1:8765/viewer/play.html

配置页(编辑动作,非播放必需):

http://127.0.0.1:8765/viewer/index.html

3. 验证连通

curl http://127.0.0.1:8765/api/catalog

返回 ok: truedata.vehicles 非空即表示服务正常。

4. 发送第一条播放命令

curl -X POST http://127.0.0.1:8765/api/agv \
  -H "Content-Type: application/json" \
  -d "{\"version\":1,\"method\":\"playAction\",\"params\":{\"modelId\":\"英招\",\"actionId\":\"zuozhuan\"}}"

响应中 data.subscribers >= 1 表示播放页在线;为 0 时命令已缓存,打开播放页后会自动执行。


依赖说明

用途 要求
运行服务 + 播放 Python 3.10+(标准库即可)
前端 Three.js CDN 加载,无需 npm install
GLB 扫描工具 可选,运行 .\start.ps1 -Setup 安装

接入方式

方式 AHTTP API(推荐)

业务系统直接请求 serve.py 提供的 REST 接口,适合 Python、Java、C#、Node.js 等任意语言。

方式 B:iframe 嵌入(适合前端大屏)

play.html 嵌入 iframe,通过 postMessageAgvPlaybackApi 控制,协议字段与 HTTP 一致。


标准流程

① 启动 serve.py
        ↓
② 打开 play.html(可全屏或 iframe 嵌入大屏)
        ↓
③ GET /api/catalog  →  获取 modelId、actionId
        ↓
④ POST /api/agv     →  playAction / stopAction / clearActions
        ↓
⑤ 检查 data.subscribers >= 1

HTTP API

Base URL http://127.0.0.1:8765
协议版本: version: 1
Content-Type application/json

GET /api/catalog — 查询目录

接入前先调用,获取可用的 modelIdactionId

GET http://127.0.0.1:8765/api/catalog

响应示例:

{
  "version": 1,
  "ok": true,
  "data": {
    "vehicles": [
      {
        "modelId": "英招",
        "name": "英招",
        "actions": [
          { "id": "zuozhuan", "label": "左转" }
        ]
      }
    ]
  },
  "error": null
}

POST /api/agv — 控制播放

method 说明 params
playAction 播放动作,自动切换车辆 modelId + actionId 必填
stopAction 停止指定动作 modelId + actionId 必填
clearActions 停止该车全部动作 modelId 必填

playAction 示例:

{
  "version": 1,
  "method": "playAction",
  "params": {
    "modelId": "英招",
    "actionId": "zuozhuan"
  }
}

成功响应:

{
  "version": 1,
  "ok": true,
  "data": {
    "dispatched": true,
    "subscribers": 1,
    "modelId": "英招",
    "activeActions": ["zuozhuan"]
  },
  "error": null
}

失败响应:

{
  "version": 1,
  "ok": false,
  "data": null,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "params.modelId 必填"
  }
}
响应字段 含义
subscribers 当前连接的播放页数量;0 = 无播放页,命令已缓存
activeActions 当前正在播放的动作 ID 列表
dispatched 命令是否已成功写入总线

更多请求/响应示例见 AGV播放API.md


代码示例

Python

import requests

BASE = "http://127.0.0.1:8765"

def get_catalog():
    r = requests.get(f"{BASE}/api/catalog", timeout=5)
    r.raise_for_status()
    return r.json()["data"]["vehicles"]

def play_action(model_id: str, action_id: str):
    payload = {
        "version": 1,
        "method": "playAction",
        "params": {"modelId": model_id, "actionId": action_id},
    }
    r = requests.post(f"{BASE}/api/agv", json=payload, timeout=5)
    r.raise_for_status()
    data = r.json()
    if not data["ok"]:
        raise RuntimeError(data["error"]["message"])
    if data["data"]["subscribers"] == 0:
        print("警告: 播放页未打开,命令已缓存")
    return data

vehicles = get_catalog()
vehicle = vehicles[0]
action_id = vehicle["actions"][0]["id"]
play_action(vehicle["modelId"], action_id)

JavaScript

const BASE = "http://127.0.0.1:8765";

async function getCatalog() {
  const res = await fetch(`${BASE}/api/catalog`);
  const json = await res.json();
  return json.data.vehicles;
}

async function playAction(modelId, actionId) {
  const res = await fetch(`${BASE}/api/agv`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      version: 1,
      method: "playAction",
      params: { modelId, actionId },
    }),
  });
  const json = await res.json();
  if (!json.ok) throw new Error(json.error.message);
  return json;
}

iframe 嵌入

<iframe
  id="agv-player"
  src="http://127.0.0.1:8765/viewer/play.html"
  style="width:100%;height:100%;border:none"
></iframe>

<script>
const iframe = document.getElementById("agv-player");

// postMessage(协议与 HTTP 一致)
iframe.contentWindow.postMessage(
  {
    version: 1,
    id: "req-1",
    method: "playAction",
    params: { modelId: "英招", actionId: "zuozhuan" },
  },
  "*"
);

window.addEventListener("message", (e) => {
  if (e.data?.id === "req-1") console.log("播放结果:", e.data);
});

// 同源时可直接调用 AgvPlaybackApi
iframe.onload = async () => {
  const api = iframe.contentWindow.AgvPlaybackApi;
  await api.request({
    version: 1,
    method: "playAction",
    params: { modelId: "英招", actionId: "zuozhuan" },
  });
};
</script>

业务映射建议

业务事件 API 调用
AGV 开始某动作 playAction + 对应 actionId
AGV 停止当前动作 stopAction + 对应 actionId
AGV 复位 / 全部停止 clearActions
切换 AGV 型号 playAction 带新的 modelId(自动切车)

建议启动时拉一次 catalog,将业务编码(如 "TURN_LEFT")映射到 actionId,避免硬编码。


生产部署

  1. 播放页常开:大屏或工控机全屏打开 play.html,或用 iframe 嵌入业务页面
  2. 单实例:同一端口只运行一个 serve.py
  3. 自定义端口PORT=9000 python serve.py.\start.ps1 -Port 9000
  4. 健康检查:定期 GET /api/catalog 确认服务存活
  5. 新增车辆:将 GLB + JSON 放入 agv/,更新 agv/manifest.json,重启服务后 catalog 自动更新

常见问题

现象 处理
subscribers: 0 先打开 play.html
页面打不开 只保留一个 serve.py 进程
Postman 卡住 重启 serve.py
params.modelId 必填 三个 method 都要带 modelId
UNKNOWN_METHOD POST 只能用 3 个 method;查目录用 GET
动作不存在 先调 catalog 核对 actionId

相关文档

文档 说明
AGV播放API.md HTTP 协议与请求/响应格式
AGV_GLB导出规范.md GLB 模型导出与配置规范

数字孪生开发组