独立渲染引擎就绪引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多段铰接柔性底盘 · 蠕动爬升原理</title>
<link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;600;700;900&family=Noto+Sans+SC:wght@300;400;600&display=swap" rel="stylesheet">
<style>
:root{--bg:#060a14;--fg:#d8e0f0;--accent:#ff6b35;--cyan:#00d4ff;--green:#00ff88;--gold:#ffd54f;--muted:#384868;--card:#0a1020;--border:#162040}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--fg);font-family:'Noto Sans SC',sans-serif;min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:20px 16px;background-image:radial-gradient(circle at 20% 30%,rgba(0,212,255,.03)0%,transparent 50%),radial-gradient(circle at 80% 70%,rgba(255,107,53,.03)0%,transparent 50%)}
header{text-align:center;margin-bottom:16px}
header h1{font-family:'Rajdhani',sans-serif;font-weight:900;font-size:1.7rem;letter-spacing:3px;color:var(--cyan);text-shadow:0 0 24px rgba(0,212,255,.25)}
header .sub{font-size:.8rem;color:var(--muted);margin-top:2px;font-weight:300;letter-spacing:1px}
.main{display:flex;gap:16px;max-width:1320px;width:100%;align-items:flex-start}
.canvas-wrap{flex:1;background:var(--card);border:1px solid var(--border);border-radius:12px;overflow:hidden;position:relative;box-shadow:0 0 40px rgba(0,0,0,.4)}
canvas{display:block;width:100%;height:auto}
.sidebar{width:260px;display:flex;flex-direction:column;gap:10px;flex-shrink:0}
.panel{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:14px}
.panel h3{font-family:'Rajdhani',sans-serif;font-size:.78rem;font-weight:700;color:var(--cyan);letter-spacing:1.5px;text-transform:uppercase;margin-bottom:10px;padding-bottom:7px;border-bottom:1px solid var(--border)}
.pr{display:flex;justify-content:space-between;align-items:center;padding:4px 0;font-size:.78rem}
.pr .l{color:var(--muted)}.pr .v{font-family:'Rajdhani',sans-serif;font-weight:600;font-size:.9rem}
.pr .v.ac{color:var(--accent)}.pr .v.gr{color:var(--green)}.pr .v.cy{color:var(--cyan)}
.cr{margin-bottom:12px}
.cr:last-child{margin-bottom:0}
.cr label{display:flex;justify-content:space-between;font-size:.72rem;color:var(--muted);margin-bottom:4px}
.cr label span{font-family:'Rajdhani',sans-serif;color:var(--cyan);font-size:.82rem}
input[type=range]{width:100%;-webkit-appearance:none;appearance:none;height:4px;background:var(--border);border-radius:2px;outline:none}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:var(--cyan);cursor:pointer;border:2px solid var(--bg);box-shadow:0 0 6px rgba(0,212,255,.4)}
.phases{display:flex;flex-direction:column;gap:6px}
.ph{display:flex;align-items:center;gap:7px;font-size:.76rem;color:var(--muted);transition:color .3s}
.ph.on{color:var(--fg)}.pd{width:7px;height:7px;border-radius:50%;background:var(--border);transition:all .3s;flex-shrink:0}
.ph.on .pd{background:var(--accent);box-shadow:0 0 8px rgba(255,107,53,.6)}
.legend{display:flex;flex-wrap:wrap;gap:8px}
.li{display:flex;align-items:center;gap:5px;font-size:.7rem;color:var(--muted)}
.lc{width:10px;height:10px;border-radius:2px;flex-shrink:0}
.ifr-box{margin-top:6px;padding:8px 10px;background:rgba(255,107,53,.06);border:1px solid rgba(255,107,53,.2);border-radius:6px;font-size:.7rem;color:rgba(255,180,130,.85);line-height:1.5}
.ifr-box strong{color:var(--accent)}
@media(max-width:900px){.main{flex-direction:column}.sidebar{width:100%;flex-direction:row;flex-wrap:wrap}.sidebar .panel{flex:1;min-width:180px}}
</style>
</head>
<body>
<header>
<h1>MULTI-SEGMENT ARTICULATED CHASSIS</h1>
<p class="sub">多段铰接柔性底盘 · 蠕动爬升原理 — IFR 最终理想解视角</p>
</header>
<div class="main">
<div class="canvas-wrap"><canvas id="cv" width="960" height="520"></canvas></div>
<aside class="sidebar">
<div class="panel">
<h3>关键参数</h3>
<div class="pr"><span class="l">舱段数量</span><span class="v cy">5</span></div>
<div class="pr"><span class="l">舱段长度</span><span class="v">60mm</span></div>
<div class="pr"><span class="l">最大弯折角</span><span class="v ac" id="mbd">45°</span></div>
<div class="pr"><span class="l">履带宽度</span><span class="v">150mm</span></div>
<div class="pr"><span class="l">当前最大弯折</span><span class="v gr" id="cbd">0°</span></div>
</div>
<div class="panel">
<h3>交互控制</h3>
<div class="cr"><label>爬升速度 <span id="sv">1.0x</span></label><input type="range" id="ss" min="0.3" max="2.5" step="0.1" value="1"></div>
<div class="cr"><label>最大弯折角 <span id="bv">45°</span></label><input type="range" id="bs" min="15" max="60" step="5" value="45"></div>
<div class="cr"><label>台阶不规则度 <span id="iv">50%</span></label><input type="range" id="is" min="0" max="100" step="5" value="50"></div>
</div>
<div class="panel">
<h3>运动阶段</h3>
<div class="phases">
<div class="ph on" id="p0"><span class="pd"></span>平地行驶</div>
<div class="ph" id="p1"><span class="pd"></span>接触台阶</div>
<div class="ph" id="p2"><span class="pd"></span>被动折叠适应</div>
<div class="ph" id="p3"><span class="pd"></span>履带持续卷动</div>
<div class="ph" id="p4"><span class="pd"></span>自重恢复平直</div>
</div>
</div>
<div class="panel">
<h3>图例</h3>
<div class="legend">
<div class="li"><span class="lc" style="background:#00b8d4"></span>舱段主体</div>
<div class="li"><span class="lc" style="background:#00ff88"></span>驱动履带</div>
<div class="li"><span class="lc" style="background:#ff6b35"></span>弯折关节</div>
<div class="li"><span class="lc" style="background:#ffd54f"></span>接触反力</div>
</div>
<div class="ifr-box"><strong>IFR 核心:</strong>利用台阶接触反力作为资源,驱动被动铰接形变——无需额外传感器或主动控制,系统自适地形。</div>
</div>
</aside>
</div>
<script>
const cv=document.getElementById('cv'),c=cv.getContext('2d'),W=960,H=520;
/* ===== 参数 ===== */
const NS=5,SL=60,SH=22,TT=7,WR=10;
let MBD=45,MB=MBD*Math.PI/180,spd=1,irr=0.5;
/* ===== 状态 ===== */
let rx=-120,tOff=0,t=0;
/* ===== 地形 ===== */
const S1X=360,S2X=500,S3X=630,GY=432;
function stairRises(){return{r1:65+irr*20,r2:65-irr*15,r3:50+irr*15}}
function tY(x){const{r1,r2,r3}=stairRises();const l1=GY-r1,l2=l1-r2,l3=l2-r3;if(x<S1X)return GY;if(x<S2X)return l1;if(x<S3X)return l2;return l3}
/* ===== 关节计算 ===== */
function calcJoints(){
const J=[{x:rx,y:tY(rx)}];
for(let i=0;i<NS;i++){
const p=J[i];let pa=0;
if(i>0){const dx=J[i].x-J[i-1].x,dy=J[i-1].y-J[i].y;pa=Math.atan2(dy,dx)}
const fx=p.x+SL,fty=tY(fx);
let ia=Math.atan2(p.y-fty,SL);
let ra=ia-pa;ra=Math.max(-MB,Math.min(MB,ra));
const aa=pa+ra;
let nx=p.x+SL*Math.cos(aa),ny=p.y-SL*Math.sin(aa);
const tt=tY(nx);if(ny>tt)ny=tt;
J.push({x:nx,y:ny});
}
return J;
}
/* ===== 弯折角计算 ===== */
function bendAngles(J){
const A=[];
for(let i=1;i<J.length;i++){
if(i===1){const dx=J[1].x-J[0].x,dy=J[0].y-J[1].y;A.push(Math.atan2(dy,dx))}
else{const dx1=J[i-1].x-J[i-2].x,dy1=J[i-2].y-J[i-1].y,dx2=J[i].x-J[i-1].x,dy2=J[i-1].y-J[i].y;A.push(Math.atan2(dy2,dx2)-Math.atan2(dy1,dx1))}
}
return A;
}
/* ===== 粒子系统 ===== */
const particles=[];
function spawnParticle(x,y,vx,vy,life,col){particles.push({x,y,vx,vy,life,maxLife:life,col})}
function updateParticles(){for(let i=particles.length-1;i>=0;i--){const p=particles[i];p.x+=p.vx;p.y+=p.vy;p.life--;if(p.life<=0)particles.splice(i,1)}}
function drawParticles(){for(const p of particles){const a=p.life/p.maxLife;c.globalAlpha=a*0.7;c.fillStyle=p.col;c.beginPath();c.arc(p.x,p.y,2*a,0,Math.PI*2);c.fill()}c.globalAlpha=1}
/* ===== 绘制 ===== */
function drawGrid(){
c.strokeStyle='#0c1428';c.lineWidth=0.5;
for(let x=0;x<W;x+=40){c.beginPath();c.moveTo(x,0);c.lineTo(x,H);c.stroke()}
for(let y=0;y<H;y+=40){c.beginPath();c.moveTo(0,y);c.lineTo(W,y);c.stroke()}
}
function drawTerrain(){
const{r1,r2,r3}=stairRises();const l1=GY-r1,l2=l1-r2,l3=l2-r3;
/* 填充 */
c.beginPath();c.moveTo(0,H);c.lineTo(0,GY);c.lineTo(S1X,GY);c.lineTo(S1X,l1);c.lineTo(S2X,l1);c.lineTo(S2X,l2);c.lineTo(S3X,l2);c.lineTo(S3X,l3);c.lineTo(W,l3);c.lineTo(W,H);c.closePath();
const g=c.createLinearGradient(0,l3-20,0,H);g.addColorStop(0,'#151e38');g.addColorStop(1,'#0a1020');c.fillStyle=g;c.fill();
/* 边缘 */
c.beginPath();c.moveTo(0,GY);c.lineTo(S1X,GY);c.lineTo(S1X,l1);c.lineTo(S2X,l1);c.lineTo(S2X,l2);c.lineTo(S3X,l2);c.lineTo(S3X,l3);c.lineTo(W,l3);c.strokeStyle='#2a4070';c.lineWidth=2;c.stroke();
/* 阶面纹理线 */
c.strokeStyle='#1a2a48';c.lineWidth=0.5;c.setLineDash([2,6]);
for(let y=GY+15;y<H;y+=15){c.beginPath();c.moveTo(0,y);c.lineTo(S1X,y);c.stroke()}
for(let y=l1+15;y<H;y+=15){c.beginPath();c.moveTo(S1X,y);c.lineTo(S2X,y);c.stroke()}
for(let y=l2+15;y<H;y+=15){c.beginPath();c.moveTo(S2X,y);c.lineTo(S3X,y);c.stroke()}
c.setLineDash([]);
/* 台阶高度标注 */
drawHAnno(S1X,GY,l1,r1);drawHAnno(S2X,l1,l2,r2);drawHAnno(S3X,l2,l3,r3);
/* 不规则标注 */
if(irr>0.2){
c.font='10px Rajdhani,sans-serif';c.fillStyle='#5a6a90';c.textAlign='center';
c.fillText('IRREGULAR',S2X,(l1+l2)/2+3);
}
}
function drawHAnno(x,y1,y2,h){
c.strokeStyle='#3a4a70';c.lineWidth=1;c.setLineDash([3,3]);
c.beginPath();c.moveTo(x+12,y1);c.lineTo(x+12,y2);c.stroke();c.setLineDash([]);
c.font='11px Rajdhani,sans-serif';c.fillStyle='#5a7aa0';c.textAlign='left';
c.fillText(Math.round(h)+'mm',x+17,(y1+y2)/2+4);
}
function drawRobot(J){
const hh=SH/2,ba=bendAngles(J);
/* 履带外壳 */
drawTrackShell(J,hh+TT/2+2);
/* 舱段 */
for(let i=0;i<NS;i++){
const a=J[i],b=J[i+1],dx=b.x-a.x,dy=b.y-a.y,ln=Math.sqrt(dx*dx+dy*dy);
if(ln<1)continue;
const ang=Math.atan2(dy,dx);
c.save();c.translate(a.x,a.y);c.rotate(ang);
const sg=c.createLinearGradient(0,-hh,0,hh);
sg.addColorStop(0,'rgba(0,160,210,0.5)');sg.addColorStop(0.5,'rgba(0,120,190,0.3)');sg.addColorStop(1,'rgba(0,80,150,0.2)');
c.fillStyle=sg;rr(c,2,-hh,ln-4,SH,4);c.fill();
c.strokeStyle='rgba(0,200,240,0.55)';c.lineWidth=1;rr(c,2,-hh,ln-4,SH,4);c.stroke();
/* 舱段内电机纹 */
c.strokeStyle='rgba(0,200,240,0.15)';c.lineWidth=0.5;
for(let k=10;k<ln-6;k+=12){c.beginPath();c.moveTo(k,-hh+3);c.lineTo(k,hh-3);c.stroke()}
c.font='9px Rajdhani,sans-serif';c.fillStyle='rgba(0,200,240,0.4)';c.textAlign='center';c.fillText('S'+(i+1),ln/2,3);
c.restore();
}
/* 履带齿纹(底部) */
drawTread(J,hh+TT/2+2,1,'#00ff88',1);
drawTread(J,-(hh+TT/2+2),-1,'rgba(0,255,136,0.35)',0.5);
/* 关节 */
for(let i=0;i<J.length;i++){
const bn=i>0?Math.abs(ba[i-1]||0):0;const act=bn>5*Math.PI/180;
drawJoint(J[i].x,J[i].y,act,bn);
if(act&&Math.random()<0.3){spawnParticle(J[i].x,J[i].y,(Math.random()-0.5)*0.8,-Math.random()*1.2,30+Math.random()*20,'#ff6b35')}
}
/* 弯折角标注 */
for(let i=1;i<J.length-1;i++){
const bn=ba[i]||0;
if(Math.abs(bn)>5*Math.PI/180)drawBendAnno(J[i],bn);
}
}
function drawTrackShell(J,off){
c.beginPath();
const pts=[];
for(let i=0;i<J.length-1;i++){
const a=J[i],b=J[i+1],dx=b.x-a.x,dy=b.y-a.y,ln=Math.sqrt(dx*dx+dy*dy);
if(ln<1)continue;
const nx=-dy/ln,ny=dx/ln;
pts.push({bx:a.x+nx*off,by:a.y+ny*off,tx:a.x-nx*off,ty:a.y-ny*off,bx2:b.x+nx*off,by2:b.y+ny*off,tx2:b.x-nx*off,ty2:b.y-ny*off});
}
if(!pts.length)return;
/* 底部 */
c.moveTo(pts[0].bx,pts[0].by);
for(const p of pts)c.lineTo(p.bx2,p.by2);
/* 前端弧 */
const fj=J[J.length-1];c.arc(fj.x,fj.y,off,0,Math.PI*2,false);
/* 顶部(反向) */
for(let i=pts.length-1;i>=0;i--)c.lineTo(pts[i].tx,pts[i].ty);
/* 后端弧 */
const rj=J[0];c.arc(rj.x,rj.y,off,0,Math.PI*2,false);
c.closePath();
c.fillStyle='rgba(0,50,35,0.25)';c.fill();
}
function drawTread(J,off,dir,col,lw){
c.beginPath();
for(let i=0;i<J.length-1;i++){
const a=J[i],b=J[i+1],dx=b.x-a.x,dy=b.y-a.y,ln=Math.sqrt(dx*dx+dy*dy);
if(ln<1)continue;
const nx=-dy/ln,ny=dx/ln;
const sx=a.x+nx*off,sy=a.y+ny*off,ex=b.x+nx*off,ey=b.y+ny*off;
if(i===0)c.moveTo(sx,sy);else c.lineTo(sx,sy);
c.lineTo(ex,ey);
}
c.setLineDash([7,4]);c.lineDashOffset=dir*tOff;c.strokeStyle=col;c.lineWidth=TT*lw;c.lineCap='round';c.lineJoin='round';c.stroke();c.setLineDash([]);
}
function drawJoint(x,y,act,bn){
/* 光晕 */
if(act){
const g=c.createRadialGradient(x,y,4,x,y,22);
g.addColorStop(0,'rgba(255,107,53,0.25)');g.addColorStop(1,'rgba(255,107,53,0)');c.fillStyle=g;c.beginPath();c.arc(x,y,22,0,Math.PI*2);c.fill();
}
c.beginPath();c.arc(x,y,8,0,Math.PI*2);c.fillStyle=act?'rgba(255,107,53,0.3)':'rgba(0,180,220,0.12)';c.fill();
c.strokeStyle=act?'#ff6b35':'#00b8d4';c.lineWidth=1.5;c.stroke();
/* 铰链标记 */
c.beginPath();c.moveTo(x-4,y);c.lineTo(x+4,y);c.moveTo(x,y-4);c.lineTo(x,y+4);c.strokeStyle=act?'rgba(255,107,53,0.8)':'rgba(0,180,220,0.35)';c.lineWidth=1;c.stroke();
/* 万向节小圆 */
c.beginPath();c.arc(x,y,3,0,Math.PI*2);c.strokeStyle=act?'rgba(255,150,80,0.6)':'rgba(0,180,220,0.25)';c.lineWidth=0.8;c.stroke();
}
function drawBendAnno(j,bn){
const ad=Math.round(Math.abs(bn*180/Math.PI));
c.font='bold 11px Rajdhani,sans-serif';c.fillStyle='#ff6b35';c.textAlign='center';c.fillText(ad+'°',j.x,j.y-26);
/* 弧线示意 */
c.beginPath();c.arc(j.x,j.y,16,Math.min(0,-bn),Math.max(0,-bn),false);c.strokeStyle='rgba(255,107,53,0.4)';c.lineWidth=1.5;c.stroke();
}
function drawContactForces(J){
const edges=[S1X,S2X,S3X];
for(const j of J){
const ty=tY(j.x);
if(Math.abs(j.y-ty)>5)continue;
for(const ex of edges){
if(Math.abs(j.x-ex)<25){
/* 力箭头 */
c.beginPath();c.moveTo(j.x,ty+4);c.lineTo(j.x,ty+22);c.strokeStyle='#ffd54f';c.lineWidth=2;c.stroke();
c.beginPath();c.moveTo(j.x-4,ty+17);c.lineTo(j.x,ty+23);c.lineTo(j.x+4,ty+17);c.strokeStyle='#ffd54f';c.lineWidth=2;c.stroke();
/* 标注 */
c.font='9px "Noto Sans SC",sans-serif';c.fillStyle='rgba(255,213,79,0.7)';c.textAlign='center';c.fillText('反力',j.x,ty+34);
/* 粒子 */
if(Math.random()<0.15)spawnParticle(j.x,ty,(Math.random()-0.5)*0.5,-Math.random()*0.8,25,'#ffd54f');
break;
}
}
}
}
function drawDriveArrow(J){
if(J.length<2)return;
const cx=(J[0].x+J[J.length-1].x)/2,cy=Math.min(...J.map(j=>j.y))-55;
c.save();c.globalAlpha=0.5;
c.beginPath();c.moveTo(cx-30,cy);c.lineTo(cx+18,cy);c.strokeStyle='#00ff88';c.lineWidth=2;c.stroke();
c.beginPath();c.moveTo(cx+13,cy-5);c.lineTo(cx+20,cy);c.lineTo(cx+13,cy+5);c.strokeStyle='#00ff88';c.lineWidth=2;c.stroke();
c.font='10px "Noto Sans SC",sans-serif';c.fillStyle='#00ff88';c.textAlign='center';c.fillText('履带驱动 →',cx,cy-9);
c.restore();
}
function drawIFRLabel(J){
/* 在弯折区域标注 IFR 核心 */
const ba=bendAngles(J);let maxI=0,maxB=0;
for(let i=0;i<ba.length;i++){if(Math.abs(ba[i])>maxB){maxB=Math.abs(ba[i]);maxI=i}}
if(maxB<10*Math.PI/180)return;
const j=J[maxI+1];
c.save();c.globalAlpha=0.7;
c.font='10px "Noto Sans SC",sans-serif';c.fillStyle='#ff6b35';c.textAlign='center';
c.fillText('被动形变 · 无需主动控制',j.x,j.y-42);
c.restore();
}
function drawWheelMarkers(J){
for(const j of J){
c.beginPath();c.arc(j.x,j.y,WR,0,Math.PI*2);c.strokeStyle='rgba(0,180,220,0.2)';c.lineWidth=1;c.stroke();
/* 旋转标记 */
const a=tOff*0.1;
c.beginPath();c.moveTo(j.x+WR*0.6*Math.cos(a),j.y+WR*0.6*Math.sin(a));c.lineTo(j.x-WR*0.6*Math.cos(a),j.y-WR*0.6*Math.sin(a));c.strokeStyle='rgba(0,180,220,0.15)';c.lineWidth=1;c.stroke();
}
}
function rr(ctx,x,y,w,h,r){r=Math.min(r,w/2,h/2);ctx.beginPath();ctx.moveTo(x+r,y);ctx.lineTo(x+w-r,y);ctx.quadraticCurveTo(x+w,y,x+w,y+r);ctx.lineTo(x+w,y+h-r);ctx.quadraticCurveTo(x+w,y+h,x+w-r,y+h);ctx.lineTo(x+r,y+h);ctx.quadraticCurveTo(x,y+h,x,y+h-r);ctx.lineTo(x,y+r);ctx.quadraticCurveTo(x,y,x+r,y);ctx.closePath()}
/* ===== UI 更新 ===== */
function updateUI(J){
const ba=bendAngles(J);let mb=0;for(const b of ba)mb=Math.max(mb,Math.abs(b));
const mbd=Math.round(mb*180/Math.PI);
document.getElementById('cbd').textContent=mbd+'°';
const ps=document.querySelectorAll('.ph');ps.forEach(p=>p.classList.remove('on'));
const frontX=J[J.length-1].x;
const onStair=frontX>S1X-20;
if(mbd<5&&!onStair){document.getElementById('p0').classList.add('on')}
else if(mbd<15&&onStair){document.getElementById('p1').classList.add('on')}
else if(mbd>=15){document.getElementById('p2').classList.add('on')}
document.getElementById('p3').classList.add('on');
if(frontX>S3X+80&&mbd<8)document.getElementById('p4').classList.add('on');
}
/* ===== 主循环 ===== */
function frame(){
t+=0.016;rx+=0.8*spd;tOff+=1.5*spd;
if(rx>W+150)rx=-200;
const J=calcJoints();
c.clearRect(0,0,W,H);
c.fillStyle='#060a14';c.fillRect(0,0,W,H);
drawGrid();drawTerrain();
updateParticles();
drawWheelMarkers(J);drawContactForces(J);drawRobot(J);drawDriveArrow(J);drawIFRLabel(J);
drawParticles();
updateUI(J);
requestAnimationFrame(frame);
}
/* ===== 控件 ===== */
document.getElementById('ss').addEventListener('input',function(){spd=parseFloat(this.value);document.getElementById('sv').textContent=spd.toFixed(1)+'x'});
document.getElementById('bs').addEventListener('input',function(){MBD=parseInt(this.value);MB=MBD*Math.PI/180;document.getElementById('bv').textContent=MBD+'°';document.getElementById('mbd').textContent=MBD+'°'});
document.getElementById('is').addEventListener('input',function(){irr=parseInt(this.value)/100;document.getElementById('iv').textContent=this.value+'%'});
/* 启动 */
frame();
</script>
</body>
</html>
这个动画实现了一个完整的多段铰接柔性底盘蠕动爬升原理演示,核心设计要点如下:
原理还原
- 5 段独立舱段通过万向节串联,每段内部有驱动电机驱动履带
- 舱段间弯折角实时受限于最大 45°(可调),接触台阶时被动折叠贴合地形
- 绿色虚线履带持续卷动(齿纹动画),模拟毛毛虫式蠕动推进
- 跨越台阶后,自重使舱段恢复水平
IFR 视觉引导
- 橙色高亮 + 径向光晕标注正在弯折的关节,附带角度数值
- 金色箭头标注台阶边缘的接触反力——这正是驱动被动形变的"免费资源"
- 弯折区域出现"被动形变 · 无需主动控制"标注,直指 IFR 核心
- 粒子效果强化关节受力和接触点的动态感知
交互控制
- 爬升速度:调整动画播放速率
- 最大弯折角:15°–60° 可调,观察不同柔性极限下的适应能力
- 台阶不规则度:0%–100%,改变三级台阶的高度差异,验证自适应优势
积分规则:第一轮对话扣减8分,后续每轮扣6分
等待动画代码生成...
