MATLAB六面体有限元编程:从形函数到刚度矩阵与悬臂梁算例
2026/9/15 17:47:15 网站建设 项目流程

简介:这套基于MATLAB的有限元程序设计算例包,覆盖从杆系到空间块体的典型单元实现,适合正在学习有限元理论、需要结合代码验证矩阵推导的本科生与工程技术人员。压缩包内共56个文件,其中41个m脚本对应各算例主程序与子函数,6个doc文档为算例说明,另有少量dat数据、txt文本及asv自动备份文件,整体仅333KB,便于快速下载与本地运行。具体算例包括三梁平面框架(Beam2D2Node)、四杆桁架(Bar2D2Node)、基于三角形、四边形单元的矩形薄板、基于四面体、六面体单元的空间块体分析等,也包含斜支座处理等典型例题;通过说明文档与MATLAB代码联动,可直观对比不同单元类型的构造差异与计算结果。目前已有1249人学习下载,适合配合有限元课程或自学使用,是理解节点编号、单元刚度矩阵与后处理流程的实用参考。

1. 用 MATLAB 写有限元程序,六面体网格为什么比想象中更好上手

用 MATLAB 写有限元程序的人,第一眼看到 quad2d4node 这个函数名,很容易产生两种反应:一个认为它是二维四边形四节点单元,和六面体没有关系;另一个直接按字面猜测它是某种六面体单元的变种。实际在网上下载的带算例源码包里,quad2d4node 往往只是作者早期的平面单元函数,它的形函数结构一旦看明白,扩展成三维六面体八节点单元其实就是多乘一项的问题。本文围绕六面体 8 节点等参元,把形函数、Jacobian 矩阵、高斯积分、MATLAB 刚度矩阵实现和悬臂梁算例串成一条能直接跑通的路线,最后给出负 Jacobian 排查与分片验证的方法。适合刚读完有限元教材、想在 MATLAB 里亲手跑通第一个实体单元分析的工程师和学生。

2. 六面体有限元基础:8节点等参元的形函数与Jacobian矩阵

2.1 从quad2d4node到hex8:二维四边形到六面体的维度扩展

quad2d4node 的本质是二维四节点四边形单元的形函数实现,四个节点的局部坐标是 (-1,-1)、(1,-1)、(1,1)、(-1,1),每个节点对应一个双线性形函数。六面体 8 节点单元与它的亲缘关系非常直接:把 xi、eta 两个方向扩展到 xi、eta、zeta 三个方向,每个节点的形函数变成三个方向双线性因子的乘积,即 N_i = (1/8)(1+xi_i·xi)(1+eta_i·eta)(1+zeta_i·zeta)。这个单元就是常说的 hex8,也有人叫它三线性六面体单元。

从 quad2d4node 迁移到 hex8,差异集中在三处。一是形函数多乘一项 (1+zeta_i·zeta),因此形函数对局部坐标的导数矩阵 dNdxi 也增加了一行;二是高斯积分点从二维平面的 2x2 变成三维立方体里的 2x2x2;三是应变位移矩阵 B 从 3x8 变成 6x24,对应三个线应变 eps_x、eps_y、eps_z 和三个剪应变 gamma_xy、gamma_yz、gamma_xz。理解这三处差异之后,单元刚度矩阵的程序框架不需要做任何结构性修改,循环、组装、求解逻辑全部沿用。这也是为什么很多开源算例包里既有 quad2d4node,又有 hex8 版本,两个函数放在一起对比,代码骨架完全一致。

2.2 高斯积分:从一维2点积分到三维2x2x2

刚度矩阵中的积分项 K_e = ∫ B^T D B dV 没有显式原函数,程序里用高斯数值积分在单元内部逐点累加。一维两点高斯规则用两个积分点加权求和,对不超过三次的多项式可以精确积分;三维情况由三个方向的张量积得到,2x2x2 表示每个方向取两个点,共八个积分点。下面的表是常用的一维高斯积分点设置,三维权重直接取三个方向权重的乘积。

积分点数 n积分点位置 xi权重 w
102
2-1/sqrt(3), 1/sqrt(3)1, 1
3-sqrt(3/5), 0, sqrt(3/5)5/9, 8/9, 5/9

例如在 MATLAB 里实现一维两点积分,代码结构是:

gp = [-1/sqrt(3), 1/sqrt(3)]; % 2点高斯积分点 gw = [1, 1]; % 对应权重 W = 0; for i = 1:2 W = W + gw(i) * f(gp(i)); % 累加 f 在各积分点的加权值 end

代码里的 f 是任意被积函数,gp 和 gw 数量必须一致;换三点点只需把 gp 和 gw 替换成表中第三行。对六面体单元,嵌套三层循环分别遍历 xi、eta、zeta 方向的积分点,每个积分点的总权重就是三个方向权重的乘积。

2.3 Jacobian矩阵与单元坐标变换

形函数给出的是局部坐标下的插值关系,而单元刚度矩阵需要全局坐标下的导数,这个转换依赖 Jacobian 矩阵 J。J 的每一行是局部坐标对全局坐标的映射,具体计算用 dNdxi 乘以单元节点坐标矩阵 node_e。J 的行列式 det(J) 是局部体积到全局体积的缩放因子,是所有高斯积分点处体积分的基础。

det(J) 如果不是正值,单元刚度矩阵就会出现不正定问题,求解出的位移完全不可信。最典型的成因是单元节点顺序不符合逆时针约定,或者单元发生了内凹、扭转。类似薄壁圆筒有限元这类厚度方向尺寸很小的模型,径向只有一层单元时 det(J) 会非常小,计算出的应力常常对网格畸变特别敏感。所以在写单元函数时,我一般会在刚度矩阵循环里同时输出每个积分点的 detJ,先定位负值,再继续求解。

3. MATLAB实现六面体刚度矩阵:从quad2d4node到hex8

3.1 形函数与B矩阵:四节点循环改写为八节点

先写形函数函数,这个函数同时返回 N 和 dNdxi,后面的 hex8 单元刚度矩阵直接调用它。节点编号顺序必须固定:前四个节点是 zeta=-1 面上的四个角点,后四个是 zeta=1 面上的对应角点,每个面内按逆时针排列。

function [N, dNdxi] = shape_hex8(xi, eta, zeta) % 8节点六面体单元形函数 % xi, eta, zeta: 局部坐标,取值 [-1, 1] % N: 1x8 形函数值 % dNdxi: 3x8 形函数对局部坐标的导数 xi_i = [-1 1 1 -1 -1 1 1 -1]; eta_i = [-1 -1 1 1 -1 -1 1 1]; zeta_i = [-1 -1 -1 -1 1 1 1 1]; for i = 1:8 N(i) = (1/8) * (1 + xi_i(i)*xi) * (1 + eta_i(i)*eta) * (1 + zeta_i(i)*zeta); dNdxi(1,i) = (1/8) * xi_i(i) * (1 + eta_i(i)*eta) * (1 + zeta_i(i)*zeta); dNdxi(2,i) = (1/8) * eta_i(i) * (1 + xi_i(i)*xi) * (1 + zeta_i(i)*zeta); dNdxi(3,i) = (1/8) * zeta_i(i) * (1 + xi_i(i)*xi) * (1 + eta_i(i)*eta); end end

这里的 xi_i、eta_i、zeta_i 是八个节点的局部坐标值,与节点编号一一对应。不想手写三个数组的话,也可以把 2x2x2 角点坐标放进 8x3 矩阵,再用循环读出,但上面的写法更直观,也方便对照教材公式排错。

3.2 单元刚度矩阵的MATLAB代码与高斯积分循环

有了形函数,再写单元刚度矩阵。材料矩阵 D 用各向同性线弹性假设,ngp 控制每个方向的积分点数,默认 2 对应 2x2x2 完整积分。整个函数返回 24x24 的单元刚度矩阵,因为每个节点三个平动自由度。

function Ke = hex8_stiffness(node_e, E, nu, ngp) % 返回 24x24 六面体单元刚度矩阵 % node_e: 8x3 单元节点全局坐标 % E, nu: 弹性模量与泊松比 % ngp: 每个方向高斯积分点数量,通常取2 C = E/((1+nu)*(1-2*nu)) * ... [1-nu nu nu 0 0 0; nu 1-nu nu 0 0 0; nu nu 1-nu 0 0 0; 0 0 0 (1-2*nu)/2 0 0; 0 0 0 0 (1-2*nu)/2 0; 0 0 0 0 0 (1-2*nu)/2]; [gp, gw] = gauss_points(ngp); Ke = zeros(24, 24); for i = 1:ngp for j = 1:ngp for k = 1:ngp [N, dNdxi] = shape_hex8(gp(i), gp(j), gp(k)); J = dNdxi * node_e; % 3x3 Jacobian 矩阵 invJ = inv(J); dNdx = invJ' * dNdxi; % 全局坐标下的形函数导数,3x8 B = zeros(6, 24); for a = 1:8 B(:, 3*a-2:3*a) = ... [dNdx(1,a) 0 0; 0 dNdx(2,a) 0; 0 0 dNdx(3,a); dNdx(2,a) dNdx(1,a) 0; 0 dNdx(3,a) dNdx(2,a); dNdx(3,a) 0 dNdx(1,a)]; end detJ = det(J); Ke = Ke + gw(i)*gw(j)*gw(k) * (B'*C*B) * detJ; end end end end function [gp, gw] = gauss_points(ngp) % 一维高斯积分点和权重 if ngp == 1 gp = 0; gw = 2; elseif ngp == 2 gp = [-1/sqrt(3), 1/sqrt(3)]; gw = [1, 1]; else gp = [-sqrt(3/5), 0, sqrt(3/5)]; gw = [5/9, 8/9, 5/9]; end end

代码里 B 矩阵的每一列对应一个节点的三个自由度,排列顺序是 ux、uy、uz。dNdx = invJ' * dNdxi 这一步容易写错,注意是转置而不是直接左乘;矩阵乘法的维度对应关系是 (3x3)^T * (3x8),结果仍是 3x8。detJ 必须大于零,因此上面的代码没有做符号判断,实际用的时候建议在每个积分点处加一个 if detJ <= 0 的警告。

注意:单元节点顺序一旦颠倒,detJ 变负,求解出的刚度矩阵不正定。出现这种情况时先检查 node_e 的行顺序,而不是怀疑公式。

3.3 整体刚度矩阵组装与节点编号规则

单元刚度矩阵算出来后,要按自由度编号放入整体矩阵。MATLAB 里用稀疏矩阵存储可以显著降低内存占用,尤其是网格规模超过几千个节点时。整体刚度矩阵 K 的组装逻辑是遍历所有单元,把单元自由度编号映射到全局自由度编号。

% node: 节点坐标矩阵,size = nnode x 3 % elem: 单元连接矩阵,size = nelem x 8 ndof = 3 * size(node, 1); K = sparse(ndof, ndof); for e = 1:size(elem, 1) idx = elem(e, :); % 当前单元的8个节点编号 edof = zeros(24, 1); for a = 1:8 edof(3*a-2:3*a) = 3*idx(a)-2 : 3*idx(a); % 节点a的三个自由度 end Ke = hex8_stiffness(node(idx, :), E, nu, ngp); K(edof, edof) = K(edof, edof) + Ke; end

edof 的生成是组装最需要小心的部分。3*idx(a)-2 是该节点第一个自由度的全局编号,例如节点编号 1 对应自由度 1、2、3;节点编号 2 对应 4、5、6,依此类推。K(edof, edof) 必须用累加而不是赋值,因为一个节点通常被多个单元共享。

4. 算例:MATLAB悬臂梁六面体网格划分与求解

4.1 悬臂梁参数表与节点坐标生成

用悬臂梁做六面体有限元编程求解实例,是验证程序正确性最直观的做法。梁沿 x 方向长度为 1 米,截面为 0.1m x 0.1m 的正方形,左端固定,右端施加向下的总力 1000N。为了兼顾计算速度和网格形态,单元数取 16x4x4,即长度方向 16 个单元,高度和宽度方向各 4 个单元。

参数取值说明
梁长 Lx1.0 mx 方向
截面 Ly x Lz0.1 x 0.1 my 与 z 方向
单元数 nx x ny x nz16 x 4 x 4长度 x 高度 x 宽度
节点总数17 x 5 x 5 = 425与单元数对应
自由度数12753 x 节点总数
弹性模量 E200 GPa各向同性材料
泊松比 nu0.3各向同性材料
端部载荷-1000 N沿 y 方向均布在自由端面节点

网格生成函数的关键是节点编号按 i 方向最快、j 次之、k 最慢的方式推进,这样单元连接矩阵的规律性最强,也最容易检查和排错。

nx = 16; ny = 4; nz = 4; Lx = 1.0; Ly = 0.1; Lz = 0.1; dx = Lx/nx; dy = Ly/ny; dz = Lz/nz; % 生成节点坐标 nnode = (nx+1)*(ny+1)*(nz+1); node = zeros(nnode, 3); n = 0; for k = 0:nz for j = 0:ny for i = 0:nx n = n + 1; node(n, :) = [i*dx, j*dy, k*dz]; end end end % 生成单元连接矩阵 nelem = nx*ny*nz; elem = zeros(nelem, 8); nxy = (nx+1)*(ny+1); e = 0; for k = 0:nz-1 for j = 0:ny-1 for i = 0:nx-1 n0 = k*nxy + j*(nx+1) + i + 1; e = e + 1; elem(e, :) = [n0, n0+1, n0+1+(nx+1), n0+(nx+1), ... n0+nxy, n0+nxy+1, n0+nxy+1+(nx+1), n0+nxy+(nx+1)]; end end end

单元连接矩阵里的 n0 是当前单元的起始节点,括号里的偏移量按照“先 x 后 y 再 z”的顺序推导。比如 n0+1 是同一层右侧节点,n0+(nx+1) 是后一排节点,n0+nxy 是上一层对应节点。这个顺序和 shape_hex8 里的节点顺序保持一致,如果调整遍历顺序,必须同步修改形函数的坐标数组。

4.2 边界条件处理与线性方程组求解

边界条件分两步:找出左端面 x=0 的节点,约束这些节点的全部自由度;找出右端面 x=Lx 的节点,把 1000N 均分到每个节点上。MATLAB 的 find 可以按坐标筛选节点,再通过自由度编号映射到全局方程组。

ndof = 3 * size(node, 1); K = sparse(ndof, ndof); for e = 1:size(elem, 1) idx = elem(e, :); edof = zeros(24, 1); for a = 1:8 edof(3*a-2:3*a) = 3*idx(a)-2 : 3*idx(a); end Ke = hex8_stiffness(node(idx, :), 200e9, 0.3, 2); K(edof, edof) = K(edof, edof) + Ke; end % 固定端:x=0 的所有节点,六个自由度全部约束 fixed_nodes = find(node(:,1) <= 1e-10); fixed_dofs = []; for i = 1:length(fixed_nodes) fixed_dofs = [fixed_dofs, ... 3*fixed_nodes(i)-2, 3*fixed_nodes(i)-1, 3*fixed_nodes(i)]; end % 载荷:x=1.0 自由端面的节点,沿 y 方向均匀分配 load_nodes = find(node(:,1) >= 1.0 - 1e-10); n_load = length(load_nodes); F = sparse(ndof, 1); for i = 1:n_load F(3*load_nodes(i)-1) = -1000.0 / n_load; end % 求解自由位移 free_dofs = setdiff(1:ndof, fixed_dofs); d = zeros(ndof, 1); d(free_dofs) = K(free_dofs, free_dofs) \ F(free_dofs);

fprintf 输出最大位移时,可以直接查看 d 中对应自由度的值。固定端全部自由度约束后,整体矩阵可逆,反斜杠求解会走直接法,对 1275 个自由度来说计算时间在一秒以内。这里没有使用缩减自由度的方法,因为先组装完整 K 再取子块,代码更容易理解,也方便后续做约束方程扩展。

4.3 位移云图绘制与结果合理性检查

求解结束后,把位移分量从 d 里拆出来,再按节点坐标绘制变形后的散点云图。位移数量级很小,直接绘制几乎看不出变形,所以需要乘一个放大系数。

Ux = d(1:3:end); Uy = d(2:3:end); Uz = d(3:3:end); umag = sqrt(Ux.^2 + Uy.^2 + Uz.^2); % 变形放大到梁长的 10% 左右 scale = 0.1 / max(abs(Uy)); node_d = node + scale * [Ux, Uy, Uz]; figure; scatter3(node_d(:,1), node_d(:,2), node_d(:,3), 36, umag, 'filled'); axis equal; colorbar; xlabel('x'); ylabel('y'); zlabel('z');

理论上梁端挠度可以用欧拉梁公式估算:I = Ly^3 * Lz / 12 = 8.33e-6 m^4,δ = P·L^3 / (3·E·I),代入数值得到约 0.2mm。六面体实体单元在 16x4x4 网格下得到的端部位移会略大于这个值,因为实体单元的剪切变形和端部载荷的等效方式都会让挠度偏大。只要误差在百分之几到十几之间,程序逻辑就是对的。

5. 验证与进阶:负Jacobian排查、单单元测试与降阶积分

5.1 负Jacobian的快速检测

网格单元数较多时,逐单元打印 detJ 太啰嗦。常见做法是在组装循环里加一个中心积分点检测,单元中心点处 detJ 最容易反映整体畸变程度。下面的代码可以在组装前单独跑一遍:

[~, dNdxi0] = shape_hex8(0, 0, 0); % 单元中心点 for e = 1:size(elem,1) Jc = dNdxi0 * node(elem(e,:), :); if det(Jc) <= 1e-12 fprintf('单元 %d 负Jacobian: %e\n', e, det(Jc)); end end

detJ 出现负值最常见的原因是单元节点顺序错误,其次是单元内凹、长宽比过大。网格长宽比超过 10 时即使 detJ 为正,应力精度也会明显下降,尤其是薄壁圆筒有限元这类厚度方向尺寸远小于其他方向的模型。

5.2 单单元压缩测试与分片验证

整体算例跑通之前,先做一个单单元压缩测试。用坐标 [0,1]^3 的单个八节点立方体,左端面 x=0 全约束,右端面 x=1 的节点直接赋位移 ux=1e-3,然后求解支反力。理想情况下应力 σx = E·ε = 200e9×1e-3 = 2e8 Pa,支反力除以面积应等于 2e8。这个测试只需要一个单元,几分钟就能定位公式错误。

5.3 降阶积分、优化工具箱与批处理

程序跑通后,把 hex8_stiffness 的 ngp 改为 1 就是降阶积分。降阶积分能缓解弯曲问题中的剪切锁闭,但会引入零能模式,实体结构只用中心点积分时刚度偏柔。我一般对弯曲占优的梁板问题用 1 点积分,体积变形占优的实体问题保持 2x2x2。如果要做尺寸优化,把整个求解流程封装成函数 solve_cantilever(ny, nz),目标函数返回最大位移,再用优化工具箱的 fmincon 自动搜索截面尺寸;多个网格方案对比时用 parfor 替换 for 循环即可。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询