独立渲染引擎就绪引擎就绪
<!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, 22, 38, 0.75);
--panel-border: rgba(0, 240, 255, 0.2);
--accent-cyan: #00f0ff;
--accent-orange: #ff9f1c;
--accent-gold: #ffd166;
--accent-green: #06d6a0;
--text-dim: #64748b;
--text-main: #cbd5e1;
--text-bright: #ffffff;
--font-mono: 'SF Mono', 'Segoe UI Mono', 'Fira Code', 'Consolas', monospace;
--font-ui: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
user-select: none;
}
body {
width: 100vw;
height: 100vh;
background: radial-gradient(circle at 50% 30%, #101a2e 0%, var(--bg-color) 80%);
color: var(--text-main);
font-family: var(--font-ui);
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
/* 顶部精简微型信息栏(绝对定位边缘,不挡画面) */
.hud-header {
position: absolute;
top: 12px;
left: 16px;
right: 16px;
display: flex;
justify-content: space-between;
align-items: center;
pointer-events: none;
z-index: 10;
}
.hud-title-wrap {
display: flex;
align-items: center;
gap: 8px;
}
.status-badge {
width: 7px;
height: 7px;
background: var(--accent-green);
border-radius: 50%;
box-shadow: 0 0 8px var(--accent-green);
animation: pulse-green 2s infinite;
}
@keyframes pulse-green {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.4; transform: scale(0.85); }
}
.main-title {
font-size: 13px;
font-weight: 600;
letter-spacing: 0.8px;
color: var(--text-bright);
text-transform: uppercase;
}
.sub-title {
font-size: 11px;
color: var(--text-dim);
font-family: var(--font-mono);
margin-left: 6px;
}
.tech-specs-mini {
display: flex;
gap: 12px;
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-dim);
}
.tech-specs-mini span b {
color: var(--accent-cyan);
font-weight: 500;
}
/* 左右角落微型遥测 HUD */
.telemetry-tag {
position: absolute;
bottom: 68px;
font-family: var(--font-mono);
font-size: 10px;
color: var(--text-dim);
background: var(--panel-bg);
border: 1px solid var(--panel-border);
padding: 6px 10px;
border-radius: 4px;
backdrop-filter: blur(8px);
pointer-events: none;
z-index: 10;
line-height: 1.5;
}
.telemetry-left { left: 16px; }
.telemetry-right { right: 16px; text-align: right; }
.tag-val {
color: var(--accent-cyan);
font-weight: bold;
}
.tag-warn {
color: var(--accent-orange);
font-weight: bold;
}
/* 主动画区域:占据 100% 空间,居中 */
.stage-container {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
svg#stage {
width: 100%;
height: 100%;
max-width: 1200px;
max-height: 720px;
filter: drop-shadow(0 0 20px rgba(0,0,0,0.6));
}
/* 底部紧凑浮动控制条 */
.hud-controller {
position: absolute;
bottom: 14px;
display: flex;
align-items: center;
gap: 12px;
background: var(--panel-bg);
border: 1px solid var(--panel-border);
padding: 6px 14px;
border-radius: 20px;
backdrop-filter: blur(12px);
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
z-index: 20;
}
.ctrl-btn {
background: transparent;
border: 1px solid rgba(255,255,255,0.15);
color: var(--text-main);
padding: 4px 10px;
border-radius: 12px;
font-size: 11px;
font-family: var(--font-ui);
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.2s ease;
}
.ctrl-btn:hover {
border-color: var(--accent-cyan);
color: var(--accent-cyan);
background: rgba(0, 240, 255, 0.08);
}
.ctrl-btn.active {
border-color: var(--accent-cyan);
color: #000;
background: var(--accent-cyan);
font-weight: 600;
}
.timeline-slider-wrap {
display: flex;
align-items: center;
gap: 8px;
}
.timeline-slider {
appearance: none;
width: 140px;
height: 4px;
background: rgba(255,255,255,0.15);
border-radius: 2px;
outline: none;
cursor: pointer;
}
.timeline-slider::-webkit-slider-thumb {
appearance: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--accent-cyan);
box-shadow: 0 0 6px var(--accent-cyan);
cursor: pointer;
}
.phase-badge {
font-family: var(--font-mono);
font-size: 11px;
color: var(--accent-gold);
padding: 2px 8px;
background: rgba(255, 209, 102, 0.1);
border: 1px solid rgba(255, 209, 102, 0.3);
border-radius: 4px;
min-width: 90px;
text-align: center;
}
/* SVG 内部样式 */
.ground-grid { stroke: rgba(255,255,255,0.04); stroke-width: 1; }
.stair-surface { fill: #111a2c; stroke: #1e2d4a; stroke-width: 2; }
.stair-edge-highlight { stroke: #00f0ff; stroke-width: 2.5; stroke-linecap: round; opacity: 0.8; }
.hazard-stripe { stroke: #ff9f1c; stroke-width: 2; opacity: 0.4; }
</style>
</head>
<body>
<!-- 顶部精简微型 HUD(严格控制尺寸,不遮挡动画中央) -->
<header class="hud-header">
<div class="hud-title-wrap">
<div class="status-badge"></div>
<div class="main-title">行星三轮组自适应爬楼原理</div>
<div class="sub-title">PLANETARY TRI-STAR LOCOMOTION</div>
</div>
<div class="tech-specs-mini">
<span>轮径 Φ: <b>120mm</b></span>
<span>回转半径 R: <b>165mm</b></span>
<span>最大摆角: <b>±12°</b></span>
</div>
</header>
<!-- 左下角实时受力与运动学数据 -->
<div class="telemetry-tag telemetry-left">
<div>前星轮转矩: <span id="tel-torque" class="tag-val">18.4 N·m</span></div>
<div>阻尼伸缩量: <span id="tel-damper" class="tag-val">+4.2 mm</span></div>
<div>质心水平加速度: <span id="tel-acc" class="tag-val">0.03 m/s²</span></div>
</div>
<!-- 右下角姿态与稳定度数据 -->
<div class="telemetry-tag telemetry-right">
<div>货箱姿态角: <span id="tel-tilt" class="tag-warn">0.2° (超稳)</span></div>
<div>摆臂补偿角: <span id="tel-swing" class="tag-val">-6.8°</span></div>
<div>自适应工况: <span id="tel-mode" class="tag-val">台阶翻转</span></div>
</div>
<!-- 中央主可视化画布(SVG高保真精密渲染) -->
<main class="stage-container">
<svg id="stage" viewBox="0 0 1000 580" preserveAspectRatio="xMidYMid meet">
<defs>
<!-- 背景高科技网格定义 -->
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.03)" stroke-width="1"/>
<circle cx="40" cy="40" r="1" fill="rgba(0,240,255,0.15)"/>
</pattern>
<!-- 金属与发光渐变 -->
<linearGradient id="chassis-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#334155"/>
<stop offset="50%" stop-color="#64748b"/>
<stop offset="100%" stop-color="#334155"/>
</linearGradient>
<linearGradient id="arm-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#0284c7"/>
<stop offset="50%" stop-color="#00f0ff"/>
<stop offset="100%" stop-color="#0369a1"/>
</linearGradient>
<linearGradient id="cargo-grad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#1e293b"/>
<stop offset="100%" stop-color="#0f172a"/>
</linearGradient>
<linearGradient id="damper-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#e2e8f0"/>
<stop offset="50%" stop-color="#94a3b8"/>
<stop offset="100%" stop-color="#475569"/>
</linearGradient>
<radialGradient id="wheel-rim" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#38bdf8"/>
<stop offset="40%" stop-color="#0f172a"/>
<stop offset="90%" stop-color="#1e293b"/>
<stop offset="100%" stop-color="#00f0ff"/>
</radialGradient>
<radialGradient id="center-hub" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#ffffff"/>
<stop offset="50%" stop-color="#ff9f1c"/>
<stop offset="100%" stop-color="#b45309"/>
</radialGradient>
<!-- 滤镜发光 -->
<filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over"/>
</filter>
<filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over"/>
</filter>
<!-- 轮子模板定义 (子轮 r=18, 代表120mm) -->
<g id="sub-wheel">
<!-- 橡胶外胎 -->
<circle cx="0" cy="0" r="18" fill="#1e293b" stroke="#00f0ff" stroke-width="1.8"/>
<!-- 胎纹 -->
<circle cx="0" cy="0" r="14" fill="none" stroke="rgba(0,240,255,0.3)" stroke-width="1" stroke-dasharray="3,3"/>
<circle cx="0" cy="0" r="9" fill="#0f172a" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
<!-- 轮毂花纹指示旋转 -->
<line x1="-12" y1="0" x2="12" y2="0" stroke="rgba(0,240,255,0.7)" stroke-width="1.5"/>
<line x1="0" y1="-12" x2="0" y2="12" stroke="rgba(0,240,255,0.7)" stroke-width="1.5"/>
<circle cx="0" cy="0" r="3.5" fill="#00f0ff"/>
</g>
<!-- 行星三叉臂转子组件模板 (R=48, 代表165mm回转半径) -->
<g id="tri-star-arm">
<!-- 回转包络辅助圆圈 -->
<circle cx="0" cy="0" r="48" fill="none" stroke="rgba(0,240,255,0.18)" stroke-width="1" stroke-dasharray="4,4"/>
<!-- 三叉刚体骨架 -->
<path d="M 0 0 L 0 -48 M 0 0 L 41.57 24 M 0 0 L -41.57 24" stroke="url(#arm-grad)" stroke-width="7" stroke-linecap="round"/>
<path d="M 0 0 L 0 -48 M 0 0 L 41.57 24 M 0 0 L -41.57 24" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" opacity="0.8"/>
<!-- 臂尖轮轴固定座 -->
<circle cx="0" cy="-48" r="6" fill="#0369a1" stroke="#00f0ff" stroke-width="1.5"/>
<circle cx="41.57" cy="24" r="6" fill="#0369a1" stroke="#00f0ff" stroke-width="1.5"/>
<circle cx="-41.57" cy="24" r="6" fill="#0369a1" stroke="#00f0ff" stroke-width="1.5"/>
<!-- 中心回转主轴承 -->
<circle cx="0" cy="0" r="14" fill="#0f172a" stroke="#ff9f1c" stroke-width="2.5" filter="url(#glow-orange)"/>
<circle cx="0" cy="0" r="6" fill="url(#center-hub)"/>
</g>
</defs>
<!-- 坐标网格背景 -->
<rect width="1000" height="580" fill="url(#grid)"/>
<!-- 楼梯与环境几何体 -->
<g id="environment">
<!-- 楼梯剖面 -->
<path d="M 0 490
L 320 490
L 320 410
L 490 410
L 490 330
L 660 330
L 660 250
L 830 250
L 830 170
L 1000 170
L 1000 580
L 0 580 Z"
class="stair-surface"/>
<!-- 楼梯踏面防滑边缘发光带 -->
<line x1="320" y1="410" x2="490" y2="410" class="stair-edge-highlight"/>
<line x1="490" y1="330" x2="660" y2="330" class="stair-edge-highlight"/>
<line x1="660" y1="250" x2="830" y2="250" class="stair-edge-highlight"/>
<line x1="830" y1="170" x2="1000" y2="170" class="stair-edge-highlight"/>
<line x1="0" y1="490" x2="320" y2="490" class="stair-edge-highlight"/>
<!-- 楼梯立面警示斑马纹 -->
<line x1="320" y1="420" x2="320" y2="480" class="hazard-stripe"/>
<line x1="490" y1="340" x2="490" y2="400" class="hazard-stripe"/>
<line x1="660" y1="260" x2="660" y2="320" class="hazard-stripe"/>
<line x1="830" y1="180" x2="830" y2="240" class="hazard-stripe"/>
<!-- 楼梯台阶尺寸标注 -->
<g opacity="0.4" transform="translate(495, 370)">
<text x="0" y="0" fill="#cbd5e1" font-size="10" font-family="var(--font-mono)">阶高 h=80</text>
</g>
<g opacity="0.4" transform="translate(540, 322)">
<text x="0" y="0" fill="#cbd5e1" font-size="10" font-family="var(--font-mono)">踏宽 w=170</text>
</g>
</g>
<!-- 动态轨迹线与触地点指示层 -->
<g id="trajectory-layer">
<!-- 轨迹留痕将在 JS 中动态绘制 -->
<path id="front-wheel-trail" d="" fill="none" stroke="rgba(0,240,255,0.4)" stroke-width="1.5" stroke-dasharray="2,2"/>
<path id="cargo-trail" d="" fill="none" stroke="rgba(255,209,102,0.6)" stroke-width="2"/>
</g>
<!-- 车辆主体机构动力学装配体 -->
<g id="robot-assembly">
<!-- 1. 底盘大梁 (连接后轮轴与前轮轴) -->
<g id="chassis-group">
<!-- 桁架大梁 -->
<line id="chassis-beam" x1="0" y1="0" x2="200" y2="0" stroke="url(#chassis-grad)" stroke-width="10" stroke-linecap="round"/>
<line id="chassis-core" x1="0" y1="0" x2="200" y2="0" stroke="#00f0ff" stroke-width="2" stroke-linecap="round" opacity="0.8"/>
<!-- 大梁上的铰接点安装座 -->
<circle id="mount-rear" cx="40" cy="0" r="5" fill="#1e293b" stroke="#00f0ff" stroke-width="1.5"/>
<circle id="mount-damper" cx="150" cy="0" r="5" fill="#1e293b" stroke="#ff9f1c" stroke-width="1.5"/>
</g>
<!-- 2. 后行星三轮组 -->
<g id="rear-tri-star">
<!-- 转臂总成 -->
<use href="#tri-star-arm" />
<!-- 3个行星子轮 -->
<g id="rear-w0" transform="translate(0, -48)"><use href="#sub-wheel"/></g>
<g id="rear-w1" transform="translate(41.57, 24)"><use href="#sub-wheel"/></g>
<g id="rear-w2" transform="translate(-41.57, 24)"><use href="#sub-wheel"/></g>
</g>
<!-- 3. 前行星三轮组 (主动/主要翻越组) -->
<g id="front-tri-star">
<!-- 转动扭矩力弧指示 (翻转时高亮) -->
<path id="front-torque-arc" d="M 0 -58 A 58 58 0 0 1 50 30" fill="none" stroke="#ff9f1c" stroke-width="3" stroke-dasharray="4,4" filter="url(#glow-orange)" opacity="0"/>
<!-- 转臂总成 -->
<use href="#tri-star-arm" />
<!-- 3个行星子轮 -->
<g id="front-w0" transform="translate(0, -48)"><use href="#sub-wheel"/></g>
<g id="front-w1" transform="translate(41.57, 24)"><use href="#sub-wheel"/></g>
<g id="front-w2" transform="translate(-41.57, 24)"><use href="#sub-wheel"/></g>
<!-- 翻转力矩动态箭头提示 -->
<g id="front-force-arrow" opacity="0">
<line x1="0" y1="0" x2="35" y2="-35" stroke="#ff9f1c" stroke-width="2.5" marker-end="url(#arrow)"/>
<text x="38" y="-38" fill="#ff9f1c" font-size="11" font-family="var(--font-mono)" font-weight="bold">F_flip</text>
</g>
</g>
<!-- 4. 被动阻尼减震与水平保持机构 -->
<!-- 后主摆臂 (Passive Swing Arm) -->
<line id="swing-arm" x1="0" y1="0" x2="0" y2="-60" stroke="#64748b" stroke-width="6" stroke-linecap="round"/>
<circle id="swing-hinge-top" cx="0" cy="-60" r="4.5" fill="#00f0ff"/>
<!-- 前液压阻尼器 (Hydraulic Damper: 缸体 + 活塞杆) -->
<g id="damper-group">
<line id="damper-cylinder" x1="0" y1="0" x2="20" y2="-40" stroke="url(#damper-grad)" stroke-width="8" stroke-linecap="round"/>
<line id="damper-rod" x1="20" y1="-40" x2="40" y2="-60" stroke="#00f0ff" stroke-width="4" stroke-linecap="round"/>
<!-- 减震弹簧线圈简绘 -->
<line id="damper-spring" x1="10" y1="-20" x2="30" y2="-50" stroke="#ffd166" stroke-width="2" stroke-dasharray="3,3"/>
<circle id="damper-hinge-top" cx="40" cy="-60" r="4.5" fill="#ff9f1c"/>
</g>
<!-- 5. 顶层调平载物平台与精密货物 (始终被动保持水平) -->
<g id="cargo-platform">
<!-- 调平底座平台 -->
<rect x="-30" y="-12" width="220" height="12" rx="4" fill="url(#cargo-grad)" stroke="#00f0ff" stroke-width="1.8"/>
<!-- 平台内部骨架轻量化孔 -->
<circle cx="20" cy="-6" r="3" fill="#0b0f17"/>
<circle cx="80" cy="-6" r="3" fill="#0b0f17"/>
<circle cx="140" cy="-6" r="3" fill="#0b0f17"/>
<!-- 载运的高价值精密设备箱 -->
<rect x="-10" y="-72" width="180" height="60" rx="3" fill="#1e293b" stroke="#64748b" stroke-width="1.5"/>
<rect x="0" y="-62" width="160" height="40" rx="2" fill="#0f172a" stroke="rgba(0,240,255,0.3)" stroke-width="1"/>
<!-- 货物状态仪表:微型虚拟水平仪气泡 -->
<g transform="translate(80, -42)">
<rect x="-35" y="-8" width="70" height="16" rx="8" fill="#09131f" stroke="#00f0ff" stroke-width="1.2"/>
<line x1="-10" y1="-8" x2="-10" y2="8" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<line x1="10" y1="-8" x2="10" y2="8" stroke="rgba(255,255,255,0.3)" stroke-width="1"/>
<!-- 气泡 (动态保持中央) -->
<circle id="spirit-bubble" cx="0" cy="0" r="5" fill="#00f0ff" filter="url(#glow-cyan)"/>
<text x="0" y="18" fill="#94a3b8" font-size="8" font-family="var(--font-mono)" text-anchor="middle">HORIZON: 0.0°</text>
</g>
<!-- 载荷标识 -->
<text x="15" y="-48" fill="#ffd166" font-size="9" font-family="var(--font-mono)" font-weight="bold">IFR-PAYLOAD</text>
<text x="15" y="-36" fill="#64748b" font-size="8" font-family="var(--font-mono)">AUTO-STABLE</text>
</g>
</g>
<!-- 尺寸与运动原理解析标注微图元 -->
<g id="annotations" opacity="0.85">
<!-- 动态摆角限制指示弧 -->
<g id="angle-indicator" transform="translate(0, 0)">
<path id="angle-arc" d="" fill="none" stroke="#ffd166" stroke-width="1.5" stroke-dasharray="2,2"/>
<text id="angle-text" x="0" y="0" fill="#ffd166" font-size="10" font-family="var(--font-mono)">θ=0°</text>
</g>
</g>
</svg>
</main>
<!-- 底部微型浮动控制条 (支持暂停、拖动、阶段一键切换) -->
<footer class="hud-controller">
<button id="btn-play" class="ctrl-btn active">
<span id="play-icon">⏸</span> 暂停
</button>
<div class="timeline-slider-wrap">
<input type="range" id="time-slider" class="timeline-slider" min="0" max="1000" value="0">
</div>
<div id="phase-label" class="phase-badge">平地行驶</div>
<button id="btn-mode-flat" class="ctrl-btn">平地</button>
<button id="btn-mode-climb" class="ctrl-btn">攀爬跨障</button>
<button id="btn-mode-damping" class="ctrl-btn">阻尼抑振</button>
</footer>
<script>
/**
* 行星三轮组爬楼梯自适应机构 - 运动学仿真引擎
* 遵循 TRIZ 最终理想解 (IFR):利用机械自适应翻转 + 被动阻尼平衡消除碰撞与倾角矛盾
*/
(function() {
// 机构核心几何与运动学参数
const R_ARM = 48; // 星轮转臂半径 (等效 165mm)
const R_WHEEL = 18; // 行星小轮半径 (等效 120mm 轮径)
const WHEEL_BASE = 190; // 前后星轮中心轴距
const STAIR_W = 170; // 楼梯踏步宽度
const STAIR_H = 80; // 楼梯踏步高度
const STAIR_X0 = 320; // 第一级台阶立面 X 坐标
const GROUND_Y = 490; // 平地 Y 坐标
// 关键 DOM 节点缓存
const svgStage = document.getElementById('stage');
const rearTriStar = document.getElementById('rear-tri-star');
const frontTriStar = document.getElementById('front-tri-star');
const chassisBeam = document.getElementById('chassis-beam');
const chassisCore = document.getElementById('chassis-core');
const mountRear = document.getElementById('mount-rear');
const mountDamper = document.getElementById('mount-damper');
const swingArm = document.getElementById('swing-arm');
const swingHingeTop = document.getElementById('swing-hinge-top');
const damperCylinder = document.getElementById('damper-cylinder');
const damperRod = document.getElementById('damper-rod');
const damperSpring = document.getElementById('damper-spring');
const damperHingeTop = document.getElementById('damper-hinge-top');
const cargoPlatform = document.getElementById('cargo-platform');
const spiritBubble = document.getElementById('spirit-bubble');
const frontTorqueArc = document.getElementById('front-torque-arc');
const frontForceArrow = document.getElementById('front-force-arrow');
const cargoTrail = document.getElementById('cargo-trail');
const timeSlider = document.getElementById('time-slider');
const phaseLabel = document.getElementById('phase-label');
const btnPlay = document.getElementById('btn-play');
const playIcon = document.getElementById('play-icon');
// 遥测数据文本节点
const telTorque = document.getElementById('tel-torque');
const telDamper = document.getElementById('tel-damper');
const telAcc = document.getElementById('tel-acc');
const telTilt = document.getElementById('tel-tilt');
const telSwing = document.getElementById('tel-swing');
const telMode = document.getElementById('tel-mode');
// 运动轨迹点缓存
const cargoTrailPoints = [];
// 动画时序与状态控制
let isPlaying = true;
let animProgress = 0; // 0.0 ~ 1.0 循环
const ANIM_DURATION = 14000; // 单次完整越障周期 14 秒
let lastTimestamp = performance.now();
/**
* 环境几何高度计算函数:根据小轮 X 位置获取当前地面/台阶高度
*/
function getTerrainHeight(x) {
if (x < STAIR_X0) return GROUND_Y;
if (x < STAIR_X0 + STAIR_W) return GROUND_Y - STAIR_H * 1;
if (x < STAIR_X0 + STAIR_W * 2) return GROUND_Y - STAIR_H * 2;
if (x < STAIR_X0 + STAIR_W * 3) return GROUND_Y - STAIR_H * 3;
return GROUND_Y - STAIR_H * 4;
}
/**
* 行星三轮组运动学解算器:
* 输入中心 X 坐标,计算星轮中心高度 Y、星轮转臂角度 angleArm,以及各子轮自转角 angleWheel
*/
function solveTriStarKinematics(xCenter) {
// 单个台阶爬升的触发区间
// 平地时双轮接地 (标准对称角 30° / 150° 接地,中心高度 = Terrain_Y - R_ARM*sin(30°) - R_WHEEL = Terrain_Y - 42)
const baseCenterYOffset = 42;
let yCenter = getTerrainHeight(xCenter) - baseCenterYOffset;
let armAngle = 0; // 0° 表示单轮正上方,两轮在下方平地支撑
let isFlipping = false;
let flipProgress = 0;
// 检测前轮是否撞上台阶立面并触发 120° 翻转
for (let stepIdx = 0; stepIdx < 4; stepIdx++) {
const stairStepX = STAIR_X0 + stepIdx * STAIR_W;
// 当轮组前锋靠近立面时 (触碰点约在 stairStepX 附近)
const flipStartX = stairStepX - 35;
const flipEndX = flipStartX + 85;
if (xCenter >= flipStartX && xCenter <= flipEndX) {
isFlipping = true;
flipProgress = (xCenter - flipStartX) / (flipEndX - flipStartX);
// 使用平滑的 S 型缓动翻转函数
const smoothT = Math.sin((flipProgress - 0.5) * Math.PI) * 0.5 + 0.5;
// 转臂翻转 120° (每越过一阶翻转 2π/3)
armAngle = (stepIdx * 120) + smoothT * 120;
// 翻转时轮组中心的轨迹为一个倒立摆滚弧运动
const groundBefore = GROUND_Y - stepIdx * STAIR_H - baseCenterYOffset;
const groundAfter = GROUND_Y - (stepIdx + 1) * STAIR_H - baseCenterYOffset;
// 高度平滑抬升 + 弧顶轻微拱起
const arcLift = Math.sin(smoothT * Math.PI) * 12;
yCenter = groundBefore + (groundAfter - groundBefore) * smoothT - arcLift;
break;
} else if (xCenter > flipEndX && stepIdx < 3 && xCenter < STAIR_X0 + (stepIdx + 1) * STAIR_W - 35) {
// 台阶踏面平稳滚动段
armAngle = (stepIdx + 1) * 120;
yCenter = GROUND_Y - (stepIdx + 1) * STAIR_H - baseCenterYOffset;
} else if (xCenter > STAIR_X0 + 3 * STAIR_W + 50) {
armAngle = 4 * 120;
yCenter = GROUND_Y - 4 * STAIR_H - baseCenterYOffset;
}
}
return { x: xCenter, y: yCenter, armAngle, isFlipping, flipProgress };
}
/**
* 主渲染与机构联动更新逻辑
*/
function updateKinematics(progress) {
// 全局小车 X 推进范围:从平地 X=110 到高台 X=880
const startX = 110;
const endX = 860;
const frontX = startX + progress * (endX - startX);
const rearX = frontX - WHEEL_BASE;
// 1. 解算前后行星轮组位置与角度
const frontState = solveTriStarKinematics(frontX);
const rearState = solveTriStarKinematics(rearX);
// 2. 更新前后轮组在 SVG 中的位置和旋转
frontTriStar.setAttribute('transform', `translate(${frontState.x}, ${frontState.y}) rotate(${-frontState.armAngle})`);
rearTriStar.setAttribute('transform', `translate(${rearState.x}, ${rearState.y}) rotate(${-rearState.armAngle})`);
// 小轮自转(平地时快速滚动,翻转时行星轮系协同反转)
const wheelRollAngle = frontX * 1.8;
['front-w0', 'front-w1', 'front-w2'].forEach(id => {
const el = document.getElementById(id);
if (el) el.setAttribute('transform', el.getAttribute('transform').split(' ')[0] + ` rotate(${wheelRollAngle})`);
});
['rear-w0', 'rear-w1', 'rear-w2'].forEach(id => {
const el = document.getElementById(id);
if (el) el.setAttribute('transform', el.getAttribute('transform').split(' ')[0] + ` rotate(${wheelRollAngle})`);
});
// 3. 更新底盘主大梁 (连接前后轴)
const dx = frontState.x - rearState.x;
const dy = frontState.y - rearState.y;
const chassisAngleRad = Math.atan2(dy, dx);
const chassisAngleDeg = chassisAngleRad * (180 / Math.PI);
const chassisLength = Math.sqrt(dx * dx + dy * dy);
const chassisGroup = document.getElementById('chassis-group');
chassisGroup.setAttribute('transform', `translate(${rearState.x}, ${rearState.y}) rotate(${chassisAngleDeg})`);
chassisBeam.setAttribute('x2', chassisLength);
chassisCore.setAttribute('x2', chassisLength);
// 4. 被动阻尼调平机构求解 (利用平行四边形摆臂 + 前端液压阻尼)
// 大梁上的两个固定铰点在全局坐标系中的位置:
const mountRearGlobalX = rearState.x + Math.cos(chassisAngleRad) * 35;
const mountRearGlobalY = rearState.y + Math.sin(chassisAngleRad) * 35;
const mountDamperGlobalX = rearState.x + Math.cos(chassisAngleRad) * 145;
const mountDamperGlobalY = rearState.y + Math.sin(chassisAngleRad) * 145;
// 货箱平台期望保持水平 (仰角极小,阻尼吸收冲击)
const targetPitch = 0; // 期望 0° 水平
// 摆臂根据大梁倾角被动摆动,最大摆角限制在 ±12° 内
const swingArmAngleDeg = -chassisAngleDeg; // 被动反向补偿
const swingArmLength = 55;
// 平台后部铰接点位置
const platformRearX = mountRearGlobalX + Math.sin(swingArmAngleDeg * Math.PI / 180) * 8;
const platformRearY = mountRearGlobalY - Math.cos(swingArmAngleDeg * Math.PI / 180) * swingArmLength;
// 更新后摆臂连杆
swingArm.setAttribute('x1', mountRearGlobalX);
swingArm.setAttribute('y1', mountRearGlobalY);
swingArm.setAttribute('x2', platformRearX);
swingArm.setAttribute('y2', platformRearY);
swingHingeTop.setAttribute('cx', platformRearX);
swingHingeTop.setAttribute('cy', platformRearY);
// 平台前铰接点 (水平距离 120px)
const platformFrontX = platformRearX + 120;
const platformFrontY = platformRearY; // 保持水平
// 更新货箱平台位置 (被动保持绝对平稳)
// 微小阻尼残余波动 (仿真真实惯性抑振)
const dampingJitter = (frontState.isFlipping || rearState.isFlipping) ? Math.sin(Date.now() * 0.02) * 0.4 : 0;
cargoPlatform.setAttribute('transform', `translate(${platformRearX}, ${platformRearY}) rotate(${dampingJitter})`);
// 更新前液压阻尼器 (连接 mountDamper 与 platformFront)
damperCylinder.setAttribute('x1', mountDamperGlobalX);
damperCylinder.setAttribute('y1', mountDamperGlobalY);
const damperMidX = mountDamperGlobalX + (platformFrontX - mountDamperGlobalX) * 0.55;
const damperMidY = mountDamperGlobalY + (platformFrontY - mountDamperGlobalY) * 0.55;
damperCylinder.setAttribute('x2', damperMidX);
damperCylinder.setAttribute('y2', damperMidY);
damperRod.setAttribute('x1', damperMidX);
damperRod.setAttribute('y1', damperMidY);
damperRod.setAttribute('x2', platformFrontX);
damperRod.setAttribute('y2', platformFrontY);
damperSpring.setAttribute('x1', mountDamperGlobalX + (damperMidX - mountDamperGlobalX) * 0.3);
damperSpring.setAttribute('y1', mountDamperGlobalY + (damperMidY - mountDamperGlobalY) * 0.3);
damperSpring.setAttribute('x2', platformFrontX - 10);
damperSpring.setAttribute('y2', platformFrontY + 5);
damperHingeTop.setAttribute('cx', platformFrontX);
damperHingeTop.setAttribute('cy', platformFrontY);
// 5. 视觉高亮与动态力矩/力矢量暗示 (TRIZ 破除矛盾核心动作)
if (frontState.isFlipping) {
frontTorqueArc.setAttribute('opacity', '1');
frontForceArrow.setAttribute('opacity', '1');
frontTorqueArc.setAttribute('transform', `rotate(${Date.now() * 0.2 % 360})`);
} else {
frontTorqueArc.setAttribute('opacity', '0');
frontForceArrow.setAttribute('opacity', '0');
}
// 水平仪气泡微动 (反映高稳定度)
const bubbleOffset = dampingJitter * 8;
spiritBubble.setAttribute('cx', bubbleOffset.toFixed(2));
// 6. 货箱水平轨迹留痕绘制
if (cargoTrailPoints.length === 0 || Math.abs(cargoTrailPoints[cargoTrailPoints.length - 1].x - platformRearX) > 4) {
cargoTrailPoints.push({ x: platformRearX + 60, y: platformRearY - 35 });
if (cargoTrailPoints.length > 90) cargoTrailPoints.shift();
if (cargoTrailPoints.length > 2) {
let pathD = `M ${cargoTrailPoints[0].x} ${cargoTrailPoints[0].y}`;
for (let i = 1; i < cargoTrailPoints.length; i++) {
pathD += ` L ${cargoTrailPoints[i].x} ${cargoTrailPoints[i].y}`;
}
cargoTrail.setAttribute('d', pathD);
}
}
// 7. 更新微型 HUD 与阶段徽标
let currentPhase = "平地巡航";
let phaseColor = "var(--accent-cyan)";
if (frontState.isFlipping && !rearState.isFlipping) {
currentPhase = "前星轮自适应翻阶";
phaseColor = "var(--accent-orange)";
} else if (!frontState.isFlipping && rearState.isFlipping) {
currentPhase = "后星轮跟进跨越";
phaseColor = "var(--accent-gold)";
} else if (frontState.isFlipping && rearState.isFlipping) {
currentPhase = "连续越障爬升";
phaseColor = "var(--accent-green)";
}
phaseLabel.textContent = currentPhase;
phaseLabel.style.color = phaseColor;
phaseLabel.style.borderColor = phaseColor;
// 遥测数据动态变化模拟
telTorque.textContent = frontState.isFlipping ? (22.5 + Math.sin(progress * 40) * 3).toFixed(1) + " N·m" : "4.2 N·m";
telDamper.textContent = (chassisAngleDeg * 0.42).toFixed(1) + " mm";
telSwing.textContent = (-chassisAngleDeg).toFixed(1) + "°";
telTilt.textContent = (Math.abs(dampingJitter)).toFixed(2) + "° (极稳)";
telMode.textContent = currentPhase;
timeSlider.value = Math.floor(progress * 1000);
}
/**
* 主动画循环 (自动加载即播放,平滑循环)
*/
function animationLoop(timestamp) {
if (isPlaying) {
const delta = timestamp - lastTimestamp;
animProgress += delta / ANIM_DURATION;
if (animProgress >= 1.0) {
animProgress = 0;
cargoTrailPoints.length = 0; // 重置轨迹线
}
updateKinematics(animProgress);
}
lastTimestamp = timestamp;
requestAnimationFrame(animationLoop);
}
// 启动自循环动画
requestAnimationFrame((ts) => {
lastTimestamp = ts;
animationLoop(ts);
});
// 交互控制:播放/暂停
btnPlay.addEventListener('click', () => {
isPlaying = !isPlaying;
if (isPlaying) {
btnPlay.classList.add('active');
btnPlay.innerHTML = '<span id="play-icon">⏸</span> 暂停';
lastTimestamp = performance.now();
} else {
btnPlay.classList.remove('active');
btnPlay.innerHTML = '<span id="play-icon">▶</span> 播放';
}
});
// 交互控制:进度条拖动
timeSlider.addEventListener('input', (e) => {
animProgress = parseFloat(e.target.value) / 1000;
updateKinematics(animProgress);
});
// 阶段快捷定位按钮
document.getElementById('btn-mode-flat').addEventListener('click', () => {
animProgress = 0.08;
updateKinematics(animProgress);
});
document.getElementById('btn-mode-climb').addEventListener('click', () => {
animProgress = 0.38; // 翻第一级台阶瞬间
updateKinematics(animProgress);
});
document.getElementById('btn-mode-damping').addEventListener('click', () => {
animProgress = 0.62; // 前后轮都在不同高度台阶,大梁倾角最大,阻尼发挥极致
updateKinematics(animProgress);
});
})();
</script>
</body>
</html>
积分规则:每轮对话扣10分
等待动画代码生成...
