程云来 / 杭州
← 工程实践
CAD · 有限元 · 应力 / 位移 · Three.js

从载荷到云图:零件仿真结果怎样落到三维模型上

从 CAD 尺寸、材料与边界条件开始,实际求解位移和应力,用解析解与反力验证,再追踪结果插值、顶点变形、色标与 GPU 绘制。


一个零件受力之后,屏幕上的红色区域到底代表什么?它是位移大、应力大,还是只是色标被调低了?如果模型被放大变形十倍,探针读数是否也应该乘十?这些问题必须沿着 CAD 几何 → 数学模型 → 求解结果 → 表面映射 → GPU 绘图 逐层回答。渲染器只消费结果,可靠的云图必须保留结果的单位、位置和计算条件。

本文先用一个可以独立校核的悬臂零件跑通完整链路,再对照已有的泵支架模态数据。悬臂案例在浏览器中实际执行有限元静力求解;泵支架读取上游预计算结果。两者对应不同的物理问题。

先把零件、载荷和坐标说清楚

设想一条矩形钢制悬臂,长度 L=200 mm,截面沿 Y 高 h=8 mm、沿 Z 宽 b=20 mm。X 轴沿长度,X=0 的端面固定,X=L 的截面中心受向下的集中力 F=−100 N。Y 正方向向上,因此挠度 w 为负。这里的集中力作用于梁模型的自由端节点;实际夹具、螺栓接触与力的分布需要另建模型。

CAD 用 CadQuery 2.6.1 参数化建模。cantilever.json 保存几何契约,建模脚本读取它生成实体与 STEP;求解器与显示表面也读取同一个契约。脚本检查单一实体、几何有效性、体积与 STEP 重导入尺寸,避免“图看起来差不多,但坐标系或单位已经变了”。

python
# examples/stage-02-engineering/user_code/build_cad.py
# X: 长度;Y: 弯曲高度;Z: 截面宽度。CAD 参数单位为 mm。
part = cq.Workplane("XY").box(200, 8, 20,
                              centered=(False, True, True))
cq.exporters.export(part, "cantilever.step")
# 单个封闭实体;6 个平面;体积 32000 mm³。

本例采用 Euler–Bernoulli 梁模型:截面在弯曲后保持平面且垂直于中性轴,忽略剪切变形。L/h=25,适合作为细长梁的小挠度基准。截面惯性矩 I=b·h³/12=853.333 mm⁴,它描述截面对弯曲的几何抵抗能力;I 不是质量惯性矩。求解全程转换为 SI:m、N、Pa。弹性模量 E=210 GPa 描述钢在弹性阶段的应力与应变比例,和零件几何共同决定 EI 弯曲刚度。

CadQuery 负责参数化实体建模;TU Delft 的梁单元推导给出本文采用的 Hermite 插值与刚度矩阵。尺寸、载荷、材料数值是本站的基准案例设定。

CAD 表面为什么不能直接拿来做有限元

STEP 描述的是连续几何与拓扑,渲染用的是三角表面,求解用的是离散的自由度与单元。三个对象服务的任务不同。画得更细的三角形,不会自动给求解器增加物理自由度;反过来,求解器输出的内部节点也不一定是表面可见顶点。

本例沿 X 把中性轴划分为 n 个梁单元。每个节点有横向位移 w 与截面转角 θ 两个自由度,共 2(n+1) 个。截面厚度通过 I 进入计算,求解器没有给实体内部生成四面体。显示则把 CAD 六个平面按长度方向分成 80 段,保留尖锐边界的独立顶点记录。这是针对矩形实体的结构化表面采样,尺寸由已经重导入验证的 CAD 契约给出;复杂 STEP 的实时三角化见另一篇 CAD 渲染实践。

正在绘制图表…
查看 Mermaid 源码
flowchart TD
  Spec[CAD 尺寸契约 / mm] --> CAD[CadQuery 实体与 STEP]
  CAD --> Check[重导入 / 尺寸与体积校核]
  Spec --> Beam[中心线梁单元 / m]
  Beam --> Solve[组装 K 与 f / 施加固定约束]
  Solve --> Result[节点 w 与 θ / 反力]
  Spec --> Surface[六个 CAD 平面的结构化采样]
  Result --> Map[Hermite 插值 / 位移与应力恢复]
  Surface --> Map
  Map --> GPU[position 与 color 缓冲 / Three.js]

Mermaid 大图

可滚动查看图表,点击缩放比例可恢复 100%。按 Esc 关闭。

对于真实复杂零件,通常需要 CAD 清理、物理区域定义、体网格生成、单元质量检查、网格收敛验证,再从求解网格提取边界。圆角、薄壁和孔洞会改变应力分布,不能为了方便画图擅自删除。展示网格若另行简化,必须保存它与求解网格的插值映射。

从一个梁单元组装到 Ku=f

梁单元的局部未知量是 d=[w1, θ1, w2, θ2]。用三次 Hermite 形函数插值挠度,可以同时维持单元交界处的位移和斜率连续。令单元长度为 ℓ,ξ=x/ℓ,挠度 w(x)=N(x)d。二阶导数给出曲率 κ=w″,截面轴向应变为 εxx=−yκ,应力为 σxx=Eεxx。

刚度矩阵来自弯曲应变能:对单元积分 E·I·(N″)TN″,得到下面的矩阵。θ 是无量纲的弧度,w 是米,所以 K 的子块具有不同量纲;不能简单把整个矩阵统一叫作 N/m。

text
┌ 12      6ℓ      −12      6ℓ   ┐
Ke = EI/ℓ³ · │ 6ℓ      4ℓ²     −6ℓ      2ℓ²  │
             │ −12     −6ℓ      12      −6ℓ   │
             └ 6ℓ      2ℓ²     −6ℓ      4ℓ²  ┘

共享节点的矩阵项累加进全局 K,端部力写入 f 的末端 w 分量。固定端 w0=θ0=0,把对应的两行两列消去,求解剩余的自由自由度。原始完整 K 和 f 仍保留:求出 u 后计算 r=Ku−f,受约束的两个分量是固定端反力与反力矩,自由分量应接近零。

typescript
// 正式求解器:src/components/engineering/beam-solver.ts
const result = solveBeam({
  length: 0.2, width: 0.02, height: 0.008,
  young: 210e9, force: -100, elements: 20,
});
const section = result.sample(0.1); // 输入位置 x,单位 m
// sample 用所在单元的四个已求解自由度作 Hermite 插值。
console.log(result.tip * 1000);          // −1.488095… mm
console.log(section.stress(0.004) / 1e6);// 上表面 +46.875 MPa

本站小矩阵采用带部分主元选择的消元,计算规模控制在 80 个单元以内。工程中的大型三维模型应使用稀疏装配、适当的直接/迭代求解器与预条件器,并记录求解容差、迭代历史和收敛状态。若漏掉固定约束,整体刚体运动会令刚度矩阵奇异;用一个极小数字替换主元,会隐藏建模错误。

先校核,再看三维结果

这组均匀梁、端部集中力的解析解是 w(x)=F·x²(3L−x)/(6EI),自由端 w(L)=FL³/(3EI),弯矩 M(x)=F(L−x)。本例在这一简单载荷下的真实解恰好是三次多项式,因此一个三次梁单元就能表达它。这不能推广为“所有模型一个单元就足够”;分布载荷、变截面、复杂边界都要重新验证。

校核量本例 F=−100 N、E=210 GPa说明
自由端位移−1.488095 mm与解析解比较,保留方向
固定端竖向反力+100 N和端部外力平衡
固定端反力矩+20 N·m和外力关于固定端的力矩平衡
根部上表面应力+93.75 MPa拉伸;下表面为 −93.75 MPa
中性轴轴向应力0 MPay=0,所以弯曲应变为零

下面把载荷从 100 N 改为 200 N:位移与应力都应加倍。把钢换成 70 GPa 的铝,位移变为三倍;在同一几何与静定端力下,弯曲应力保持不变。再改变色标和变形倍率,探针数值应完全相同。

静力求解 / CAD 悬臂零件下载 STEP · 200 × 8 × 20 mm

X=0 固定,X=200 mm 端部向下受力;灰线保留 CAD 原始轮廓。

拉伸 +100 MPa压缩 −100 MPa
自由端位移 / 求解-1.4881 mm解析解 -1.4881 mm
根部最大 |σxx|93.750 MPaI=853.333 mm⁴
固定端反力 / 反力矩100.00 N20.000 N·m
自由自由度平衡残差6.8e-10 N3.6e-11 N·m
浏览器实际组装并求解 Ku=f · 三维表面用 Hermite 插值接收结果小挠度梁理论 · 灰线为未变形几何 · 放大与色标不改变计算值

物理量各自回答什么问题

图上的颜色只有在变量与单位明确时才有意义。位移是长度,应变是长度相对变化,应力是内部截面上的力密度。它们存在推导关系,但不能相互替代。例如,端部位移最大,弯矩却在根部最大,所以“最红的位置”会随展示变量改变。

量符号 / 单位本例的含义与使用方式
载荷F / N输入的端部外力;向下为负
弹性模量E / Pa线性弹性材料的应力/应变比例;210 GPa=210×109 Pa
截面惯性矩I / m⁴截面几何参数;高度变化具有三次方影响
挠度w / m中性轴沿 Y 的位移;界面换算成 mm
转角θ / rad截面的斜率 w′;界面换算成 mrad
曲率κ / m−1w″;决定截面不同 y 位置的伸缩
轴向应变εxx / 1−yκ;με 表示百万分之一
轴向应力σxx / Pa−Eyκ;拉伸为正、压缩为负,界面换算成 MPa
反力与反力矩R / N、Mr / N·m约束施加到结构上的作用;用于整体平衡校核

三维实体的应力通常是对称二阶张量,包含三个法向分量与三个剪切分量。von Mises 等效应力由偏应力组合而来,常用于延性材料屈服判断;它是非负标量,不能表示拉压方向。在理想单轴应力假设下它等于 |σxx|,但本页展示的是带符号的弯曲正应力。本例不恢复横向剪应力,也不据此给出零件全场 von Mises 云图。

屈服强度、疲劳载荷谱、接触和缺口效应仍是独立输入。仅知道 E 或看到一张红色云图,不能宣称零件安全。要比较应力与材料许用值,先核对具体牌号、工况、模型假设和结果位置。

求解节点怎样对应到显示顶点

当前求解网格是一条中心线,显示网格是三维表面,节点数根本不同。对每个显示顶点保留未变形坐标 p0=(x,y,z),找到 x 所在的梁单元,以其四个自由度计算 w、θ、κ。小转角运动给出该表面点的位移 U=(−yθ,w,0)。这样同一截面的上下表面既共同向下移动,也随截面转角发生沿 X 的相反位移。

应力读取的是原始横向坐标 y,σxx=−Eyκ。先恢复物理量,再做色标归一化。色标饱和只让多个不同数值显示同一个端点色,绝不能把原始值截断。探针直接调用 sample(x),从求解结果计算应力和应变,不从截图颜色估算数值。

typescript
// createBeamScene.update 的连续数据路径(单位均为 m / Pa)
const section = result.sample(x); // FE 插值,不是再套解析位移公式
const displacement = [-y * section.theta, section.w, 0];
position.setXYZ(i,
  x + displayScale * displacement[0],
  y + displayScale * displacement[1], z);
const sigmaMPa = section.stress(y) / 1e6;
const t = Math.max(0, Math.min(1, 0.5 + sigmaMPa / (2 * legendLimit)));
// t 查蓝—白—红色标,然后写入第 i 条 color 记录。
position.needsUpdate = true;
color.needsUpdate = true;
geometry.computeVertexNormals();
geometry.computeBoundingSphere();

displayScale 是展示倍率:0 显示原几何,1 显示计算位移,10 用于看清弯曲。每次都从 p0 计算,不能把上一帧变形后的坐标继续累加。σxx 的对称色标保留正负,零值接近白色;|w| 用从零开始的顺序色标。两种变量的色标单位与范围同步变化。

对实体有限元,表面点可能通过原始 nodeId 直接引用节点位移;如果来自另一个细化网格,要保存 elementId 与形函数权重。求解器常在积分点计算应力,把它外推或平均到节点时必须标明策略。跨材料界面、接触面或几何尖角随意平均,会抹掉真实跳跃;拾取到的渲染顶点索引也未必等于求解器节点号。

缓冲数据如何变成屏幕像素

Three.js 的 BufferGeometry 保存 position、color 与三角索引。位置缓冲更新后上传给 GPU,顶点阶段把模型坐标变换到世界、相机和裁剪空间;三角形经过裁剪与光栅化,片元阶段得到插值颜色,深度测试决定哪个表面可见。BufferGeometry 官方文档描述这些顶点属性的组织方式。

应力云图用无光照的 MeshBasicMaterial,减少灯光让同一物理量显出不同亮度的干扰。CAD 外观材质则可以使用带光照的金属/粗糙度模型。场数据解释与真实外观展示可以切换,但不能让用户误以为阴影是应力降低。尖锐 CAD 边缘保留重复顶点记录,在颜色接缝、法线与 resultId 映射上维持一致。

顶点变形后重算包围体,避免相机裁剪或射线拾取仍使用旧范围;有光照的模式还要重算法线。只改数据缓冲,不重建相机。当前模型采用按需绘制、离屏停绘和卸载释放;生产大数据还应考虑二进制 TypedArray、分块加载、Worker 插值、GPU 位移以及结果版本校验。

对照复杂零件:模态形状为什么没有毫米幅值

真实泵支架模型提供了 40,928 个表面节点和 81,852 个三角形。下面保留三个上游预计算振型,用于观察复杂几何的场数据渲染。模态问题解的是 Kφ=ω²Mφ:M 是质量矩阵,ω 是固有角频率(rad/s),对应固有频率 fn=ω/(2π),单位 Hz;φ 是振型。密度 ρ(kg/m³)影响 M,因此会影响固有频率;密度没有出现在前面的静力梁求解中。

振型特征向量可以乘任意非零常数,仍满足特征值方程。本站把每个振型分别按其最大向量长度归一化,所以色标是无量纲形状信息。不同振型的红色区域不能用来比较实际响应幅值。实际动态响应需要激励、阻尼、初始条件和时间积分/频域求解。正弦动画也只有在时间尺度、频率与振幅真实定义后,才能叫作响应动画。

泵支架 · 位移场
场 / 01拖动旋转 · 滚动缩放 · 点击查询
归一化位移
01.00

正在读取网格与位移数据…

—预计算结果 · 各振型独立归一化 · 无实时求解

泵支架是 PyVista / SimScale 公开模态案例,本页实现结果转换、查看器与解释,原始模型与求解结果属于上游。本站没有该模型的 CAD 建模历史与完整求解设置,因此它适合作为后处理案例;可重建的 CAD 到静力解主链路由上面的悬臂基准提供。

两类错误要沿数据边界定位

整件形状正确,但位移差一千倍。 先读取 STEP 的单位和包围盒,再检查求解输入是否已经从 mm 转 m、输出是否又误转一次。显示归一化缩放也可能被写回模型。修复后核对 200 mm 长度、1.488095 mm 端位移、100 N 反力;不能只看截图是否“更像”。

云图数值正确,热点却出现在另一侧。 核查坐标轴、局部到全局的向量变换、nodeId 映射与几何版本。应力张量的坐标变换不能只交换三个法向值。用根部上下表面的 +93.75/−93.75 MPa 和中性轴零值做方向基准;同一位置在探针、导出与列表中必须一致。

网格越细,根部最大值越高。 对带尖角或理想点载荷的实体模型,这可能是应力奇异性,而不是自动获得更准确的安全结论。对位移、能量、反力和距奇异点一定距离的应力分别做收敛研究;检查是否应该建真实圆角、接触区域或分布载荷。本例梁模型的有限根部应力不会证明实体固定端局部应力也一样。

旧计算覆盖了新载荷。 大模型放进 Worker 后,每次请求携带 runId、geometryHash、载荷与材料版本。只接受当前 runId;新结果失败时保留上一份有效结果并标注旧工况。显示倍率和色标是另一个状态域,不应该触发物理求解或污染结果归档。

复现与交付依据

在本仓库中先运行使用者入口,再跟踪正式求解器:

bash
# Node >= 22.18,直接运行本站 TypeScript 求解器
node examples/stage-02-engineering/user_code/solve-beam.mjs
# CAD 生成需要 Python + CadQuery 2.6.1,建议独立虚拟环境
python -m pip install cadquery==2.6.1
python examples/stage-02-engineering/user_code/build_cad.py
npm run dev
# 打开 /zh/engineering/industrial

计算实验检查 1、2、10、20、40 单元的位移、应力、反力与自由自由度残差,并检查零载荷、载荷倍增、高度加倍后的 1/8 挠度和非法输入拒绝。对应实现是 solveBeam → sample → createBeamScene.update。当前固定矩形几何、线弹性、小转角、端力条件均随结果显示;页面的 2% 挠度/长度提示是本例复核阈值,不能当作普适理论边界。

交付一个工程后处理器时,应一起归档源 CAD 与哈希、求解网格、材料、约束、工况、求解器版本、单位和结果位置,最后再记录展示网格与插值策略。能够旋转三维模型只是绘图完成;位移与解析解、反力与平衡、应力与取样定义都对得上,读者才有依据解释那片红色。

资料核对:2026-10-10。数值推导采用上述梁单元教材;网页 API 以仓库锁定的 Three.js 与 MapLibre 版本为准。