分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>行星三轮组越障小车机构原理动画</title>
  <style>
    :root {
      --bg-color: #080c14;
      --panel-bg: rgba(13, 20, 36, 0.75);
      --border-color: rgba(0, 229, 255, 0.2);
      --border-glow: rgba(0, 229, 255, 0.4);
      --cyan-core: #00f0ff;
      --orange-accent: #ff6b00;
      --orange-glow: rgba(255, 107, 0, 0.4);
      --green-ready: #00ff88;
      --text-main: #e2e8f0;
      --text-muted: #64748b;
      --font-mono: "JetBrains Mono", "SF Mono", "Fira Code", monospace;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }

    body {
      background-color: var(--bg-color);
      color: var(--text-main);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      overflow: hidden;
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      position: relative;
    }

    /* 背景精细 CAD 测绘网格 */
    .bg-grid {
      position: absolute;
      inset: 0;
      background-image: 
        radial-gradient(circle at 50% 50%, rgba(0, 240, 255, 0.05) 0%, transparent 80%),
        linear-gradient(rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(rgba(255, 255, 255, 0.015) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.015) 1px, transparent 1px);
      background-size: 100% 100%, 100px 100px, 100px 100px, 20px 20px, 20px 20px;
      pointer-events: none;
      z-index: 0;
    }

    /* 顶部紧凑状态条 - 极低存在感、不遮挡中央 */
    .header-bar {
      position: absolute;
      top: 16px;
      left: 20px;
      right: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      z-index: 20;
      pointer-events: none;
    }

    .brand-tag {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 10px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 6px 14px;
      border-radius: 6px;
      backdrop-filter: blur(10px);
    }

    .status-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: var(--green-ready);
      box-shadow: 0 0 10px var(--green-ready);
      animation: pulse-dot 2s infinite ease-in-out;
    }

    .title-text {
      font-size: 13px;
      font-weight: 600;
      letter-spacing: 0.5px;
      color: #fff;
    }

    .title-sub {
      font-size: 11px;
      color: var(--cyan-core);
      font-family: var(--font-mono);
      margin-left: 4px;
    }

    /* 右上角图层切换器 */
    .hud-switches {
      pointer-events: auto;
      display: flex;
      gap: 8px;
    }

    .hud-btn {
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      color: var(--text-muted);
      font-size: 11px;
      padding: 5px 10px;
      border-radius: 4px;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 6px;
      transition: all 0.2s;
    }

    .hud-btn:hover {
      border-color: var(--cyan-core);
      color: var(--text-main);
    }

    .hud-btn.active {
      border-color: var(--cyan-core);
      color: var(--cyan-core);
      background: rgba(0, 240, 255, 0.08);
      box-shadow: 0 0 10px rgba(0, 240, 255, 0.15);
    }

    .hud-btn .key-dot {
      width: 5px;
      height: 5px;
      border-radius: 50%;
      background: currentColor;
    }

    /* 中央核心动画舞台 */
    .viewport-container {
      position: absolute;
      inset: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 10;
    }

    #cad-stage {
      width: 100%;
      height: 100%;
      max-width: 1400px;
      max-height: 860px;
    }

    /* 底部紧凑控制与原理步骤提示条 */
    .footer-bar {
      position: absolute;
      bottom: 16px;
      left: 20px;
      right: 20px;
      z-index: 20;
      display: flex;
      flex-direction: column;
      gap: 8px;
      pointer-events: none;
    }

    .phase-indicator {
      display: flex;
      justify-content: space-between;
      gap: 12px;
      pointer-events: auto;
    }

    .phase-chip {
      flex: 1;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.08);
      padding: 7px 12px;
      border-radius: 5px;
      display: flex;
      align-items: center;
      gap: 8px;
      backdrop-filter: blur(12px);
      transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    }

    .phase-chip.active {
      border-color: var(--orange-accent);
      background: rgba(255, 107, 0, 0.12);
      box-shadow: 0 0 15px rgba(255, 107, 0, 0.2);
    }

    .phase-idx {
      font-family: var(--font-mono);
      font-size: 11px;
      font-weight: bold;
      color: var(--text-muted);
      min-width: 16px;
    }

    .phase-chip.active .phase-idx {
      color: var(--orange-accent);
    }

    .phase-name {
      font-size: 12px;
      color: var(--text-muted);
      white-space: nowrap;
    }

    .phase-chip.active .phase-name {
      color: #fff;
      font-weight: 500;
    }

    /* 控制面板 */
    .controls-panel {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 14px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 7px 16px;
      border-radius: 6px;
      backdrop-filter: blur(12px);
    }

    .btn-action {
      background: transparent;
      border: 1px solid var(--border-color);
      color: var(--cyan-core);
      width: 30px;
      height: 30px;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.2s;
    }

    .btn-action:hover {
      background: var(--cyan-core);
      color: #000;
      box-shadow: 0 0 10px var(--cyan-core);
    }

    .timeline-slider {
      flex: 1;
      height: 4px;
      -webkit-appearance: none;
      background: rgba(255, 255, 255, 0.15);
      border-radius: 2px;
      outline: none;
      cursor: pointer;
    }

    .timeline-slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: var(--cyan-core);
      box-shadow: 0 0 8px var(--cyan-core);
      cursor: ew-resize;
      transition: transform 0.1s;
    }

    .timeline-slider::-webkit-slider-thumb:hover {
      transform: scale(1.3);
    }

    .telemetry-tag {
      font-family: var(--font-mono);
      font-size: 11px;
      color: var(--cyan-core);
      min-width: 140px;
      text-align: right;
    }

    @keyframes pulse-dot {
      0%, 100% { opacity: 0.4; transform: scale(0.9); }
      50% { opacity: 1; transform: scale(1.15); }
    }

    /* 微型悬浮参数卡 - 紧贴右下,不挡中央视觉 */
    .param-overlay {
      position: absolute;
      top: 60px;
      right: 20px;
      z-index: 15;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.1);
      padding: 10px 14px;
      border-radius: 6px;
      font-size: 11px;
      line-height: 1.6;
      pointer-events: none;
      backdrop-filter: blur(8px);
    }

    .param-overlay .item {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      color: #94a3b8;
    }

    .param-overlay .val {
      font-family: var(--font-mono);
      color: var(--cyan-core);
      font-weight: 500;
    }
  </style>
</head>
<body>
  <div class="bg-grid"></div>

  <!-- 顶部状态栏 -->
  <div class="header-bar">
    <div class="brand-tag">
      <div class="status-dot"></div>
      <span class="title-text">行星三轮组阶跃越障机构</span>
      <span class="title-sub">[IFR 连续小步自适应解]</span>
    </div>

    <div class="hud-switches">
      <button class="hud-btn active" id="btn-toggle-forces" onclick="toggleLayer('forces')">
        <span class="key-dot"></span> 力矩/矢量 (<span id="t-forces">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-orbits" onclick="toggleLayer('orbits')">
        <span class="key-dot"></span> 回转包络线 (<span id="t-orbits">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-leveler" onclick="toggleLayer('leveler')">
        <span class="key-dot"></span> 货箱水平阻尼 (<span id="t-leveler">开</span>)
      </button>
    </div>
  </div>

  <!-- 关键参数指示 -->
  <div class="param-overlay">
    <div class="item"><span>单轮直径 (2r):</span><span class="val">Ø 120 mm</span></div>
    <div class="item"><span>回转半径 (R):</span><span class="val">165 mm</span></div>
    <div class="item"><span>单向力矩阈值:</span><span class="val">6.0 N·m</span></div>
    <div class="item"><span>台阶单级标高:</span><span class="val">140 mm</span></div>
  </div>

  <!-- 中央主舞台 SVG -->
  <div class="viewport-container">
    <svg id="cad-stage" viewBox="0 0 1200 680" preserveAspectRatio="xMidYMid meet">
      <defs>
        <!-- 发光滤镜 -->
        <filter id="glow-cyan" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="3.5" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>
        <filter id="glow-orange" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="4" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>

        <!-- 箭头标记 -->
        <marker id="arrow-cyan" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00f0ff" />
        </marker>
        <marker id="arrow-orange" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#ff6b00" />
        </marker>
        <marker id="arrow-green" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00ff88" />
        </marker>

        <!-- 材质填充 -->
        <pattern id="hatch-stairs" width="12" height="12" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
          <line x1="0" y1="0" x2="0" y2="12" stroke="rgba(0, 240, 255, 0.08)" stroke-width="1.5" />
        </pattern>
        <pattern id="cargo-texture" width="10" height="10" patternUnits="userSpaceOnUse">
          <rect width="10" height="10" fill="#132035" />
          <path d="M 0 10 L 10 0 M 0 0 L 10 10" stroke="rgba(0, 240, 255, 0.12)" stroke-width="0.75"/>
        </pattern>
      </defs>

      <!-- 1. 楼梯与地形环境 -->
      <g id="terrain-group">
        <!-- 楼梯剖面基座 -->
        <path id="stair-base" d="M -50 540 L 400 540 L 400 420 L 750 420 L 750 300 L 1250 300 L 1250 640 L -50 640 Z" 
              fill="url(#hatch-stairs)" stroke="rgba(0, 240, 255, 0.4)" stroke-width="2" />
        
        <!-- 楼梯踏面加固线 -->
        <path d="M -50 540 L 400 540 L 400 420 L 750 420 L 750 300 L 1250 300" 
              fill="none" stroke="#00f0ff" stroke-width="3" filter="url(#glow-cyan)" />

        <!-- 踏面防滑纹理与倒角高亮 -->
        <g stroke="rgba(0, 240, 255, 0.3)" stroke-width="1">
          <line x1="400" y1="540" x2="400" y2="420" stroke="#ff6b00" stroke-width="2.5" />
          <line x1="750" y1="420" x2="750" y2="300" stroke="#ff6b00" stroke-width="2.5" />
          <circle cx="400" cy="420" r="3" fill="#ff6b00" />
          <circle cx="750" cy="300" r="3" fill="#ff6b00" />
        </g>

        <!-- 尺寸标注 (高度与宽度) -->
        <g id="layer-dim" stroke="#64748b" stroke-width="1" font-family="JetBrains Mono" font-size="11" fill="#64748b">
          <!-- 阶高标尺 -->
          <line x1="380" y1="540" x2="380" y2="420" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="365" y="485" text-anchor="end" fill="#00f0ff">H=140mm</text>
          
          <line x1="730" y1="420" x2="730" y2="300" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="715" y="365" text-anchor="end" fill="#00f0ff">H=140mm</text>

          <!-- 踏宽标尺 -->
          <line x1="400" y1="560" x2="750" y2="560" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="575" y="580" text-anchor="middle" fill="#00f0ff">Step Width = 350mm</text>
        </g>
      </g>

      <!-- 2. 小车主机构动态装配组 (由 JS 驱动位置与姿态) -->
      <g id="cart-assembly">
        
        <!-- 回转轨迹层 -->
        <g id="layer-orbits" stroke="rgba(0, 240, 255, 0.25)" stroke-dasharray="4,4" fill="none">
          <circle id="front-orbit" cx="0" cy="0" r="68" />
          <circle id="rear-orbit" cx="0" cy="0" r="68" />
        </g>

        <!-- 车架主梁 -->
        <path id="chassis-beam" d="M 0 0 L 260 0" stroke="#334155" stroke-width="14" stroke-linecap="round" />
        <path id="chassis-core" d="M 0 0 L 260 0" stroke="#00f0ff" stroke-width="3" />

        <!-- 阻尼器与自调平机构连杆 -->
        <g id="layer-leveler">
          <!-- 阻尼器套筒 -->
          <line id="damper-line" x1="130" y1="0" x2="130" y2="-65" stroke="#ff6b00" stroke-width="4" stroke-dasharray="6,3" />
          <circle id="damper-joint-b" cx="130" cy="0" r="5" fill="#ff6b00" />
          <circle id="damper-joint-t" cx="130" cy="-65" r="5" fill="#ff6b00" />
          
          <!-- 被动防倾摆臂 -->
          <line id="arm-f" x1="40" y1="0" x2="60" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
          <line id="arm-r" x1="220" y1="0" x2="200" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
        </g>

        <!-- 货箱平台 (保持水平) -->
        <g id="cargo-platform">
          <rect id="cargo-box" x="20" y="-120" width="220" height="58" rx="6" fill="url(#cargo-texture)" stroke="#00f0ff" stroke-width="2" />
          <!-- 货物重心与水平仪 -->
          <line x1="30" y1="-91" x2="230" y2="-91" stroke="rgba(0,240,255,0.3)" stroke-width="1" stroke-dasharray="3,3" />
          <circle cx="130" cy="-91" r="9" fill="rgba(0, 255, 136, 0.2)" stroke="#00ff88" stroke-width="1.5" />
          <line x1="121" y1="-91" x2="139" y2="-91" stroke="#00ff88" stroke-width="1.5" />
          <line x1="130" y1="-100" x2="130" y2="-82" stroke="#00ff88" stroke-width="1.5" />
          <text x="145" y="-88" font-family="JetBrains Mono" font-size="10" fill="#00ff88">CARGO LEVEL (0°)</text>
        </g>

        <!-- 后行星三轮组 -->
        <g id="rear-planetary-set" transform="translate(0, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="rear-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <g id="rear-spider-glow" stroke="#00f0ff" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" fill="none">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <!-- 3个行星橡胶轮 (R=25, 直径120mm缩比) -->
          <g id="rear-wheels">
            <g id="rw-0" transform="translate(0, -68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-1" transform="translate(58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-2" transform="translate(-58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向轴承主轴 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 前行星三轮组 -->
        <g id="front-planetary-set" transform="translate(260, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="front-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <g id="front-spider-glow" stroke="#ff6b00" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" fill="none" filter="url(#glow-orange)">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <!-- 3个行星橡胶轮 -->
          <g id="front-wheels">
            <g id="fw-0" transform="translate(0, -68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-1" transform="translate(58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-2" transform="translate(-58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向离合轴承 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 3. 动态受力与力矩矢量指示层 -->
        <g id="layer-forces">
          <!-- 前轮受阻碰撞法向力 (台阶阻力) -->
          <g id="force-barrier" opacity="0">
            <line x1="318.89" y1="34" x2="270" y2="34" stroke="#ff0055" stroke-width="4" marker-end="url(#arrow-orange)" />
            <text x="325" y="30" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff0055">F_阻挡</text>
          </g>

          <!-- 转臂翻转驱动力矩圆弧 (Torque >= 6N·m) -->
          <g id="torque-arc" opacity="0">
            <path d="M 260 -35 A 35 35 0 0 1 295 0" fill="none" stroke="#ff6b00" stroke-width="3" marker-end="url(#arrow-orange)" filter="url(#glow-orange)" />
            <text x="290" y="-40" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff6b00">M &gt; 6N·m 触发翻转</text>
          </g>

          <!-- 爬阶抬升力矢量 -->
          <g id="force-lift" opacity="0">
            <line x1="260" y1="0" x2="260" y2="-50" stroke="#00ff88" stroke-width="3.5" marker-end="url(#arrow-green)" filter="url(#glow-cyan)" />
            <text x="270" y="-30" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#00ff88">F_抬升 (Step Up)</text>
          </g>
        </g>

      </g>
    </svg>
  </div>

  <!-- 底部交互控制与时序微步骤 -->
  <div class="footer-bar">
    <div class="phase-indicator">
      <div class="phase-chip" id="chip-0">
        <span class="phase-idx">01</span>
        <span class="phase-name">平地滚动行驶</span>
      </div>
      <div class="phase-chip" id="chip-1">
        <span class="phase-idx">02</span>
        <span class="phase-name">前轮触阶·力矩积聚</span>
      </div>
      <div class="phase-chip" id="chip-2">
        <span class="phase-idx">03</span>
        <span class="phase-name">星轮翻转 120° 跨级</span>
      </div>
      <div class="phase-chip" id="chip-3">
        <span class="phase-idx">04</span>
        <span class="phase-name">上轮咬合·后组接力</span>
      </div>
      <div class="phase-chip" id="chip-4">
        <span class="phase-idx">05</span>
        <span class="phase-name">阻尼自调平·平稳恢复</span>
      </div>
    </div>

    <div class="controls-panel">
      <button class="btn-action" id="btn-play-pause" onclick="togglePlay()" title="播放/暂停">
        <svg id="icon-pause" width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
        <svg id="icon-play" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="display:none;"><polygon points="5 3 19 12 5 21 5 3"/></svg>
      </button>

      <button class="btn-action" onclick="resetAnimation()" title="重置">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
      </button>

      <input type="range" id="timeline" class="timeline-slider" min="0" max="1000" value="0" oninput="onSeek(this.value)" />

      <div class="telemetry-tag" id="telemetry-status">
        STAGE: 01 | 0.00s
      </div>
    </div>
  </div>

  <script>
    /* 全局图层显隐开关 */
    const layers = {
      forces: true,
      orbits: true,
      leveler: true
    };

    function toggleLayer(layerName) {
      layers[layerName] = !layers[layerName];
      const btn = document.getElementById('btn-toggle-' + layerName);
      const txt = document.getElementById('t-' + layerName);
      if (layers[layerName]) {
        btn.classList.add('active');
        txt.innerText = '开';
      } else {
        btn.classList.remove('active');
        txt.innerText = '关';
      }
      
      const elem = document.getElementById('layer-' + layerName);
      if (elem) {
        elem.style.display = layers[layerName] ? 'inline' : 'none';
      }
    }

    /* 运动学时序与状态机模型 */
    const TOTAL_DURATION = 9000; // 完整单次越障循环 9秒
    let isPlaying = true;
    let startTime = null;
    let pausedTime = 0;
    let currentProgress = 0; // 0.0 ~ 1.0

    // DOM 元素引用
    const cartAssembly = document.getElementById('cart-assembly');
    const frontSpider = document.getElementById('front-planetary-set');
    const rearSpider = document.getElementById('rear-planetary-set');
    const cargoPlatform = document.getElementById('cargo-platform');
    const timeline = document.getElementById('timeline');
    const telemetry = document.getElementById('telemetry-status');
    const iconPlay = document.getElementById('icon-play');
    const iconPause = document.getElementById('icon-pause');

    // 矢量受力 DOM
    const forceBarrier = document.getElementById('force-barrier');
    const torqueArc = document.getElementById('torque-arc');
    const forceLift = document.getElementById('force-lift');

    // 行星轮子单独自转
    const fwWheels = [document.getElementById('fw-0'), document.getElementById('fw-1'), document.getElementById('fw-2')];
    const rwWheels = [document.getElementById('rw-0'), document.getElementById('rw-1'), document.getElementById('rw-2')];

    // 缓动函数
    function easeInOutQuad(t) {
      return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    }
    function clamp(val, min, max) {
      return Math.min(Math.max(val, min), max);
    }

    /* 运动学姿态求解器 */
    function computeKinematics(p) {
      // p: 0.0 ~ 1.0
      // 场景设计:小车从 X=-100 运动到 X=650
      // 地形:平地 (Y=506), 踏面一 (X=400, Y=386), 踏面二 (X=750, Y=266)
      // 前轮在 X=140 时遇到第一阶 (车身X+260+58.89 = 400 -> 车身X=81)
      
      let carX = -60 + p * 620; // 车架后轴X位置
      let rearX = carX;
      let frontX = carX + 260;

      // 阶段1:前轮触阶 & 翻转120度 (p: 0.16 ~ 0.38)
      // 阶段2:后轮触阶 & 翻转120度 (p: 0.50 ~ 0.72)
      
      let frontAngle = 0;
      let rearAngle = 0;
      let wheelSpin = carX * 1.5; // 自转角

      let frontY = 506;
      let rearY = 506;

      // 前行星架越障解算
      const fStart = 0.15, fEnd = 0.38;
      let fFlipProgress = 0;
      if (p >= fStart && p <= fEnd) {
        fFlipProgress = (p - fStart) / (fEnd - fStart);
        let smoothP = easeInOutQuad(fFlipProgress);
        frontAngle = -smoothP * 120; // 逆时针翻转 120°
        frontY = 506 - smoothP * 120; // 上升到第一级台阶
      } else if (p > fEnd) {
        frontAngle = -120;
        frontY = 386;
      }

      // 后行星架越障解算
      const rStart = 0.48, rEnd = 0.71;
      let rFlipProgress = 0;
      if (p >= rStart && p <= rEnd) {
        rFlipProgress = (p - rStart) / (rEnd - rStart);
        let smoothP = easeInOutQuad(rFlipProgress);
        rearAngle = -smoothP * 120;
        rearY = 506 - smoothP * 120;
      } else if (p > rEnd) {
        rearAngle = -120;
        rearY = 386;
      }

      // 车身主梁倾角与自调平货箱角度
      const chassisAngleRad = Math.atan2(frontY - rearY, 260);
      const chassisAngleDeg = (chassisAngleRad * 180) / Math.PI;

      // 矢量指示器状态
      let barrierOpacity = 0;
      let torqueOpacity = 0;
      let liftOpacity = 0;

      if (p >= 0.12 && p < 0.18) {
        // 刚顶住台阶,力矩聚积
        barrierOpacity = clamp((p - 0.12) / 0.03, 0, 1);
        torqueOpacity = barrierOpacity;
      } else if (p >= 0.18 && p <= 0.32) {
        // 翻转抬升中
        barrierOpacity = 0.2;
        torqueOpacity = 1.0;
        liftOpacity = clamp(Math.sin((p - 0.18) / 0.14 * Math.PI), 0, 1);
      }

      // 确定阶段指示高亮
      let phaseIndex = 0;
      if (p < 0.14) phaseIndex = 0;
      else if (p < 0.22) phaseIndex = 1;
      else if (p < 0.45) phaseIndex = 2;
      else if (p < 0.75) phaseIndex = 3;
      else phaseIndex = 4;

      return {
        carX,
        frontX,
        rearX,
        frontY,
        rearY,
        frontAngle,
        rearAngle,
        wheelSpin,
        chassisAngleDeg,
        barrierOpacity,
        torqueOpacity,
        liftOpacity,
        phaseIndex
      };
    }

    /* 渲染一帧 */
    function renderFrame(state) {
      // 1. 底盘定位与俯仰
      cartAssembly.setAttribute('transform', `translate(${state.rearX}, ${state.rearY}) rotate(${state.chassisAngleDeg})`);

      // 2. 前后星形转臂旋转 (需要抵消底盘倾角)
      frontSpider.setAttribute('transform', `translate(260, 0) rotate(${state.frontAngle - state.chassisAngleDeg})`);
      rearSpider.setAttribute('transform', `translate(0, 0) rotate(${state.rearAngle - state.chassisAngleDeg})`);

      // 3. 轮子自转 (给车轮视觉滚动效果)
      const wRot = `rotate(${state.wheelSpin})`;
      fwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));
      rwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));

      // 4. 自调平货箱:反向旋转抵消车身倾角,保持水平
      cargoPlatform.setAttribute('transform', `rotate(${-state.chassisAngleDeg}, 130, 0)`);

      // 5. 受力与力矩矢量透明度
      if (layers.forces) {
        forceBarrier.setAttribute('opacity', state.barrierOpacity);
        torqueArc.setAttribute('opacity', state.torqueOpacity);
        forceLift.setAttribute('opacity', state.liftOpacity);
      }

      // 6. 阶段高亮更新
      for (let i = 0; i <= 4; i++) {
        const chip = document.getElementById(`chip-${i}`);
        if (i === state.phaseIndex) {
          chip.classList.add('active');
        } else {
          chip.classList.remove('active');
        }
      }

      // 7. 更新底部滑块与遥测数据
      timeline.value = currentProgress * 1000;
      const sec = (currentProgress * (TOTAL_DURATION / 1000)).toFixed(2);
      telemetry.innerText = `PHASE: 0${state.phaseIndex + 1} | T: ${sec}s | α: ${state.chassisAngleDeg.toFixed(1)}°`;
    }

    // 初始化轮子相对基座位置缓存
    function initWheelTransforms() {
      fwWheels[0].setAttribute('data-base', 'translate(0, -68)');
      fwWheels[1].setAttribute('data-base', 'translate(58.89, 34)');
      fwWheels[2].setAttribute('data-base', 'translate(-58.89, 34)');

      rwWheels[0].setAttribute('data-base', 'translate(0, -68)');
      rwWheels[1].setAttribute('data-base', 'translate(58.89, 34)');
      rwWheels[2].setAttribute('data-base', 'translate(-58.89, 34)');
    }

    /* 动画循环驱动 */
    function animate(timestamp) {
      if (!startTime) startTime = timestamp;

      if (isPlaying) {
        const elapsed = (timestamp - startTime + pausedTime) % TOTAL_DURATION;
        currentProgress = elapsed / TOTAL_DURATION;
      }

      const state = computeKinematics(currentProgress);
      renderFrame(state);

      requestAnimationFrame(animate);
    }

    /* 交互控制函数 */
    function togglePlay() {
      isPlaying = !isPlaying;
      if (isPlaying) {
        startTime = performance.now();
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'none';
        iconPause.style.display = 'inline';
      } else {
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'inline';
        iconPause.style.display = 'none';
      }
    }

    function resetAnimation() {
      currentProgress = 0;
      pausedTime = 0;
      startTime = performance.now();
      const state = computeKinematics(0);
      renderFrame(state);
    }

    function onSeek(val) {
      currentProgress = val / 1000;
      pausedTime = currentProgress * TOTAL_DURATION;
      startTime = performance.now();
      const state = computeKinematics(currentProgress);
      renderFrame(state);
    }

    // DOM 加载完成自动启动
    document.addEventListener('DOMContentLoaded', () => {
      initWheelTransforms();
      requestAnimationFrame(animate);
    });
  </script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>行星三轮组越障小车机构原理动画</title>
  <style>
    :root {
      --bg-color: #080c14;
      --panel-bg: rgba(13, 20, 36, 0.75);
      --border-color: rgba(0, 229, 255, 0.2);
      --border-glow: rgba(0, 229, 255, 0.4);
      --cyan-core: #00f0ff;
      --orange-accent: #ff6b00;
      --orange-glow: rgba(255, 107, 0, 0.4);
      --green-ready: #00ff88;
      --text-main: #e2e8f0;
      --text-muted: #64748b;
      --font-mono: "JetBrains Mono", "SF Mono", "Fira Code", monospace;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }

    body {
      background-color: var(--bg-color);
      color: var(--text-main);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      overflow: hidden;
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      position: relative;
    }

    /* 背景精细 CAD 测绘网格 */
    .bg-grid {
      position: absolute;
      inset: 0;
      background-image: 
        radial-gradient(circle at 50% 50%, rgba(0, 240, 255, 0.05) 0%, transparent 80%),
        linear-gradient(rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(rgba(255, 255, 255, 0.015) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.015) 1px, transparent 1px);
      background-size: 100% 100%, 100px 100px, 100px 100px, 20px 20px, 20px 20px;
      pointer-events: none;
      z-index: 0;
    }

    /* 顶部紧凑状态条 */
    .header-bar {
      position: absolute;
      top: 16px;
      left: 20px;
      right: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      z-index: 20;
      pointer-events: none;
    }

    .brand-tag {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 10px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 6px 14px;
      border-radius: 6px;
      backdrop-filter: blur(10px);
    }

    .status-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: var(--green-ready);
      box-shadow: 0 0 10px var(--green-ready);
      animation: pulse-dot 2s infinite ease-in-out;
    }

    .title-text {
      font-size: 13px;
      font-weight: 600;
      letter-spacing: 0.5px;
      color: #fff;
    }

    .title-sub {
      font-size: 11px;
      color: var(--cyan-core);
      font-family: var(--font-mono);
      margin-left: 4px;
    }

    /* 右上角图层切换器 */
    .hud-switches {
      pointer-events: auto;
      display: flex;
      gap: 8px;
    }

    .hud-btn {
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      color: var(--text-muted);
      font-size: 11px;
      padding: 5px 10px;
      border-radius: 4px;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 6px;
      transition: all 0.2s;
    }

    .hud-btn:hover {
      border-color: var(--cyan-core);
      color: var(--text-main);
    }

    .hud-btn.active {
      border-color: var(--cyan-core);
      color: var(--cyan-core);
      background: rgba(0, 240, 255, 0.08);
      box-shadow: 0 0 10px rgba(0, 240, 255, 0.15);
    }

    .hud-btn .key-dot {
      width: 5px;
      height: 5px;
      border-radius: 50%;
      background: currentColor;
    }

    /* 中央核心动画舞台 */
    .viewport-container {
      position: absolute;
      inset: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 10;
    }

    #cad-stage {
      width: 100%;
      height: 100%;
      max-width: 1400px;
      max-height: 860px;
    }

    /* 底部紧凑控制与原理步骤提示条 */
    .footer-bar {
      position: absolute;
      bottom: 16px;
      left: 20px;
      right: 20px;
      z-index: 20;
      display: flex;
      flex-direction: column;
      gap: 8px;
      pointer-events: none;
    }

    .phase-indicator {
      display: flex;
      justify-content: space-between;
      gap: 12px;
      pointer-events: auto;
    }

    .phase-chip {
      flex: 1;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.08);
      padding: 7px 12px;
      border-radius: 5px;
      display: flex;
      align-items: center;
      gap: 8px;
      backdrop-filter: blur(12px);
      transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    }

    .phase-chip.active {
      border-color: var(--orange-accent);
      background: rgba(255, 107, 0, 0.12);
      box-shadow: 0 0 15px rgba(255, 107, 0, 0.2);
    }

    .phase-idx {
      font-family: var(--font-mono);
      font-size: 11px;
      font-weight: bold;
      color: var(--text-muted);
      min-width: 16px;
    }

    .phase-chip.active .phase-idx {
      color: var(--orange-accent);
    }

    .phase-name {
      font-size: 12px;
      color: var(--text-muted);
      white-space: nowrap;
    }

    .phase-chip.active .phase-name {
      color: #fff;
      font-weight: 500;
    }

    /* 控制面板 */
    .controls-panel {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 14px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 7px 16px;
      border-radius: 6px;
      backdrop-filter: blur(12px);
    }

    .btn-action {
      background: transparent;
      border: 1px solid var(--border-color);
      color: var(--cyan-core);
      width: 30px;
      height: 30px;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.2s;
    }

    .btn-action:hover {
      background: var(--cyan-core);
      color: #000;
      box-shadow: 0 0 10px var(--cyan-core);
    }

    .timeline-slider {
      flex: 1;
      height: 4px;
      -webkit-appearance: none;
      background: rgba(255, 255, 255, 0.15);
      border-radius: 2px;
      outline: none;
      cursor: pointer;
    }

    .timeline-slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: var(--cyan-core);
      box-shadow: 0 0 8px var(--cyan-core);
      cursor: ew-resize;
      transition: transform 0.1s;
    }

    .timeline-slider::-webkit-slider-thumb:hover {
      transform: scale(1.3);
    }

    .telemetry-tag {
      font-family: var(--font-mono);
      font-size: 11px;
      color: var(--cyan-core);
      min-width: 140px;
      text-align: right;
    }

    @keyframes pulse-dot {
      0%, 100% { opacity: 0.4; transform: scale(0.9); }
      50% { opacity: 1; transform: scale(1.15); }
    }

    /* 微型悬浮参数卡 */
    .param-overlay {
      position: absolute;
      top: 60px;
      right: 20px;
      z-index: 15;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.1);
      padding: 10px 14px;
      border-radius: 6px;
      font-size: 11px;
      line-height: 1.6;
      pointer-events: none;
      backdrop-filter: blur(8px);
    }

    .param-overlay .item {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      color: #94a3b8;
    }

    .param-overlay .val {
      font-family: var(--font-mono);
      color: var(--cyan-core);
      font-weight: 500;
    }
  </style>
</head>
<body>
  <div class="bg-grid"></div>

  <!-- 顶部状态栏 -->
  <div class="header-bar">
    <div class="brand-tag">
      <div class="status-dot"></div>
      <span class="title-text">行星三轮组阶跃越障机构</span>
      <span class="title-sub">[顺时针跨级·中心等高台阶]</span>
    </div>

    <div class="hud-switches">
      <button class="hud-btn active" id="btn-toggle-forces" onclick="toggleLayer('forces')">
        <span class="key-dot"></span> 力矩/矢量 (<span id="t-forces">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-orbits" onclick="toggleLayer('orbits')">
        <span class="key-dot"></span> 回转包络线 (<span id="t-orbits">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-leveler" onclick="toggleLayer('leveler')">
        <span class="key-dot"></span> 货箱水平阻尼 (<span id="t-leveler">开</span>)
      </button>
    </div>
  </div>

  <!-- 关键参数指示 -->
  <div class="param-overlay">
    <div class="item"><span>单轮直径 (2r):</span><span class="val">Ø 120 mm</span></div>
    <div class="item"><span>回转半径 (R):</span><span class="val">165 mm</span></div>
    <div class="item"><span>台阶高度 (H=R):</span><span class="val">68 mm (至中心)</span></div>
    <div class="item"><span>越障翻转方向:</span><span class="val">顺时针 (+120°)</span></div>
  </div>

  <!-- 中央主舞台 SVG -->
  <div class="viewport-container">
    <svg id="cad-stage" viewBox="0 0 1200 680" preserveAspectRatio="xMidYMid meet">
      <defs>
        <!-- 发光滤镜 -->
        <filter id="glow-cyan" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="3.5" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>
        <filter id="glow-orange" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="4" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>

        <!-- 箭头标记 -->
        <marker id="arrow-cyan" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00f0ff" />
        </marker>
        <marker id="arrow-orange" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#ff6b00" />
        </marker>
        <marker id="arrow-green" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00ff88" />
        </marker>

        <!-- 材质填充 -->
        <pattern id="hatch-stairs" width="12" height="12" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
          <line x1="0" y1="0" x2="0" y2="12" stroke="rgba(0, 240, 255, 0.08)" stroke-width="1.5" />
        </pattern>
        <pattern id="cargo-texture" width="10" height="10" patternUnits="userSpaceOnUse">
          <rect width="10" height="10" fill="#132035" />
          <path d="M 0 10 L 10 0 M 0 0 L 10 10" stroke="rgba(0, 240, 255, 0.12)" stroke-width="0.75"/>
        </pattern>
      </defs>

      <!-- 1. 楼梯与地形环境 (台阶高度调整为行星轮中心高度 68px) -->
      <g id="terrain-group">
        <!-- 楼梯剖面基座: 地面 Y=540, 一阶踏面 Y=472 (高68px), 二阶踏面 Y=404 -->
        <path id="stair-base" d="M -50 540 L 400 540 L 400 472 L 750 472 L 750 404 L 1250 404 L 1250 640 L -50 640 Z" 
              fill="url(#hatch-stairs)" stroke="rgba(0, 240, 255, 0.4)" stroke-width="2" />
        
        <!-- 楼梯踏面加固线 -->
        <path d="M -50 540 L 400 540 L 400 472 L 750 472 L 750 404 L 1250 404" 
              fill="none" stroke="#00f0ff" stroke-width="3" filter="url(#glow-cyan)" />

        <!-- 踏面防滑纹理与倒角高亮 -->
        <g stroke="rgba(0, 240, 255, 0.3)" stroke-width="1">
          <line x1="400" y1="540" x2="400" y2="472" stroke="#ff6b00" stroke-width="2.5" />
          <line x1="750" y1="472" x2="750" y2="404" stroke="#ff6b00" stroke-width="2.5" />
          <circle cx="400" cy="472" r="3" fill="#ff6b00" />
          <circle cx="750" cy="404" r="3" fill="#ff6b00" />
        </g>

        <!-- 尺寸标注 (高度与宽度) -->
        <g id="layer-dim" stroke="#64748b" stroke-width="1" font-family="JetBrains Mono" font-size="11" fill="#64748b">
          <!-- 阶高标尺 (与中心等高 H=68mm) -->
          <line x1="380" y1="540" x2="380" y2="472" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="365" y="510" text-anchor="end" fill="#00f0ff">H=68mm (至中心轴)</text>
          
          <line x1="730" y1="472" x2="730" y2="404" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="715" y="442" text-anchor="end" fill="#00f0ff">H=68mm</text>

          <!-- 踏宽标尺 -->
          <line x1="400" y1="560" x2="750" y2="560" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="575" y="580" text-anchor="middle" fill="#00f0ff">Step Width = 350mm</text>
        </g>
      </g>

      <!-- 2. 小车主机构动态装配组 -->
      <g id="cart-assembly">
        
        <!-- 回转轨迹层 -->
        <g id="layer-orbits" stroke="rgba(0, 240, 255, 0.25)" stroke-dasharray="4,4" fill="none">
          <circle id="front-orbit" cx="260" cy="0" r="68" />
          <circle id="rear-orbit" cx="0" cy="0" r="68" />
        </g>

        <!-- 车架主梁 -->
        <path id="chassis-beam" d="M 0 0 L 260 0" stroke="#334155" stroke-width="14" stroke-linecap="round" />
        <path id="chassis-core" d="M 0 0 L 260 0" stroke="#00f0ff" stroke-width="3" />

        <!-- 阻尼器与自调平机构连杆 -->
        <g id="layer-leveler">
          <!-- 阻尼器套筒 -->
          <line id="damper-line" x1="130" y1="0" x2="130" y2="-65" stroke="#ff6b00" stroke-width="4" stroke-dasharray="6,3" />
          <circle id="damper-joint-b" cx="130" cy="0" r="5" fill="#ff6b00" />
          <circle id="damper-joint-t" cx="130" cy="-65" r="5" fill="#ff6b00" />
          
          <!-- 被动防倾摆臂 -->
          <line id="arm-f" x1="40" y1="0" x2="60" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
          <line id="arm-r" x1="220" y1="0" x2="200" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
        </g>

        <!-- 货箱平台 (保持水平) -->
        <g id="cargo-platform">
          <rect id="cargo-box" x="20" y="-120" width="220" height="58" rx="6" fill="url(#cargo-texture)" stroke="#00f0ff" stroke-width="2" />
          <!-- 货物重心与水平仪 -->
          <line x1="30" y1="-91" x2="230" y2="-91" stroke="rgba(0,240,255,0.3)" stroke-width="1" stroke-dasharray="3,3" />
          <circle cx="130" cy="-91" r="9" fill="rgba(0, 255, 136, 0.2)" stroke="#00ff88" stroke-width="1.5" />
          <line x1="121" y1="-91" x2="139" y2="-91" stroke="#00ff88" stroke-width="1.5" />
          <line x1="130" y1="-100" x2="130" y2="-82" stroke="#00ff88" stroke-width="1.5" />
          <text x="145" y="-88" font-family="JetBrains Mono" font-size="10" fill="#00ff88">CARGO LEVEL (0°)</text>
        </g>

        <!-- 后行星三轮组 -->
        <g id="rear-planetary-set" transform="translate(0, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="rear-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="68" />
            <line x1="0" y1="0" x2="58.89" y2="-34" />
            <line x1="0" y1="0" x2="-58.89" y2="-34" />
          </g>
          <g id="rear-spider-glow" stroke="#00f0ff" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" fill="none">
            <line x1="0" y1="0" x2="0" y2="68" />
            <line x1="0" y1="0" x2="58.89" y2="-34" />
            <line x1="0" y1="0" x2="-58.89" y2="-34" />
          </g>
          <!-- 3个行星橡胶轮 (R=25, 直径120mm缩比) -->
          <g id="rear-wheels">
            <g id="rw-0" transform="translate(0, 68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-1" transform="translate(58.89, -34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-2" transform="translate(-58.89, -34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向轴承主轴 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 前行星三轮组 -->
        <g id="front-planetary-set" transform="translate(260, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="front-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="68" />
            <line x1="0" y1="0" x2="58.89" y2="-34" />
            <line x1="0" y1="0" x2="-58.89" y2="-34" />
          </g>
          <g id="front-spider-glow" stroke="#ff6b00" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" fill="none" filter="url(#glow-orange)">
            <line x1="0" y1="0" x2="0" y2="68" />
            <line x1="0" y1="0" x2="58.89" y2="-34" />
            <line x1="0" y1="0" x2="-58.89" y2="-34" />
          </g>
          <!-- 3个行星橡胶轮 -->
          <g id="front-wheels">
            <g id="fw-0" transform="translate(0, 68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-1" transform="translate(58.89, -34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-2" transform="translate(-58.89, -34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向离合轴承 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 3. 动态受力与力矩矢量指示层 -->
        <g id="layer-forces">
          <!-- 前轮受阻碰撞法向力 (台阶阻力) -->
          <g id="force-barrier" opacity="0">
            <line x1="285" y1="68" x2="240" y2="68" stroke="#ff0055" stroke-width="4" marker-end="url(#arrow-orange)" />
            <text x="290" y="65" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff0055">F_阻挡</text>
          </g>

          <!-- 转臂顺时针翻转力矩圆弧 (Clockwise Torque) -->
          <g id="torque-arc" opacity="0">
            <path d="M 260 -35 A 35 35 0 0 1 295 0" fill="none" stroke="#ff6b00" stroke-width="3" marker-end="url(#arrow-orange)" filter="url(#glow-orange)" />
            <text x="305" y="-10" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff6b00">顺时针力矩 M &gt; 6N·m</text>
          </g>

          <!-- 爬阶抬升力矢量 -->
          <g id="force-lift" opacity="0">
            <line x1="260" y1="0" x2="260" y2="-45" stroke="#00ff88" stroke-width="3.5" marker-end="url(#arrow-green)" filter="url(#glow-cyan)" />
            <text x="270" y="-25" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#00ff88">F_抬升 (跨级上行)</text>
          </g>
        </g>

      </g>
    </svg>
  </div>

  <!-- 底部交互控制与时序微步骤 -->
  <div class="footer-bar">
    <div class="phase-indicator">
      <div class="phase-chip" id="chip-0">
        <span class="phase-idx">01</span>
        <span class="phase-name">平地滚动行驶</span>
      </div>
      <div class="phase-chip" id="chip-1">
        <span class="phase-idx">02</span>
        <span class="phase-name">触阶受阻·积聚顺时针力矩</span>
      </div>
      <div class="phase-chip" id="chip-2">
        <span class="phase-idx">03</span>
        <span class="phase-name">星轮顺时针翻转 120°</span>
      </div>
      <div class="phase-chip" id="chip-3">
        <span class="phase-idx">04</span>
        <span class="phase-name">前轮落上踏面·后组接力</span>
      </div>
      <div class="phase-chip" id="chip-4">
        <span class="phase-idx">05</span>
        <span class="phase-name">平台阻尼回正·恢复水平</span>
      </div>
    </div>

    <div class="controls-panel">
      <button class="btn-action" id="btn-play-pause" onclick="togglePlay()" title="播放/暂停">
        <svg id="icon-pause" width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
        <svg id="icon-play" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="display:none;"><polygon points="5 3 19 12 5 21 5 3"/></svg>
      </button>

      <button class="btn-action" onclick="resetAnimation()" title="重置">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
      </button>

      <input type="range" id="timeline" class="timeline-slider" min="0" max="1000" value="0" oninput="onSeek(this.value)" />

      <div class="telemetry-tag" id="telemetry-status">
        STAGE: 01 | 0.00s
      </div>
    </div>
  </div>

  <script>
    /* 全局图层显隐开关 */
    const layers = {
      forces: true,
      orbits: true,
      leveler: true
    };

    function toggleLayer(layerName) {
      layers[layerName] = !layers[layerName];
      const btn = document.getElementById('btn-toggle-' + layerName);
      const txt = document.getElementById('t-' + layerName);
      if (layers[layerName]) {
        btn.classList.add('active');
        txt.innerText = '开';
      } else {
        btn.classList.remove('active');
        txt.innerText = '关';
      }
      
      const elem = document.getElementById('layer-' + layerName);
      if (elem) {
        elem.style.display = layers[layerName] ? 'inline' : 'none';
      }
    }

    /* 运动学时序与状态机模型 */
    const TOTAL_DURATION = 9000;
    let isPlaying = true;
    let startTime = null;
    let pausedTime = 0;
    let currentProgress = 0;

    // DOM 元素引用
    const cartAssembly = document.getElementById('cart-assembly');
    const frontSpider = document.getElementById('front-planetary-set');
    const rearSpider = document.getElementById('rear-planetary-set');
    const cargoPlatform = document.getElementById('cargo-platform');
    const timeline = document.getElementById('timeline');
    const telemetry = document.getElementById('telemetry-status');
    const iconPlay = document.getElementById('icon-play');
    const iconPause = document.getElementById('icon-pause');

    // 矢量受力 DOM
    const forceBarrier = document.getElementById('force-barrier');
    const torqueArc = document.getElementById('torque-arc');
    const forceLift = document.getElementById('force-lift');

    // 行星轮子单独自转
    const fwWheels = [document.getElementById('fw-0'), document.getElementById('fw-1'), document.getElementById('fw-2')];
    const rwWheels = [document.getElementById('rw-0'), document.getElementById('rw-1'), document.getElementById('rw-2')];

    // 缓动函数
    function easeInOutQuad(t) {
      return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    }
    function clamp(val, min, max) {
      return Math.min(Math.max(val, min), max);
    }

    /* 运动学姿态求解器 (修改为顺时针旋转 +68px台阶跨越) */
    function computeKinematics(p) {
      // 地面接触基准:地面 Y=540,轮底接触时中心 Y=447 (540 - 68 - 25 = 447)
      // 台阶高 68mm (等于行星轮中心高度),上一阶中心 Y=379
      let carX = -40 + p * 600;
      let rearX = carX;
      let frontX = carX + 260;

      let frontAngle = 0;
      let rearAngle = 0;
      let wheelSpin = carX * 2.0;

      let frontY = 447;
      let rearY = 447;

      // 前行星架越障顺时针翻转 +120° (p: 0.16 ~ 0.38)
      const fStart = 0.15, fEnd = 0.38;
      if (p >= fStart && p <= fEnd) {
        let fFlipProgress = (p - fStart) / (fEnd - fStart);
        let smoothP = easeInOutQuad(fFlipProgress);
        frontAngle = smoothP * 120; // 顺时针旋转 +120°
        frontY = 447 - smoothP * 68; // 抬升至第一阶踏面 (高68mm)
      } else if (p > fEnd) {
        frontAngle = 120;
        frontY = 379;
      }

      // 后行星架越障顺时针翻转 +120° (p: 0.48 ~ 0.71)
      const rStart = 0.48, rEnd = 0.71;
      if (p >= rStart && p <= rEnd) {
        let rFlipProgress = (p - rStart) / (rEnd - rStart);
        let smoothP = easeInOutQuad(rFlipProgress);
        rearAngle = smoothP * 120; // 顺时针旋转 +120°
        rearY = 447 - smoothP * 68;
      } else if (p > rEnd) {
        rearAngle = 120;
        rearY = 379;
      }

      // 车身主梁倾角与自调平货箱角度
      const chassisAngleRad = Math.atan2(frontY - rearY, 260);
      const chassisAngleDeg = (chassisAngleRad * 180) / Math.PI;

      // 矢量指示器状态
      let barrierOpacity = 0;
      let torqueOpacity = 0;
      let liftOpacity = 0;

      if (p >= 0.12 && p < 0.18) {
        barrierOpacity = clamp((p - 0.12) / 0.03, 0, 1);
        torqueOpacity = barrierOpacity;
      } else if (p >= 0.18 && p <= 0.34) {
        barrierOpacity = 0.2;
        torqueOpacity = 1.0;
        liftOpacity = clamp(Math.sin((p - 0.18) / 0.16 * Math.PI), 0, 1);
      }

      // 确定阶段指示高亮
      let phaseIndex = 0;
      if (p < 0.14) phaseIndex = 0;
      else if (p < 0.22) phaseIndex = 1;
      else if (p < 0.45) phaseIndex = 2;
      else if (p < 0.75) phaseIndex = 3;
      else phaseIndex = 4;

      return {
        carX,
        frontX,
        rearX,
        frontY,
        rearY,
        frontAngle,
        rearAngle,
        wheelSpin,
        chassisAngleDeg,
        barrierOpacity,
        torqueOpacity,
        liftOpacity,
        phaseIndex
      };
    }

    /* 渲染一帧 */
    function renderFrame(state) {
      // 1. 底盘定位与俯仰
      cartAssembly.setAttribute('transform', `translate(${state.rearX}, ${state.rearY}) rotate(${state.chassisAngleDeg})`);

      // 2. 前后星形转臂顺时针旋转 (抵消底盘倾角)
      frontSpider.setAttribute('transform', `translate(260, 0) rotate(${state.frontAngle - state.chassisAngleDeg})`);
      rearSpider.setAttribute('transform', `translate(0, 0) rotate(${state.rearAngle - state.chassisAngleDeg})`);

      // 3. 轮子自转
      const wRot = `rotate(${state.wheelSpin})`;
      fwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));
      rwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));

      // 4. 自调平货箱:反向旋转抵消车身倾角,保持水平
      cargoPlatform.setAttribute('transform', `rotate(${-state.chassisAngleDeg}, 130, 0)`);

      // 5. 受力与力矩矢量透明度
      if (layers.forces) {
        forceBarrier.setAttribute('opacity', state.barrierOpacity);
        torqueArc.setAttribute('opacity', state.torqueOpacity);
        forceLift.setAttribute('opacity', state.liftOpacity);
      }

      // 6. 阶段高亮更新
      for (let i = 0; i <= 4; i++) {
        const chip = document.getElementById(`chip-${i}`);
        if (i === state.phaseIndex) {
          chip.classList.add('active');
        } else {
          chip.classList.remove('active');
        }
      }

      // 7. 更新底部滑块与遥测数据
      timeline.value = currentProgress * 1000;
      const sec = (currentProgress * (TOTAL_DURATION / 1000)).toFixed(2);
      telemetry.innerText = `PHASE: 0${state.phaseIndex + 1} | T: ${sec}s | α: ${state.chassisAngleDeg.toFixed(1)}°`;
    }

    // 初始化轮子相对基座位置缓存
    function initWheelTransforms() {
      fwWheels[0].setAttribute('data-base', 'translate(0, 68)');
      fwWheels[1].setAttribute('data-base', 'translate(58.89, -34)');
      fwWheels[2].setAttribute('data-base', 'translate(-58.89, -34)');

      rwWheels[0].setAttribute('data-base', 'translate(0, 68)');
      rwWheels[1].setAttribute('data-base', 'translate(58.89, -34)');
      rwWheels[2].setAttribute('data-base', 'translate(-58.89, -34)');
    }

    /* 动画循环驱动 */
    function animate(timestamp) {
      if (!startTime) startTime = timestamp;

      if (isPlaying) {
        const elapsed = (timestamp - startTime + pausedTime) % TOTAL_DURATION;
        currentProgress = elapsed / TOTAL_DURATION;
      }

      const state = computeKinematics(currentProgress);
      renderFrame(state);

      requestAnimationFrame(animate);
    }

    /* 交互控制函数 */
    function togglePlay() {
      isPlaying = !isPlaying;
      if (isPlaying) {
        startTime = performance.now();
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'none';
        iconPause.style.display = 'inline';
      } else {
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'inline';
        iconPause.style.display = 'none';
      }
    }

    function resetAnimation() {
      currentProgress = 0;
      pausedTime = 0;
      startTime = performance.now();
      const state = computeKinematics(0);
      renderFrame(state);
    }

    function onSeek(val) {
      currentProgress = val / 1000;
      pausedTime = currentProgress * TOTAL_DURATION;
      startTime = performance.now();
      const state = computeKinematics(currentProgress);
      renderFrame(state);
    }

    // DOM 加载完成自动启动
    document.addEventListener('DOMContentLoaded', () => {
      initWheelTransforms();
      requestAnimationFrame(animate);
    });
  </script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>行星三轮组越障小车机构原理动画</title>
  <style>
    :root {
      --bg-color: #080c14;
      --panel-bg: rgba(13, 20, 36, 0.75);
      --border-color: rgba(0, 229, 255, 0.2);
      --border-glow: rgba(0, 229, 255, 0.4);
      --cyan-core: #00f0ff;
      --orange-accent: #ff6b00;
      --orange-glow: rgba(255, 107, 0, 0.4);
      --green-ready: #00ff88;
      --text-main: #e2e8f0;
      --text-muted: #64748b;
      --font-mono: "JetBrains Mono", "SF Mono", "Fira Code", monospace;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }

    body {
      background-color: var(--bg-color);
      color: var(--text-main);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      overflow: hidden;
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      position: relative;
    }

    /* 背景精细 CAD 测绘网格 */
    .bg-grid {
      position: absolute;
      inset: 0;
      background-image: 
        radial-gradient(circle at 50% 50%, rgba(0, 240, 255, 0.05) 0%, transparent 80%),
        linear-gradient(rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(rgba(255, 255, 255, 0.015) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.015) 1px, transparent 1px);
      background-size: 100% 100%, 100px 100px, 100px 100px, 20px 20px, 20px 20px;
      pointer-events: none;
      z-index: 0;
    }

    /* 顶部紧凑状态条 */
    .header-bar {
      position: absolute;
      top: 16px;
      left: 20px;
      right: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      z-index: 20;
      pointer-events: none;
    }

    .brand-tag {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 10px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 6px 14px;
      border-radius: 6px;
      backdrop-filter: blur(10px);
    }

    .status-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: var(--green-ready);
      box-shadow: 0 0 10px var(--green-ready);
      animation: pulse-dot 2s infinite ease-in-out;
    }

    .title-text {
      font-size: 13px;
      font-weight: 600;
      letter-spacing: 0.5px;
      color: #fff;
    }

    .title-sub {
      font-size: 11px;
      color: var(--cyan-core);
      font-family: var(--font-mono);
      margin-left: 4px;
    }

    /* 右上角图层切换器 */
    .hud-switches {
      pointer-events: auto;
      display: flex;
      gap: 8px;
    }

    .hud-btn {
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      color: var(--text-muted);
      font-size: 11px;
      padding: 5px 10px;
      border-radius: 4px;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 6px;
      transition: all 0.2s;
    }

    .hud-btn:hover {
      border-color: var(--cyan-core);
      color: var(--text-main);
    }

    .hud-btn.active {
      border-color: var(--cyan-core);
      color: var(--cyan-core);
      background: rgba(0, 240, 255, 0.08);
      box-shadow: 0 0 10px rgba(0, 240, 255, 0.15);
    }

    .hud-btn .key-dot {
      width: 5px;
      height: 5px;
      border-radius: 50%;
      background: currentColor;
    }

    /* 中央核心动画舞台 */
    .viewport-container {
      position: absolute;
      inset: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 10;
    }

    #cad-stage {
      width: 100%;
      height: 100%;
      max-width: 1400px;
      max-height: 860px;
    }

    /* 底部紧凑控制与原理步骤提示条 */
    .footer-bar {
      position: absolute;
      bottom: 16px;
      left: 20px;
      right: 20px;
      z-index: 20;
      display: flex;
      flex-direction: column;
      gap: 8px;
      pointer-events: none;
    }

    .phase-indicator {
      display: flex;
      justify-content: space-between;
      gap: 12px;
      pointer-events: auto;
    }

    .phase-chip {
      flex: 1;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.08);
      padding: 7px 12px;
      border-radius: 5px;
      display: flex;
      align-items: center;
      gap: 8px;
      backdrop-filter: blur(12px);
      transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    }

    .phase-chip.active {
      border-color: var(--orange-accent);
      background: rgba(255, 107, 0, 0.12);
      box-shadow: 0 0 15px rgba(255, 107, 0, 0.2);
    }

    .phase-idx {
      font-family: var(--font-mono);
      font-size: 11px;
      font-weight: bold;
      color: var(--text-muted);
      min-width: 16px;
    }

    .phase-chip.active .phase-idx {
      color: var(--orange-accent);
    }

    .phase-name {
      font-size: 12px;
      color: var(--text-muted);
      white-space: nowrap;
    }

    .phase-chip.active .phase-name {
      color: #fff;
      font-weight: 500;
    }

    /* 控制面板 */
    .controls-panel {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 14px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 7px 16px;
      border-radius: 6px;
      backdrop-filter: blur(12px);
    }

    .btn-action {
      background: transparent;
      border: 1px solid var(--border-color);
      color: var(--cyan-core);
      width: 30px;
      height: 30px;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.2s;
    }

    .btn-action:hover {
      background: var(--cyan-core);
      color: #000;
      box-shadow: 0 0 10px var(--cyan-core);
    }

    .timeline-slider {
      flex: 1;
      height: 4px;
      -webkit-appearance: none;
      background: rgba(255, 255, 255, 0.15);
      border-radius: 2px;
      outline: none;
      cursor: pointer;
    }

    .timeline-slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: var(--cyan-core);
      box-shadow: 0 0 8px var(--cyan-core);
      cursor: ew-resize;
      transition: transform 0.1s;
    }

    .timeline-slider::-webkit-slider-thumb:hover {
      transform: scale(1.3);
    }

    .telemetry-tag {
      font-family: var(--font-mono);
      font-size: 11px;
      color: var(--cyan-core);
      min-width: 140px;
      text-align: right;
    }

    @keyframes pulse-dot {
      0%, 100% { opacity: 0.4; transform: scale(0.9); }
      50% { opacity: 1; transform: scale(1.15); }
    }

    /* 微型悬浮参数卡 */
    .param-overlay {
      position: absolute;
      top: 60px;
      right: 20px;
      z-index: 15;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.1);
      padding: 10px 14px;
      border-radius: 6px;
      font-size: 11px;
      line-height: 1.6;
      pointer-events: none;
      backdrop-filter: blur(8px);
    }

    .param-overlay .item {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      color: #94a3b8;
    }

    .param-overlay .val {
      font-family: var(--font-mono);
      color: var(--cyan-core);
      font-weight: 500;
    }
  </style>
</head>
<body>
  <div class="bg-grid"></div>

  <!-- 顶部状态栏 -->
  <div class="header-bar">
    <div class="brand-tag">
      <div class="status-dot"></div>
      <span class="title-text">行星三轮组阶跃越障机构</span>
      <span class="title-sub">[双轮双支承·顺时针120°翻阶]</span>
    </div>

    <div class="hud-switches">
      <button class="hud-btn active" id="btn-toggle-forces" onclick="toggleLayer('forces')">
        <span class="key-dot"></span> 力矩/矢量 (<span id="t-forces">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-orbits" onclick="toggleLayer('orbits')">
        <span class="key-dot"></span> 回转包络线 (<span id="t-orbits">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-leveler" onclick="toggleLayer('leveler')">
        <span class="key-dot"></span> 货箱水平阻尼 (<span id="t-leveler">开</span>)
      </button>
    </div>
  </div>

  <!-- 关键参数指示 -->
  <div class="param-overlay">
    <div class="item"><span>单轮直径 (2r):</span><span class="val">Ø 120 mm</span></div>
    <div class="item"><span>回转半径 (R):</span><span class="val">165 mm</span></div>
    <div class="item"><span>平地姿态:</span><span class="val">双轮着地支承</span></div>
    <div class="item"><span>越障翻转:</span><span class="val">顺时针 (+120°)</span></div>
    <div class="item"><span>台阶单级标高:</span><span class="val">68 mm (至中心轴)</span></div>
  </div>

  <!-- 中央主舞台 SVG -->
  <div class="viewport-container">
    <svg id="cad-stage" viewBox="0 0 1200 680" preserveAspectRatio="xMidYMid meet">
      <defs>
        <!-- 发光滤镜 -->
        <filter id="glow-cyan" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="3.5" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>
        <filter id="glow-orange" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="4" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>

        <!-- 箭头标记 -->
        <marker id="arrow-cyan" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00f0ff" />
        </marker>
        <marker id="arrow-orange" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#ff6b00" />
        </marker>
        <marker id="arrow-green" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00ff88" />
        </marker>

        <!-- 材质填充 -->
        <pattern id="hatch-stairs" width="12" height="12" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
          <line x1="0" y1="0" x2="0" y2="12" stroke="rgba(0, 240, 255, 0.08)" stroke-width="1.5" />
        </pattern>
        <pattern id="cargo-texture" width="10" height="10" patternUnits="userSpaceOnUse">
          <rect width="10" height="10" fill="#132035" />
          <path d="M 0 10 L 10 0 M 0 0 L 10 10" stroke="rgba(0, 240, 255, 0.12)" stroke-width="0.75"/>
        </pattern>
      </defs>

      <!-- 1. 楼梯与地形环境 (台阶标高 H=68mm,与行星轮中心等高) -->
      <g id="terrain-group">
        <!-- 楼梯剖面基座: 地面 Y=540, 一阶踏面 Y=472 (高68px), 二阶踏面 Y=404 -->
        <path id="stair-base" d="M -50 540 L 400 540 L 400 472 L 750 472 L 750 404 L 1250 404 L 1250 640 L -50 640 Z" 
              fill="url(#hatch-stairs)" stroke="rgba(0, 240, 255, 0.4)" stroke-width="2" />
        
        <!-- 楼梯踏面加固线 -->
        <path d="M -50 540 L 400 540 L 400 472 L 750 472 L 750 404 L 1250 404" 
              fill="none" stroke="#00f0ff" stroke-width="3" filter="url(#glow-cyan)" />

        <!-- 踏面防滑纹理与倒角高亮 -->
        <g stroke="rgba(0, 240, 255, 0.3)" stroke-width="1">
          <line x1="400" y1="540" x2="400" y2="472" stroke="#ff6b00" stroke-width="2.5" />
          <line x1="750" y1="472" x2="750" y2="404" stroke="#ff6b00" stroke-width="2.5" />
          <circle cx="400" cy="472" r="3" fill="#ff6b00" />
          <circle cx="750" cy="404" r="3" fill="#ff6b00" />
        </g>

        <!-- 尺寸标注 (高度与宽度) -->
        <g id="layer-dim" stroke="#64748b" stroke-width="1" font-family="JetBrains Mono" font-size="11" fill="#64748b">
          <!-- 阶高标尺 -->
          <line x1="380" y1="540" x2="380" y2="472" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="365" y="510" text-anchor="end" fill="#00f0ff">H=68mm (中心等高)</text>
          
          <line x1="730" y1="472" x2="730" y2="404" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="715" y="442" text-anchor="end" fill="#00f0ff">H=68mm</text>

          <!-- 踏宽标尺 -->
          <line x1="400" y1="560" x2="750" y2="560" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="575" y="580" text-anchor="middle" fill="#00f0ff">踏面宽度 Step Width = 350mm</text>
        </g>
      </g>

      <!-- 2. 小车主机构动态装配组 -->
      <g id="cart-assembly">
        
        <!-- 回转轨迹层 -->
        <g id="layer-orbits" stroke="rgba(0, 240, 255, 0.25)" stroke-dasharray="4,4" fill="none">
          <circle id="front-orbit" cx="260" cy="0" r="68" />
          <circle id="rear-orbit" cx="0" cy="0" r="68" />
        </g>

        <!-- 车架主梁 -->
        <path id="chassis-beam" d="M 0 0 L 260 0" stroke="#334155" stroke-width="14" stroke-linecap="round" />
        <path id="chassis-core" d="M 0 0 L 260 0" stroke="#00f0ff" stroke-width="3" />

        <!-- 阻尼器与自调平机构连杆 -->
        <g id="layer-leveler">
          <!-- 阻尼器套筒 -->
          <line id="damper-line" x1="130" y1="0" x2="130" y2="-65" stroke="#ff6b00" stroke-width="4" stroke-dasharray="6,3" />
          <circle id="damper-joint-b" cx="130" cy="0" r="5" fill="#ff6b00" />
          <circle id="damper-joint-t" cx="130" cy="-65" r="5" fill="#ff6b00" />
          
          <!-- 被动防倾摆臂 -->
          <line id="arm-f" x1="40" y1="0" x2="60" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
          <line id="arm-r" x1="220" y1="0" x2="200" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
        </g>

        <!-- 货箱平台 (保持水平) -->
        <g id="cargo-platform">
          <rect id="cargo-box" x="20" y="-120" width="220" height="58" rx="6" fill="url(#cargo-texture)" stroke="#00f0ff" stroke-width="2" />
          <!-- 货物重心与水平仪 -->
          <line x1="30" y1="-91" x2="230" y2="-91" stroke="rgba(0,240,255,0.3)" stroke-width="1" stroke-dasharray="3,3" />
          <circle cx="130" cy="-91" r="9" fill="rgba(0, 255, 136, 0.2)" stroke="#00ff88" stroke-width="1.5" />
          <line x1="121" y1="-91" x2="139" y2="-91" stroke="#00ff88" stroke-width="1.5" />
          <line x1="130" y1="-100" x2="130" y2="-82" stroke="#00ff88" stroke-width="1.5" />
          <text x="145" y="-88" font-family="JetBrains Mono" font-size="10" fill="#00ff88">CARGO LEVEL (0°)</text>
        </g>

        <!-- 后行星三轮组 (初始构型:上1轮、下2轮双轮着地) -->
        <g id="rear-planetary-set" transform="translate(0, 0)">
          <!-- 转臂 (Y型等角 120度,初始一臂朝上,两臂朝下) -->
          <g id="rear-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <g id="rear-spider-glow" stroke="#00f0ff" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" fill="none">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <!-- 3个行星橡胶轮 (R=25,下两轮接触地面支撑) -->
          <g id="rear-wheels">
            <g id="rw-0" transform="translate(0, -68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-1" transform="translate(58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-2" transform="translate(-58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向轴承主轴 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 前行星三轮组 (初始构型:上1轮、下2轮双轮着地) -->
        <g id="front-planetary-set" transform="translate(260, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="front-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <g id="front-spider-glow" stroke="#ff6b00" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" fill="none" filter="url(#glow-orange)">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <!-- 3个行星橡胶轮 -->
          <g id="front-wheels">
            <g id="fw-0" transform="translate(0, -68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-1" transform="translate(58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-2" transform="translate(-58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向离合轴承 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 3. 动态受力与力矩矢量指示层 -->
        <g id="layer-forces">
          <!-- 前右下轮受阻碰撞法向力 (台阶阻力) -->
          <g id="force-barrier" opacity="0">
            <line x1="343" y1="34" x2="295" y2="34" stroke="#ff0055" stroke-width="4" marker-end="url(#arrow-orange)" />
            <text x="348" y="30" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff0055">F_阻挡</text>
          </g>

          <!-- 转臂顺时针翻转力矩圆弧 (Clockwise Torque) -->
          <g id="torque-arc" opacity="0">
            <path d="M 260 -35 A 35 35 0 0 1 295 0" fill="none" stroke="#ff6b00" stroke-width="3" marker-end="url(#arrow-orange)" filter="url(#glow-orange)" />
            <text x="305" y="-12" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff6b00">顺时针力矩 M &gt; 6N·m</text>
          </g>

          <!-- 爬阶抬升力矢量 -->
          <g id="force-lift" opacity="0">
            <line x1="260" y1="0" x2="260" y2="-45" stroke="#00ff88" stroke-width="3.5" marker-end="url(#arrow-green)" filter="url(#glow-cyan)" />
            <text x="270" y="-25" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#00ff88">F_抬升 (跨级上行)</text>
          </g>
        </g>

      </g>
    </svg>
  </div>

  <!-- 底部交互控制与时序微步骤 -->
  <div class="footer-bar">
    <div class="phase-indicator">
      <div class="phase-chip" id="chip-0">
        <span class="phase-idx">01</span>
        <span class="phase-name">平地双轮着地行驶</span>
      </div>
      <div class="phase-chip" id="chip-1">
        <span class="phase-idx">02</span>
        <span class="phase-name">前下轮触阶·顺时针力矩聚积</span>
      </div>
      <div class="phase-chip" id="chip-2">
        <span class="phase-idx">03</span>
        <span class="phase-name">前星轮顺时针翻转 120° 跨级</span>
      </div>
      <div class="phase-chip" id="chip-3">
        <span class="phase-idx">04</span>
        <span class="phase-name">前组双轮落阶·后组跟进翻转</span>
      </div>
      <div class="phase-chip" id="chip-4">
        <span class="phase-idx">05</span>
        <span class="phase-name">全车恢复双轮着地·平稳行驶</span>
      </div>
    </div>

    <div class="controls-panel">
      <button class="btn-action" id="btn-play-pause" onclick="togglePlay()" title="播放/暂停">
        <svg id="icon-pause" width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
        <svg id="icon-play" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="display:none;"><polygon points="5 3 19 12 5 21 5 3"/></svg>
      </button>

      <button class="btn-action" onclick="resetAnimation()" title="重置">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
      </button>

      <input type="range" id="timeline" class="timeline-slider" min="0" max="1000" value="0" oninput="onSeek(this.value)" />

      <div class="telemetry-tag" id="telemetry-status">
        STAGE: 01 | 0.00s
      </div>
    </div>
  </div>

  <script>
    /* 全局图层显隐开关 */
    const layers = {
      forces: true,
      orbits: true,
      leveler: true
    };

    function toggleLayer(layerName) {
      layers[layerName] = !layers[layerName];
      const btn = document.getElementById('btn-toggle-' + layerName);
      const txt = document.getElementById('t-' + layerName);
      if (layers[layerName]) {
        btn.classList.add('active');
        txt.innerText = '开';
      } else {
        btn.classList.remove('active');
        txt.innerText = '关';
      }
      
      const elem = document.getElementById('layer-' + layerName);
      if (elem) {
        elem.style.display = layers[layerName] ? 'inline' : 'none';
      }
    }

    /* 运动学时序与状态机模型 */
    const TOTAL_DURATION = 9000;
    let isPlaying = true;
    let startTime = null;
    let pausedTime = 0;
    let currentProgress = 0;

    // DOM 元素引用
    const cartAssembly = document.getElementById('cart-assembly');
    const frontSpider = document.getElementById('front-planetary-set');
    const rearSpider = document.getElementById('rear-planetary-set');
    const cargoPlatform = document.getElementById('cargo-platform');
    const timeline = document.getElementById('timeline');
    const telemetry = document.getElementById('telemetry-status');
    const iconPlay = document.getElementById('icon-play');
    const iconPause = document.getElementById('icon-pause');

    // 矢量受力 DOM
    const forceBarrier = document.getElementById('force-barrier');
    const torqueArc = document.getElementById('torque-arc');
    const forceLift = document.getElementById('force-lift');

    // 行星轮子单独自转
    const fwWheels = [document.getElementById('fw-0'), document.getElementById('fw-1'), document.getElementById('fw-2')];
    const rwWheels = [document.getElementById('rw-0'), document.getElementById('rw-1'), document.getElementById('rw-2')];

    // 缓动函数
    function easeInOutQuad(t) {
      return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    }
    function clamp(val, min, max) {
      return Math.min(Math.max(val, min), max);
    }

    /* 运动学姿态求解器:双轮着地基准 + 触阶顺时针翻转 120° 恢复双轮着地 */
    function computeKinematics(p) {
      // 地面接触基准:地面 Y=540,双轮着地时两小轮轴心 Y=515,转臂中心轴 Y=481 (515 - 34 = 481)
      // 台阶高 68mm (至中心轴高度),上一阶双轮着地时转臂中心轴 Y=413 (481 - 68 = 413)
      let carX = -40 + p * 600;
      let rearX = carX;
      let frontX = carX + 260;

      let frontAngle = 0;
      let rearAngle = 0;
      let wheelSpin = carX * 2.0;

      let frontY = 481;
      let rearY = 481;

      // 前行星架越障顺时针翻转 +120° (p: 0.15 ~ 0.38)
      // 翻转前:上方1轮(0,-68),下方2轮(±58.89, 34)双轮着地
      // 翻转中:绕中心顺时针旋转 120°,上方轮翻跨到右前上方并落入台阶踏面
      // 翻转后:角度达 +120°,原本上方轮变为右下轮,三轮构型完全恢复为上方1轮、下方2轮双轮着地!
      const fStart = 0.15, fEnd = 0.38;
      if (p >= fStart && p <= fEnd) {
        let fFlipProgress = (p - fStart) / (fEnd - fStart);
        let smoothP = easeInOutQuad(fFlipProgress);
        frontAngle = smoothP * 120; // 顺时针旋转 +120°
        frontY = 481 - smoothP * 68; // 中心抬升 68mm
      } else if (p > fEnd) {
        frontAngle = 120;
        frontY = 413;
      }

      // 后行星架越障顺时针翻转 +120° (p: 0.48 ~ 0.71)
      const rStart = 0.48, rEnd = 0.71;
      if (p >= rStart && p <= rEnd) {
        let rFlipProgress = (p - rStart) / (rEnd - rStart);
        let smoothP = easeInOutQuad(rFlipProgress);
        rearAngle = smoothP * 120; // 顺时针旋转 +120°
        rearY = 481 - smoothP * 68;
      } else if (p > rEnd) {
        rearAngle = 120;
        rearY = 413;
      }

      // 车身主梁倾角与自调平货箱角度
      const chassisAngleRad = Math.atan2(frontY - rearY, 260);
      const chassisAngleDeg = (chassisAngleRad * 180) / Math.PI;

      // 矢量指示器状态
      let barrierOpacity = 0;
      let torqueOpacity = 0;
      let liftOpacity = 0;

      if (p >= 0.12 && p < 0.18) {
        barrierOpacity = clamp((p - 0.12) / 0.03, 0, 1);
        torqueOpacity = barrierOpacity;
      } else if (p >= 0.18 && p <= 0.34) {
        barrierOpacity = 0.2;
        torqueOpacity = 1.0;
        liftOpacity = clamp(Math.sin((p - 0.18) / 0.16 * Math.PI), 0, 1);
      }

      // 确定阶段指示高亮
      let phaseIndex = 0;
      if (p < 0.14) phaseIndex = 0;
      else if (p < 0.22) phaseIndex = 1;
      else if (p < 0.45) phaseIndex = 2;
      else if (p < 0.75) phaseIndex = 3;
      else phaseIndex = 4;

      return {
        carX,
        frontX,
        rearX,
        frontY,
        rearY,
        frontAngle,
        rearAngle,
        wheelSpin,
        chassisAngleDeg,
        barrierOpacity,
        torqueOpacity,
        liftOpacity,
        phaseIndex
      };
    }

    /* 渲染一帧 */
    function renderFrame(state) {
      // 1. 底盘定位与俯仰
      cartAssembly.setAttribute('transform', `translate(${state.rearX}, ${state.rearY}) rotate(${state.chassisAngleDeg})`);

      // 2. 前后星形转臂顺时针旋转 (抵消底盘倾角以保持世界绝对角度正确)
      frontSpider.setAttribute('transform', `translate(260, 0) rotate(${state.frontAngle - state.chassisAngleDeg})`);
      rearSpider.setAttribute('transform', `translate(0, 0) rotate(${state.rearAngle - state.chassisAngleDeg})`);

      // 3. 轮子自转
      const wRot = `rotate(${state.wheelSpin})`;
      fwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));
      rwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));

      // 4. 自调平货箱:反向旋转抵消车身倾角,保持水平
      cargoPlatform.setAttribute('transform', `rotate(${-state.chassisAngleDeg}, 130, 0)`);

      // 5. 受力与力矩矢量透明度
      if (layers.forces) {
        forceBarrier.setAttribute('opacity', state.barrierOpacity);
        torqueArc.setAttribute('opacity', state.torqueOpacity);
        forceLift.setAttribute('opacity', state.liftOpacity);
      }

      // 6. 阶段高亮更新
      for (let i = 0; i <= 4; i++) {
        const chip = document.getElementById(`chip-${i}`);
        if (i === state.phaseIndex) {
          chip.classList.add('active');
        } else {
          chip.classList.remove('active');
        }
      }

      // 7. 更新底部滑块与遥测数据
      timeline.value = currentProgress * 1000;
      const sec = (currentProgress * (TOTAL_DURATION / 1000)).toFixed(2);
      telemetry.innerText = `PHASE: 0${state.phaseIndex + 1} | T: ${sec}s | α: ${state.chassisAngleDeg.toFixed(1)}°`;
    }

    // 初始化轮子相对基座位置缓存 (上1下2双轮着地构型)
    function initWheelTransforms() {
      fwWheels[0].setAttribute('data-base', 'translate(0, -68)');
      fwWheels[1].setAttribute('data-base', 'translate(58.89, 34)');
      fwWheels[2].setAttribute('data-base', 'translate(-58.89, 34)');

      rwWheels[0].setAttribute('data-base', 'translate(0, -68)');
      rwWheels[1].setAttribute('data-base', 'translate(58.89, 34)');
      rwWheels[2].setAttribute('data-base', 'translate(-58.89, 34)');
    }

    /* 动画循环驱动 */
    function animate(timestamp) {
      if (!startTime) startTime = timestamp;

      if (isPlaying) {
        const elapsed = (timestamp - startTime + pausedTime) % TOTAL_DURATION;
        currentProgress = elapsed / TOTAL_DURATION;
      }

      const state = computeKinematics(currentProgress);
      renderFrame(state);

      requestAnimationFrame(animate);
    }

    /* 交互控制函数 */
    function togglePlay() {
      isPlaying = !isPlaying;
      if (isPlaying) {
        startTime = performance.now();
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'none';
        iconPause.style.display = 'inline';
      } else {
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'inline';
        iconPause.style.display = 'none';
      }
    }

    function resetAnimation() {
      currentProgress = 0;
      pausedTime = 0;
      startTime = performance.now();
      const state = computeKinematics(0);
      renderFrame(state);
    }

    function onSeek(val) {
      currentProgress = val / 1000;
      pausedTime = currentProgress * TOTAL_DURATION;
      startTime = performance.now();
      const state = computeKinematics(currentProgress);
      renderFrame(state);
    }

    // DOM 加载完成自动启动
    document.addEventListener('DOMContentLoaded', () => {
      initWheelTransforms();
      requestAnimationFrame(animate);
    });
  </script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>行星三轮组越障小车机构原理动画</title>
  <style>
    :root {
      --bg-color: #080c14;
      --panel-bg: rgba(13, 20, 36, 0.75);
      --border-color: rgba(0, 229, 255, 0.2);
      --border-glow: rgba(0, 229, 255, 0.4);
      --cyan-core: #00f0ff;
      --orange-accent: #ff6b00;
      --orange-glow: rgba(255, 107, 0, 0.4);
      --green-ready: #00ff88;
      --text-main: #e2e8f0;
      --text-muted: #64748b;
      --font-mono: "JetBrains Mono", "SF Mono", "Fira Code", monospace;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }

    body {
      background-color: var(--bg-color);
      color: var(--text-main);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      overflow: hidden;
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      position: relative;
    }

    /* 背景精细 CAD 测绘网格 */
    .bg-grid {
      position: absolute;
      inset: 0;
      background-image: 
        radial-gradient(circle at 50% 50%, rgba(0, 240, 255, 0.05) 0%, transparent 80%),
        linear-gradient(rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(rgba(255, 255, 255, 0.015) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.015) 1px, transparent 1px);
      background-size: 100% 100%, 100px 100px, 100px 100px, 20px 20px, 20px 20px;
      pointer-events: none;
      z-index: 0;
    }

    /* 顶部紧凑状态条 */
    .header-bar {
      position: absolute;
      top: 16px;
      left: 20px;
      right: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      z-index: 20;
      pointer-events: none;
    }

    .brand-tag {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 10px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 6px 14px;
      border-radius: 6px;
      backdrop-filter: blur(10px);
    }

    .status-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: var(--green-ready);
      box-shadow: 0 0 10px var(--green-ready);
      animation: pulse-dot 2s infinite ease-in-out;
    }

    .title-text {
      font-size: 13px;
      font-weight: 600;
      letter-spacing: 0.5px;
      color: #fff;
    }

    .title-sub {
      font-size: 11px;
      color: var(--cyan-core);
      font-family: var(--font-mono);
      margin-left: 4px;
    }

    /* 右上角图层切换器 */
    .hud-switches {
      pointer-events: auto;
      display: flex;
      gap: 8px;
    }

    .hud-btn {
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      color: var(--text-muted);
      font-size: 11px;
      padding: 5px 10px;
      border-radius: 4px;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 6px;
      transition: all 0.2s;
    }

    .hud-btn:hover {
      border-color: var(--cyan-core);
      color: var(--text-main);
    }

    .hud-btn.active {
      border-color: var(--cyan-core);
      color: var(--cyan-core);
      background: rgba(0, 240, 255, 0.08);
      box-shadow: 0 0 10px rgba(0, 240, 255, 0.15);
    }

    .hud-btn .key-dot {
      width: 5px;
      height: 5px;
      border-radius: 50%;
      background: currentColor;
    }

    /* 中央核心动画舞台 */
    .viewport-container {
      position: absolute;
      inset: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 10;
    }

    #cad-stage {
      width: 100%;
      height: 100%;
      max-width: 1400px;
      max-height: 860px;
    }

    /* 底部紧凑控制与原理步骤提示条 */
    .footer-bar {
      position: absolute;
      bottom: 16px;
      left: 20px;
      right: 20px;
      z-index: 20;
      display: flex;
      flex-direction: column;
      gap: 8px;
      pointer-events: none;
    }

    .phase-indicator {
      display: flex;
      justify-content: space-between;
      gap: 8px;
      pointer-events: auto;
    }

    .phase-chip {
      flex: 1;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.08);
      padding: 6px 10px;
      border-radius: 5px;
      display: flex;
      align-items: center;
      gap: 6px;
      backdrop-filter: blur(12px);
      transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    }

    .phase-chip.active {
      border-color: var(--orange-accent);
      background: rgba(255, 107, 0, 0.12);
      box-shadow: 0 0 15px rgba(255, 107, 0, 0.2);
    }

    .phase-idx {
      font-family: var(--font-mono);
      font-size: 11px;
      font-weight: bold;
      color: var(--text-muted);
      min-width: 16px;
    }

    .phase-chip.active .phase-idx {
      color: var(--orange-accent);
    }

    .phase-name {
      font-size: 11.5px;
      color: var(--text-muted);
      white-space: nowrap;
    }

    .phase-chip.active .phase-name {
      color: #fff;
      font-weight: 500;
    }

    /* 控制面板 */
    .controls-panel {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 14px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 7px 16px;
      border-radius: 6px;
      backdrop-filter: blur(12px);
    }

    .btn-action {
      background: transparent;
      border: 1px solid var(--border-color);
      color: var(--cyan-core);
      width: 30px;
      height: 30px;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.2s;
    }

    .btn-action:hover {
      background: var(--cyan-core);
      color: #000;
      box-shadow: 0 0 10px var(--cyan-core);
    }

    .timeline-slider {
      flex: 1;
      height: 4px;
      -webkit-appearance: none;
      background: rgba(255, 255, 255, 0.15);
      border-radius: 2px;
      outline: none;
      cursor: pointer;
    }

    .timeline-slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: var(--cyan-core);
      box-shadow: 0 0 8px var(--cyan-core);
      cursor: ew-resize;
      transition: transform 0.1s;
    }

    .timeline-slider::-webkit-slider-thumb:hover {
      transform: scale(1.3);
    }

    .telemetry-tag {
      font-family: var(--font-mono);
      font-size: 11px;
      color: var(--cyan-core);
      min-width: 140px;
      text-align: right;
    }

    @keyframes pulse-dot {
      0%, 100% { opacity: 0.4; transform: scale(0.9); }
      50% { opacity: 1; transform: scale(1.15); }
    }

    /* 微型悬浮参数卡 */
    .param-overlay {
      position: absolute;
      top: 60px;
      right: 20px;
      z-index: 15;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.1);
      padding: 10px 14px;
      border-radius: 6px;
      font-size: 11px;
      line-height: 1.6;
      pointer-events: none;
      backdrop-filter: blur(8px);
    }

    .param-overlay .item {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      color: #94a3b8;
    }

    .param-overlay .val {
      font-family: var(--font-mono);
      color: var(--cyan-core);
      font-weight: 500;
    }
  </style>
</head>
<body>
  <div class="bg-grid"></div>

  <!-- 顶部状态栏 -->
  <div class="header-bar">
    <div class="brand-tag">
      <div class="status-dot"></div>
      <span class="title-text">行星三轮组阶跃越障机构</span>
      <span class="title-sub">[双级阶梯·连续双轮自适应顺时针翻转]</span>
    </div>

    <div class="hud-switches">
      <button class="hud-btn active" id="btn-toggle-forces" onclick="toggleLayer('forces')">
        <span class="key-dot"></span> 力矩/矢量 (<span id="t-forces">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-orbits" onclick="toggleLayer('orbits')">
        <span class="key-dot"></span> 回转包络线 (<span id="t-orbits">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-leveler" onclick="toggleLayer('leveler')">
        <span class="key-dot"></span> 货箱水平阻尼 (<span id="t-leveler">开</span>)
      </button>
    </div>
  </div>

  <!-- 关键参数指示 -->
  <div class="param-overlay">
    <div class="item"><span>单轮直径 (2r):</span><span class="val">Ø 120 mm</span></div>
    <div class="item"><span>回转半径 (R):</span><span class="val">165 mm</span></div>
    <div class="item"><span>平地姿态:</span><span class="val">双轮着地支承</span></div>
    <div class="item"><span>连跨双阶:</span><span class="val">顺时针 2 × 120° = 240°</span></div>
    <div class="item"><span>单级台阶高:</span><span class="val">68 mm (至中心轴)</span></div>
  </div>

  <!-- 中央主舞台 SVG -->
  <div class="viewport-container">
    <svg id="cad-stage" viewBox="0 0 1200 680" preserveAspectRatio="xMidYMid meet">
      <defs>
        <!-- 发光滤镜 -->
        <filter id="glow-cyan" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="3.5" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>
        <filter id="glow-orange" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="4" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>

        <!-- 箭头标记 -->
        <marker id="arrow-cyan" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00f0ff" />
        </marker>
        <marker id="arrow-orange" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#ff6b00" />
        </marker>
        <marker id="arrow-green" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00ff88" />
        </marker>

        <!-- 材质填充 -->
        <pattern id="hatch-stairs" width="12" height="12" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
          <line x1="0" y1="0" x2="0" y2="12" stroke="rgba(0, 240, 255, 0.08)" stroke-width="1.5" />
        </pattern>
        <pattern id="cargo-texture" width="10" height="10" patternUnits="userSpaceOnUse">
          <rect width="10" height="10" fill="#132035" />
          <path d="M 0 10 L 10 0 M 0 0 L 10 10" stroke="rgba(0, 240, 255, 0.12)" stroke-width="0.75"/>
        </pattern>
      </defs>

      <!-- 1. 楼梯与地形环境 (两级台阶,每级高 68mm) -->
      <g id="terrain-group">
        <!-- 楼梯剖面基座: 地面 Y=540, 一阶踏面 Y=472 (高68px), 二阶踏面 Y=404 (高68px) -->
        <path id="stair-base" d="M -50 540 L 350 540 L 350 472 L 700 472 L 700 404 L 1250 404 L 1250 640 L -50 640 Z" 
              fill="url(#hatch-stairs)" stroke="rgba(0, 240, 255, 0.4)" stroke-width="2" />
        
        <!-- 楼梯踏面加固线 -->
        <path d="M -50 540 L 350 540 L 350 472 L 700 472 L 700 404 L 1250 404" 
              fill="none" stroke="#00f0ff" stroke-width="3" filter="url(#glow-cyan)" />

        <!-- 踏面防滑纹理与倒角高亮 -->
        <g stroke="rgba(0, 240, 255, 0.3)" stroke-width="1">
          <line x1="350" y1="540" x2="350" y2="472" stroke="#ff6b00" stroke-width="2.5" />
          <line x1="700" y1="472" x2="700" y2="404" stroke="#ff6b00" stroke-width="2.5" />
          <circle cx="350" cy="472" r="3" fill="#ff6b00" />
          <circle cx="700" cy="404" r="3" fill="#ff6b00" />
        </g>

        <!-- 尺寸标注 (高度与宽度) -->
        <g id="layer-dim" stroke="#64748b" stroke-width="1" font-family="JetBrains Mono" font-size="11" fill="#64748b">
          <!-- 阶高标尺 -->
          <line x1="330" y1="540" x2="330" y2="472" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="315" y="510" text-anchor="end" fill="#00f0ff">H1=68mm</text>
          
          <line x1="680" y1="472" x2="680" y2="404" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="665" y="442" text-anchor="end" fill="#00f0ff">H2=68mm</text>

          <!-- 踏宽标尺 -->
          <line x1="350" y1="560" x2="700" y2="560" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="525" y="580" text-anchor="middle" fill="#00f0ff">Step Width = 350mm</text>
        </g>
      </g>

      <!-- 2. 小车主机构动态装配组 -->
      <g id="cart-assembly">
        
        <!-- 回转轨迹层 -->
        <g id="layer-orbits" stroke="rgba(0, 240, 255, 0.25)" stroke-dasharray="4,4" fill="none">
          <circle id="front-orbit" cx="260" cy="0" r="68" />
          <circle id="rear-orbit" cx="0" cy="0" r="68" />
        </g>

        <!-- 车架主梁 -->
        <path id="chassis-beam" d="M 0 0 L 260 0" stroke="#334155" stroke-width="14" stroke-linecap="round" />
        <path id="chassis-core" d="M 0 0 L 260 0" stroke="#00f0ff" stroke-width="3" />

        <!-- 阻尼器与自调平机构连杆 -->
        <g id="layer-leveler">
          <!-- 阻尼器套筒 -->
          <line id="damper-line" x1="130" y1="0" x2="130" y2="-65" stroke="#ff6b00" stroke-width="4" stroke-dasharray="6,3" />
          <circle id="damper-joint-b" cx="130" cy="0" r="5" fill="#ff6b00" />
          <circle id="damper-joint-t" cx="130" cy="-65" r="5" fill="#ff6b00" />
          
          <!-- 被动防倾摆臂 -->
          <line id="arm-f" x1="40" y1="0" x2="60" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
          <line id="arm-r" x1="220" y1="0" x2="200" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
        </g>

        <!-- 货箱平台 (保持水平) -->
        <g id="cargo-platform">
          <rect id="cargo-box" x="20" y="-120" width="220" height="58" rx="6" fill="url(#cargo-texture)" stroke="#00f0ff" stroke-width="2" />
          <!-- 货物重心与水平仪 -->
          <line x1="30" y1="-91" x2="230" y2="-91" stroke="rgba(0,240,255,0.3)" stroke-width="1" stroke-dasharray="3,3" />
          <circle cx="130" cy="-91" r="9" fill="rgba(0, 255, 136, 0.2)" stroke="#00ff88" stroke-width="1.5" />
          <line x1="121" y1="-91" x2="139" y2="-91" stroke="#00ff88" stroke-width="1.5" />
          <line x1="130" y1="-100" x2="130" y2="-82" stroke="#00ff88" stroke-width="1.5" />
          <text x="145" y="-88" font-family="JetBrains Mono" font-size="10" fill="#00ff88">CARGO LEVEL (0°)</text>
        </g>

        <!-- 后行星三轮组 (初始构型:上1轮、下2轮双轮着地) -->
        <g id="rear-planetary-set" transform="translate(0, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="rear-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <g id="rear-spider-glow" stroke="#00f0ff" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" fill="none">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <!-- 3个行星橡胶轮 (R=25,下两轮接触地面支撑) -->
          <g id="rear-wheels">
            <g id="rw-0" transform="translate(0, -68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-1" transform="translate(58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-2" transform="translate(-58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向轴承主轴 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 前行星三轮组 (初始构型:上1轮、下2轮双轮着地) -->
        <g id="front-planetary-set" transform="translate(260, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="front-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <g id="front-spider-glow" stroke="#ff6b00" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" fill="none" filter="url(#glow-orange)">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <!-- 3个行星橡胶轮 -->
          <g id="front-wheels">
            <g id="fw-0" transform="translate(0, -68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-1" transform="translate(58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-2" transform="translate(-58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向离合轴承 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 3. 动态受力与力矩矢量指示层 -->
        <g id="layer-forces">
          <!-- 前右下轮受阻碰撞法向力 (台阶阻力) -->
          <g id="force-barrier" opacity="0">
            <line x1="343" y1="34" x2="295" y2="34" stroke="#ff0055" stroke-width="4" marker-end="url(#arrow-orange)" />
            <text x="348" y="30" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff0055">F_阻挡</text>
          </g>

          <!-- 转臂顺时针翻转力矩圆弧 (Clockwise Torque) -->
          <g id="torque-arc" opacity="0">
            <path d="M 260 -35 A 35 35 0 0 1 295 0" fill="none" stroke="#ff6b00" stroke-width="3" marker-end="url(#arrow-orange)" filter="url(#glow-orange)" />
            <text x="305" y="-12" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff6b00">顺时针力矩 M &gt; 6N·m</text>
          </g>

          <!-- 爬阶抬升力矢量 -->
          <g id="force-lift" opacity="0">
            <line x1="260" y1="0" x2="260" y2="-45" stroke="#00ff88" stroke-width="3.5" marker-end="url(#arrow-green)" filter="url(#glow-cyan)" />
            <text x="270" y="-25" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#00ff88">F_抬升 (跨级上行)</text>
          </g>
        </g>

      </g>
    </svg>
  </div>

  <!-- 底部交互控制与时序微步骤 -->
  <div class="footer-bar">
    <div class="phase-indicator">
      <div class="phase-chip" id="chip-0">
        <span class="phase-idx">01</span>
        <span class="phase-name">平地双轮滚动</span>
      </div>
      <div class="phase-chip" id="chip-1">
        <span class="phase-idx">02</span>
        <span class="phase-name">前轮顺时针翻第1阶</span>
      </div>
      <div class="phase-chip" id="chip-2">
        <span class="phase-idx">03</span>
        <span class="phase-name">后轮顺时针翻第1阶</span>
      </div>
      <div class="phase-chip" id="chip-3">
        <span class="phase-idx">04</span>
        <span class="phase-name">前轮顺时针翻第2阶</span>
      </div>
      <div class="phase-chip" id="chip-4">
        <span class="phase-idx">05</span>
        <span class="phase-name">后轮顺时针翻第2阶</span>
      </div>
      <div class="phase-chip" id="chip-5">
        <span class="phase-idx">06</span>
        <span class="phase-name">高台双轮恢复平驶</span>
      </div>
    </div>

    <div class="controls-panel">
      <button class="btn-action" id="btn-play-pause" onclick="togglePlay()" title="播放/暂停">
        <svg id="icon-pause" width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
        <svg id="icon-play" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="display:none;"><polygon points="5 3 19 12 5 21 5 3"/></svg>
      </button>

      <button class="btn-action" onclick="resetAnimation()" title="重置">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
      </button>

      <input type="range" id="timeline" class="timeline-slider" min="0" max="1000" value="0" oninput="onSeek(this.value)" />

      <div class="telemetry-tag" id="telemetry-status">
        STAGE: 01 | 0.00s
      </div>
    </div>
  </div>

  <script>
    /* 全局图层显隐开关 */
    const layers = {
      forces: true,
      orbits: true,
      leveler: true
    };

    function toggleLayer(layerName) {
      layers[layerName] = !layers[layerName];
      const btn = document.getElementById('btn-toggle-' + layerName);
      const txt = document.getElementById('t-' + layerName);
      if (layers[layerName]) {
        btn.classList.add('active');
        txt.innerText = '开';
      } else {
        btn.classList.remove('active');
        txt.innerText = '关';
      }
      
      const elem = document.getElementById('layer-' + layerName);
      if (elem) {
        elem.style.display = layers[layerName] ? 'inline' : 'none';
      }
    }

    /* 运动学时序与状态机模型 (两级台阶连续翻转,总时长 12秒) */
    const TOTAL_DURATION = 12000;
    let isPlaying = true;
    let startTime = null;
    let pausedTime = 0;
    let currentProgress = 0;

    // DOM 元素引用
    const cartAssembly = document.getElementById('cart-assembly');
    const frontSpider = document.getElementById('front-planetary-set');
    const rearSpider = document.getElementById('rear-planetary-set');
    const cargoPlatform = document.getElementById('cargo-platform');
    const timeline = document.getElementById('timeline');
    const telemetry = document.getElementById('telemetry-status');
    const iconPlay = document.getElementById('icon-play');
    const iconPause = document.getElementById('icon-pause');

    // 矢量受力 DOM
    const forceBarrier = document.getElementById('force-barrier');
    const torqueArc = document.getElementById('torque-arc');
    const forceLift = document.getElementById('force-lift');

    // 行星轮子单独自转
    const fwWheels = [document.getElementById('fw-0'), document.getElementById('fw-1'), document.getElementById('fw-2')];
    const rwWheels = [document.getElementById('rw-0'), document.getElementById('rw-1'), document.getElementById('rw-2')];

    // 缓动函数
    function easeInOutQuad(t) {
      return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    }
    function clamp(val, min, max) {
      return Math.min(Math.max(val, min), max);
    }

    /* 运动学姿态求解器:连跨两阶台阶,每次顺时针翻转 120° 并恢复双轮着地 */
    function computeKinematics(p) {
      // 行程:小车从 X=-80 移动至 X=760
      // 地形标高:地面 Y=481 (小车中心), 一阶踏面 Y=413 (抬升68mm), 二阶踏面 Y=345 (再抬升68mm)
      let carX = -80 + p * 840;
      let rearX = carX;
      let frontX = carX + 260;

      let frontAngle = 0;
      let rearAngle = 0;
      let wheelSpin = carX * 2.0;

      let frontY = 481;
      let rearY = 481;

      // 阶段定义:
      // [1] 前轮翻第1阶 (p: 0.10 ~ 0.23) -> 角度 0° -> 120°, frontY 481 -> 413
      // [2] 后轮翻第1阶 (p: 0.28 ~ 0.41) -> 角度 0° -> 120°, rearY 481 -> 413
      // [3] 前轮翻第2阶 (p: 0.47 ~ 0.60) -> 角度 120° -> 240°, frontY 413 -> 345
      // [4] 后轮翻第2阶 (p: 0.65 ~ 0.78) -> 角度 120° -> 240°, rearY 413 -> 345
      // [5] 高台双轮平稳行驶 (p > 0.78)

      // 前行星架计算
      if (p < 0.10) {
        frontAngle = 0;
        frontY = 481;
      } else if (p >= 0.10 && p <= 0.23) {
        let pNorm = (p - 0.10) / 0.13;
        let sm = easeInOutQuad(pNorm);
        frontAngle = sm * 120;
        frontY = 481 - sm * 68;
      } else if (p > 0.23 && p < 0.47) {
        frontAngle = 120;
        frontY = 413;
      } else if (p >= 0.47 && p <= 0.60) {
        let pNorm = (p - 0.47) / 0.13;
        let sm = easeInOutQuad(pNorm);
        frontAngle = 120 + sm * 120;
        frontY = 413 - sm * 68;
      } else {
        frontAngle = 240;
        frontY = 345;
      }

      // 后行星架计算
      if (p < 0.28) {
        rearAngle = 0;
        rearY = 481;
      } else if (p >= 0.28 && p <= 0.41) {
        let pNorm = (p - 0.28) / 0.13;
        let sm = easeInOutQuad(pNorm);
        rearAngle = sm * 120;
        rearY = 481 - sm * 68;
      } else if (p > 0.41 && p < 0.65) {
        rearAngle = 120;
        rearY = 413;
      } else if (p >= 0.65 && p <= 0.78) {
        let pNorm = (p - 0.65) / 0.13;
        let sm = easeInOutQuad(pNorm);
        rearAngle = 120 + sm * 120;
        rearY = 413 - sm * 68;
      } else {
        rearAngle = 240;
        rearY = 345;
      }

      // 车身主梁倾角与自调平货箱角度
      const chassisAngleRad = Math.atan2(frontY - rearY, 260);
      const chassisAngleDeg = (chassisAngleRad * 180) / Math.PI;

      // 矢量指示器状态 (在前轮翻第1阶与第2阶时高亮)
      let barrierOpacity = 0;
      let torqueOpacity = 0;
      let liftOpacity = 0;

      if ((p >= 0.08 && p <= 0.21) || (p >= 0.45 && p <= 0.58)) {
        let localP = (p >= 0.08 && p <= 0.21) ? (p - 0.08) / 0.13 : (p - 0.45) / 0.13;
        barrierOpacity = localP < 0.3 ? 1.0 : clamp(1.0 - (localP - 0.3) / 0.7, 0, 1);
        torqueOpacity = 1.0;
        liftOpacity = Math.sin(localP * Math.PI);
      }

      // 确定阶段指示高亮
      let phaseIndex = 0;
      if (p < 0.10) phaseIndex = 0;
      else if (p < 0.26) phaseIndex = 1;
      else if (p < 0.44) phaseIndex = 2;
      else if (p < 0.63) phaseIndex = 3;
      else if (p < 0.80) phaseIndex = 4;
      else phaseIndex = 5;

      return {
        carX,
        frontX,
        rearX,
        frontY,
        rearY,
        frontAngle,
        rearAngle,
        wheelSpin,
        chassisAngleDeg,
        barrierOpacity,
        torqueOpacity,
        liftOpacity,
        phaseIndex
      };
    }

    /* 渲染一帧 */
    function renderFrame(state) {
      // 1. 底盘定位与俯仰
      cartAssembly.setAttribute('transform', `translate(${state.rearX}, ${state.rearY}) rotate(${state.chassisAngleDeg})`);

      // 2. 前后星形转臂顺时针旋转 (抵消底盘倾角以保持世界绝对角度正确)
      frontSpider.setAttribute('transform', `translate(260, 0) rotate(${state.frontAngle - state.chassisAngleDeg})`);
      rearSpider.setAttribute('transform', `translate(0, 0) rotate(${state.rearAngle - state.chassisAngleDeg})`);

      // 3. 轮子自转
      const wRot = `rotate(${state.wheelSpin})`;
      fwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));
      rwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));

      // 4. 自调平货箱:反向旋转抵消车身倾角,保持水平
      cargoPlatform.setAttribute('transform', `rotate(${-state.chassisAngleDeg}, 130, 0)`);

      // 5. 受力与力矩矢量透明度
      if (layers.forces) {
        forceBarrier.setAttribute('opacity', state.barrierOpacity);
        torqueArc.setAttribute('opacity', state.torqueOpacity);
        forceLift.setAttribute('opacity', state.liftOpacity);
      }

      // 6. 阶段高亮更新
      for (let i = 0; i <= 5; i++) {
        const chip = document.getElementById(`chip-${i}`);
        if (i === state.phaseIndex) {
          chip.classList.add('active');
        } else {
          chip.classList.remove('active');
        }
      }

      // 7. 更新底部滑块与遥测数据
      timeline.value = currentProgress * 1000;
      const sec = (currentProgress * (TOTAL_DURATION / 1000)).toFixed(2);
      telemetry.innerText = `PHASE: 0${state.phaseIndex + 1} | T: ${sec}s | α: ${state.chassisAngleDeg.toFixed(1)}°`;
    }

    // 初始化轮子相对基座位置缓存 (上1下2双轮着地构型)
    function initWheelTransforms() {
      fwWheels[0].setAttribute('data-base', 'translate(0, -68)');
      fwWheels[1].setAttribute('data-base', 'translate(58.89, 34)');
      fwWheels[2].setAttribute('data-base', 'translate(-58.89, 34)');

      rwWheels[0].setAttribute('data-base', 'translate(0, -68)');
      rwWheels[1].setAttribute('data-base', 'translate(58.89, 34)');
      rwWheels[2].setAttribute('data-base', 'translate(-58.89, 34)');
    }

    /* 动画循环驱动 */
    function animate(timestamp) {
      if (!startTime) startTime = timestamp;

      if (isPlaying) {
        const elapsed = (timestamp - startTime + pausedTime) % TOTAL_DURATION;
        currentProgress = elapsed / TOTAL_DURATION;
      }

      const state = computeKinematics(currentProgress);
      renderFrame(state);

      requestAnimationFrame(animate);
    }

    /* 交互控制函数 */
    function togglePlay() {
      isPlaying = !isPlaying;
      if (isPlaying) {
        startTime = performance.now();
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'none';
        iconPause.style.display = 'inline';
      } else {
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'inline';
        iconPause.style.display = 'none';
      }
    }

    function resetAnimation() {
      currentProgress = 0;
      pausedTime = 0;
      startTime = performance.now();
      const state = computeKinematics(0);
      renderFrame(state);
    }

    function onSeek(val) {
      currentProgress = val / 1000;
      pausedTime = currentProgress * TOTAL_DURATION;
      startTime = performance.now();
      const state = computeKinematics(currentProgress);
      renderFrame(state);
    }

    // DOM 加载完成自动启动
    document.addEventListener('DOMContentLoaded', () => {
      initWheelTransforms();
      requestAnimationFrame(animate);
    });
  </script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>行星三轮组越障小车机构原理动画</title>
  <style>
    :root {
      --bg-color: #080c14;
      --panel-bg: rgba(13, 20, 36, 0.75);
      --border-color: rgba(0, 229, 255, 0.2);
      --border-glow: rgba(0, 229, 255, 0.4);
      --cyan-core: #00f0ff;
      --orange-accent: #ff6b00;
      --orange-glow: rgba(255, 107, 0, 0.4);
      --green-ready: #00ff88;
      --text-main: #e2e8f0;
      --text-muted: #64748b;
      --font-mono: "JetBrains Mono", "SF Mono", "Fira Code", monospace;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      user-select: none;
    }

    body {
      background-color: var(--bg-color);
      color: var(--text-main);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      overflow: hidden;
      width: 100vw;
      height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      position: relative;
    }

    /* 背景精细 CAD 测绘网格 */
    .bg-grid {
      position: absolute;
      inset: 0;
      background-image: 
        radial-gradient(circle at 50% 50%, rgba(0, 240, 255, 0.05) 0%, transparent 80%),
        linear-gradient(rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0, 240, 255, 0.03) 1px, transparent 1px),
        linear-gradient(rgba(255, 255, 255, 0.015) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.015) 1px, transparent 1px);
      background-size: 100% 100%, 100px 100px, 100px 100px, 20px 20px, 20px 20px;
      pointer-events: none;
      z-index: 0;
    }

    /* 顶部紧凑状态条 */
    .header-bar {
      position: absolute;
      top: 16px;
      left: 20px;
      right: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      z-index: 20;
      pointer-events: none;
    }

    .brand-tag {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 10px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 6px 14px;
      border-radius: 6px;
      backdrop-filter: blur(10px);
    }

    .status-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: var(--green-ready);
      box-shadow: 0 0 10px var(--green-ready);
      animation: pulse-dot 2s infinite ease-in-out;
    }

    .title-text {
      font-size: 13px;
      font-weight: 600;
      letter-spacing: 0.5px;
      color: #fff;
    }

    .title-sub {
      font-size: 11px;
      color: var(--cyan-core);
      font-family: var(--font-mono);
      margin-left: 4px;
    }

    /* 右上角图层切换器 */
    .hud-switches {
      pointer-events: auto;
      display: flex;
      gap: 8px;
    }

    .hud-btn {
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      color: var(--text-muted);
      font-size: 11px;
      padding: 5px 10px;
      border-radius: 4px;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 6px;
      transition: all 0.2s;
    }

    .hud-btn:hover {
      border-color: var(--cyan-core);
      color: var(--text-main);
    }

    .hud-btn.active {
      border-color: var(--cyan-core);
      color: var(--cyan-core);
      background: rgba(0, 240, 255, 0.08);
      box-shadow: 0 0 10px rgba(0, 240, 255, 0.15);
    }

    .hud-btn .key-dot {
      width: 5px;
      height: 5px;
      border-radius: 50%;
      background: currentColor;
    }

    /* 中央核心动画舞台 */
    .viewport-container {
      position: absolute;
      inset: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 10;
    }

    #cad-stage {
      width: 100%;
      height: 100%;
      max-width: 1400px;
      max-height: 860px;
    }

    /* 底部紧凑控制与原理步骤提示条 */
    .footer-bar {
      position: absolute;
      bottom: 16px;
      left: 20px;
      right: 20px;
      z-index: 20;
      display: flex;
      flex-direction: column;
      gap: 8px;
      pointer-events: none;
    }

    .phase-indicator {
      display: flex;
      justify-content: space-between;
      gap: 8px;
      pointer-events: auto;
    }

    .phase-chip {
      flex: 1;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.08);
      padding: 6px 10px;
      border-radius: 5px;
      display: flex;
      align-items: center;
      gap: 6px;
      backdrop-filter: blur(12px);
      transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    }

    .phase-chip.active {
      border-color: var(--orange-accent);
      background: rgba(255, 107, 0, 0.12);
      box-shadow: 0 0 15px rgba(255, 107, 0, 0.2);
    }

    .phase-idx {
      font-family: var(--font-mono);
      font-size: 11px;
      font-weight: bold;
      color: var(--text-muted);
      min-width: 16px;
    }

    .phase-chip.active .phase-idx {
      color: var(--orange-accent);
    }

    .phase-name {
      font-size: 11.5px;
      color: var(--text-muted);
      white-space: nowrap;
    }

    .phase-chip.active .phase-name {
      color: #fff;
      font-weight: 500;
    }

    /* 控制面板 */
    .controls-panel {
      pointer-events: auto;
      display: flex;
      align-items: center;
      gap: 14px;
      background: var(--panel-bg);
      border: 1px solid var(--border-color);
      padding: 7px 16px;
      border-radius: 6px;
      backdrop-filter: blur(12px);
    }

    .btn-action {
      background: transparent;
      border: 1px solid var(--border-color);
      color: var(--cyan-core);
      width: 30px;
      height: 30px;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.2s;
    }

    .btn-action:hover {
      background: var(--cyan-core);
      color: #000;
      box-shadow: 0 0 10px var(--cyan-core);
    }

    .timeline-slider {
      flex: 1;
      height: 4px;
      -webkit-appearance: none;
      background: rgba(255, 255, 255, 0.15);
      border-radius: 2px;
      outline: none;
      cursor: pointer;
    }

    .timeline-slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: var(--cyan-core);
      box-shadow: 0 0 8px var(--cyan-core);
      cursor: ew-resize;
      transition: transform 0.1s;
    }

    .timeline-slider::-webkit-slider-thumb:hover {
      transform: scale(1.3);
    }

    .telemetry-tag {
      font-family: var(--font-mono);
      font-size: 11px;
      color: var(--cyan-core);
      min-width: 140px;
      text-align: right;
    }

    @keyframes pulse-dot {
      0%, 100% { opacity: 0.4; transform: scale(0.9); }
      50% { opacity: 1; transform: scale(1.15); }
    }

    /* 微型悬浮参数卡 */
    .param-overlay {
      position: absolute;
      top: 60px;
      right: 20px;
      z-index: 15;
      background: var(--panel-bg);
      border: 1px solid rgba(255, 255, 255, 0.1);
      padding: 10px 14px;
      border-radius: 6px;
      font-size: 11px;
      line-height: 1.6;
      pointer-events: none;
      backdrop-filter: blur(8px);
    }

    .param-overlay .item {
      display: flex;
      justify-content: space-between;
      gap: 16px;
      color: #94a3b8;
    }

    .param-overlay .val {
      font-family: var(--font-mono);
      color: var(--cyan-core);
      font-weight: 500;
    }
  </style>
</head>
<body>
  <div class="bg-grid"></div>

  <!-- 顶部状态栏 -->
  <div class="header-bar">
    <div class="brand-tag">
      <div class="status-dot"></div>
      <span class="title-text">行星三轮组阶跃越障机构</span>
      <span class="title-sub">[触阶精准触发·连续双轮顺时针跨级]</span>
    </div>

    <div class="hud-switches">
      <button class="hud-btn active" id="btn-toggle-forces" onclick="toggleLayer('forces')">
        <span class="key-dot"></span> 力矩/矢量 (<span id="t-forces">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-orbits" onclick="toggleLayer('orbits')">
        <span class="key-dot"></span> 回转包络线 (<span id="t-orbits">开</span>)
      </button>
      <button class="hud-btn active" id="btn-toggle-leveler" onclick="toggleLayer('leveler')">
        <span class="key-dot"></span> 货箱水平阻尼 (<span id="t-leveler">开</span>)
      </button>
    </div>
  </div>

  <!-- 关键参数指示 -->
  <div class="param-overlay">
    <div class="item"><span>单轮直径 (2r):</span><span class="val">Ø 120 mm</span></div>
    <div class="item"><span>回转半径 (R):</span><span class="val">165 mm</span></div>
    <div class="item"><span>平地姿态:</span><span class="val">双轮着地支承</span></div>
    <div class="item"><span>触发逻辑:</span><span class="val">轮缘精准触立面起翻</span></div>
    <div class="item"><span>单级台阶高:</span><span class="val">68 mm (至中心轴)</span></div>
  </div>

  <!-- 中央主舞台 SVG -->
  <div class="viewport-container">
    <svg id="cad-stage" viewBox="0 0 1200 680" preserveAspectRatio="xMidYMid meet">
      <defs>
        <!-- 发光滤镜 -->
        <filter id="glow-cyan" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="3.5" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>
        <filter id="glow-orange" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="4" result="blur" />
          <feMerge>
            <feMergeNode in="blur" />
            <feMergeNode in="SourceGraphic" />
          </feMerge>
        </filter>

        <!-- 箭头标记 -->
        <marker id="arrow-cyan" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00f0ff" />
        </marker>
        <marker id="arrow-orange" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#ff6b00" />
        </marker>
        <marker id="arrow-green" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 1 L 9 5 L 0 9 z" fill="#00ff88" />
        </marker>

        <!-- 材质填充 -->
        <pattern id="hatch-stairs" width="12" height="12" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
          <line x1="0" y1="0" x2="0" y2="12" stroke="rgba(0, 240, 255, 0.08)" stroke-width="1.5" />
        </pattern>
        <pattern id="cargo-texture" width="10" height="10" patternUnits="userSpaceOnUse">
          <rect width="10" height="10" fill="#132035" />
          <path d="M 0 10 L 10 0 M 0 0 L 10 10" stroke="rgba(0, 240, 255, 0.12)" stroke-width="0.75"/>
        </pattern>
      </defs>

      <!-- 1. 楼梯与地形环境 (两级台阶,每级高 68mm) -->
      <g id="terrain-group">
        <!-- 楼梯剖面基座: 地面 Y=540, 一阶立面 X=350, 踏面 Y=472; 二阶立面 X=700, 踏面 Y=404 -->
        <path id="stair-base" d="M -50 540 L 350 540 L 350 472 L 700 472 L 700 404 L 1250 404 L 1250 640 L -50 640 Z" 
              fill="url(#hatch-stairs)" stroke="rgba(0, 240, 255, 0.4)" stroke-width="2" />
        
        <!-- 楼梯踏面加固线 -->
        <path d="M -50 540 L 350 540 L 350 472 L 700 472 L 700 404 L 1250 404" 
              fill="none" stroke="#00f0ff" stroke-width="3" filter="url(#glow-cyan)" />

        <!-- 踏面防滑纹理与倒角高亮 -->
        <g stroke="rgba(0, 240, 255, 0.3)" stroke-width="1">
          <line x1="350" y1="540" x2="350" y2="472" stroke="#ff6b00" stroke-width="2.5" />
          <line x1="700" y1="472" x2="700" y2="404" stroke="#ff6b00" stroke-width="2.5" />
          <circle cx="350" cy="472" r="3" fill="#ff6b00" />
          <circle cx="700" cy="404" r="3" fill="#ff6b00" />
        </g>

        <!-- 尺寸标注 (高度与宽度) -->
        <g id="layer-dim" stroke="#64748b" stroke-width="1" font-family="JetBrains Mono" font-size="11" fill="#64748b">
          <!-- 阶高标尺 -->
          <line x1="330" y1="540" x2="330" y2="472" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="315" y="510" text-anchor="end" fill="#00f0ff">H1=68mm</text>
          
          <line x1="680" y1="472" x2="680" y2="404" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="665" y="442" text-anchor="end" fill="#00f0ff">H2=68mm</text>

          <!-- 踏宽标尺 -->
          <line x1="350" y1="560" x2="700" y2="560" marker-start="url(#arrow-cyan)" marker-end="url(#arrow-cyan)" stroke="#00f0ff" stroke-opacity="0.6"/>
          <text x="525" y="580" text-anchor="middle" fill="#00f0ff">Step Width = 350mm</text>
        </g>
      </g>

      <!-- 2. 小车主机构动态装配组 -->
      <g id="cart-assembly">
        
        <!-- 回转轨迹层 -->
        <g id="layer-orbits" stroke="rgba(0, 240, 255, 0.25)" stroke-dasharray="4,4" fill="none">
          <circle id="front-orbit" cx="260" cy="0" r="68" />
          <circle id="rear-orbit" cx="0" cy="0" r="68" />
        </g>

        <!-- 车架主梁 -->
        <path id="chassis-beam" d="M 0 0 L 260 0" stroke="#334155" stroke-width="14" stroke-linecap="round" />
        <path id="chassis-core" d="M 0 0 L 260 0" stroke="#00f0ff" stroke-width="3" />

        <!-- 阻尼器与自调平机构连杆 -->
        <g id="layer-leveler">
          <!-- 阻尼器套筒 -->
          <line id="damper-line" x1="130" y1="0" x2="130" y2="-65" stroke="#ff6b00" stroke-width="4" stroke-dasharray="6,3" />
          <circle id="damper-joint-b" cx="130" cy="0" r="5" fill="#ff6b00" />
          <circle id="damper-joint-t" cx="130" cy="-65" r="5" fill="#ff6b00" />
          
          <!-- 被动防倾摆臂 -->
          <line id="arm-f" x1="40" y1="0" x2="60" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
          <line id="arm-r" x1="220" y1="0" x2="200" y2="-60" stroke="#00f0ff" stroke-width="3" stroke-opacity="0.8" />
        </g>

        <!-- 货箱平台 (保持水平) -->
        <g id="cargo-platform">
          <rect id="cargo-box" x="20" y="-120" width="220" height="58" rx="6" fill="url(#cargo-texture)" stroke="#00f0ff" stroke-width="2" />
          <!-- 货物重心与水平仪 -->
          <line x1="30" y1="-91" x2="230" y2="-91" stroke="rgba(0,240,255,0.3)" stroke-width="1" stroke-dasharray="3,3" />
          <circle cx="130" cy="-91" r="9" fill="rgba(0, 255, 136, 0.2)" stroke="#00ff88" stroke-width="1.5" />
          <line x1="121" y1="-91" x2="139" y2="-91" stroke="#00ff88" stroke-width="1.5" />
          <line x1="130" y1="-100" x2="130" y2="-82" stroke="#00ff88" stroke-width="1.5" />
          <text x="145" y="-88" font-family="JetBrains Mono" font-size="10" fill="#00ff88">CARGO LEVEL (0°)</text>
        </g>

        <!-- 左边行星三轮组(后轮组,初始构型:上1轮、下2轮双轮着地) -->
        <g id="rear-planetary-set" transform="translate(0, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="rear-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <g id="rear-spider-glow" stroke="#00f0ff" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" fill="none">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <!-- 3个行星橡胶轮 (R=25,下两轮接触地面支撑) -->
          <g id="rear-wheels">
            <g id="rw-0" transform="translate(0, -68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-1" transform="translate(58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="rw-2" transform="translate(-58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向轴承主轴 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 右边行星三轮组(前轮组,初始构型:上1轮、下2轮双轮着地) -->
        <g id="front-planetary-set" transform="translate(260, 0)">
          <!-- 转臂 (Y型等角 120度) -->
          <g id="front-spider" stroke="#475569" stroke-width="12" stroke-linejoin="round" stroke-linecap="round">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <g id="front-spider-glow" stroke="#ff6b00" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round" fill="none" filter="url(#glow-orange)">
            <line x1="0" y1="0" x2="0" y2="-68" />
            <line x1="0" y1="0" x2="58.89" y2="34" />
            <line x1="0" y1="0" x2="-58.89" y2="34" />
          </g>
          <!-- 3个行星橡胶轮 -->
          <g id="front-wheels">
            <g id="fw-0" transform="translate(0, -68)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-1" transform="translate(58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
            <g id="fw-2" transform="translate(-58.89, 34)">
              <circle r="25" fill="#1e293b" stroke="#00f0ff" stroke-width="3" />
              <circle r="8" fill="#080c14" stroke="#64748b" stroke-width="2" />
              <line x1="-20" y1="0" x2="20" y2="0" stroke="#00f0ff" stroke-width="1.5" />
              <line x1="0" y1="-20" x2="0" y2="20" stroke="#00f0ff" stroke-width="1.5" />
            </g>
          </g>
          <!-- 中心单向离合轴承 -->
          <circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff6b00" stroke-width="3" />
          <circle cx="0" cy="0" r="5" fill="#ff6b00" />
        </g>

        <!-- 3. 动态受力与力矩矢量指示层 -->
        <g id="layer-forces">
          <!-- 触阶受阻法向力 (台阶阻力) -->
          <g id="force-barrier" opacity="0">
            <line id="force-line" x1="343" y1="34" x2="295" y2="34" stroke="#ff0055" stroke-width="4" marker-end="url(#arrow-orange)" />
            <text id="force-txt" x="348" y="30" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff0055">F_阻挡</text>
          </g>

          <!-- 转臂顺时针翻转力矩圆弧 -->
          <g id="torque-arc" opacity="0">
            <path d="M 260 -35 A 35 35 0 0 1 295 0" fill="none" stroke="#ff6b00" stroke-width="3" marker-end="url(#arrow-orange)" filter="url(#glow-orange)" />
            <text x="305" y="-12" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#ff6b00">顺时针力矩 M &gt; 6N·m</text>
          </g>

          <!-- 爬阶抬升力矢量 -->
          <g id="force-lift" opacity="0">
            <line x1="260" y1="0" x2="260" y2="-45" stroke="#00ff88" stroke-width="3.5" marker-end="url(#arrow-green)" filter="url(#glow-cyan)" />
            <text x="270" y="-25" font-family="JetBrains Mono" font-size="11" font-weight="bold" fill="#00ff88">F_抬升 (跨级上行)</text>
          </g>
        </g>

      </g>
    </svg>
  </div>

  <!-- 底部交互控制与时序微步骤 -->
  <div class="footer-bar">
    <div class="phase-indicator">
      <div class="phase-chip" id="chip-0">
        <span class="phase-idx">01</span>
        <span class="phase-name">平地双轮滚动</span>
      </div>
      <div class="phase-chip" id="chip-1">
        <span class="phase-idx">02</span>
        <span class="phase-name">前轮触阶1·翻转120°</span>
      </div>
      <div class="phase-chip" id="chip-2">
        <span class="phase-idx">03</span>
        <span class="phase-name">左轮触阶1·翻转120°</span>
      </div>
      <div class="phase-chip" id="chip-3">
        <span class="phase-idx">04</span>
        <span class="phase-name">前轮触阶2·翻转120°</span>
      </div>
      <div class="phase-chip" id="chip-4">
        <span class="phase-idx">05</span>
        <span class="phase-name">左轮触阶2·翻转120°</span>
      </div>
      <div class="phase-chip" id="chip-5">
        <span class="phase-idx">06</span>
        <span class="phase-name">高台双轮恢复平驶</span>
      </div>
    </div>

    <div class="controls-panel">
      <button class="btn-action" id="btn-play-pause" onclick="togglePlay()" title="播放/暂停">
        <svg id="icon-pause" width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>
        <svg id="icon-play" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="display:none;"><polygon points="5 3 19 12 5 21 5 3"/></svg>
      </button>

      <button class="btn-action" onclick="resetAnimation()" title="重置">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
      </button>

      <input type="range" id="timeline" class="timeline-slider" min="0" max="1000" value="0" oninput="onSeek(this.value)" />

      <div class="telemetry-tag" id="telemetry-status">
        STAGE: 01 | 0.00s
      </div>
    </div>
  </div>

  <script>
    /* 全局图层显隐开关 */
    const layers = {
      forces: true,
      orbits: true,
      leveler: true
    };

    function toggleLayer(layerName) {
      layers[layerName] = !layers[layerName];
      const btn = document.getElementById('btn-toggle-' + layerName);
      const txt = document.getElementById('t-' + layerName);
      if (layers[layerName]) {
        btn.classList.add('active');
        txt.innerText = '开';
      } else {
        btn.classList.remove('active');
        txt.innerText = '关';
      }
      
      const elem = document.getElementById('layer-' + layerName);
      if (elem) {
        elem.style.display = layers[layerName] ? 'inline' : 'none';
      }
    }

    /* 运动学时序与状态机模型 (精准碰撞几何:车身移动距离与触阶点物理严格对应) */
    const TOTAL_DURATION = 12000;
    let isPlaying = true;
    let startTime = null;
    let pausedTime = 0;
    let currentProgress = 0;

    // DOM 元素引用
    const cartAssembly = document.getElementById('cart-assembly');
    const frontSpider = document.getElementById('front-planetary-set');
    const rearSpider = document.getElementById('rear-planetary-set');
    const cargoPlatform = document.getElementById('cargo-platform');
    const timeline = document.getElementById('timeline');
    const telemetry = document.getElementById('telemetry-status');
    const iconPlay = document.getElementById('icon-play');
    const iconPause = document.getElementById('icon-pause');

    // 矢量受力 DOM
    const forceBarrier = document.getElementById('force-barrier');
    const torqueArc = document.getElementById('torque-arc');
    const forceLift = document.getElementById('force-lift');

    // 行星轮子单独自转
    const fwWheels = [document.getElementById('fw-0'), document.getElementById('fw-1'), document.getElementById('fw-2')];
    const rwWheels = [document.getElementById('rw-0'), document.getElementById('rw-1'), document.getElementById('rw-2')];

    // 缓动函数
    function easeInOutQuad(t) {
      return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
    }
    function clamp(val, min, max) {
      return Math.min(Math.max(val, min), max);
    }

    /* 
      精确触碰几何学:
      - 台阶1立面在 X=350,台阶2立面在 X=700
      - 右边前轮右下前缘在车架后轴 +260 + 58.89 + 25 = 后轴 + 343.89 处
      - 左边后轮右下前缘在车架后轴 + 0 + 58.89 + 25 = 后轴 + 83.89 处
      - 小车后轴 X 从 -80 匀速运动到 760 (全程位移 840)
      - 前轮右缘碰台阶1(X=350) -> 后轴 X = 6.11  -> 对应进度 p ≈ 0.102
      - 左轮右缘碰台阶1(X=350) -> 后轴 X = 266.11 -> 对应进度 p ≈ 0.412 (碰阶前左轮一直双轮着地平驶,碰到立即翻转)
      - 前轮右缘碰台阶2(X=700) -> 后轴 X = 356.11 -> 对应进度 p ≈ 0.519
      - 左轮右缘碰台阶2(X=700) -> 后轴 X = 616.11 -> 对应进度 p ≈ 0.828
    */
    function computeKinematics(p) {
      let carX = -80 + p * 840;
      let rearX = carX;
      let frontX = carX + 260;

      let frontAngle = 0;
      let rearAngle = 0;
      let wheelSpin = carX * 2.0;

      let frontY = 481;
      let rearY = 481;

      // 1. 右边(前)行星轮翻第1阶 (p: 0.10 ~ 0.22)
      if (p < 0.10) {
        frontAngle = 0;
        frontY = 481;
      } else if (p >= 0.10 && p <= 0.22) {
        let pNorm = (p - 0.10) / 0.12;
        let sm = easeInOutQuad(pNorm);
        frontAngle = sm * 120;
        frontY = 481 - sm * 68;
      } else if (p > 0.22 && p < 0.52) {
        frontAngle = 120;
        frontY = 413;
      } else if (p >= 0.52 && p <= 0.64) {
        // 3. 右边(前)行星轮翻第2阶 (p: 0.52 ~ 0.64)
        let pNorm = (p - 0.52) / 0.12;
        let sm = easeInOutQuad(pNorm);
        frontAngle = 120 + sm * 120;
        frontY = 413 - sm * 68;
      } else {
        frontAngle = 240;
        frontY = 345;
      }

      // 2. 左边(后)行星轮:直到 p=0.41 触碰到第1级台阶立面时才开始翻转!
      if (p < 0.41) {
        rearAngle = 0;
        rearY = 481;
      } else if (p >= 0.41 && p <= 0.53) {
        // 触碰第1级台阶立面,顺时针翻转 120°
        let pNorm = (p - 0.41) / 0.12;
        let sm = easeInOutQuad(pNorm);
        rearAngle = sm * 120;
        rearY = 481 - sm * 68;
      } else if (p > 0.53 && p < 0.82) {
        rearAngle = 120;
        rearY = 413;
      } else if (p >= 0.82 && p <= 0.94) {
        // 4. 左边(后)行星轮触碰第2级台阶立面(p=0.82),开始顺时针翻转第2阶!
        let pNorm = (p - 0.82) / 0.12;
        let sm = easeInOutQuad(pNorm);
        rearAngle = 120 + sm * 120;
        rearY = 413 - sm * 68;
      } else {
        rearAngle = 240;
        rearY = 345;
      }

      // 车身主梁倾角与自调平货箱角度
      const chassisAngleRad = Math.atan2(frontY - rearY, 260);
      const chassisAngleDeg = (chassisAngleRad * 180) / Math.PI;

      // 矢量指示器状态 (在触碰台阶发生翻转时高亮)
      let barrierOpacity = 0;
      let torqueOpacity = 0;
      let liftOpacity = 0;

      const isFlipping = (p >= 0.09 && p <= 0.21) || (p >= 0.40 && p <= 0.52) || 
                         (p >= 0.51 && p <= 0.63) || (p >= 0.81 && p <= 0.93);
      if (isFlipping) {
        barrierOpacity = 0.9;
        torqueOpacity = 1.0;
        liftOpacity = 0.85;
      }

      // 确定阶段指示高亮
      let phaseIndex = 0;
      if (p < 0.10) phaseIndex = 0;
      else if (p < 0.41) phaseIndex = 1;
      else if (p < 0.52) phaseIndex = 2;
      else if (p < 0.82) phaseIndex = 3;
      else if (p < 0.94) phaseIndex = 4;
      else phaseIndex = 5;

      return {
        carX,
        frontX,
        rearX,
        frontY,
        rearY,
        frontAngle,
        rearAngle,
        wheelSpin,
        chassisAngleDeg,
        barrierOpacity,
        torqueOpacity,
        liftOpacity,
        phaseIndex
      };
    }

    /* 渲染一帧 */
    function renderFrame(state) {
      // 1. 底盘定位与俯仰
      cartAssembly.setAttribute('transform', `translate(${state.rearX}, ${state.rearY}) rotate(${state.chassisAngleDeg})`);

      // 2. 前后星形转臂顺时针旋转 (抵消底盘倾角以保持世界绝对角度正确)
      frontSpider.setAttribute('transform', `translate(260, 0) rotate(${state.frontAngle - state.chassisAngleDeg})`);
      rearSpider.setAttribute('transform', `translate(0, 0) rotate(${state.rearAngle - state.chassisAngleDeg})`);

      // 3. 轮子自转
      const wRot = `rotate(${state.wheelSpin})`;
      fwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));
      rwWheels.forEach(w => w.setAttribute('transform', `${w.getAttribute('data-base') || ''} ${wRot}`));

      // 4. 自调平货箱:反向旋转抵消车身倾角,保持水平
      cargoPlatform.setAttribute('transform', `rotate(${-state.chassisAngleDeg}, 130, 0)`);

      // 5. 受力与力矩矢量透明度
      if (layers.forces) {
        forceBarrier.setAttribute('opacity', state.barrierOpacity);
        torqueArc.setAttribute('opacity', state.torqueOpacity);
        forceLift.setAttribute('opacity', state.liftOpacity);
      }

      // 6. 阶段高亮更新
      for (let i = 0; i <= 5; i++) {
        const chip = document.getElementById(`chip-${i}`);
        if (i === state.phaseIndex) {
          chip.classList.add('active');
        } else {
          chip.classList.remove('active');
        }
      }

      // 7. 更新底部滑块与遥测数据
      timeline.value = currentProgress * 1000;
      const sec = (currentProgress * (TOTAL_DURATION / 1000)).toFixed(2);
      telemetry.innerText = `PHASE: 0${state.phaseIndex + 1} | T: ${sec}s | α: ${state.chassisAngleDeg.toFixed(1)}°`;
    }

    // 初始化轮子相对基座位置缓存 (上1下2双轮着地构型)
    function initWheelTransforms() {
      fwWheels[0].setAttribute('data-base', 'translate(0, -68)');
      fwWheels[1].setAttribute('data-base', 'translate(58.89, 34)');
      fwWheels[2].setAttribute('data-base', 'translate(-58.89, 34)');

      rwWheels[0].setAttribute('data-base', 'translate(0, -68)');
      rwWheels[1].setAttribute('data-base', 'translate(58.89, 34)');
      rwWheels[2].setAttribute('data-base', 'translate(-58.89, 34)');
    }

    /* 动画循环驱动 */
    function animate(timestamp) {
      if (!startTime) startTime = timestamp;

      if (isPlaying) {
        const elapsed = (timestamp - startTime + pausedTime) % TOTAL_DURATION;
        currentProgress = elapsed / TOTAL_DURATION;
      }

      const state = computeKinematics(currentProgress);
      renderFrame(state);

      requestAnimationFrame(animate);
    }

    /* 交互控制函数 */
    function togglePlay() {
      isPlaying = !isPlaying;
      if (isPlaying) {
        startTime = performance.now();
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'none';
        iconPause.style.display = 'inline';
      } else {
        pausedTime = currentProgress * TOTAL_DURATION;
        iconPlay.style.display = 'inline';
        iconPause.style.display = 'none';
      }
    }

    function resetAnimation() {
      currentProgress = 0;
      pausedTime = 0;
      startTime = performance.now();
      const state = computeKinematics(0);
      renderFrame(state);
    }

    function onSeek(val) {
      currentProgress = val / 1000;
      pausedTime = currentProgress * TOTAL_DURATION;
      startTime = performance.now();
      const state = computeKinematics(currentProgress);
      renderFrame(state);
    }

    // DOM 加载完成自动启动
    document.addEventListener('DOMContentLoaded', () => {
      initWheelTransforms();
      requestAnimationFrame(animate);
    });
  </script>
</body>
</html>
积分规则:每轮对话扣10分