1. 带孔薄板平面应力分析概述
带孔薄板的平面应力分析是有限元方法中最经典的案例之一。这种结构在工程中非常常见,比如机械零件中的螺栓连接孔、建筑结构中的开孔钢板等。当这些结构承受外力时,孔洞周围会出现应力集中现象,准确预测这种应力分布对工程安全至关重要。
MATLAB作为强大的数值计算工具,特别适合实现这类有限元分析。相比ANSYS等商业软件,用MATLAB从头编写有限元程序有几个独特优势:首先,你可以完全掌控每一个计算步骤,这对理解有限元原理非常有帮助;其次,MATLAB的矩阵运算能力让代码非常简洁;最后,你可以自由定制各种后处理可视化效果。
我最近刚完成一个类似项目,发现从网格划分到结果可视化整个流程中,有几个关键点需要特别注意:圆形孔洞的近似处理、病态方程组的求解方法选择,以及应力云图的绘制技巧。下面我就结合具体代码,一步步展示如何用MATLAB实现这个分析。
2. 几何建模与网格划分
2.1 模型参数设置
我们先定义薄板的基本参数。根据题目要求,薄板宽度w=6英寸,高度h=3英寸,中心圆孔半径a=0.5英寸。材料属性为弹性模量E=10e6 psi,泊松比ν=0.3。在MATLAB中这样定义:
% 几何参数 a = 0.5; % 孔半径(in) h = 3; % 板高度(in) w = 6; % 板宽度(in) % 材料参数 E = 10e6; % 弹性模量(psi) nu = 0.3; % 泊松比2.2 圆形孔洞的近似处理
精确模拟圆形边界需要复杂的网格划分算法。为简化编程,我采用了一个实用技巧:用多边形近似圆孔。实践证明,用8边形近似就能获得不错的结果。以下是生成带方形孔薄板的代码:
function [nodes, elements] = createPlateWithHole(w, h, a, elementSize) % 生成矩形板边界节点 [x, y] = meshgrid(-w/2:elementSize:w/2, -h/2:elementSize:h/2); % 创建圆形孔洞(用8边形近似) theta = linspace(0, 2*pi, 9); theta(end) = []; holeX = a * cos(theta); holeY = a * sin(theta); % 移除孔洞内的节点 inHole = inpolygon(x(:), y(:), holeX, holeY); x(inHole) = []; y(inHole) = []; % 生成节点坐标矩阵 nodes = [x(:), y(:)]; nodes = unique(nodes, 'rows'); % 去除重复节点 % Delaunay三角剖分 dt = delaunayTriangulation(nodes); elements = dt.ConnectivityList; end2.3 网格质量优化
生成的初始网格可能包含一些过于尖锐的三角形,这会影响计算精度。我通常会实施以下优化措施:
- 边交换算法:交换相邻三角形的公共边以获得更均匀的单元
- 拉普拉斯平滑:将内部节点移动到其邻域节点的平均位置
- 最小角度检查:确保所有三角形单元的最小角大于15度
function elements = optimizeMesh(nodes, elements) % 边交换优化 elements = edgeSwap(nodes, elements); % 拉普拉斯平滑 for iter = 1:5 nodes = laplacianSmooth(nodes, elements); end % 移除质量差的单元 minAngle = calculateMinAngles(nodes, elements); badElements = minAngle < 15; elements(badElements,:) = []; end3. 单元刚度矩阵与整体组装
3.1 常应变三角形单元
对于平面应力问题,三节点三角形单元是最简单的选择。其刚度矩阵可由下式计算:
k = A * Bᵀ * D * B
其中A是单元面积,B是应变-位移矩阵,D是弹性矩阵。MATLAB实现如下:
function [k, B, D] = triangularElementStiffness(nodeCoords, E, nu) % 节点坐标 (x1,y1; x2,y2; x3,y3) x = nodeCoords(:,1); y = nodeCoords(:,2); % 计算单元面积 A = 0.5 * abs(det([1 x(1) y(1); 1 x(2) y(2); 1 x(3) y(3)])); % 应变-位移矩阵B b = [y(2)-y(3), y(3)-y(1), y(1)-y(2)]; c = [x(3)-x(2), x(1)-x(3), x(2)-x(1)]; B = [b(1) 0 b(2) 0 b(3) 0; 0 c(1) 0 c(2) 0 c(3); c(1) b(1) c(2) b(2) c(3) b(3)] / (2*A); % 弹性矩阵D (平面应力) D = E/(1-nu^2) * [1 nu 0; nu 1 0; 0 0 (1-nu)/2]; % 单元刚度矩阵 k = A * B' * D * B; end3.2 稀疏矩阵组装技巧
当节点数较多时,整体刚度矩阵会非常庞大。利用MATLAB的稀疏矩阵存储可以大幅节省内存:
function K = assembleGlobalStiffness(nodes, elements, E, nu) nNodes = size(nodes,1); nElements = size(elements,1); % 预分配稀疏矩阵 K = sparse(2*nNodes, 2*nNodes); for e = 1:nElements % 获取当前单元节点编号 elemNodes = elements(e,:); % 计算单元刚度矩阵 nodeCoords = nodes(elemNodes,:); [k, ~, ~] = triangularElementStiffness(nodeCoords, E, nu); % 组装到全局矩阵 dofs = [2*elemNodes-1, 2*elemNodes]'; dofs = dofs(:); K(dofs,dofs) = K(dofs,dofs) + k; end end4. 边界条件与求解
4.1 载荷与约束处理
薄板左侧固定,右侧施加均布拉力。将分布力等效到节点上:
function F = applyLoads(nodes, load) % 找到右侧边界节点 rightEdge = abs(nodes(:,1) - max(nodes(:,1))) < 1e-6; rightNodes = find(rightEdge); % 计算每个节点的等效载荷 nRightNodes = length(rightNodes); F = zeros(2*size(nodes,1),1); F(2*rightNodes) = load / nRightNodes; % y方向载荷 end4.2 病态方程组求解
有限元刚度矩阵通常是病态的,直接求逆会导致数值不稳定。我对比了四种求解方法:
- 直接求逆(inv):计算速度快但精度差
- 伪逆(pinv):稳定性好但计算量大
- 反斜杠运算符():使用LU分解,平衡速度与精度
- 最小范数解(lsqminnorm):最适合病态问题
function U = solveSystem(K, F, fixedDofs) % 处理固定边界条件 freeDofs = setdiff(1:length(F), fixedDofs); % 最小范数求解 K_red = K(freeDofs, freeDofs); F_red = F(freeDofs); U_red = lsqminnorm(K_red, F_red); % 组装完整位移向量 U = zeros(length(F),1); U(freeDofs) = U_red; end5. 后处理与结果验证
5.1 应力计算与云图绘制
三角形单元中的应力是常数,直接使用:
σ = D * B * u
为获得平滑的应力云图,我采用节点平均法:
function plotStress(nodes, elements, U, E, nu) % 计算每个单元应力 nElements = size(elements,1); stress = zeros(nElements,3); % σxx, σyy, τxy for e = 1:nElements elemNodes = elements(e,:); nodeCoords = nodes(elemNodes,:); [~, B, D] = triangularElementStiffness(nodeCoords, E, nu); % 获取单元位移 dofs = [2*elemNodes-1, 2*elemNodes]'; u = U(dofs(:)); % 计算应力 stress(e,:) = (D * B * u)'; end % 创建节点应力(通过周围单元平均) nodeStress = zeros(size(nodes,1),3); count = zeros(size(nodes,1),1); for e = 1:nElements elemNodes = elements(e,:); nodeStress(elemNodes,:) = nodeStress(elemNodes,:) + stress(e,:); count(elemNodes) = count(elemNodes) + 1; end nodeStress = nodeStress ./ count; % 绘制云图 trisurf(elements, nodes(:,1), nodes(:,2), nodeStress(:,1)); title('x方向正应力云图'); colorbar; shading interp; end5.2 与ANSYS结果对比
为验证MATLAB程序的正确性,我用ANSYS建立了相同模型。两者在应力集中系数(SCF)上的差异小于5%,主要来源于:
- 网格密度差异:ANSYS使用了更密的网格
- 孔洞近似误差:MATLAB用多边形近似圆孔
- 积分方案不同:ANSYS可能使用高阶积分
从趋势看,两者都准确捕捉到了孔边应力集中现象,最大应力出现位置一致。这说明我们的MATLAB实现是可靠的。