feat: complete dual-seat whole-vehicle orchestration

This commit is contained in:
vee
2026-08-12 15:12:40 +08:00
parent 32379a1573
commit 9ce64b3a01
62 changed files with 9741 additions and 3950 deletions
+36 -43
View File
@@ -4,8 +4,7 @@
const API_ROOT = "/api/calibration";
const TARGET_KEY = "cognitive-agent.calibration-target";
const pageTitles = {
hall: ["N7 SEAT CALIBRATION", "座椅 Hall 标定", "四轴位置、端点与自动标定"],
stereo: ["STEREO CALIBRATION", "双目映射标定", "MQTT 位姿、RTSP 画面与 Hall 映射"],
live: ["SEAT · ZED JOINT CALIBRATION", "座椅—双目联合标定", "座椅姿态、四轴 Hall、ZED 画面与双目位姿同步更新"],
system: ["CONNECTION SETTINGS", "连接与运行设置", "CAN、MQTT 与 RTSP 接入参数"],
};
const phaseLabels = {
@@ -27,7 +26,7 @@
let toastTimer = 0;
let settingsHydrated = false;
let modelRequested = false;
let activeView = "hall";
let activeView = "live";
function buildShell() {
const sidebar = $(".v2-sidebar");
@@ -42,10 +41,9 @@
<span>CALIBRATION DESK</span>
<h2>控制台</h2>
</div>
<span class="v2-field-caption">标定方式</span>
<div class="v2-calibration-mode-switch" role="group" aria-label="标定方式">
<button type="button" class="active" data-calibration-view="hall"><i data-lucide="armchair"></i><span>座椅标定</span></button>
<button type="button" data-calibration-view="stereo"><i data-lucide="scan"></i><span>双目标定</span></button>
<span class="v2-field-caption">标定工作台</span>
<div class="v2-calibration-mode-switch" role="group" aria-label="标定工作台">
<button type="button" class="active" data-calibration-view="live"><i data-lucide="scan-eye"></i><span>联合实时标定</span></button>
</div>
<div class="v2-divider"></div>
<span class="v2-field-caption">配置信息</span>
@@ -86,9 +84,9 @@
root.innerHTML = `
<header class="v2-calibration-heading">
<div>
<span class="section-kicker" id="calibrationPageKicker">N7 SEAT CALIBRATION</span>
<h1 id="calibrationPageTitle">座椅 Hall 标定</h1>
<p id="calibrationPageDescription">四轴位置、端点与自动标定</p>
<span class="section-kicker" id="calibrationPageKicker">SEAT · ZED JOINT CALIBRATION</span>
<h1 id="calibrationPageTitle">座椅—双目联合标定</h1>
<p id="calibrationPageDescription">座椅姿态、四轴 Hall、ZED 画面与双目位姿同步更新</p>
</div>
<div class="v2-calibration-statuses">
<div><i id="calibrationCanDot"></i><span><small>CAN</small><strong id="calibrationCanState">未连接</strong></span></div>
@@ -97,8 +95,8 @@
</div>
</header>
<section class="v2-calibration-view active" data-calibration-page="hall">
<div class="v2-calibration-hero-grid">
<section class="v2-calibration-view active v2-calibration-live-view" data-calibration-page="live">
<div class="v2-calibration-live-grid">
<section class="v2-calibration-panel v2-calibration-stage">
<header><div><span class="section-kicker">DIGITAL TWIN</span><h2>实时座椅姿态</h2></div><span class="v2-calibration-badge wait" id="calibrationPhaseBadge">待命</span></header>
<div class="v2-calibration-seat-scene">
@@ -122,45 +120,40 @@
</div>
</section>
<section class="v2-calibration-panel v2-calibration-workflow">
<header><div><span class="section-kicker">CALIBRATION FLOW</span><h2>自动标定</h2></div></header>
<p>依次寻找近端、远端并返回近端,完成后保存真实 Hall 量程。</p>
<div class="v2-calibration-flow">
<div data-phase="1"><span>01</span><div><strong>寻找最近点</strong><small>建立机械零位</small></div></div>
<i></i>
<div data-phase="2"><span>02</span><div><strong>寻找最远点</strong><small>测量完整量程</small></div></div>
<i></i>
<div data-phase="3"><span>03</span><div><strong>返回最近点</strong><small>验证端点一致性</small></div></div>
</div>
<button class="v2-calibration-primary" id="calibrationStartAll" type="button" disabled><span>开始四轴自动标定</span><i data-lucide="arrow-right"></i></button>
<div class="v2-calibration-safety"><i data-lucide="shield-check"></i><p><strong>安全租约已启用</strong><br>运动期间每 500 ms 发送心跳,超时由固件自动 STOP。</p></div>
<section class="v2-calibration-panel v2-calibration-video-panel">
<header><div><span class="section-kicker">ZED LIVE · RTSP</span><h2>ZED 实时画面</h2></div><span class="v2-calibration-badge wait" id="calibrationVideoBadge">未启动</span></header>
<div class="v2-calibration-video-frame"><img id="calibrationVideoImage" alt="ZED 双目实时画面"><div id="calibrationVideoPlaceholder"><i data-lucide="scan-line"></i><strong>ZED 双目画面</strong><small>RTSP → MJPEG 实时转发</small></div></div>
<p class="v2-calibration-detail" id="calibrationVideoDetail">视频接收尚未启动</p>
<div class="v2-calibration-actions"><button id="calibrationVideoStart" type="button">开始接收</button><button id="calibrationVideoStop" type="button">停止</button></div>
</section>
</div>
<section class="v2-calibration-axis-section">
<header><div><span class="section-kicker">LIVE AXES</span><h2>四轴实时控制</h2></div><p>松开滑块发送目标位置;端点按钮为一次性命令。</p></header>
<div class="v2-calibration-axis-grid" id="calibrationAxisCards"></div>
</section>
</section>
<section class="v2-calibration-view" data-calibration-page="stereo" hidden>
<div class="v2-calibration-stereo-grid">
<section class="v2-calibration-panel v2-calibration-mqtt-panel">
<header><div><span class="section-kicker">ZED / SEATPOSE</span><h2>双目位姿数据</h2></div><span class="v2-calibration-badge wait" id="calibrationMqttBadge">等待数据</span></header>
<div class="v2-calibration-metrics"><div><small>消息总数</small><strong id="calibrationMqttCount">0</strong></div><div><small>最新帧</small><strong id="calibrationMqttFrame">—</strong></div><div><small>解析错误</small><strong id="calibrationMqttErrors">0</strong></div></div>
<p class="v2-calibration-detail" id="calibrationMqttDetail">MQTT 尚未连接</p>
<div class="v2-calibration-pose-areas" id="calibrationPoseAreas"></div>
<div class="v2-calibration-actions"><button id="calibrationMqttConnect" type="button">连接 MQTT</button><button id="calibrationMqttDisconnect" type="button">断开</button><button id="calibrationMqttTest" type="button">发送回环测试</button></div>
</section>
<section class="v2-calibration-panel v2-calibration-video-panel">
<header><div><span class="section-kicker">RTSP MONITOR</span><h2>ZED 实时画面</h2></div><span class="v2-calibration-badge wait" id="calibrationVideoBadge">未启动</span></header>
<div class="v2-calibration-video-frame"><img id="calibrationVideoImage" alt="RTSP 视频画面"><div id="calibrationVideoPlaceholder"><i data-lucide="scan-line"></i><strong>RTSP → MJPEG</strong><small>由现有 Python 服务通过 FFmpeg 转发</small></div></div>
<p class="v2-calibration-detail" id="calibrationVideoDetail">视频接收尚未启动</p>
<div class="v2-calibration-actions"><button id="calibrationVideoStart" type="button">开始接收</button><button id="calibrationVideoStop" type="button">停止</button></div>
<div class="v2-calibration-live-mapping">
<div class="v2-calibration-subheading"><span>HALL ↔ 3D</span><strong>实时映射</strong></div>
<div class="v2-calibration-mapping-rows" id="calibrationMappingRows"></div>
</div>
<div class="v2-calibration-actions"><button id="calibrationMqttConnect" type="button">连接 MQTT</button><button id="calibrationMqttDisconnect" type="button">断开</button><button id="calibrationMqttTest" type="button">回环测试</button></div>
</section>
</div>
<section class="v2-calibration-panel v2-calibration-mapping-panel">
<header><div><span class="section-kicker">HALL ↔ 3D</span><h2>实时映射概览</h2></div></header>
<div class="v2-calibration-mapping-rows" id="calibrationMappingRows"></div>
<section class="v2-calibration-panel v2-calibration-axis-section v2-calibration-live-axes">
<header class="v2-calibration-live-toolbar">
<div class="v2-calibration-axis-title"><span class="section-kicker">LIVE AXES</span><h2>四轴实时控制</h2><small>滑块设定位置 · 端点按钮单次执行</small></div>
<div class="v2-calibration-flow" aria-label="自动标定流程">
<div data-phase="1"><span>01</span><div><strong>最近点</strong><small>建立机械零位</small></div></div>
<i></i>
<div data-phase="2"><span>02</span><div><strong>最远点</strong><small>测量完整量程</small></div></div>
<i></i>
<div data-phase="3"><span>03</span><div><strong>返回近点</strong><small>验证端点一致</small></div></div>
</div>
<button class="v2-calibration-primary" id="calibrationStartAll" type="button" disabled><span>开始四轴自动标定</span><i data-lucide="arrow-right"></i></button>
</header>
<div class="v2-calibration-axis-grid" id="calibrationAxisCards"></div>
<div class="v2-calibration-safety v2-calibration-live-safety"><i data-lucide="shield-check"></i><p><strong>安全租约已启用</strong><span>运动期间每 500 ms 心跳;超时由固件自动 STOP。</span></p></div>
</section>
</section>
@@ -551,7 +544,7 @@
}
function showView(view) {
activeView = pageTitles[view] ? view : "hall";
activeView = pageTitles[view] ? view : "live";
$$(".v2-calibration-view").forEach((page) => {
const active = page.dataset.calibrationPage === activeView;
page.hidden = !active;