双摆混沌实验室:两根摆杆一松手,轨迹三秒看上头

举报
deli007 发表于 2026/09/15 12:07:04 2026/09/15
【摘要】 双摆混沌实验室:两根摆杆一松手,轨迹三秒看上头

双摆最好玩的地方是「完全不可预测」:同样的初始角度,多摆 0.1 度轨迹都会彻底分道扬镳。这个工具把双摆的微分方程搬进浏览器,两根摆臂按真实物理过程实时演算,第二个摆锤会拖出越画越密的混沌轨迹;长度、质量、初始角度、重力都能拖滑块调整,暂停后还能直接拽摆锤改角度,松手就是一副不一样的图。先看成品,再讲这个网页怎么用一段提示词做成单文件应用并部署上线。

这个工具做什么

它把「双摆混沌模拟」做成一个零依赖的单页应用:

  • 真物理演算:不是用正弦波糊弄,而是按双摆拉格朗日方程写角加速度,再用 RK4 积分推进每一帧。
  • 两个摆实时联动:摆 1 / 摆 2 的长度、质量、初始角度,以及重力和演算速度,全部实时可调。
  • 轨迹逐渐混沌:第二个摆锤的运动轨迹最多保留 4000 个点,跑起来就是一副越绕越密的图。
  • 拖拽改角:暂停状态下直接拖住任一摆锤,初始角度跟随鼠标变化,滑块同步更新。
  • 6 种轨迹配色:青紫、红黄、绿青、橙粉、白灰、紫粉一键切换。
  • 随时暂停 / 重置 / 清空:空格暂停、R 重置、C 清空轨迹,快捷键全部接好。
  • 帧率显示:右上角实时显示 fps,方便判断当前设备跑得顺不顺。

双摆混沌实验室运行界面

准备环境:进入码道CodeArts

本文用 CodeArts 的 WebUI 版来演示。先进入 CodeArts 并开通体验版,不需要装任何软件,浏览器就能用:

进入 CodeArts:https://developer.huaweicloud.com/codeartsco.html?source=dmzntgwsf&sourcead=dmzntgwwbwz

开通后有三种使用方式:WebUI(浏览器对话)、TUI(终端命令行)和桌面 IDE(IDE 插件)。本文用 WebUI 版演示。

进入 AI 开发者空间

  1. 浏览器打开 https://devstation.developer.huaweicloud.com/aidevspace?utm_source=opcsfsq&utm_medium=wbwz
  2. 弹窗「请选择要进入的版本」→ 点击 体验版
  3. 首次使用会跳到华为云登录页,扫码登录一次,后续浏览器会话复用
  4. 弹窗「临时凭据自动配置」→ 点击 同意,沙箱自动创建

进入 AI 开发者空间主界面后,先等左侧菜单加载出来,再找到 CodeArts,点击它右侧的浏览器图标,打开码道 WebUI 对话窗口:

AI 开发者空间主界面

打开后码道 WebUI 会先和你打招呼,下方就是需求输入框:

码道 WebUI 欢迎页

小贴士:体验版沙箱 3 小时后自动重置,生成完记得把 index.html 下载保存。

任务描述

在码道 WebUI 输入框粘贴以下需求,回车发送:

开发一个「双摆混沌模拟」网页应用:画布上用两条摆臂串联两个摆锤,分别显示长度、质量、初始角度和重力参数;点击开始后按真实物理规律实时演算,并在画布上画出第二个摆锤的轨迹,轨迹会逐渐呈现混沌图案;支持暂停、重置,支持拖拽一个摆锤改变初始角度,支持切换轨迹颜色和清空轨迹;界面简洁美观,单个网页即跑即玩。完成后用部署功能部署成可访问的网页应用,并告诉我访问地址。

输入需求后开始生成

这轮码道具体做了什么

不是复述通用 AI 能力,而是它在这个项目里的实际动作:

  • /workspace/double-pendulum 下创建单文件 index.html,把运动方程、RK4 积分、Canvas 绘制、拖拽交互和快捷键都放进一个文件,无第三方依赖。
  • 先探测空闲端口,再写文件、用 python3 -m http.server 启动服务,curl 自检 HTTP 200 后给出预览地址。
  • 最后拼接出内部预览 URL,并附上功能说明和文件位置。

代码亮点

以下是 index.html 里值得关注的设计思路和实现细节。

1. 角加速度来自微分方程,不是动画预设

双摆的运动没有解析解,代码直接根据拉格朗日方程把两个摆的角加速度写成 derivs。看不懂公式也没关系,只要知道 th1/th2 是角度、w1/w2 是角速度,函数返回的就是每帧的变化率:

// 双摆角加速度(拉格朗日方程数值形式)
function derivs(s, p){
  const d = s.th1 - s.th2;
  const sd = Math.sin(d), cd = Math.cos(d);
  const den1 = (p.m1 + p.m2)*p.L1 - p.m2*p.L1*cd*cd;
  const a1 = ( p.m2*p.L1*s.w1*s.w1*sd*cd
             + p.m2*p.g*Math.sin(s.th2)*cd
             + p.m2*p.L2*s.w2*s.w2*sd
             - (p.m1+p.m2)*p.g*Math.sin(s.th1) ) / den1;
  const den2 = (p.L2/p.L1)*den1;
  const a2 = ( -p.m2*p.L2*s.w2*s.w2*sd*cd
             + (p.m1+p.m2)*p.g*Math.sin(s.th1)*cd
             - (p.m1+p.m2)*p.L1*s.w1*s.w1*sd
             - (p.m1+p.m2)*p.g*Math.sin(s.th2) ) / den2;
  return { th1:s.w1, w1:a1, th2:s.w2, w2:a2 };
}

2. RK4 推进,一帧拆成 8 步

单步欧拉积分在双摆这种高敏感系统里会把能量算飞。代码用经典的四阶龙格-库塔积分,每帧实际按 dt/8 连续推进 8 次,轨迹才能稳定:

// RK4 积分一步
function rk4(s, p, dt){
  const k1 = derivs(s, p);
  const s2 = { th1:s.th1+k1.th1*dt/2, w1:s.w1+k1.w1*dt/2, th2:s.th2+k1.th2*dt/2, w2:s.w2+k1.w2*dt/2 };
  const k2 = derivs(s2, p);
  const s3 = { th1:s.th1+k2.th1*dt/2, w1:s.w1+k2.w1*dt/2, th2:s.th2+k2.th2*dt/2, w2:s.w2+k2.w2*dt/2 };
  const k3 = derivs(s3, p);
  const s4 = { th1:s.th1+k3.th1*dt, w1:s.w1+k3.w1*dt, th2:s.th2+k3.th2*dt, w2:s.w2+k3.w2*dt };
  const k4 = derivs(s4, p);
  return {
    th1: s.th1 + (k1.th1+2*k2.th1+2*k3.th1+k4.th1)*dt/6,
    w1:  s.w1  + (k1.w1 +2*k2.w1 +2*k3.w1 +k4.w1 )*dt/6,
    th2: s.th2 + (k1.th2+2*k2.th2+2*k3.th2+k4.th2)*dt/6,
    w2:  s.w2  + (k1.w2 +2*k2.w2 +2*k3.w2 +k4.w2 )*dt/6,
  };
}

主循环里也没有每帧重新起 timer,而是持续复用 requestAnimationFrame,按真实帧间隔算 dt,卡顿自动调小步长:

function loop(ts){
  if(!last) last = ts;
  let dt = (ts - last) / 1000;
  last = ts;
  if(dt > 0.05) dt = 0.05;

  if(running){
    const speed = parseFloat(document.getElementById('spd').value);
    const total = dt * speed;
    const sub = 8;
    const h = total / sub;
    for(let i = 0; i < sub; i++){
      state = rk4(state, params, h);
    }
    const pos = positions();
    trail.push({x: pos.x2, y: pos.y2});
    if(trail.length > MAX_TRAIL) trail.shift();
  }

  draw();
  requestAnimationFrame(loop);
}

3. 拖拽改角:把鼠标位置反向算回角度

暂停时拖摆锤,本质是在「从已完成状态恢复到重新开始时」直接把摆锤拨到鼠标指向的位置。atan2 算相对角度,syncAngleSliders 再反过来刷新滑块,保证画布和面板始终一致:

function hitTest(m){
  const pos = positions();
  const r1 = 6 + Math.sqrt(params.m1)*2.2 + 8;
  const r2 = 6 + Math.sqrt(params.m2)*2.2 + 8;
  if(Math.hypot(m.x-pos.x2, m.y-pos.y2) < r2) return 2;
  if(Math.hypot(m.x-pos.x1, m.y-pos.y1) < r1) return 1;
  return null;
}

拖拽第二个摆锤时,以摆锤 1 为原点反算方向;拖第一个摆锤时,以支点为原点。拖完 initial 同步更新,下次点「重置」也会回到这个新姿势。

4. Canvas 只负责画,状态全部放在内存对象

state = { th1, w1, th2, w2 } 就是全部物理状态,params 存长度、质量、重力,trail 存轨迹点数组。视图层每帧清空画布重画,避免任何元素进退场动画带来的额外状态。轨迹颜色用线性渐变一笔画出,保留 4000 个点后从队头丢弃最旧点,性能也稳。

直接下载试玩

本次生成的项目就是单文件 index.html,没有第三方依赖,把文件保存到本地后双击用浏览器打开即可运行。源码同步到公开 Demo Park 仓库:

总结

双摆混沌实验室把可玩性压回三个点:运动方程来自真实微分方程,RK4 每帧 8 步保证轨迹稳定,拖拽交互让混沌从「看」变成「能上手拨」。把角度、质量、重力都暴露成滑块后,一套代码能玩出无穷种轨迹。后续可以继续加「轨迹保存/对比」「多摆同时跑」「慢动作回放」,或者把初始条件做成随机种子,一键生成一张混沌海报。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。