分享图
动画工坊
引擎就绪
<!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: #060B12;
            --grid-color: rgba(0, 240, 255, 0.05);
            --stair-color: #121A28;
            --stair-edge: #00F0FF;
            --chassis: #1F2836;
            --neon-cyan: #00F0FF;
            --neon-orange: #FF4400;
            --text-main: #FFFFFF;
            --text-dim: #7A8EAA;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background-color: var(--bg-color);
            overflow: hidden;
            font-family: 'Consolas', 'Courier New', monospace;
            color: var(--text-main);
            user-select: none;
        }

        #canvas-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
        }

        .hud-overlay {
            position: absolute;
            z-index: 10;
            pointer-events: none;
            text-transform: uppercase;
        }

        .hud-top-left {
            top: 30px;
            left: 30px;
        }

        .hud-bottom-left {
            bottom: 30px;
            left: 30px;
        }

        .hud-bottom-right {
            bottom: 30px;
            right: 30px;
            text-align: right;
        }

        .hud-title {
            font-size: 14px;
            font-weight: bold;
            color: var(--neon-cyan);
            margin-bottom: 8px;
            letter-spacing: 1px;
            text-shadow: 0 0 10px rgba(0, 240, 255, 0.4);
        }

        .hud-text {
            font-size: 11px;
            color: var(--text-dim);
            line-height: 1.6;
        }

        .hud-highlight {
            color: var(--text-main);
            font-weight: bold;
        }

        .hud-value {
            font-size: 28px;
            font-weight: bold;
            color: var(--neon-cyan);
            text-shadow: 0 0 15px rgba(0, 240, 255, 0.6);
            margin-top: 5px;
            font-variant-numeric: tabular-nums;
        }

        .tag-orange { color: var(--neon-orange); }
        .tag-cyan { color: var(--neon-cyan); }

        .scan-line {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 4px;
            background: linear-gradient(to bottom, transparent, rgba(0, 240, 255, 0.2), transparent);
            opacity: 0.5;
            z-index: 20;
            animation: scan 4s linear infinite;
            pointer-events: none;
        }

        @keyframes scan {
            0% { transform: translateY(-10vh); }
            100% { transform: translateY(110vh); }
        }

        @keyframes pulse-ring {
            0% { stroke-width: 1; opacity: 0.8; transform: scale(0.9); }
            50% { stroke-width: 2; opacity: 1; transform: scale(1.1); text-shadow: 0 0 10px var(--neon-cyan); }
            100% { stroke-width: 1; opacity: 0.8; transform: scale(0.9); }
        }

        .gyro-ring {
            transform-origin: center;
            animation: pulse-ring 2s ease-in-out infinite;
        }
    </style>
</head>
<body>

    <div class="scan-line"></div>

    <!-- UI Overlays -->
    <div class="hud-overlay hud-top-left">
        <div class="hud-title">■ 系统 IFR 状态:零姿态偏移</div>
        <div class="hud-text">
            <span>核心原理</span> // <span class="hud-highlight">移动越障与姿态平衡物理绝对解耦</span><br>
            <span>底层构型</span> // <span class="tag-orange">行星式四轮组 (被动翻滚跨级)</span><br>
            <span>顶层构型</span> // <span class="tag-cyan">主动液压自平衡云台 (动态逆向补偿)</span><br>
            <span>响应延迟</span> // &lt; 50ms<br>
            <span>控制目标</span> // 载货平台倾覆力矩始终为零
        </div>
    </div>

    <div class="hud-overlay hud-bottom-left">
        <div class="hud-text" style="color:var(--neon-orange)" id="ui-status">当前状态:平地巡航模式</div>
        <div class="hud-title" style="margin-top: 10px;">三轴陀螺仪姿态角</div>
        <div class="hud-value" id="ui-tilt">0.00°</div>
    </div>

    <div class="hud-overlay hud-bottom-right">
        <div class="hud-text">
            ■ 视觉引导图例<br>
            <span style="color:#FF4400;">██</span> 动态受力翻转件<br>
            <span style="color:#00F0FF;">██</span> 主动补偿液压缸<br>
            <span style="color:#FFFFFF;">██</span> 零姿态载货平台
        </div>
    </div>

    <!-- SVG Canvas -->
    <div id="canvas-container">
        <svg id="main-svg" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <filter id="neon-glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur1" />
                    <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur2" />
                    <feMerge>
                        <feMergeNode in="blur2" />
                        <feMergeNode in="blur1" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                
                <filter id="neon-glow-orange" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur1" />
                    <feMerge>
                        <feMergeNode in="blur1" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>

                <pattern id="grid-pattern" width="60" height="60" patternUnits="userSpaceOnUse">
                    <path d="M 60 0 L 0 0 0 60" fill="none" stroke="rgba(0, 240, 255, 0.07)" stroke-width="1"/>
                    <circle cx="0" cy="0" r="1.5" fill="rgba(0, 240, 255, 0.3)"/>
                </pattern>

                <!-- Small Wheel Component -->
                <g id="small-wheel-comp">
                    <circle cx="0" cy="0" r="16" fill="#1A222E" stroke="#7A8EAA" stroke-width="3"/>
                    <circle cx="0" cy="0" r="6" fill="#FF4400"/>
                    <path d="M 0 -16 L 0 16 M -16 0 L 16 0" stroke="#7A8EAA" stroke-width="2" stroke-dasharray="3 3"/>
                </g>
            </defs>

            <!-- Background Grid (Fixed to camera) -->
            <rect width="100%" height="100%" fill="url(#grid-pattern)" />

            <!-- Translating Scene -->
            <g id="scene">
                <!-- Dynamic Terrain Path -->
                <path id="terrain" fill="var(--stair-color)" stroke="var(--stair-edge)" stroke-width="3" filter="url(#neon-glow-cyan)"/>
                
                <!-- Inner Terrain Fill for solid look without glowing bottom -->
                <path id="terrain-fill" fill="var(--stair-color)" stroke="none"/>

                <!-- Robot Assembly -->
                <g id="robot">
                    <!-- Rear Planetary Wheel Assembly -->
                    <g id="rear-assembly">
                        <g id="rear-bracket" filter="url(#neon-glow-orange)">
                            <path d="M -65 -12 L 65 -12 L 65 12 L -65 12 Z" fill="#FF4400"/>
                            <path d="M -12 -65 L 12 -65 L 12 65 L -12 65 Z" fill="#FF4400"/>
                            <circle cx="0" cy="0" r="18" fill="#121A28" stroke="#FF4400" stroke-width="4"/>
                        </g>
                        <!-- 4 Small Wheels -->
                        <g transform="translate(-65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        <g transform="translate(65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        <g transform="translate(0, -65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        <g transform="translate(0, 65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                    </g>

                    <!-- Front Planetary Wheel Assembly -->
                    <g id="front-assembly">
                        <g id="front-bracket" filter="url(#neon-glow-orange)">
                            <path d="M -65 -12 L 65 -12 L 65 12 L -65 12 Z" fill="#FF4400"/>
                            <path d="M -12 -65 L 12 -65 L 12 65 L -12 65 Z" fill="#FF4400"/>
                            <circle cx="0" cy="0" r="18" fill="#121A28" stroke="#FF4400" stroke-width="4"/>
                        </g>
                        <g transform="translate(-65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        <g transform="translate(65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        <g transform="translate(0, -65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        <g transform="translate(0, 65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                    </g>

                    <!-- Main Chassis -->
                    <g id="chassis">
                        <!-- Structural links between axles and chassis center (Visual only) -->
                        <line id="rear-link" x1="-160" y1="0" x2="0" y2="0" stroke="#334455" stroke-width="14" stroke-linecap="round"/>
                        <line id="front-link" x1="160" y1="0" x2="0" y2="0" stroke="#334455" stroke-width="14" stroke-linecap="round"/>

                        <!-- Chassis Body -->
                        <polygon points="-180,-30 180,-30 210,15 -210,15" fill="#1A222E" stroke="#4A5A72" stroke-width="3"/>
                        <path d="M -100 -20 L 100 -20" stroke="#FF4400" stroke-width="2" stroke-dasharray="10 5" opacity="0.6"/>

                        <!-- Hydraulic Cylinder Bases (Fixed to Chassis) -->
                        <!-- Left Base -->
                        <path d="M -125 -30 L -115 -30 L -115 -70 L -125 -70 Z" fill="#2A3648" stroke="#4A5A72" stroke-width="2"/>
                        <circle cx="-120" cy="-25" r="6" fill="#00F0FF"/>
                        <!-- Right Base -->
                        <path d="M 115 -30 L 125 -30 L 125 -70 L 115 -70 Z" fill="#2A3648" stroke="#4A5A72" stroke-width="2"/>
                        <circle cx="120" cy="-25" r="6" fill="#00F0FF"/>

                        <!-- Hydraulic Pistons (Dynamic) -->
                        <line id="hyd-left" x1="-120" y1="-70" x2="-120" y2="-120" stroke="#00F0FF" stroke-width="6" stroke-linecap="round" filter="url(#neon-glow-cyan)"/>
                        <line id="hyd-right" x1="120" y1="-70" x2="120" y2="-120" stroke="#00F0FF" stroke-width="6" stroke-linecap="round" filter="url(#neon-glow-cyan)"/>

                        <!-- Central Pivot Base -->
                        <path d="M -20 -30 L 20 -30 L 10 -80 L -10 -80 Z" fill="#2A3648"/>
                        <circle cx="0" cy="-80" r="8" fill="#00F0FF" filter="url(#neon-glow-cyan)"/>

                        <!-- Cargo Platform (Absolute Horizontal) -->
                        <g id="platform">
                            <!-- Platform Base -->
                            <rect x="-160" y="-10" width="320" height="12" rx="4" fill="#FFFFFF"/>
                            <rect x="-150" y="-4" width="300" height="4" fill="#E0E0E0"/>
                            
                            <!-- Mount points on platform -->
                            <circle cx="-120" cy="0" r="5" fill="#FF4400"/>
                            <circle cx="120" cy="0" r="5" fill="#FF4400"/>
                            <circle cx="0" cy="0" r="6" fill="#00F0FF"/>

                            <!-- Cargo Container -->
                            <g transform="translate(0, -70)">
                                <rect x="-100" y="-50" width="200" height="110" rx="6" fill="rgba(0, 240, 255, 0.08)" stroke="#00F0FF" stroke-width="2" filter="url(#neon-glow-cyan)"/>
                                <path d="M -80 -40 L -40 -40 M 80 -40 L 40 -40" stroke="#00F0FF" stroke-width="3"/>
                                
                                <!-- Gyroscope IFR Indicator -->
                                <g class="gyro-ring">
                                    <circle cx="0" cy="5" r="25" fill="none" stroke="#00F0FF" stroke-width="2" stroke-dasharray="12 6"/>
                                    <circle cx="0" cy="5" r="15" fill="none" stroke="#00F0FF" stroke-width="1.5"/>
                                    <circle cx="0" cy="5" r="4" fill="#00F0FF" filter="url(#neon-glow-cyan)"/>
                                </g>
                                <text x="0" y="-20" text-anchor="middle" fill="#00F0FF" font-size="12" font-weight="bold" letter-spacing="2">ZERO-TILT CARGO</text>
                            </g>
                        </g>
                    </g>
                </g>
            </g>
        </svg>
    </div>

    <script>
        // --- System Kinematics Configuration ---
        const config = {
            W: 320,               // Wheelbase (Distance between front and rear planetary centers)
            R: 65,                // Planetary bracket radius
            r: 16,                // Small wheel radius
            stairH: 140,          // Step height
            stairW: 900,          // Distance between steps
            climbDist: 140,       // Horizontal distance over which the bracket climb rotation occurs
            speed: 3.5,           // Animation base speed (px per frame)
            platformCenterY: -130 // Platform absolute center Y offset relative to chassis axle line
        };

        const groundClearance = config.R + config.r; // 81px

        // Element references
        const scene = document.getElementById('scene');
        const terrain = document.getElementById('terrain');
        const terrainFill = document.getElementById('terrain-fill');
        const chassis = document.getElementById('chassis');
        const platform = document.getElementById('platform');
        const rearAssembly = document.getElementById('rear-assembly');
        const frontAssembly = document.getElementById('front-assembly');
        const rearBracket = document.getElementById('rear-bracket');
        const frontBracket = document.getElementById('front-bracket');
        const rearLink = document.getElementById('rear-link');
        const frontLink = document.getElementById('front-link');
        const hydLeft = document.getElementById('hyd-left');
        const hydRight = document.getElementById('hyd-right');
        const uiTilt = document.getElementById('ui-tilt');
        const uiStatus = document.getElementById('ui-status');
        const swRotators = document.querySelectorAll('.sw-rotator');

        let cameraX = 0;
        let cameraY = 0;
        let globalTime = 0;

        /**
         * Calculates the absolute Y position of a planetary axle given its absolute X.
         */
        function getAxleY(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW; 
            let stepCount = Math.floor(x / config.stairW);
            let baseY = stepCount * -config.stairH;
            let localY = 0;
            
            // Stair physical corner is at localX = 400
            // Climb starts earlier by (config.climbDist / 2)
            const cornerX = 400;
            const startClimbX = cornerX - 20;

            if (localX > startClimbX && localX <= startClimbX + config.climbDist) {
                // Cosine interpolation for smooth axle arc over the step
                let t = (localX - startClimbX) / config.climbDist;
                localY = -config.stairH * (0.5 - 0.5 * Math.cos(t * Math.PI));
            } else if (localX > startClimbX + config.climbDist) {
                localY = -config.stairH;
            }
            
            return baseY + localY;
        }

        /**
         * Calculates the rotation angle of the planetary bracket.
         */
        function getBracketAngle(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW;
            let stepCount = Math.floor(x / config.stairW);
            let baseAngle = stepCount * 90;
            let climbAngle = 0;
            
            const cornerX = 400;
            const startClimbX = cornerX - 20;

            if (localX > startClimbX && localX <= startClimbX + config.climbDist) {
                 let t = (localX - startClimbX) / config.climbDist;
                 climbAngle = 90 * t;
            } else if (localX > startClimbX + config.climbDist) {
                 climbAngle = 90;
            }
            return baseAngle + climbAngle;
        }

        /**
         * Generates the SVG path for rigid square stairs.
         */
        function updateTerrainPath(cx) {
            // Draw terrain from cx - 1500 to cx + 2000
            let startX = Math.floor((cx - 1500) / config.stairW) * config.stairW;
            let endX = cx + 2000;
            
            let pathD = `M ${startX}, 3000 `;
            for (let x = startX; x < endX; x += config.stairW) {
                let yTop = Math.floor(x / config.stairW) * -config.stairH + groundClearance;
                let yBot = yTop - config.stairH;
                pathD += `L ${x}, ${yTop} L ${x + 400}, ${yTop} L ${x + 400}, ${yBot} L ${x + config.stairW}, ${yBot} `;
            }
            pathD += `L ${endX}, 3000 Z`;
            
            terrain.setAttribute('d', pathD);
            terrainFill.setAttribute('d', pathD);
        }

        function animate() {
            globalTime += config.speed;
            
            // 1. Calculate Rear Axle
            let Rx = globalTime;
            let Ry = getAxleY(Rx);

            // 2. Iteratively solve for Front Axle to maintain constant rigid wheelbase W
            let Fx = Rx + config.W;
            for(let i = 0; i < 5; i++) {
                let tempFy = getAxleY(Fx);
                let currentDist = Math.hypot(Fx - Rx, tempFy - Ry);
                let error = config.W - currentDist;
                Fx += error * 0.9;
            }
            let Fy = getAxleY(Fx);

            // 3. Chassis Kinematics (Center & Pitch Angle)
            let Cx = (Rx + Fx) / 2;
            let Cy = (Ry + Fy) / 2;
            let thetaRad = Math.atan2(Fy - Ry, Fx - Rx);
            let thetaDeg = thetaRad * 180 / Math.PI;

            // 4. Update Camera / Scene Translation (Keep Robot Centered)
            const screenCx = window.innerWidth / 2;
            const screenCy = window.innerHeight / 2 + 50; // slightly lowered center
            scene.setAttribute('transform', `translate(${screenCx - Cx}, ${screenCy - Cy})`);

            // 5. Render Terrain
            updateTerrainPath(Cx);

            // 6. Update Robot Elements Positioning
            // Transform assemblies relative to Chassis Center
            rearAssembly.setAttribute('transform', `translate(${Rx - Cx}, ${Ry - Cy})`);
            frontAssembly.setAttribute('transform', `translate(${Fx - Cx}, ${Fy - Cy})`);
            
            // Connect links visually
            rearLink.setAttribute('x1', Rx - Cx);
            rearLink.setAttribute('y1', Ry - Cy);
            frontLink.setAttribute('x1', Fx - Cx);
            frontLink.setAttribute('y1', Fy - Cy);

            // Rotate Planetary Brackets
            let rAngle = getBracketAngle(Rx);
            let fAngle = getBracketAngle(Fx);
            rearBracket.setAttribute('transform', `rotate(${rAngle})`);
            frontBracket.setAttribute('transform', `rotate(${fAngle})`);

            // Rotate Small Wheels (Rolling effect)
            let wheelSpin = globalTime * 2.5; 
            swRotators.forEach(sw => sw.setAttribute('transform', `rotate(${wheelSpin})`));

            // Rotate Main Chassis
            chassis.setAttribute('transform', `translate(${Cx}, ${Cy}) rotate(${thetaDeg})`);

            // 7. Active IFR Hydraulic Stabilization Logic
            // The platform must be strictly horizontal globally. 
            // Since it's a child of chassis, we apply a counter-rotation `-thetaDeg`.
            platform.setAttribute('transform', `translate(0, ${config.platformCenterY}) rotate(${-thetaDeg})`);

            // Calculate active hydraulic piston extensions
            // Left/Right mounts are at (-120, 0) and (120, 0) relative to platform base.
            let rad = -thetaRad; 
            
            // Left Piston Target (Platform Local to Chassis Local)
            let pLeftX_local = -120 * Math.cos(rad);
            let pLeftY_local = -120 * Math.sin(rad);
            let leftTargetX = pLeftX_local;
            let leftTargetY = config.platformCenterY + pLeftY_local;
            
            hydLeft.setAttribute('x2', leftTargetX);
            hydLeft.setAttribute('y2', leftTargetY);

            // Right Piston Target
            let pRightX_local = 120 * Math.cos(rad);
            let pRightY_local = 120 * Math.sin(rad);
            let rightTargetX = pRightX_local;
            let rightTargetY = config.platformCenterY + pRightY_local;

            hydRight.setAttribute('x2', rightTargetX);
            hydRight.setAttribute('y2', rightTargetY);

            // 8. Update HUD Info
            uiTilt.textContent = Math.abs(thetaDeg).toFixed(2) + '°';
            if (Math.abs(thetaDeg) > 1.5) {
                uiStatus.textContent = "当前状态:[告警] 主动越障平衡介入中...";
                uiStatus.style.color = "var(--neon-cyan)";
            } else {
                uiStatus.textContent = "当前状态:平地巡航模式";
                uiStatus.style.color = "var(--neon-orange)";
            }

            requestAnimationFrame(animate);
        }

        // Handle Resize
        window.addEventListener('resize', () => {
            // Camera automatically adjusts in the animation loop based on innerWidth/innerHeight
        });

        // Start Loop
        requestAnimationFrame(animate);
    </script>
</body>
</html>

非常抱歉之前的版本在结构嵌套上出现了小失误,导致行星轮的坐标轴未能跟随十字支架公转,从而“隐藏”在了中心点。

为了完美展示“彻底解耦”这一 TRIZ 最终理想解(IFR),我重新设计了底盘的动力学嵌套逻辑。现在,行星轮系将清晰可见,你将直观地看到小轮自转与十字支架翻滚跨级的协同过程。同时,我进一步提升了赛博朋克工业风的视觉表现力,增加了“绝对水平激光基准线”和实时的液压杆伸缩动画,以凸显理想解状态。

以下是修复且全面升级后的高保真交互式原理动画代码:

<!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: #03060A;
            --grid-line: rgba(0, 240, 255, 0.08);
            --stair-fill: #0B111A;
            --stair-stroke: #00F0FF;
            --chassis-dark: #121824;
            --chassis-light: #202A3C;
            --neon-cyan: #00F0FF;
            --neon-orange: #FF5500;
            --text-main: #FFFFFF;
            --text-dim: #5C7699;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background: radial-gradient(circle at center, #08101A 0%, var(--bg-color) 100%);
            overflow: hidden;
            font-family: 'Consolas', 'Courier New', monospace;
            color: var(--text-main);
            user-select: none;
        }

        #canvas-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
        }

        /* UI 面板与排版限制:精简、边缘化、不遮挡动画 */
        .hud-overlay {
            position: absolute;
            z-index: 10;
            pointer-events: none;
        }

        .hud-top-left { top: 25px; left: 25px; }
        .hud-bottom-left { bottom: 25px; left: 25px; }
        .hud-top-right { top: 25px; right: 25px; text-align: right; }

        .hud-title {
            font-size: 13px;
            font-weight: bold;
            color: var(--neon-cyan);
            margin-bottom: 6px;
            letter-spacing: 1px;
            text-shadow: 0 0 8px rgba(0, 240, 255, 0.5);
        }

        .hud-text {
            font-size: 11px;
            color: var(--text-dim);
            line-height: 1.5;
            background: rgba(3, 6, 10, 0.6);
            padding: 10px 15px;
            border-left: 2px solid var(--neon-cyan);
            backdrop-filter: blur(4px);
        }

        .hud-highlight { color: var(--text-main); font-weight: bold; }
        .tag-orange { color: var(--neon-orange); }
        .tag-cyan { color: var(--neon-cyan); }

        .hud-value-box {
            background: rgba(0, 240, 255, 0.05);
            border: 1px solid rgba(0, 240, 255, 0.2);
            padding: 8px 15px;
            margin-top: 10px;
            display: inline-block;
        }

        .hud-value {
            font-size: 24px;
            font-weight: bold;
            color: var(--neon-cyan);
            text-shadow: 0 0 10px rgba(0, 240, 255, 0.5);
            font-variant-numeric: tabular-nums;
        }

        .hud-sub {
            font-size: 10px;
            color: var(--neon-orange);
            margin-top: 4px;
        }

        /* 绝对水平基准激光线 */
        .laser-level {
            position: absolute;
            top: 50%;
            left: 0;
            width: 100%;
            height: 1px;
            background: var(--neon-cyan);
            box-shadow: 0 0 10px var(--neon-cyan), 0 0 20px var(--neon-cyan);
            opacity: 0.3;
            z-index: 5;
            pointer-events: none;
            transform: translateY(-90px); /* 对齐货台高度 */
        }
    </style>
</head>
<body>

    <div class="laser-level"></div>

    <!-- UI 层 -->
    <div class="hud-overlay hud-top-left">
        <div class="hud-title">▼ 最终理想解 (IFR) 原理重构</div>
        <div class="hud-text">
            <span>设计原则</span> : <span class="hud-highlight">解耦“底层移动”与“顶层姿态”</span><br>
            <span>被动翻越</span> : <span class="tag-orange">行星轮组 (撞击自适应翻滚)</span><br>
            <span>主动补偿</span> : <span class="tag-cyan">三轴陀螺仪 + 液压云台逆向调平</span><br>
            <span>消除矛盾</span> : 克服越障必需的剧烈姿态变化带来的货损倾覆
        </div>
    </div>

    <div class="hud-overlay hud-top-right">
        <div class="hud-text" style="border-left:none; border-right:2px solid var(--neon-orange);">
            <span style="color:#FF5500;">■</span> 翻滚越障构件 (动态)<br>
            <span style="color:#00F0FF;">■</span> 液压自平衡云台 (主动)<br>
            <span style="color:#FFFFFF;">■</span> 零位移理想解平台
        </div>
    </div>

    <div class="hud-overlay hud-bottom-left">
        <div class="hud-value-box">
            <div style="font-size: 10px; color: var(--text-dim);">顶层货台绝对倾角</div>
            <div class="hud-value" id="ui-tilt">0.00°</div>
            <div class="hud-sub" id="ui-status">▶ 状态:平地巡航</div>
        </div>
    </div>

    <!-- 核心动画 SVG 层 -->
    <div id="canvas-container">
        <svg id="main-svg" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur" />
                    <feMerge>
                        <feMergeNode in="blur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                <filter id="glow-orange" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur" />
                    <feMerge>
                        <feMergeNode in="blur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                <pattern id="grid" width="80" height="80" patternUnits="userSpaceOnUse">
                    <path d="M 80 0 L 0 0 0 80" fill="none" stroke="var(--grid-line)" stroke-width="1"/>
                    <circle cx="0" cy="0" r="1.5" fill="rgba(0, 240, 255, 0.4)"/>
                </pattern>

                <!-- 重构的行星小轮:高可见度、带齿纹轮毂以便观察自转 -->
                <g id="small-wheel-comp">
                    <!-- 轮胎外圈 -->
                    <circle cx="0" cy="0" r="18" fill="#0A0E14" stroke="#FF5500" stroke-width="2"/>
                    <!-- 自转指示辐条 -->
                    <path d="M 0 -18 L 0 -8 M 0 18 L 0 8 M -18 0 L -8 0 M 18 0 L 8 0" stroke="#FF5500" stroke-width="3" stroke-linecap="round"/>
                    <circle cx="0" cy="0" r="14" fill="none" stroke="#202A3C" stroke-width="2"/>
                    <circle cx="0" cy="0" r="5" fill="#00F0FF" filter="url(#glow-cyan)"/>
                </g>
            </defs>

            <!-- 固定背景网格 -->
            <rect width="100%" height="100%" fill="url(#grid)" />

            <!-- 随镜头移动的场景 -->
            <g id="scene">
                <!-- 阶梯地形 -->
                <path id="terrain" fill="var(--stair-fill)" stroke="var(--stair-stroke)" stroke-width="3" filter="url(#glow-cyan)"/>
                <path id="terrain-fill" fill="var(--stair-fill)" stroke="none"/>

                <!-- 机器人总成 -->
                <g id="robot">
                    
                    <!-- 后轮:行星轮组 -->
                    <g id="rear-assembly">
                        <!-- 行星轮系旋转主轴组 (包含支架与轮子) -->
                        <g id="rear-planetary">
                            <!-- 十字支架 -->
                            <g filter="url(#glow-orange)">
                                <path d="M -65 -10 L 65 -10 L 65 10 L -65 10 Z" fill="#FF5500"/>
                                <path d="M -10 -65 L 10 -65 L 10 65 L -10 65 Z" fill="#FF5500"/>
                                <circle cx="0" cy="0" r="22" fill="#121824" stroke="#FF5500" stroke-width="4"/>
                            </g>
                            <!-- 嵌套的小轮容器,保留位移,内部施加自转 -->
                            <g transform="translate(-65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(0, -65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(0, 65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        </g>
                        <!-- 悬挂连杆 (连接到中心) -->
                        <line x1="0" y1="0" x2="160" y2="-30" stroke="#202A3C" stroke-width="12" stroke-linecap="round"/>
                    </g>

                    <!-- 前轮:行星轮组 -->
                    <g id="front-assembly">
                        <g id="front-planetary">
                            <g filter="url(#glow-orange)">
                                <path d="M -65 -10 L 65 -10 L 65 10 L -65 10 Z" fill="#FF5500"/>
                                <path d="M -10 -65 L 10 -65 L 10 65 L -10 65 Z" fill="#FF5500"/>
                                <circle cx="0" cy="0" r="22" fill="#121824" stroke="#FF5500" stroke-width="4"/>
                            </g>
                            <g transform="translate(-65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(0, -65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(0, 65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        </g>
                        <!-- 悬挂连杆 -->
                        <line x1="0" y1="0" x2="-160" y2="-30" stroke="#202A3C" stroke-width="12" stroke-linecap="round"/>
                    </g>

                    <!-- 主底盘 (随地形倾斜) -->
                    <g id="chassis">
                        <!-- 底盘主体 -->
                        <polygon points="-180,-20 180,-20 200,10 -200,10" fill="#121824" stroke="#4A5A72" stroke-width="2"/>
                        <rect x="-50" y="-30" width="100" height="20" fill="#202A3C"/>

                        <!-- 主动液压自平衡系统 -->
                        <!-- 液压缸底座 -->
                        <rect x="-140" y="-25" width="20" height="15" fill="#202A3C"/>
                        <rect x="120" y="-25" width="20" height="15" fill="#202A3C"/>
                        
                        <!-- 动态伸缩液压杆 -->
                        <line id="hyd-left" x1="-130" y1="-20" x2="-130" y2="-80" stroke="#00F0FF" stroke-width="8" stroke-linecap="round" filter="url(#glow-cyan)"/>
                        <line id="hyd-right" x1="130" y1="-20" x2="130" y2="-80" stroke="#00F0FF" stroke-width="8" stroke-linecap="round" filter="url(#glow-cyan)"/>

                        <!-- 中心铰接基座 -->
                        <path d="M -15 -30 L 15 -30 L 0 -80 Z" fill="#3B4C63"/>
                        <circle cx="0" cy="-80" r="6" fill="#00F0FF" filter="url(#glow-cyan)"/>

                        <!-- 载货云台:逆向补偿保持绝对水平 -->
                        <g id="platform">
                            <!-- 云台板 -->
                            <rect x="-160" y="-8" width="320" height="10" rx="3" fill="#FFFFFF"/>
                            <rect x="-155" y="-3" width="310" height="3" fill="#E0E0E0"/>
                            
                            <!-- 液压杆上部球铰挂载点 -->
                            <circle cx="-130" cy="0" r="5" fill="#00F0FF"/>
                            <circle cx="130" cy="0" r="5" fill="#00F0FF"/>
                            <circle cx="0" cy="0" r="7" fill="#FF5500"/>

                            <!-- 精密货物箱 -->
                            <g transform="translate(0, -60)">
                                <!-- 陀螺仪外壳 -->
                                <rect x="-100" y="-50" width="200" height="100" rx="4" fill="rgba(0, 240, 255, 0.05)" stroke="#00F0FF" stroke-width="2" filter="url(#glow-cyan)"/>
                                <path d="M -80 -30 L -40 -30 M 80 -30 L 40 -30" stroke="#00F0FF" stroke-width="2"/>
                                <!-- 内部稳定悬浮球 -->
                                <circle cx="0" cy="0" r="25" fill="none" stroke="#00F0FF" stroke-width="2" stroke-dasharray="10 5"/>
                                <circle cx="0" cy="0" r="15" fill="none" stroke="#00F0FF" stroke-width="1"/>
                                <circle cx="0" cy="0" r="6" fill="#00F0FF" filter="url(#glow-cyan)"/>
                                <text x="0" y="-12" text-anchor="middle" fill="#00F0FF" font-size="10" font-weight="bold" letter-spacing="1">ZERO-TILT</text>
                            </g>
                        </g>
                    </g>
                </g>
            </g>
        </svg>
    </div>

    <script>
        // --- 核心动力学与尺寸配置 ---
        const config = {
            W: 320,               // 轴距 (前后行星轮中心距离)
            R: 65,                // 十字支架旋转半径
            r: 18,                // 行星小轮半径
            stairH: 150,          // 台阶高度
            stairW: 800,          // 台阶跨度 (平地长度)
            speed: 3.5,           // 基础行进速度
            pivotY: -80           // 底盘到云台中心铰接点的高度
        };

        // 离地间隙:十字臂处于 '+' 姿态时,主轴距离地面的高度
        const groundClearance = config.R + config.r; // 83px
        // 定义台阶垂直面的 X 坐标 (局部坐标系)
        const cornerX = 400;
        // 行星轮在碰到台阶立面时开始“翻滚越级”的起始点
        const startClimbX = cornerX - groundClearance; 
        // 翻滚动作水平推进距离 (约等于旋转跨越的过程)
        const climbDist = 160; 

        // DOM 元素引用
        const scene = document.getElementById('scene');
        const terrain = document.getElementById('terrain');
        const terrainFill = document.getElementById('terrain-fill');
        const chassis = document.getElementById('chassis');
        const platform = document.getElementById('platform');
        const rearAssembly = document.getElementById('rear-assembly');
        const frontAssembly = document.getElementById('front-assembly');
        const rearPlanetary = document.getElementById('rear-planetary');
        const frontPlanetary = document.getElementById('front-planetary');
        const hydLeft = document.getElementById('hyd-left');
        const hydRight = document.getElementById('hyd-right');
        const uiTilt = document.getElementById('ui-tilt');
        const uiStatus = document.getElementById('ui-status');
        const swRotators = document.querySelectorAll('.sw-rotator');

        let globalTime = 0;

        /**
         * 基于全局 X 坐标计算当前主轴的绝对 Y 坐标 (台阶跨越平滑函数)
         */
        function getAxleY(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW; 
            let stepCount = Math.floor(x / config.stairW);
            // 基础地面高度随阶梯递减
            let baseY = stepCount * -config.stairH - groundClearance;
            
            if (localX <= startClimbX) {
                return baseY; 
            } else if (localX > startClimbX && localX <= startClimbX + climbDist) {
                // 利用 smoothstep 贝塞尔缓动模拟行星轮绕台阶顶点的起伏轨迹
                let t = (localX - startClimbX) / climbDist;
                let smoothT = t * t * (3 - 2 * t);
                return baseY - config.stairH * smoothT;
            } else {
                return baseY - config.stairH;
            }
        }

        /**
         * 计算行星十字支架的被动翻转角度
         */
        function getBracketAngle(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW;
            let stepCount = Math.floor(x / config.stairW);
            let baseAngle = stepCount * 90;
            
            if (localX <= startClimbX) {
                return baseAngle;
            } else if (localX > startClimbX && localX <= startClimbX + climbDist) {
                // 同步匹配翻越高度进行 90 度支架翻滚
                let t = (localX - startClimbX) / climbDist;
                let smoothT = t * t * (3 - 2 * t);
                return baseAngle + 90 * smoothT;
            } else {
                return baseAngle + 90;
            }
        }

        /**
         * 动态生成楼梯 SVG 路径
         */
        function updateTerrainPath(cx) {
            let startX = Math.floor((cx - 1500) / config.stairW) * config.stairW;
            let endX = cx + 2000;
            
            let pathD = `M ${startX}, 2000 `;
            for (let x = startX; x < endX; x += config.stairW) {
                let yTop = Math.floor(x / config.stairW) * -config.stairH;
                let yBot = yTop - config.stairH;
                let stepX = x + cornerX;
                pathD += `L ${x}, ${yTop} L ${stepX}, ${yTop} L ${stepX}, ${yBot} L ${x + config.stairW}, ${yBot} `;
            }
            pathD += `L ${endX}, 2000 Z`;
            
            terrain.setAttribute('d', pathD);
            terrainFill.setAttribute('d', pathD);
        }

        function animate() {
            globalTime += config.speed;
            
            // 1. 后轴驱动计算
            let Rx = globalTime;
            let Ry = getAxleY(Rx);

            // 2. 基于刚性底盘连杆迭代约束前轴位置 (保证轴距恒定)
            let Fx = Rx + config.W;
            for(let i = 0; i < 5; i++) {
                let tempFy = getAxleY(Fx);
                let currentDist = Math.hypot(Fx - Rx, tempFy - Ry);
                let error = config.W - currentDist;
                Fx += error * ((Fx - Rx) / currentDist) * 0.8;
            }
            let Fy = getAxleY(Fx);

            // 3. 计算底盘中心点与车体俯仰角 (剧烈变化的数据)
            let Cx = (Rx + Fx) / 2;
            let Cy = (Ry + Fy) / 2;
            let thetaRad = Math.atan2(Fy - Ry, Fx - Rx);
            let thetaDeg = thetaRad * 180 / Math.PI;

            // 4. 镜头跟踪:确保车体始终居于视口中央
            const screenCx = window.innerWidth / 2;
            const screenCy = window.innerHeight / 2 + 30;
            scene.setAttribute('transform', `translate(${screenCx - Cx}, ${screenCy - Cy})`);

            // 5. 渲染地形背景
            updateTerrainPath(Cx);

            // 6. 应用底盘部件绝对位移与旋转
            rearAssembly.setAttribute('transform', `translate(${Rx - Cx}, ${Ry - Cy})`);
            frontAssembly.setAttribute('transform', `translate(${Fx - Cx}, ${Fy - Cy})`);
            
            // [关键修复]:行星支架执行公转翻滚
            let rAngle = getBracketAngle(Rx);
            let fAngle = getBracketAngle(Fx);
            rearPlanetary.setAttribute('transform', `rotate(${rAngle})`);
            frontPlanetary.setAttribute('transform', `rotate(${fAngle})`);

            // [关键修复]:小轮内部继续自转,不覆盖原本的端点平移坐标
            let wheelSpin = globalTime * 3; 
            swRotators.forEach(sw => {
                // 因为 sw 仅仅是一个使用 <use> 调用的子容器,可以直接对其覆盖自转
                sw.setAttribute('transform', `rotate(${wheelSpin})`);
            });

            // 旋转主底盘受力
            chassis.setAttribute('transform', `translate(${Cx}, ${Cy}) rotate(${thetaDeg})`);

            // 7. IFR核心解:液压自平衡云台的绝对逆向补偿
            // 货物平台始终保持与大地平行,抵消底盘的所有 theta 倾角
            platform.setAttribute('transform', `translate(0, ${config.pivotY}) rotate(${-thetaDeg})`);

            // 8. 精确实时的液压缸伸缩测算
            let rad = -thetaRad; 
            let mountDist = 130;
            // 平台左挂载点相对于底座中心旋转后的绝对坐标映射
            let pLeftX = -mountDist * Math.cos(rad);
            let pLeftY = config.pivotY - mountDist * Math.sin(rad);
            hydLeft.setAttribute('x2', pLeftX);
            hydLeft.setAttribute('y2', pLeftY);

            // 平台右挂载点映射
            let pRightX = mountDist * Math.cos(rad);
            let pRightY = config.pivotY + mountDist * Math.sin(rad);
            hydRight.setAttribute('x2', pRightX);
            hydRight.setAttribute('y2', pRightY);

            // 9. UI 状态监控刷新
            uiTilt.textContent = Math.abs(thetaDeg).toFixed(2) + '°';
            if (Math.abs(thetaDeg) > 1.0) {
                uiStatus.textContent = "▶ 状态:[警报] 姿态激变!液压极速补偿中...";
                uiStatus.style.color = "var(--neon-orange)";
                document.querySelector('.laser-level').style.boxShadow = "0 0 15px #FF5500, 0 0 30px #FF5500";
                document.querySelector('.laser-level').style.background = "#FF5500";
            } else {
                uiStatus.textContent = "▶ 状态:平地巡航 / 姿态归零";
                uiStatus.style.color = "var(--neon-cyan)";
                document.querySelector('.laser-level').style.boxShadow = "0 0 10px var(--neon-cyan), 0 0 20px var(--neon-cyan)";
                document.querySelector('.laser-level').style.background = "var(--neon-cyan)";
            }

            requestAnimationFrame(animate);
        }

        // 窗口尺寸自适应
        window.addEventListener('resize', () => {
            // 镜头计算依赖于 innerWidth,会在下一帧自动居中
        });

        // 启动动画循环
        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: #050914;
            --grid-color: rgba(0, 240, 255, 0.05);
            --stair-color: #0A111C;
            --stair-edge: #00F0FF;
            --chassis: #1F2836;
            --neon-cyan: #00F0FF;
            --neon-orange: #FF4400;
            --text-main: #FFFFFF;
            --text-dim: #7A8EAA;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background-color: var(--bg-color);
            background-image: radial-gradient(circle at 50% 50%, #0A111C 0%, #030508 100%);
            overflow: hidden;
            font-family: 'Consolas', 'Courier New', monospace;
            color: var(--text-main);
            user-select: none;
        }

        #canvas-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
        }

        .hud-overlay {
            position: absolute;
            z-index: 10;
            pointer-events: none;
            text-transform: uppercase;
        }

        .hud-top-left {
            top: 30px;
            left: 30px;
        }

        .hud-bottom-left {
            bottom: 30px;
            left: 30px;
        }

        .hud-bottom-right {
            bottom: 30px;
            right: 30px;
            text-align: right;
        }

        .hud-title {
            font-size: 14px;
            font-weight: bold;
            color: var(--neon-cyan);
            margin-bottom: 8px;
            letter-spacing: 1px;
            text-shadow: 0 0 10px rgba(0, 240, 255, 0.4);
        }

        .hud-text {
            font-size: 11px;
            color: var(--text-dim);
            line-height: 1.6;
        }

        .hud-highlight {
            color: var(--text-main);
            font-weight: bold;
        }

        .hud-value {
            font-size: 28px;
            font-weight: bold;
            color: var(--neon-cyan);
            text-shadow: 0 0 15px rgba(0, 240, 255, 0.6);
            margin-top: 5px;
            font-variant-numeric: tabular-nums;
        }

        .tag-orange { color: var(--neon-orange); }
        .tag-cyan { color: var(--neon-cyan); }

        .scan-line {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 2px;
            background: linear-gradient(to bottom, transparent, rgba(0, 240, 255, 0.4), transparent);
            opacity: 0.3;
            z-index: 20;
            animation: scan 3s linear infinite;
            pointer-events: none;
        }

        @keyframes scan {
            0% { transform: translateY(-10vh); }
            100% { transform: translateY(110vh); }
        }

        @keyframes pulse-ring {
            0% { stroke-width: 1; opacity: 0.8; transform: scale(0.9); }
            50% { stroke-width: 2; opacity: 1; transform: scale(1.1); text-shadow: 0 0 10px var(--neon-cyan); }
            100% { stroke-width: 1; opacity: 0.8; transform: scale(0.9); }
        }

        .gyro-ring {
            transform-origin: center;
            animation: pulse-ring 2s ease-in-out infinite;
        }
    </style>
</head>
<body>

    <div class="scan-line"></div>

    <!-- UI Overlays -->
    <div class="hud-overlay hud-top-left">
        <div class="hud-title">■ 系统 IFR 状态:零姿态偏移</div>
        <div class="hud-text">
            <span>核心原理</span> // <span class="hud-highlight">移动越障与姿态平衡物理绝对解耦</span><br>
            <span>底层构型</span> // <span class="tag-orange">行星式四轮组 (被动翻滚跨级)</span><br>
            <span>顶层构型</span> // <span class="tag-cyan">主动液压自平衡云台 (动态逆向补偿)</span><br>
            <span>响应延迟</span> // &lt; 50ms<br>
            <span>控制目标</span> // 载货平台倾覆力矩始终为零
        </div>
    </div>

    <div class="hud-overlay hud-bottom-left">
        <div class="hud-text" style="color:var(--neon-orange)" id="ui-status">当前状态:平地巡航模式</div>
        <div class="hud-title" style="margin-top: 10px;">三轴陀螺仪姿态角</div>
        <div class="hud-value" id="ui-tilt">0.00°</div>
    </div>

    <div class="hud-overlay hud-bottom-right">
        <div class="hud-text">
            ■ 视觉引导图例<br>
            <span style="color:#FF4400;">██</span> 行星受力翻转臂与随动轮<br>
            <span style="color:#00F0FF;">██</span> 主动补偿液压缸<br>
            <span style="color:#FFFFFF;">██</span> 零姿态载货平台
        </div>
    </div>

    <!-- SVG Canvas -->
    <div id="canvas-container">
        <svg id="main-svg" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <filter id="neon-glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur1" />
                    <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur2" />
                    <feMerge>
                        <feMergeNode in="blur2" />
                        <feMergeNode in="blur1" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                
                <filter id="neon-glow-orange" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="6" result="blur1" />
                    <feMerge>
                        <feMergeNode in="blur1" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>

                <pattern id="grid-pattern" width="80" height="80" patternUnits="userSpaceOnUse">
                    <path d="M 80 0 L 0 0 0 80" fill="none" stroke="rgba(0, 240, 255, 0.05)" stroke-width="1"/>
                    <circle cx="0" cy="0" r="1.5" fill="rgba(0, 240, 255, 0.2)"/>
                </pattern>

                <!-- Small Planetary Wheel Component -->
                <g id="small-wheel-comp">
                    <!-- Outer Tire -->
                    <circle cx="0" cy="0" r="20" fill="#0A111C" stroke="#FF4400" stroke-width="3"/>
                    <!-- Inner mechanical rim -->
                    <circle cx="0" cy="0" r="14" fill="none" stroke="#FF4400" stroke-width="1.5" stroke-dasharray="4 4"/>
                    <!-- Hub -->
                    <circle cx="0" cy="0" r="6" fill="#FF4400" filter="url(#neon-glow-orange)"/>
                    <!-- Spokes -->
                    <path d="M 0 -20 L 0 20 M -20 0 L 20 0" stroke="#FF4400" stroke-width="2"/>
                </g>
            </defs>

            <!-- Background Grid (Fixed to camera) -->
            <rect width="100%" height="100%" fill="url(#grid-pattern)" />

            <!-- Translating Scene -->
            <g id="scene">
                <!-- Dynamic Terrain Path -->
                <path id="terrain" fill="var(--stair-color)" stroke="var(--stair-edge)" stroke-width="3" filter="url(#neon-glow-cyan)"/>
                <path id="terrain-fill" fill="var(--stair-color)" stroke="none"/>

                <!-- Robot Assembly -->
                <g id="robot">
                    <!-- Rear Planetary Wheel Assembly -->
                    <g id="rear-assembly">
                        <!-- Group that rotates together: bracket + 4 wheels -->
                        <g id="rear-planetary-system">
                            <g id="rear-bracket" filter="url(#neon-glow-orange)">
                                <path d="M -65 -14 L 65 -14 L 65 14 L -65 14 Z" fill="#FF4400"/>
                                <path d="M -14 -65 L 14 -65 L 14 65 L -14 65 Z" fill="#FF4400"/>
                                <circle cx="0" cy="0" r="22" fill="#121A28" stroke="#FF4400" stroke-width="4"/>
                            </g>
                            <!-- 4 Small Wheels positioned at the ends of the bracket -->
                            <g transform="translate(-65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(0, -65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(0, 65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        </g>
                    </g>

                    <!-- Front Planetary Wheel Assembly -->
                    <g id="front-assembly">
                        <!-- Group that rotates together: bracket + 4 wheels -->
                        <g id="front-planetary-system">
                            <g id="front-bracket" filter="url(#neon-glow-orange)">
                                <path d="M -65 -14 L 65 -14 L 65 14 L -65 14 Z" fill="#FF4400"/>
                                <path d="M -14 -65 L 14 -65 L 14 65 L -14 65 Z" fill="#FF4400"/>
                                <circle cx="0" cy="0" r="22" fill="#121A28" stroke="#FF4400" stroke-width="4"/>
                            </g>
                            <!-- 4 Small Wheels positioned at the ends of the bracket -->
                            <g transform="translate(-65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(65, 0)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(0, -65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                            <g transform="translate(0, 65)"><use href="#small-wheel-comp" class="sw-rotator"/></g>
                        </g>
                    </g>

                    <!-- Main Chassis -->
                    <g id="chassis">
                        <!-- Structural links between axles and chassis center (Visual only) -->
                        <line id="rear-link" x1="-160" y1="0" x2="0" y2="0" stroke="#334455" stroke-width="16" stroke-linecap="round"/>
                        <line id="front-link" x1="160" y1="0" x2="0" y2="0" stroke="#334455" stroke-width="16" stroke-linecap="round"/>

                        <!-- Chassis Body -->
                        <polygon points="-180,-30 180,-30 220,15 -220,15" fill="#1A222E" stroke="#4A5A72" stroke-width="3"/>
                        <path d="M -110 -20 L 110 -20" stroke="#FF4400" stroke-width="2" stroke-dasharray="10 6" opacity="0.8"/>

                        <!-- Hydraulic Cylinder Bases (Fixed to Chassis) -->
                        <path d="M -130 -30 L -110 -30 L -110 -70 L -130 -70 Z" fill="#2A3648" stroke="#4A5A72" stroke-width="2"/>
                        <circle cx="-120" cy="-25" r="7" fill="#00F0FF"/>
                        <path d="M 110 -30 L 130 -30 L 130 -70 L 110 -70 Z" fill="#2A3648" stroke="#4A5A72" stroke-width="2"/>
                        <circle cx="120" cy="-25" r="7" fill="#00F0FF"/>

                        <!-- Hydraulic Pistons (Dynamic) -->
                        <line id="hyd-left" x1="-120" y1="-70" x2="-120" y2="-120" stroke="#00F0FF" stroke-width="8" stroke-linecap="round" filter="url(#neon-glow-cyan)"/>
                        <line id="hyd-right" x1="120" y1="-70" x2="120" y2="-120" stroke="#00F0FF" stroke-width="8" stroke-linecap="round" filter="url(#neon-glow-cyan)"/>

                        <!-- Central Pivot Base -->
                        <path d="M -25 -30 L 25 -30 L 12 -80 L -12 -80 Z" fill="#2A3648"/>
                        <circle cx="0" cy="-80" r="10" fill="#00F0FF" filter="url(#neon-glow-cyan)"/>

                        <!-- Cargo Platform (Absolute Horizontal) -->
                        <g id="platform">
                            <!-- Platform Base -->
                            <rect x="-170" y="-12" width="340" height="14" rx="4" fill="#FFFFFF"/>
                            <rect x="-160" y="-4" width="320" height="4" fill="#E0E0E0"/>
                            
                            <!-- Mount points on platform -->
                            <circle cx="-120" cy="0" r="6" fill="#FF4400"/>
                            <circle cx="120" cy="0" r="6" fill="#FF4400"/>
                            <circle cx="0" cy="0" r="8" fill="#00F0FF"/>

                            <!-- Cargo Container -->
                            <g transform="translate(0, -80)">
                                <rect x="-110" y="-60" width="220" height="128" rx="8" fill="rgba(0, 240, 255, 0.05)" stroke="#00F0FF" stroke-width="2" filter="url(#neon-glow-cyan)"/>
                                <path d="M -90 -45 L -40 -45 M 90 -45 L 40 -45" stroke="#00F0FF" stroke-width="3"/>
                                
                                <!-- Gyroscope IFR Indicator -->
                                <g class="gyro-ring">
                                    <circle cx="0" cy="5" r="30" fill="none" stroke="#00F0FF" stroke-width="2" stroke-dasharray="15 8"/>
                                    <circle cx="0" cy="5" r="18" fill="none" stroke="#00F0FF" stroke-width="1.5"/>
                                    <circle cx="0" cy="5" r="5" fill="#00F0FF" filter="url(#neon-glow-cyan)"/>
                                </g>
                                <text x="0" y="-20" text-anchor="middle" fill="#00F0FF" font-size="14" font-weight="bold" letter-spacing="3">ZERO-TILT CARGO</text>
                            </g>
                        </g>
                    </g>
                </g>
            </g>
        </svg>
    </div>

    <script>
        // --- System Kinematics Configuration ---
        const config = {
            W: 320,               // Wheelbase (Distance between front and rear planetary centers)
            R: 65,                // Planetary bracket radius
            r: 20,                // Small wheel radius
            stairH: 140,          // Step height
            stairW: 900,          // Distance between steps
            speed: 3.5,           // Animation base speed (px per frame)
            platformCenterY: -130 // Platform absolute center Y offset relative to chassis axle line
        };

        const groundClearance = config.R + config.r; // 85px
        // Climb calculations based on real collision geometry
        const cornerX = 400;
        const startClimbX = cornerX - config.R - 10; // Trigger slightly before wheel hits
        const climbDist = config.R * 1.6; // Extended smooth climb duration

        // Element references
        const scene = document.getElementById('scene');
        const terrain = document.getElementById('terrain');
        const terrainFill = document.getElementById('terrain-fill');
        const chassis = document.getElementById('chassis');
        const platform = document.getElementById('platform');
        
        const rearAssembly = document.getElementById('rear-assembly');
        const frontAssembly = document.getElementById('front-assembly');
        const rearPlanetarySystem = document.getElementById('rear-planetary-system');
        const frontPlanetarySystem = document.getElementById('front-planetary-system');
        
        const rearLink = document.getElementById('rear-link');
        const frontLink = document.getElementById('front-link');
        const hydLeft = document.getElementById('hyd-left');
        const hydRight = document.getElementById('hyd-right');
        const uiTilt = document.getElementById('ui-tilt');
        const uiStatus = document.getElementById('ui-status');
        const swRotators = document.querySelectorAll('.sw-rotator');

        let globalTime = 0;

        /**
         * Calculates the absolute Y position of a planetary axle given its absolute X.
         */
        function getAxleY(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW; 
            let stepCount = Math.floor(x / config.stairW);
            let baseY = stepCount * -config.stairH;
            let localY = 0;
            
            if (localX > startClimbX && localX <= startClimbX + climbDist) {
                // Smooth sine interpolation for realistic climb arc
                let t = (localX - startClimbX) / climbDist;
                localY = -config.stairH * (0.5 - 0.5 * Math.cos(t * Math.PI));
            } else if (localX > startClimbX + climbDist) {
                localY = -config.stairH;
            }
            
            return baseY + localY;
        }

        /**
         * Calculates the rotation angle of the entire planetary system (bracket + wheels).
         */
        function getBracketAngle(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW;
            let stepCount = Math.floor(x / config.stairW);
            let baseAngle = stepCount * 90;
            let climbAngle = 0;

            if (localX > startClimbX && localX <= startClimbX + climbDist) {
                 let t = (localX - startClimbX) / climbDist;
                 // Eased rotation syncing with the vertical climb
                 climbAngle = 90 * (0.5 - 0.5 * Math.cos(t * Math.PI));
            } else if (localX > startClimbX + climbDist) {
                 climbAngle = 90;
            }
            return baseAngle + climbAngle;
        }

        /**
         * Generates the SVG path for rigid square stairs.
         */
        function updateTerrainPath(cx) {
            let startX = Math.floor((cx - 1500) / config.stairW) * config.stairW;
            let endX = cx + 2000;
            
            let pathD = `M ${startX}, 3000 `;
            for (let x = startX; x < endX; x += config.stairW) {
                let yTop = Math.floor(x / config.stairW) * -config.stairH + groundClearance;
                let yBot = yTop - config.stairH;
                pathD += `L ${x}, ${yTop} L ${x + 400}, ${yTop} L ${x + 400}, ${yBot} L ${x + config.stairW}, ${yBot} `;
            }
            pathD += `L ${endX}, 3000 Z`;
            
            terrain.setAttribute('d', pathD);
            terrainFill.setAttribute('d', pathD);
        }

        function animate() {
            globalTime += config.speed;
            
            // 1. Calculate Rear Axle Position
            let Rx = globalTime;
            let Ry = getAxleY(Rx);

            // 2. Iteratively solve for Front Axle to maintain constant rigid wheelbase W
            let Fx = Rx + config.W;
            for(let i = 0; i < 6; i++) {
                let tempFy = getAxleY(Fx);
                let currentDist = Math.hypot(Fx - Rx, tempFy - Ry);
                let error = config.W - currentDist;
                Fx += error * 0.9;
            }
            let Fy = getAxleY(Fx);

            // 3. Chassis Kinematics (Center & Pitch Angle)
            let Cx = (Rx + Fx) / 2;
            let Cy = (Ry + Fy) / 2;
            let thetaRad = Math.atan2(Fy - Ry, Fx - Rx);
            let thetaDeg = thetaRad * 180 / Math.PI;

            // 4. Update Camera / Scene Translation (Keep Robot Centered)
            const screenCx = window.innerWidth / 2;
            const screenCy = window.innerHeight / 2 + 50; 
            scene.setAttribute('transform', `translate(${screenCx - Cx}, ${screenCy - Cy})`);

            // 5. Render Terrain
            updateTerrainPath(Cx);

            // 6. Update Robot Elements Positioning
            rearAssembly.setAttribute('transform', `translate(${Rx - Cx}, ${Ry - Cy})`);
            frontAssembly.setAttribute('transform', `translate(${Fx - Cx}, ${Fy - Cy})`);
            
            rearLink.setAttribute('x1', Rx - Cx);
            rearLink.setAttribute('y1', Ry - Cy);
            frontLink.setAttribute('x1', Fx - Cx);
            frontLink.setAttribute('y1', Fy - Cy);

            // [Crucial Fix] Rotate entire planetary systems (bracket + all 4 wheels)
            let rAngle = getBracketAngle(Rx);
            let fAngle = getBracketAngle(Fx);
            rearPlanetarySystem.setAttribute('transform', `rotate(${rAngle})`);
            frontPlanetarySystem.setAttribute('transform', `rotate(${fAngle})`);

            // Rotate Small Wheels individually around their own centers (Rolling effect)
            let wheelSpin = globalTime * 3; 
            swRotators.forEach(sw => sw.setAttribute('transform', `rotate(${wheelSpin})`));

            // Rotate Main Chassis body
            chassis.setAttribute('transform', `translate(${Cx}, ${Cy}) rotate(${thetaDeg})`);

            // 7. Active IFR Hydraulic Stabilization Logic
            platform.setAttribute('transform', `translate(0, ${config.platformCenterY}) rotate(${-thetaDeg})`);

            let rad = -thetaRad; 
            
            // Left Piston Vector Solving
            let pLeftX_local = -120 * Math.cos(rad);
            let pLeftY_local = -120 * Math.sin(rad);
            hydLeft.setAttribute('x2', pLeftX_local);
            hydLeft.setAttribute('y2', config.platformCenterY + pLeftY_local);

            // Right Piston Vector Solving
            let pRightX_local = 120 * Math.cos(rad);
            let pRightY_local = 120 * Math.sin(rad);
            hydRight.setAttribute('x2', pRightX_local);
            hydRight.setAttribute('y2', config.platformCenterY + pRightY_local);

            // 8. Update HUD Info
            uiTilt.textContent = Math.abs(thetaDeg).toFixed(2) + '°';
            if (Math.abs(thetaDeg) > 1.5) {
                uiStatus.textContent = "当前状态:[告警] 主动越障平衡介入中...";
                uiStatus.style.color = "var(--neon-cyan)";
            } else {
                uiStatus.textContent = "当前状态:平地巡航模式";
                uiStatus.style.color = "var(--neon-orange)";
            }

            requestAnimationFrame(animate);
        }

        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: #030509;
            --grid-color: rgba(0, 240, 255, 0.04);
            --stair-fill: #0A1018;
            --stair-stroke: #00E5FF;
            --chassis-dark: #121822;
            --chassis-light: #1A2433;
            --neon-cyan: #00E5FF;
            --neon-orange: #FF4D00;
            --text-main: #FFFFFF;
            --text-dim: #4B6382;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background: radial-gradient(circle at center, #08121C 0%, var(--bg-color) 100%);
            overflow: hidden;
            font-family: 'Consolas', 'Courier New', monospace;
            color: var(--text-main);
            user-select: none;
        }

        #canvas-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
        }

        /* 极简面板排版,严格限制大小避免遮挡核心动画 */
        .hud-overlay {
            position: absolute;
            z-index: 10;
            pointer-events: none;
            background: rgba(3, 5, 9, 0.65);
            border: 1px solid rgba(0, 229, 255, 0.15);
            backdrop-filter: blur(6px);
            padding: 12px 16px;
        }

        .hud-top-left { top: 20px; left: 20px; border-left: 3px solid var(--neon-cyan); }
        .hud-bottom-right { bottom: 20px; right: 20px; border-right: 3px solid var(--neon-orange); text-align: right; }
        .hud-bottom-left { bottom: 20px; left: 20px; border-left: 3px solid var(--neon-cyan); }

        .hud-title {
            font-size: 13px;
            font-weight: bold;
            color: var(--neon-cyan);
            margin-bottom: 8px;
            letter-spacing: 1.5px;
        }

        .hud-text {
            font-size: 11px;
            color: var(--text-dim);
            line-height: 1.6;
        }

        .hud-highlight { color: var(--text-main); font-weight: bold; }
        .tag-orange { color: var(--neon-orange); font-weight: bold; }
        .tag-cyan { color: var(--neon-cyan); font-weight: bold; }

        .hud-value {
            font-size: 22px;
            font-weight: bold;
            color: var(--neon-cyan);
            text-shadow: 0 0 10px rgba(0, 229, 255, 0.4);
            font-variant-numeric: tabular-nums;
            margin-top: 4px;
        }

        /* 绝对水平物理基准线 */
        .laser-level {
            position: absolute;
            top: 50%;
            left: 0;
            width: 100%;
            height: 1px;
            background: var(--neon-cyan);
            box-shadow: 0 0 8px var(--neon-cyan), 0 0 16px var(--neon-cyan);
            opacity: 0.35;
            z-index: 5;
            pointer-events: none;
            transform: translateY(-130px);
        }

        .laser-glow {
            position: absolute;
            top: 50%;
            left: 0;
            width: 100%;
            height: 30px;
            background: linear-gradient(to bottom, transparent, rgba(0, 229, 255, 0.05), transparent);
            transform: translateY(-145px);
            z-index: 4;
            pointer-events: none;
        }

    </style>
</head>
<body>

    <div class="laser-level" id="laser-line"></div>
    <div class="laser-glow"></div>

    <!-- UI 面板 -->
    <div class="hud-overlay hud-top-left">
        <div class="hud-title">▼ TRIZ最终理想解 / 功能解耦</div>
        <div class="hud-text">
            <span>物理状态</span> : <span class="hud-highlight">移动机构与姿态系统 100% 解耦</span><br>
            <span>底层越障</span> : <span class="tag-orange">行星四轮组 (结构翻转跨级)</span><br>
            <span>顶层姿态</span> : <span class="tag-cyan">主动液压云台 (全时逆向补偿)</span><br>
            <span>核心机制</span> : 吸收底层底盘剧烈翻滚,输出顶层绝对平稳
        </div>
    </div>

    <div class="hud-overlay hud-bottom-right">
        <div class="hud-text">
            <span style="color:#FF4D00;">██</span> 动态受力翻转悬挂<br>
            <span style="color:#00E5FF;">██</span> 主动逆向液压补偿缸<br>
            <span style="color:#FFFFFF;">██</span> 绝对零姿态理想平台
        </div>
    </div>

    <div class="hud-overlay hud-bottom-left">
        <div class="hud-text">底盘俯仰角追踪</div>
        <div class="hud-value" id="ui-tilt">0.00°</div>
        <div class="hud-text" id="ui-status" style="margin-top:4px; color:var(--neon-orange);">[平地巡航状态]</div>
    </div>

    <!-- 核心 SVG 动画区 -->
    <div id="canvas-container">
        <svg id="main-svg" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur" />
                    <feMerge>
                        <feMergeNode in="blur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                <filter id="glow-orange" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur" />
                    <feMerge>
                        <feMergeNode in="blur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                <pattern id="grid" width="100" height="100" patternUnits="userSpaceOnUse">
                    <path d="M 100 0 L 0 0 0 100" fill="none" stroke="var(--grid-color)" stroke-width="1"/>
                    <circle cx="0" cy="0" r="1" fill="rgba(0, 229, 255, 0.3)"/>
                </pattern>

                <!-- 重型行星小轮组件 -->
                <g id="wheel-unit">
                    <!-- 外侧越野胎面 -->
                    <circle cx="0" cy="0" r="22" fill="#0A0F14" stroke="#FF4D00" stroke-width="3"/>
                    <circle cx="0" cy="0" r="16" fill="none" stroke="#FF4D00" stroke-width="1" stroke-dasharray="4 4"/>
                    <!-- 机械辐条 -->
                    <path d="M 0 -22 L 0 22 M -22 0 L 22 0" stroke="#FF4D00" stroke-width="2.5"/>
                    <!-- 轮毂中心 -->
                    <circle cx="0" cy="0" r="6" fill="#00E5FF" filter="url(#glow-cyan)"/>
                </g>
            </defs>

            <!-- 摄像机背景 -->
            <rect width="100%" height="100%" fill="url(#grid)" />

            <!-- 全局场景容器 -->
            <g id="scene">
                <!-- 环境阶梯 -->
                <path id="terrain" fill="var(--stair-fill)" stroke="var(--stair-stroke)" stroke-width="3" filter="url(#glow-cyan)"/>
                <path id="terrain-fill" fill="var(--stair-fill)" stroke="none"/>

                <!-- 机器人总成 (无根节点位移,全靠绝对坐标渲染) -->
                <g id="robot">
                    
                    <!-- 行星轮系:直接挂载在绝对坐标 -->
                    <!-- 后桥 -->
                    <g id="rear-assembly">
                        <g id="rear-planetary-bracket">
                            <!-- 翻转十字支架 -->
                            <g filter="url(#glow-orange)">
                                <path d="M -75 -15 L 75 -15 L 75 15 L -75 15 Z" fill="#FF4D00"/>
                                <path d="M -15 -75 L 15 -75 L 15 75 L -15 75 Z" fill="#FF4D00"/>
                                <circle cx="0" cy="0" r="24" fill="#121822" stroke="#FF4D00" stroke-width="4"/>
                            </g>
                            <!-- 4个独立自转的端点小轮 -->
                            <g transform="translate(-75, 0)"><use href="#wheel-unit" class="wheel-spin"/></g>
                            <g transform="translate(75, 0)"><use href="#wheel-unit" class="wheel-spin"/></g>
                            <g transform="translate(0, -75)"><use href="#wheel-unit" class="wheel-spin"/></g>
                            <g transform="translate(0, 75)"><use href="#wheel-unit" class="wheel-spin"/></g>
                        </g>
                    </g>

                    <!-- 前桥 -->
                    <g id="front-assembly">
                        <g id="front-planetary-bracket">
                            <g filter="url(#glow-orange)">
                                <path d="M -75 -15 L 75 -15 L 75 15 L -75 15 Z" fill="#FF4D00"/>
                                <path d="M -15 -75 L 15 -75 L 15 75 L -15 75 Z" fill="#FF4D00"/>
                                <circle cx="0" cy="0" r="24" fill="#121822" stroke="#FF4D00" stroke-width="4"/>
                            </g>
                            <g transform="translate(-75, 0)"><use href="#wheel-unit" class="wheel-spin"/></g>
                            <g transform="translate(75, 0)"><use href="#wheel-unit" class="wheel-spin"/></g>
                            <g transform="translate(0, -75)"><use href="#wheel-unit" class="wheel-spin"/></g>
                            <g transform="translate(0, 75)"><use href="#wheel-unit" class="wheel-spin"/></g>
                        </g>
                    </g>

                    <!-- 底盘本体 (随形翻滚倾斜) -->
                    <g id="chassis">
                        <!-- 与前后轮轴硬连接的悬挂臂 -->
                        <line x1="-170" y1="0" x2="0" y2="0" stroke="#1A2433" stroke-width="20" stroke-linecap="round"/>
                        <line x1="170" y1="0" x2="0" y2="0" stroke="#1A2433" stroke-width="20" stroke-linecap="round"/>
                        <circle cx="-170" cy="0" r="10" fill="#FF4D00"/>
                        <circle cx="170" cy="0" r="10" fill="#FF4D00"/>

                        <!-- 主车架 -->
                        <polygon points="-190,-35 190,-35 220,15 -220,15" fill="#121822" stroke="#3A4A63" stroke-width="3"/>
                        <!-- 动态指示带 -->
                        <rect x="-100" y="-15" width="200" height="4" fill="#FF4D00" opacity="0.7"/>

                        <!-- 液压自稳定云台底座 -->
                        <path d="M -140 -35 L -110 -35 L -110 -80 L -140 -80 Z" fill="#1A2433"/>
                        <circle cx="-125" cy="-30" r="8" fill="#00E5FF"/>
                        <path d="M 110 -35 L 140 -35 L 140 -80 L 110 -80 Z" fill="#1A2433"/>
                        <circle cx="125" cy="-30" r="8" fill="#00E5FF"/>

                        <!-- 动态液压连杆 -->
                        <line id="hyd-left" x1="-125" y1="-80" x2="-125" y2="-130" stroke="#00E5FF" stroke-width="10" stroke-linecap="round" filter="url(#glow-cyan)"/>
                        <line id="hyd-right" x1="125" y1="-80" x2="125" y2="-130" stroke="#00E5FF" stroke-width="10" stroke-linecap="round" filter="url(#glow-cyan)"/>

                        <!-- 中心主控铰链 -->
                        <path d="M -30 -35 L 30 -35 L 15 -90 L -15 -90 Z" fill="#2A3A52"/>
                        <circle cx="0" cy="-90" r="12" fill="#00E5FF" filter="url(#glow-cyan)"/>

                        <!-- ★ 最终理想解货运云台 (完全抵消底盘倾斜,保持绝对水平) ★ -->
                        <g id="platform">
                            <!-- 水平承载面 -->
                            <rect x="-180" y="-15" width="360" height="15" rx="4" fill="#FFFFFF"/>
                            <rect x="-170" y="-5" width="340" height="5" fill="#E2E8F0"/>
                            
                            <!-- 液压受力铰接点 -->
                            <circle cx="-125" cy="0" r="7" fill="#FF4D00"/>
                            <circle cx="125" cy="0" r="7" fill="#FF4D00"/>

                            <!-- 货舱实体 -->
                            <g transform="translate(0, -85)">
                                <rect x="-120" y="-60" width="240" height="130" rx="8" fill="rgba(0, 229, 255, 0.08)" stroke="#00E5FF" stroke-width="2" filter="url(#glow-cyan)"/>
                                <path d="M -100 -40 L -50 -40 M 100 -40 L 50 -40" stroke="#00E5FF" stroke-width="3"/>
                                
                                <!-- IFR 陀螺仪视觉反馈 -->
                                <circle cx="0" cy="5" r="35" fill="none" stroke="#00E5FF" stroke-width="2" stroke-dasharray="10 6"/>
                                <circle cx="0" cy="5" r="22" fill="none" stroke="#00E5FF" stroke-width="1"/>
                                <circle cx="0" cy="5" r="6" fill="#00E5FF" filter="url(#glow-cyan)"/>
                                <text x="0" y="-20" text-anchor="middle" fill="#00E5FF" font-size="14" font-weight="bold" letter-spacing="2">ZERO-TILT</text>
                            </g>
                        </g>
                    </g>
                </g>
            </g>
        </svg>
    </div>

    <script>
        // === 核心工程参数配置 ===
        const config = {
            W: 340,               // 轴距 (前后十字支架中心点的刚性间距)
            R: 75,                // 行星轮十字支架回转半径
            r: 22,                // 行星小轮自身半径
            stairH: 140,          // 单级台阶高度
            stairW: 1000,         // 单级台阶平台深度
            speed: 3.5,           // 动画演进速度
            climbDist: 150,       // 完成一次翻越动作跨越的水平距离
            pivotY: -130          // 载货云台挂载铰链在底盘坐标系中的绝对高度
        };

        const groundClearance = config.R + config.r; // 十字臂正置时,中心轴离地高度 97px
        const cornerX = 400;                         // 台阶立面物理位置
        // 运动学起跳触发点:轮胎接触到立面前一点开始主动翻转
        const startClimbX = cornerX - config.R - 10; 

        // 绑定 DOM 元素
        const scene = document.getElementById('scene');
        const terrain = document.getElementById('terrain');
        const terrainFill = document.getElementById('terrain-fill');
        
        const chassis = document.getElementById('chassis');
        const platform = document.getElementById('platform');
        const rearAssembly = document.getElementById('rear-assembly');
        const frontAssembly = document.getElementById('front-assembly');
        
        const rearBracket = document.getElementById('rear-planetary-bracket');
        const frontBracket = document.getElementById('front-planetary-bracket');
        
        const hydLeft = document.getElementById('hyd-left');
        const hydRight = document.getElementById('hyd-right');
        const wheels = document.querySelectorAll('.wheel-spin');
        
        const uiTilt = document.getElementById('ui-tilt');
        const uiStatus = document.getElementById('ui-status');
        const laserLine = document.getElementById('laser-line');

        let globalTime = 0;

        /**
         * 几何函数:根据水平坐标计算轮轴在垂直方向应处的绝对高度
         * 物理机理:模拟轮组滚过平地后,撞击台阶边缘被迫抬升的弧线
         */
        function getAxleY(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW; 
            let stepCount = Math.floor(x / config.stairW);
            let baseY = stepCount * -config.stairH; // 楼梯整体逐步抬高 (SVG y向负移动)
            let climbY = 0;
            
            if (localX > startClimbX && localX <= startClimbX + config.climbDist) {
                // 使用 Cosine 缓动实现完美的半周期抬升模拟翻越弧线
                let t = (localX - startClimbX) / config.climbDist;
                climbY = -config.stairH * (0.5 - 0.5 * Math.cos(t * Math.PI));
            } else if (localX > startClimbX + config.climbDist) {
                climbY = -config.stairH;
            }
            
            return baseY + climbY;
        }

        /**
         * 几何函数:计算十字支架强制翻转的角度
         * 物理机理:底部小轮卡死,电机驱动整个十字臂旋转90度完成翻越
         */
        function getBracketAngle(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW;
            let stepCount = Math.floor(x / config.stairW);
            let baseAngle = stepCount * 90;
            let climbAngle = 0;

            if (localX > startClimbX && localX <= startClimbX + config.climbDist) {
                 let t = (localX - startClimbX) / config.climbDist;
                 // 翻滚动作与抬升弧线绝对同步
                 climbAngle = 90 * (0.5 - 0.5 * Math.cos(t * Math.PI));
            } else if (localX > startClimbX + config.climbDist) {
                 climbAngle = 90;
            }
            return baseAngle + climbAngle;
        }

        /**
         * 渲染刚性阶梯地形
         */
        function updateTerrainPath(cx) {
            let startX = Math.floor((cx - 1500) / config.stairW) * config.stairW;
            let endX = cx + 2500;
            
            let pathD = `M ${startX}, 3000 `;
            for (let x = startX; x < endX; x += config.stairW) {
                let stepCount = Math.floor(x / config.stairW);
                // 确保地形与轮轴的离地高度计算匹配
                let groundY = stepCount * -config.stairH + groundClearance;
                let nextGroundY = groundY - config.stairH;
                let stepCorner = x + cornerX;
                
                pathD += `L ${x}, ${groundY} L ${stepCorner}, ${groundY} L ${stepCorner}, ${nextGroundY} L ${x + config.stairW}, ${nextGroundY} `;
            }
            pathD += `L ${endX}, 3000 Z`;
            
            terrain.setAttribute('d', pathD);
            terrainFill.setAttribute('d', pathD);
        }

        function animate() {
            globalTime += config.speed;
            
            // 1. 求解后轴驱动点绝对坐标
            let Rx = globalTime;
            let Ry = getAxleY(Rx);

            // 2. 迭代求解前轴位置:确保在跌宕起伏中【轴距 W 永远保持绝对刚性】
            let Fx = Rx + config.W;
            for(let i = 0; i < 8; i++) {
                let tempFy = getAxleY(Fx);
                let currentDist = Math.hypot(Fx - Rx, tempFy - Ry);
                let error = config.W - currentDist;
                Fx += error * 0.9; 
            }
            let Fy = getAxleY(Fx);

            // 3. 计算底盘中心锚点及整体俯仰角
            let Cx = (Rx + Fx) / 2;
            let Cy = (Ry + Fy) / 2;
            let thetaRad = Math.atan2(Fy - Ry, Fx - Rx);
            let thetaDeg = thetaRad * 180 / Math.PI;

            // 4. 运镜追踪:保持车辆在屏幕中心
            const screenCx = window.innerWidth / 2;
            const screenCy = window.innerHeight / 2 + 80;
            scene.setAttribute('transform', `translate(${screenCx - Cx}, ${screenCy - Cy})`);

            // 5. 渲染当前视野段地形
            updateTerrainPath(Cx);

            // 6. 应用核心部件位移,彻底解决视觉分离问题
            // 【关键】:将支架直接贴靠在场景绝对坐标,其几何距离刚好匹配底盘连杆长度
            rearAssembly.setAttribute('transform', `translate(${Rx}, ${Ry})`);
            frontAssembly.setAttribute('transform', `translate(${Fx}, ${Fy})`);

            // 7. 同步触发十字支架翻转跳跃动作
            let rAngle = getBracketAngle(Rx);
            let fAngle = getBracketAngle(Fx);
            rearBracket.setAttribute('transform', `rotate(${rAngle})`);
            frontBracket.setAttribute('transform', `rotate(${fAngle})`);

            // 端点小轮自由持续滚转
            let wheelSpin = globalTime * 3;
            wheels.forEach(w => w.setAttribute('transform', `rotate(${wheelSpin})`));

            // 8. 倾斜底盘车架主体
            chassis.setAttribute('transform', `translate(${Cx}, ${Cy}) rotate(${thetaDeg})`);

            // 9. TRIZ 顶层主动补偿计算 (液压逆向动作)
            // 货物平台通过与底盘的逆向等大反转 (-thetaDeg),抵消全部倾覆力矩
            platform.setAttribute('transform', `translate(0, ${config.pivotY}) rotate(${-thetaDeg})`);

            // 10. 精确解算液压伸缩杆空间矢量,模拟真实推挽
            let rad = -thetaRad; 
            
            // 左缸连杆在云台局部坐标系的挂点位置是 (-125, 0)
            // 计算经过旋转后映射到底盘坐标系的末端落点
            let pLeftX = -125 * Math.cos(rad);
            let pLeftY = config.pivotY - 125 * Math.sin(rad);
            hydLeft.setAttribute('x2', pLeftX);
            hydLeft.setAttribute('y2', pLeftY);

            // 右缸连杆映射
            let pRightX = 125 * Math.cos(rad);
            let pRightY = config.pivotY + 125 * Math.sin(rad);
            hydRight.setAttribute('x2', pRightX);
            hydRight.setAttribute('y2', pRightY);

            // 11. 更新交互态与告警 UI
            let tiltVal = Math.abs(thetaDeg);
            uiTilt.textContent = tiltVal.toFixed(2) + '°';
            
            if (tiltVal > 1.5) {
                uiStatus.textContent = "▶ 极端姿态:液压极速顶升补偿中...";
                uiStatus.style.color = "var(--neon-cyan)";
                laserLine.style.boxShadow = "0 0 15px var(--neon-cyan), 0 0 30px var(--neon-cyan)";
                laserLine.style.opacity = "0.7";
            } else {
                uiStatus.textContent = "▶ 平地巡航:系统空载待命";
                uiStatus.style.color = "var(--text-dim)";
                laserLine.style.boxShadow = "0 0 5px var(--neon-cyan)";
                laserLine.style.opacity = "0.3";
            }

            requestAnimationFrame(animate);
        }

        // 初始化驱动
        requestAnimationFrame(animate);
    </script>
</body>
</html>

为了彻底解决前两版中行星轮视觉脱节、结构不清晰的问题,本次设计完全按照你的要求进行了重构。

方案核心更新如下:

  1. 构型精准还原:将十字轮替换为经典的三轮行星构型(呈 Y 字形),每隔 120 度分布一个小轮。
  2. 中心刚性固定:行星轮支架的中心绝对锁定在车体底盘的主轴上。底盘采用了直接贯穿前后行星轮中心轴的连杆设计,在动画中你可以清晰地看到无论地势如何起伏,行星轮永远牢牢长在车上,绝不分离
  3. 被动翻滚逻辑:平时在平地上,下方的一个小轮接地自转;遇到台阶时,受阻力矩触发,整个 Y 型支架围绕车轴翻滚 120 度,完成“跨步跃级”。
  4. IFR 绝对解耦:底层由于翻滚产生的剧烈震荡与俯仰倾角,依然由上方的主动液压陀螺仪云台瞬间逆向对冲,维持货物的“零姿态”。

以下是可直接运行的高保真重构代码:

<!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-base: #02050A;
            --bg-glow: #08121D;
            --grid: rgba(0, 240, 255, 0.05);
            --stair-fill: #0F1621;
            --stair-stroke: #00D5FF;
            --mech-dark: #1A2230;
            --mech-light: #2A364A;
            --accent-cyan: #00D5FF;
            --accent-orange: #FF4D00;
            --text-main: #FFFFFF;
            --text-dim: #647B99;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background: radial-gradient(circle at center, var(--bg-glow) 0%, var(--bg-base) 100%);
            overflow: hidden;
            font-family: 'Consolas', 'Courier New', monospace;
            color: var(--text-main);
            user-select: none;
        }

        #canvas-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
        }

        /* 极简信息面板,避免遮挡核心动画 */
        .hud {
            position: absolute;
            z-index: 10;
            pointer-events: none;
        }

        .hud-top-left { top: 30px; left: 30px; }
        .hud-bottom-left { bottom: 30px; left: 30px; }
        .hud-top-right { top: 30px; right: 30px; text-align: right; }

        .hud-title {
            font-size: 14px;
            font-weight: bold;
            color: var(--accent-cyan);
            margin-bottom: 8px;
            letter-spacing: 2px;
            text-shadow: 0 0 10px rgba(0, 213, 255, 0.4);
        }

        .hud-panel {
            font-size: 11px;
            color: var(--text-dim);
            line-height: 1.6;
            background: rgba(2, 5, 10, 0.7);
            padding: 12px 18px;
            border-left: 3px solid var(--accent-cyan);
            backdrop-filter: blur(8px);
        }

        .highlight-c { color: var(--accent-cyan); font-weight: bold; }
        .highlight-o { color: var(--accent-orange); font-weight: bold; }

        .data-box {
            background: rgba(0, 213, 255, 0.05);
            border: 1px solid rgba(0, 213, 255, 0.2);
            padding: 10px 20px;
            margin-top: 10px;
            display: inline-block;
        }

        .data-value {
            font-size: 28px;
            font-weight: bold;
            color: var(--accent-cyan);
            text-shadow: 0 0 12px rgba(0, 213, 255, 0.5);
            font-variant-numeric: tabular-nums;
            margin: 5px 0;
        }

        .data-status {
            font-size: 11px;
            color: var(--accent-orange);
            letter-spacing: 1px;
        }

        /* 水平基准线 */
        .laser-level {
            position: absolute;
            top: 50%;
            left: 0;
            width: 100%;
            height: 1px;
            background: var(--accent-cyan);
            box-shadow: 0 0 8px var(--accent-cyan), 0 0 20px var(--accent-cyan);
            opacity: 0.25;
            z-index: 5;
            transform: translateY(-110px);
        }
    </style>
</head>
<body>

    <div class="laser-level"></div>

    <div class="hud hud-top-left">
        <div class="hud-title">■ 移动与姿态解耦系统 (IFR)</div>
        <div class="hud-panel">
            <span>底层强悍越障</span> // <span class="highlight-o">三轮行星支架 (遇障被动翻滚 120°)</span><br>
            <span>顶层刚性约束</span> // 行星轮中心轴绝对固定于底盘<br>
            <span>姿态逆向对冲</span> // <span class="highlight-c">三轴陀螺仪 + 主动液压自平衡云台</span><br>
            <span>最终理想解</span> // 保留越障能力,消除货损倾角矛盾
        </div>
    </div>

    <div class="hud hud-top-right">
        <div class="hud-panel" style="border-left:none; border-right:3px solid var(--accent-orange);">
            <span style="color:#FF4D00;">■</span> 三星被动翻转臂<br>
            <span style="color:#00D5FF;">■</span> 逆向补偿液压缸<br>
            <span style="color:#FFFFFF;">■</span> 绝对水平货载域
        </div>
    </div>

    <div class="hud hud-bottom-left">
        <div class="data-box">
            <div style="font-size: 11px; color: var(--text-dim);">顶层货台绝对倾角偏差</div>
            <div class="data-value" id="ui-tilt">0.00°</div>
            <div class="data-status" id="ui-status">● 状态监控:平地巡航</div>
        </div>
    </div>

    <div id="canvas-container">
        <svg id="main-svg" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <!-- 霓虹发光滤镜 -->
                <filter id="glow-cyan" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur1" />
                    <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur2" />
                    <feMerge>
                        <feMergeNode in="blur2" />
                        <feMergeNode in="blur1" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                <filter id="glow-orange" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur" />
                    <feMerge>
                        <feMergeNode in="blur" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                <pattern id="grid" width="60" height="60" patternUnits="userSpaceOnUse">
                    <path d="M 60 0 L 0 0 0 60" fill="none" stroke="var(--grid)" stroke-width="1"/>
                    <circle cx="0" cy="0" r="1" fill="rgba(0, 213, 255, 0.3)"/>
                </pattern>

                <!-- 重构的单个小行星轮 -->
                <g id="small-wheel">
                    <circle cx="0" cy="0" r="18" fill="#0A0E14" stroke="#FF4D00" stroke-width="3"/>
                    <!-- 轮毂纹理(用于观察滚动) -->
                    <path d="M 0 -18 L 0 18 M -18 0 L 18 0 M -12.7 -12.7 L 12.7 12.7 M -12.7 12.7 L 12.7 -12.7" stroke="#FF4D00" stroke-width="1.5" opacity="0.6"/>
                    <circle cx="0" cy="0" r="12" fill="none" stroke="#2A364A" stroke-width="2"/>
                    <circle cx="0" cy="0" r="5" fill="#FF4D00" filter="url(#glow-orange)"/>
                </g>

                <!-- 重构的 Y 型(三轮)行星支架系统 -->
                <g id="tri-star-system">
                    <!-- Y 型强固支架 -->
                    <g filter="url(#glow-orange)">
                        <!-- 三条悬臂 (角度: 90, -30, 210) -->
                        <line x1="0" y1="0" x2="0" y2="50" stroke="#FF4D00" stroke-width="18" stroke-linecap="round"/>
                        <line x1="0" y1="0" x2="43.3" y2="-25" stroke="#FF4D00" stroke-width="18" stroke-linecap="round"/>
                        <line x1="0" y1="0" x2="-43.3" y2="-25" stroke="#FF4D00" stroke-width="18" stroke-linecap="round"/>
                        
                        <!-- 中心铰接固定点(锁定在车轴上的核心视觉件) -->
                        <circle cx="0" cy="0" r="18" fill="#1A2230" stroke="#FF4D00" stroke-width="4"/>
                        <circle cx="0" cy="0" r="8" fill="#00D5FF"/>
                    </g>
                    <!-- 三个小行星轮安装点 -->
                    <g transform="translate(0, 50)"><use href="#small-wheel" class="sw-rotator"/></g>
                    <g transform="translate(43.3, -25)"><use href="#small-wheel" class="sw-rotator"/></g>
                    <g transform="translate(-43.3, -25)"><use href="#small-wheel" class="sw-rotator"/></g>
                </g>
            </defs>

            <rect width="100%" height="100%" fill="url(#grid)" />

            <g id="scene">
                <!-- 地形 -->
                <path id="terrain" fill="var(--stair-fill)" stroke="var(--stair-stroke)" stroke-width="3" filter="url(#glow-cyan)"/>
                <path id="terrain-fill" fill="var(--stair-fill)" stroke="none"/>

                <!-- 机器人整体装配 -->
                <g id="robot">
                    
                    <!-- 1. 主底盘梁(绝对刚性连杆,连接前后轮轴心) -->
                    <!-- 将底盘置于底层,确保行星轮装配在其上方,视觉上完全锁定 -->
                    <g id="chassis-beam">
                        <!-- 连接前后轮中心的贯穿梁 -->
                        <line id="main-beam" x1="-150" y1="0" x2="150" y2="0" stroke="#1A2230" stroke-width="32" stroke-linecap="round"/>
                        <line id="main-beam-hl" x1="-140" y1="0" x2="140" y2="0" stroke="#2A364A" stroke-width="12" stroke-linecap="round"/>
                        
                        <!-- 悬挂与液压基座外壳 -->
                        <polygon points="-100,-16 100,-16 120,10 -120,10" fill="#1A2230" stroke="#4A5A72" stroke-width="2"/>
                        
                        <!-- 液压底座 -->
                        <path d="M -95,-16 L -75,-16 L -75,-60 L -95,-60 Z" fill="#2A364A" stroke="#4A5A72" stroke-width="2"/>
                        <circle cx="-85" cy="-20" r="5" fill="#00D5FF"/>
                        <path d="M 75,-16 L 95,-16 L 95,-60 L 75,-60 Z" fill="#2A364A" stroke="#4A5A72" stroke-width="2"/>
                        <circle cx="85" cy="-20" r="5" fill="#00D5FF"/>

                        <!-- 动态伸缩的液压杆 -->
                        <line id="hyd-left" x1="-85" y1="-60" x2="-85" y2="-120" stroke="#00D5FF" stroke-width="8" stroke-linecap="round" filter="url(#glow-cyan)"/>
                        <line id="hyd-right" x1="85" y1="-60" x2="85" y2="-120" stroke="#00D5FF" stroke-width="8" stroke-linecap="round" filter="url(#glow-cyan)"/>

                        <!-- 云台中心铰链支撑塔 -->
                        <path d="M -15,-16 L 15,-16 L 8,-90 L -8,-90 Z" fill="#1A2230"/>
                        <circle cx="0" cy="-90" r="8" fill="#00D5FF" filter="url(#glow-cyan)"/>

                        <!-- 绝对水平载货云台 (平台相对底盘反向旋转保持平衡) -->
                        <g id="platform">
                            <rect x="-140" y="-8" width="280" height="12" rx="4" fill="#FFFFFF"/>
                            <rect x="-135" y="-3" width="270" height="4" fill="#D0D0D0"/>
                            
                            <!-- 左右液压挂载点 -->
                            <circle cx="-85" cy="0" r="6" fill="#00D5FF"/>
                            <circle cx="85" cy="0" r="6" fill="#00D5FF"/>
                            <!-- 中心挂载点 -->
                            <circle cx="0" cy="0" r="8" fill="#FF4D00"/>

                            <!-- 货舱与 IFR 陀螺仪指示器 -->
                            <g transform="translate(0, -60)">
                                <rect x="-90" y="-45" width="180" height="95" rx="6" fill="rgba(0, 213, 255, 0.05)" stroke="#00D5FF" stroke-width="2" filter="url(#glow-cyan)"/>
                                <path d="M -70,-25 L -30,-25 M 70,-25 L 30,-25" stroke="#00D5FF" stroke-width="2"/>
                                
                                <g class="gyro-ring" filter="url(#glow-cyan)">
                                    <circle cx="0" cy="5" r="24" fill="none" stroke="#00D5FF" stroke-width="2" stroke-dasharray="10 5"/>
                                    <circle cx="0" cy="5" r="14" fill="none" stroke="#00D5FF" stroke-width="1.5"/>
                                    <circle cx="0" cy="5" r="5" fill="#00D5FF"/>
                                </g>
                                <text x="0" y="-15" text-anchor="middle" fill="#00D5FF" font-size="12" font-weight="bold" letter-spacing="2">ZERO TILT</text>
                            </g>
                        </g>
                    </g>

                    <!-- 2. 后轮三星总成 (绝对定位) -->
                    <g id="rear-planetary">
                        <use href="#tri-star-system"/>
                    </g>

                    <!-- 3. 前轮三星总成 (绝对定位) -->
                    <g id="front-planetary">
                        <use href="#tri-star-system"/>
                    </g>

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

    <script>
        // --- 核心物理与尺寸参数 ---
        const config = {
            W: 300,               // 底盘轴距 (前后轮中心距)
            R: 50,                // Y型支架旋转半径 (中心到小轮中心的距离)
            r: 18,                // 小轮半径
            stairH: 110,          // 阶梯高度
            stairW: 850,          // 阶梯水平间距
            speed: 3.5,           // 全局行进速度
            pivotY: -90           // 底盘到载货台云台的铰接高度偏移
        };

        // Y型支架正常姿态时,底部小轮垂直向下,离地间隙为 R + r
        const groundClearance = config.R + config.r; // 68px
        
        // 碰撞计算
        const cornerX = 400; // 台阶拐角局部X坐标
        const startClimbX = cornerX - config.R - config.r; // 轮子触及墙壁开始翻滚
        const climbDist = 160; // 跨越所需水平距离的缓动空间

        const scene = document.getElementById('scene');
        const terrain = document.getElementById('terrain');
        const terrainFill = document.getElementById('terrain-fill');
        
        const chassisBeam = document.getElementById('chassis-beam');
        const platform = document.getElementById('platform');
        const rearPlanetary = document.getElementById('rear-planetary');
        const frontPlanetary = document.getElementById('front-planetary');
        
        const hydLeft = document.getElementById('hyd-left');
        const hydRight = document.getElementById('hyd-right');
        const uiTilt = document.getElementById('ui-tilt');
        const uiStatus = document.getElementById('ui-status');
        const swRotators = document.querySelectorAll('.sw-rotator');

        let globalTime = 0;

        /**
         * 缓动函数 - 使跨越过程平滑
         */
        function easeInOut(t) {
            return t * t * (3 - 2 * t);
        }

        /**
         * 计算轮轴(即支架中心)基于全局X的绝对高度Y
         */
        function getAxleY(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW; 
            let stepCount = Math.floor(x / config.stairW);
            let baseY = stepCount * -config.stairH;
            
            if (localX > startClimbX && localX <= startClimbX + climbDist) {
                let t = (localX - startClimbX) / climbDist;
                return baseY - config.stairH * easeInOut(t);
            } else if (localX > startClimbX + climbDist) {
                return baseY - config.stairH;
            }
            return baseY;
        }

        /**
         * 计算三星支架翻滚的绝对角度
         * 每次跨级翻滚 120 度
         */
        function getBracketAngle(x) {
            let localX = ((x % config.stairW) + config.stairW) % config.stairW;
            let stepCount = Math.floor(x / config.stairW);
            let baseAngle = stepCount * 120;

            if (localX > startClimbX && localX <= startClimbX + climbDist) {
                 let t = (localX - startClimbX) / climbDist;
                 return baseAngle + 120 * easeInOut(t);
            } else if (localX > startClimbX + climbDist) {
                 return baseAngle + 120;
            }
            return baseAngle;
        }

        /**
         * 生成楼梯台阶路径
         */
        function updateTerrainPath(cx) {
            let startX = Math.floor((cx - 1500) / config.stairW) * config.stairW;
            let endX = cx + 2000;
            
            let pathD = `M ${startX}, 2000 `;
            for (let x = startX; x < endX; x += config.stairW) {
                let yTop = Math.floor(x / config.stairW) * -config.stairH + groundClearance;
                let yBot = yTop - config.stairH;
                let stepX = x + cornerX;
                pathD += `L ${x}, ${yTop} L ${stepX}, ${yTop} L ${stepX}, ${yBot} L ${x + config.stairW}, ${yBot} `;
            }
            pathD += `L ${endX}, 2000 Z`;
            
            terrain.setAttribute('d', pathD);
            terrainFill.setAttribute('d', pathD);
        }

        function animate() {
            globalTime += config.speed;
            
            // 1. 计算后轴坐标
            let Rx = globalTime;
            let Ry = getAxleY(Rx);

            // 2. 迭代前轴位置,确保底盘作为刚体,轴距精准保持 W 不变
            let Fx = Rx + config.W;
            for(let i = 0; i < 6; i++) {
                let tempFy = getAxleY(Fx);
                let currentDist = Math.hypot(Fx - Rx, tempFy - Ry);
                let error = config.W - currentDist;
                Fx += error * 0.9;
            }
            let Fy = getAxleY(Fx);

            // 3. 底盘中心坐标与俯仰姿态角
            let Cx = (Rx + Fx) / 2;
            let Cy = (Ry + Fy) / 2;
            let thetaRad = Math.atan2(Fy - Ry, Fx - Rx);
            let thetaDeg = thetaRad * 180 / Math.PI;

            // 4. 镜头跟踪锁定车体
            const screenCx = window.innerWidth / 2;
            const screenCy = window.innerHeight / 2 + 60; 
            scene.setAttribute('transform', `translate(${screenCx - Cx}, ${screenCy - Cy})`);

            // 5. 渲染楼梯
            updateTerrainPath(Cx);

            // 6. 行星轮位置与姿态更新 (直接放置在轮轴计算点)
            rearPlanetary.setAttribute('transform', `translate(${Rx}, ${Ry}) rotate(${getBracketAngle(Rx)})`);
            frontPlanetary.setAttribute('transform', `translate(${Fx}, ${Fy}) rotate(${getBracketAngle(Fx)})`);

            // 小轮自转:向前滚动为正向角度
            let wheelSpin = globalTime * 4; 
            swRotators.forEach(sw => sw.setAttribute('transform', `rotate(${wheelSpin})`));

            // 7. 底盘位姿更新 (位于 Cx, Cy)
            chassisBeam.setAttribute('transform', `translate(${Cx}, ${Cy}) rotate(${thetaDeg})`);

            // 8. 云台自调平 (完全消除 thetaDeg 的倾角影响,保持对地 0 度)
            platform.setAttribute('transform', `translate(0, ${config.pivotY}) rotate(${-thetaDeg})`);

            // 9. 主动补偿液压杆计算
            let rad = -thetaRad; 
            let pLeftX = -85 * Math.cos(rad);
            let pLeftY = -85 * Math.sin(rad);
            hydLeft.setAttribute('x2', pLeftX);
            hydLeft.setAttribute('y2', config.pivotY + pLeftY);

            let pRightX = 85 * Math.cos(rad);
            let pRightY = 85 * Math.sin(rad);
            hydRight.setAttribute('x2', pRightX);
            hydRight.setAttribute('y2', config.pivotY + pRightY);

            // 10. UI 数据刷新
            let tiltVal = Math.abs(thetaDeg).toFixed(2);
            uiTilt.textContent = tiltVal + '°';
            
            if (Math.abs(thetaDeg) > 0.5) {
                uiStatus.textContent = "● 状态监控:极速液压对冲中...";
                uiStatus.style.color = "var(--accent-orange)";
            } else {
                uiStatus.textContent = "● 状态监控:平地巡航";
                uiStatus.style.color = "var(--accent-cyan)";
            }

            requestAnimationFrame(animate);
        }

        requestAnimationFrame(animate);
    </script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分