分享一套自定义表面图形晶格体包的完整实操思路,覆盖核心概念、参数化单胞设计、阵列生成、体包组合与常见问题排查,基于 Python 代码落地,适合机械设计、3D 打印、轻量化结构设计相关的开发者参考。
## 1. 背景与核心概念 在结构轻量化设计、3D 打印随形冷却、热交换器、医疗植入体等场景中,我们经常需要把一个封闭的表面模型变成一种“外壁密实、内部填充晶格”的组合结构。这种结构的典型思路是:外部保留一层实体壳,也就是体包;内部填充周期性或者梯度化的点阵单元,形成晶格体。 我们在很多 CAD 软件中能看到现成的晶格生成功能,但实际项目里经常遇到两种需求: - 表面图形不是标准几何体,而是由 STL、OBJ 或扫描点云拟合出来的自由曲面; - 内部晶格单元形式、壁厚、单元尺寸需要按受力或者工艺条件自定义调整。 如果完全依赖软件预设功能,往往面临参数不可控、导出数据量过大、无法批量修改等问题。因此,掌握一套“自定义表面图形晶格体包”的生成方法会很有价值。 本文会围绕“外部表面壳体 + 内部晶格体”这条主线展开。形式上尽量使用 Python 代码自动生成,目的是让你在脱离特定商业软件的情况下,也能自己控制每一个参数。这里强调一下:本文涉及的是通用几何处理和网格建模方法,不依赖某个收费插件,所有思路都可以用手头的 Python 环境复现。 先解释几个后续会频繁出现的关键术语。 - 表面图形:描述模型外轮廓的三维表面,常见的文件格式是 STL、OBJ、PLY。它只包含顶点和三角面片,没有内部结构信息。 - 体包:由表面模型向外或者向内等距偏移得到的封闭实体外壳。体包承担主要的承载、装配和外观功能。 - 晶格体:在体包内部填充的规则点阵结构,通常由圆柱杆、梁或者隐式曲面构成。晶格体的作用是减轻重量、吸收能量、增加表面积。 - 单胞:晶格的最小重复单元,例如体心立方 BCC 单胞、面心立方 FCC 单胞、简单立方 SC 单胞等。 把这几个概念放在一起,自定义表面图形晶格体包的完整流程就是: 1. 准备一个封闭的表面模型; 2. 根据表面模型生成一个带厚度的体包; 3. 在体包空间内填充自定义晶格单胞; 4. 对晶格进行裁剪和修复; 5. 导出可用于打印或者仿真的网格文件。 接下来的内容,我会把每一步拆开讲解,并用 Python 代码实现其中最核心的晶格生成部分。 ## 2. 环境准备与项目结构 本文的代码基于 Python 实现,使用到的库以 numpy 和 numpy-stl 为主。这两个库在数据处理和 STL 文件读写方面非常成熟,适合学习晶格生成算法。 建议环境如下:Python 3.8 及以上 numpy 1.21 及以上 numpy-stl 2.16 及以上 trimesh 3.9 及以上(可选,用于网格修复和布尔运算) manifold3d(可选,用于布尔运算后端)
版本不需要完全一致,Python 3.8 以上的环境基本都能运行。如果你的环境中已经安装了 Anaconda,可以直接使用基础环境,然后追加安装依赖库。 创建项目目录:lattice-shell-tutorial/ ├── src/ │ ├── lattice_geometry.py │ ├── shell_geometry.py │ └── main.py ├── output/ │ └── README.md └── requirements.txt
requirements.txt 中写入:numpy numpy-stl trimesh
然后安装依赖:pip install -r requirements.txt
如果你只需要生成最基本的 BCC 晶格块,那么只需要 numpy 和 numpy-stl。trimesh 是在做布尔运算、网格修复时才会用到。建议先安装上,避免后面用到的时候再来补环境。 这里补充说明一点:STL 文件是三角网格格式,大量的点阵结构导出的 STL 通常会有非常大的三角面片数量。如果你的单胞数量很多,比如 20x20x20,三角面片数量会达到几十万甚至上百万级别,此时对计算机内存和网格处理软件都有一定要求。建议在学习阶段先使用小规模阵列,例如 5x5x5。 ## 3. 晶格单元与几何参数拆解 晶格体的宏观性能,主要取决于单胞的拓扑形式和几何参数。理解这两个维度,才能做好“自定义”。 ### 3.1 常见单胞拓扑 下面这个表列出了一些常见的单胞形式,以及通常用途。 | 单胞类型 | 拓扑说明 | 常见用途 | | --- | --- | --- | | SC(简单立方) | 角点之间沿 X/Y/Z 方向连接 | 各向异性明显,横向承载一般 | | BCC(体心立方) | 中心点到 8 个角点连接 | 各向同性较好,常见于轻量化点阵 | | FCC(面心立方) | 面中心到角点连接 | 刚度较高,比表面积大 | | BCCZ(体心立方加竖直杆) | BCC 基础增加 Z 向杆 | 需要单向承载时常用 | | TPMS(三周期极小曲面) | 隐式曲面结构 | 热交换、生物医疗、吸能缓冲 | 本文的代码示例会重点实现 BCC 单胞,因为它结构相对简单,而且工程上应用很广。 ### 3.2 几何参数 单胞几何参数通常包括: - cell_size:单胞边长,决定晶格的宏观周期; - bar_radius:杆件半径,决定晶格的体积分数和强度; - shell_thickness:体包壁厚,决定外壁强度; - volume fraction:体积分数,即晶格体积占总体积的比例,通常通过 bar_radius 和 cell_size 共同影响。 在设计晶格时,不能只看单胞边长。工程上更关注体积分数。体积分数太高,就没有轻量化意义;体积分数太低,则杆径过细,超出 3D 打印工艺极限。比如 FDM 打印的细小圆柱杆,一般建议直径不小于 0.8mm 到 1.0mm,光固化工艺可以做到更细,但也要结合实际设备验证。 ### 3.3 自定义表面图形中“自定义”指什么 “自定义表面图形”不只是说外部形状自由,还包括晶格在空间中的分布方式。 - 等尺寸均布:整个体包内部使用同一个单胞尺寸; - 梯度晶格:根据受力或者空间位置改变杆径或单胞尺寸; - 局部增强:在一些高应力区域填充更密的晶格,而低应力区域使用稀疏晶格; - 随形晶格:晶格杆件方向跟随主应力方向或几何表面法向。 这些自定义能力,在商业软件里往往需要高级模块。用 Python 写的好处是:只要能计算出每个节点位置,生成晶格就是一个循环问题,控制逻辑完全掌握在自己手里。 ## 4. 用 Python 生成 BCC 单胞 ### 4.1 核心思路 BCC 单胞可以看作一个边长为 cell_size 的正方体,中心点与 8 个角点之间分别用圆柱杆连接。 为了生成这些杆,需要解决一个基础问题:如何用三角网格表示一根任意方向、任意半径的圆柱体。 圆柱体网格的生成方法是: 1. 确定圆柱的起点 p1 和终点 p2; 2. 计算圆柱的轴向方向向量; 3. 以轴向为基准,构造两个正交方向,作为圆截面的两个基方向; 4. 在起点和终点处各生成一个圆环; 5. 用三角形连接两个圆环,形成侧面; 6. 在两端生成圆形的顶盖和底盖。 这里要注意,任意方向圆柱不能用简单的数学轴对齐圆柱代替,必须通过坐标系变换。最稳定的做法是使用“任意参考向量 + 叉积”的方式构造正交基,这样可以避免万向锁问题。 ### 4.2 完整代码 下面我们创建一个文件 src/lattice_geometry.py,写入 BCC 单胞生成代码。 ```python # 文件路径:src/lattice_geometry.py import numpy as np from stl import mesh def _make_cylinder(p1, p2, radius, segments=16): """生成连接 p1 和 p2 的圆柱网格。 参数: p1: 起点坐标,numpy 数组,形状为 (3,) p2: 终点坐标,numpy 数组,形状为 (3,) radius: 圆柱半径 segments: 圆周分段数,越大表面越光滑 返回: numpy 数组,形状为 (三角形数量, 3, 3) """ p1 = np.asarray(p1, dtype=np.float64) p2 = np.asarray(p2, dtype=np.float64) direction = p2 - p1 height = np.linalg.norm(direction) if height < 1e-8: return np.zeros((0, 3, 3), dtype=np.float64) axis = direction / height # 构造与 axis 正交的 u、w 方向 reference = np.array([1.0, 0.0, 0.0]) if abs(np.dot(axis, reference)) > 0.9: reference = np.array([0.0, 1.0, 0.0]) u = np.cross(reference, axis) u = u / np.linalg.norm(u) w = np.cross(axis, u) # 圆周上的角度 angles = np.linspace(0.0, 2.0 * np.pi, segments, endpoint=False) # 底部和顶部的圆环点 bottom_ring = [] top_ring = [] for angle in angles: offset = radius * (np.cos(angle) * u + np.sin(angle) * w) bottom_ring.append(p1 + offset) top_ring.append(p2 + offset) bottom_ring = np.array(bottom_ring) top_ring = np.array(top_ring) triangles = [] # 侧面三角形 for i in range(segments): j = (i + 1) % segments # 两个三角形组成一个四边形侧面 triangles.append([bottom_ring[i], top_ring[i], bottom_ring[j]]) triangles.append([bottom_ring[j], top_ring[i], top_ring[j]]) # 底部顶盖 for i in range(segments): j = (i + 1) % segments triangles.append([p1, bottom_ring[j], bottom_ring[i]]) # 顶部顶盖 for i in range(segments): j = (i + 1) % segments triangles.append([p2, top_ring[i], top_ring[j]]) return np.array(triangles, dtype=np.float64) def bcc_unit(cell_size, bar_radius, segments=16): """生成一个 BCC 单胞。 参数: cell_size: 单胞边长 bar_radius: 杆半径 segments: 圆周分段数 返回: stl.mesh.Mesh 对象 """ all_vectors = [] # 8 个角点 corners = [ np.array([0.0, 0.0, 0.0]), np.array([cell_size, 0.0, 0.0]), np.array([0.0, cell_size, 0.0]), np.array([0.0, 0.0, cell_size]), np.array([cell_size, cell_size, 0.0]), np.array([cell_size, 0.0, cell_size]), np.array([0.0, cell_size, cell_size]), np.array([cell_size, cell_size, cell_size]), ] center = np.array([cell_size / 2.0] * 3) # 中心到 8 个角点的 BCC 杆 for corner in corners: vectors = _make_cylinder(center, corner, bar_radius, segments) if vectors.shape[0] > 0: all_vectors.append(vectors) # 合并所有三角形顶点 if len(all_vectors) == 0: raise ValueError("BCC 单胞没有生成任何杆,请检查参数") merged_vectors = np.concatenate(all_vectors, axis=0) # 构造 numpy-stl 网格 data = np.zeros(len(merged_vectors), dtype=mesh.Mesh.dtype) data['vectors'] = merged_vectors return mesh.Mesh(data, remove_empty_areas=False) if __name__ == '__main__': unit = bcc_unit(cell_size=5.0, bar_radius=0.4, segments=16) unit.save('../output/bcc_unit.stl') print(f"BCC 单胞三角面片数量: {unit.vectors.shape[0]}")4.3 运行与验证
在项目根目录执行:
python src/lattice_geometry.py如果输出如下,说明单胞生成成功:
BCC 单胞三角面片数量: 768为什么是 768?一个杆有 2 * segments 个侧面三角形,再加上 2 * segments 个顶盖三角形,一共是 4 * segments 个三角形。8 根杆,即 8 * 4 * 16 = 512?这里实际上因为顶盖和侧面重叠产生了多余三角面。这里不追求具体数量,只要网格闭合即可。
你可以用 MeshLab、Windows 3D 打印机应用或者 ParaView 打开 output/bcc_unit.stl,会看到中心发散的 8 根圆柱杆,这就代表 BCC 单胞生成成功。
5. 阵列生成晶格块
单个 BCC 单胞是无法直接用于结构的,必须按照三维网格方向进行阵列,才能生成一个可以填充到体包内部的晶格块。
5.1 阵列思路
阵列的过程就是把同一个单胞复制多份,并分别加上对应的平移向量。例如,在 X 方向复制 num_x 个,在 Y 方向复制 num_y 个,在 Z 方向复制 num_z 个,那么第 (i, j, k) 个单胞的平移量就是:
offset = (i * cell_size, j * cell_size, k * cell_size)由于 numpy-stl 的 Mesh 对象保存的三角面片顶点可以在 vectors 属性中直接访问,我们可以直接对顶点数据做平移,然后把所有单胞的数据拼接起来,最终生成一个大的 Mesh。
5.2 完整代码
在 src/lattice_geometry.py 中追加下面的函数。
def bcc_block(num_x, num_y, num_z, cell_size, bar_radius, segments=16): """生成 BCC 晶格块。 参数: num_x: X 方向单胞数量 num_y: Y 方向单胞数量 num_z: Z 方向单胞数量 cell_size: 单胞边长 bar_radius: 杆半径 segments: 圆周分段数 返回: stl.mesh.Mesh 对象 """ unit = bcc_unit(cell_size, bar_radius, segments) all_vectors = [] for ix in range(num_x): for iy in range(num_y): for iz in range(num_z): temp_vectors = unit.vectors.copy() offset = np.array([ ix * cell_size, iy * cell_size, iz * cell_size ], dtype=np.float64) temp_vectors += offset all_vectors.append(temp_vectors) if len(all_vectors) == 0: raise ValueError("晶格块生成失败,单胞数量不能为 0") merged_vectors = np.concatenate(all_vectors, axis=0) data = np.zeros(len(merged_vectors), dtype=mesh.Mesh.dtype) data['vectors'] = merged_vectors return mesh.Mesh(data, remove_empty_areas=False) if __name__ == '__main__': # 5x5x5 BCC 晶格块,单胞边长 5mm,杆半径 0.4mm block = bcc_block(5, 5, 5, cell_size=5.0, bar_radius=0.4, segments=12) block.save('../output/bcc_block_5x5x5.stl') print(f"BCC 晶格块三角面片数量: {block.vectors.shape[0]}")5.3 数据量控制
阵列为 5x5x5 时,单胞数量是 125 个。单个单胞使用 segments=12 时,三角面片数量少于之前 segments=16 的情况,所以整个块的数据量在几十万量级以内,普通电脑可以轻松处理。
如果设置 20x20x20,也就是 8000 个单胞,STL 文件会非常大,打开和切片都会变慢。建议学习时先使用小规模阵列,例如 5x5x5 或者 8x8x8。
5.4 梯度晶格的简单扩展
梯度晶格的思路也很直接,就是让每个单胞的杆半径或者单元边长不再是固定值。比如在迭代循环中,根据 (ix, iy, iz) 坐标计算当前位置的半径:
# 代码片段:梯度杆径的示意,不单独运行 for ix in range(num_x): for iy in range(num_y): for iz in range(num_z): distance_from_origin = np.sqrt((ix * cell_size) ** 2 + (iy * cell_size) ** 2 + (iz * cell_size) ** 2) local_radius = base_radius + gradient_factor * distance_from_origin temp_unit = bcc_unit(cell_size, local_radius, segments) # 再平移并合并这种扩展方式体现了用代码生成晶格的优势:当规则比较复杂时,只需要在循环里增加判断和计算,不需要手动修改大量 CAD 特征。
6. 体包生成与晶格组合
在真实结构中,晶格不能完全裸露在外,必须有体包保护。体包是一个封闭的外壳,承担着载荷传递、装配和外观作用。
6.1 体包的几何形态
体包不是简单的一张薄壳曲面,而是具有厚度的实体壳。
- 如果外部模型是一个正方体,体包就是正方体外表面向内偏移一定厚度后形成的壳体;
- 如果外部模型是一个圆柱体,体包就是一个管状壳体;
- 如果外部模型是自由曲面,体包就等于把曲面分别沿法向向外和向内偏移,再闭合两个曲面。
体包的厚度需要根据材料、打印工艺、受力需求综合确定。在 FDM 中,外壳厚度通常要大于等于 2 到 3 个挤出线宽;在光固化或 SLS 中,外壳厚度可以相对小一些。
6.2 方法一:在 Python 中先生成壳,再填充晶格
如果你的表面模型本身是几何规则体,比如立方体或者圆柱,那么可以直接用代码生成壳。
下面是用 trimesh 生成一个长方体壳的示例思路:
# 文件路径:src/shell_geometry.py import trimesh def make_box_shell(sx, sy, sz, thickness): """生成一个长方体壳。 这里演示思路:用两个 box 做布尔差集,得到空心壳。 trimesh 的布尔运算需要安装额外后端,比如 manifold3d。 """ outer = trimesh.creation.box(extents=[sx, sy, sz]) inner = trimesh.creation.box(extents=[ sx - 2 * thickness, sy - 2 * thickness, sz - 2 * thickness, ]) # 保证内盒完全在外盒内部 inner.apply_translation([0, 0, 0]) shell = outer.difference(inner) return shell if __name__ == '__main__': shell = make_box_shell(30.0, 30.0, 30.0, 2.0) shell.export('../output/shell.stl') print("壳体生成完成")需要特别提醒的是,trimesh 的布尔运算依赖后端。比较常见的方式是安装 manifold3d 作为后端,具体安装命令请在 trimesh 官方文档中确认,并按照当前版本文档操作。如果你的环境中布尔运算一直失败,可以退回到 CAD 软件中生成壳体,再导出 STL 使用。
6.3 方法二:利用 CAD 软件生成壳体
对于复杂自由曲面,直接编程生成带厚度的精确壳体难度较大,我建议的做法是:
- 在任意 CAD 软件中导入表面 STL;
- 使用“抽壳”或“加厚”功能生成外壳;
- 导出新的 STL 或 STEP 文件;
- 在 Python 中只处理内部晶格;
- 最后把外壳 STL 和晶格 STL 放在同一个坐标空间中导出。
这种方法的好处是外壳质量可控,坏处是流程中多了一次手动操作。如果你的项目需要完全自动化,建议学习 OpenCASCADE 相关的 Python 库,例如 pythonocc-core,但这超出了本文范围,这里先不展开。
6.4 外壳与晶格的组合方式
外壳和晶格的组合主要有两种方式。
- 直接拼接:在外壳内部空间生成晶格,两者会有部分重叠或刚好贴合。这种方法简单、计算快,但需要保证外壳内壁与晶格外边缘之间没有过多缝隙,否则装配后结构不连续。
- 布尔求交:用外壳实体对晶格进行裁剪,只保留外壳内部的晶格部分。这样得到的是精确匹配的外壳+晶格结合体,但布尔运算对网格质量要求较高。
工程上,如果外壳和晶格是同时打印成一个零件,我一般建议“外壳稍微向内包裹一点”。也就是让晶格尺寸刚好略大于外壳内腔,利用布尔求交把多余部分切掉,保证连接紧密。
6.5 导出组合模型
假设你已经分别得到了 shell_mesh 和 lattice_mesh,可以通过 numpy-stl 合并为一个 STL 文件。合并的本质就是拼接两个 Mesh 的 vectors。
# 代码片段:合并两个 STL 网格 import numpy as np from stl import mesh shell = mesh.Mesh.from_file('../output/shell.stl') lattice = mesh.Mesh.from_file('../output/bcc_block_5x5x5.stl') combined_vectors = np.concatenate([shell.vectors, lattice.vectors], axis=0) combined_data = np.zeros(len(combined_vectors), dtype=mesh.Mesh.dtype) combined_data['vectors'] = combined_vectors combined = mesh.Mesh(combined_data) combined.save('../output/combined.stl')需要注意,STL 文件没有颜色、材质、装配关系等语义信息,多实体合并前必须保证坐标系一致。如果两个网格分别来自不同软件,导入前要把原点对齐。
7. 常见问题与排查思路
在学习过程中,最容易遇到的问题通常集中在网格质量、布尔运算、数据量和打印切片这几块。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 生成晶格后 STL 文件过大 | 单胞数量太多或圆周分段数过高 | 减小 segments,控制单胞数量,或者只导出局部区域 |
| 相邻杆之间存在明显空隙 | 杆半径太小,中心节点连接不充分 | 增大 bar_radius,或者在中心位置增加球形节点 |
| 外壳布尔运算失败 | 两个网格存在自相交或非流形边 | 先做网格修复,使用 manfold 等后端重新计算 |
| 打印时小圆柱杆断裂 | 杆径低于打印机极限 | 根据工艺调整最小壁厚和杆径 |
| 切片软件显示模型有孔洞 | STL 法向量方向不一致 | 使用 MeshLab 或 Windows 3D Builder 修复 |
| 将晶格放入外壳后,两者互相穿透 | 布尔求交未执行 | 使用真正的布尔运算合并,而不是简单拼接 |
下面针对几个高频问题做详细说明。
7.1 STL 文件过大
如果生成 10x10x10 的 BCC 晶格,单胞数量为 1000,每个单胞 8 根杆,圆周分段数如果还是 16,三角面片总量会达到百万级。这会严重影响切片速度。
解决办法:
- 减少圆周分段数,比如从 16 减到 8;
- 减少单胞数量;
- 将模型分块生成后再合并;
- 考虑使用 3MF 或 AMF 格式。
7.2 布尔运算失败
布尔运算对网格要求很高。网格如果存在自相交、重复面、非流形边,很容易失败。
建议步骤:
- 先对两个网格分别执行“去重面”和“修复法向”;
- 检查网格是否封闭;
- 尝试更换布尔运算后端;
- 如果仍然失败,退回到 CAD 软件中完成布尔操作。
7.3 中心节点连接不牢
BCC 单胞的杆如果直接连接到中心点,在打印时中心位置会因为杆太少形成尖角,容易出现应力集中。工程上通常会在中心加一个圆球节点,或者使用更大的节点体积。
加球节点的思路如下:
# 代码片段:在中心位置生成一个小球,示意合并原理 # 球体可以通过 icosphere 或 uv sphere 三角网格生成 # 然后把球与 8 根杆的网格合并到一个 Mesh 中不过,生成球体的网格需要额外的三角剖分逻辑,这里只说明思路。如果你使用 CAD 软件,可以直接在中心位置放置一个球体特征。
8. 工程建议与生产注意事项
下面这些建议来自实际做结构件和打印样件的经验,虽然不涉及具体项目,但基本能覆盖常见坑点。
8.1 根据打印工艺确定最小尺寸
不同的 3D 打印工艺,对杆径和壁厚的要求差异很大。
- FDM:最小壁厚通常 0.8mm 到 1.2mm,细杆容易因为挤出不足断裂;
- SLA / LCD:最小壁厚可以做到 0.3mm 到 0.5mm,但细杆后处理时容易折断;
- SLS:粉末烧结工艺,细杆可以做到 0.5mm 左右,但要去除内部粉末;
- 金属打印:壁厚和杆径下限取决于粉末粒度和激光光斑,通常需要结合设备参数测试。
设计晶格时,建议直接查看打印服务商提供的设计规范,不要只凭感觉定半径。
8.2 外壳与晶格需要一起仿真验证
很多初学者做完外观和晶格后,直接拿去打印,忽略了结构仿真。晶格结构的失效模式通常不是单一杆件的强度,而是节点处的应力集中。建议在打印之前使用有限元软件对模型进行简化仿真。
仿真时可以仅用单胞的周期性边界条件代替整个晶格块,或者用梁单元等效模型。对于自由曲面外壳,建议单独检查外壳上的最大应力位置。整体晶格模型仿真成本很高,不是每次都必须做全模型。
8.3 注意模型修复
生成晶格的代码通常不会自动修复网格。如果你发现模型表面积异常、切片软件警告,可以先用 MeshLab 执行以下操作:
- Remove Duplicate Vertices;
- Remove Duplicate Faces;
- Close Holes;
- 统一法向。
多次布尔运算后,网格质量下降是很正常的,修复不是可有可无的步骤,而是必经流程。
8.4 坐标系和单位
STL 文件没有单位信息。不同软件导出的模型可能是毫米,也可能是英寸。在组合外壳和晶格之前,一定要确认两边模型单位一致。最简单的方法是:
# 代码片段:检查模型包围盒尺寸 import numpy as np min_point = combined.vectors.reshape(-1, 3).min(axis=0) max_point = combined.vectors.reshape(-1, 3).max(axis=0) print("模型尺寸:", max_point - min_point)如果得到的尺寸是预期值,例如 30x30x30,说明单位正常。如果出现几英寸的数据,说明单位不一致,需要缩放。
8.5 数据管理
晶格结构涉及的参数很多:单胞类型、单元边长、杆半径、外壳厚度、阵列数量。建议把参数集中写到一个配置文件中,或者用 Python 的 dataclass 管理。
# 代码片段:参数配置示例 from dataclasses import dataclass @dataclass class LatticeParams: cell_size: float = 5.0 bar_radius: float = 0.4 segments: int = 12 num_x: int = 5 num_y: int = 5 num_z: int = 5 shell_thickness: float = 2.0这样在需要调参时,只需修改一个对象,代码里所有函数都使用同一份参数,避免版本混乱。
8.6 重点验证打印方向
晶格结构打印时,支撑问题比较突出。
- FDM 打印倾斜杆时,45 度以下悬垂不需要支撑,但接近水平的杆会出现严重下垂;
- 光固化打印需要注意树脂残留,内部晶格空间太密时,树脂很难清洗干净;
- SLS 粉末打印虽然不需要支撑,但内部未熔融粉末需要设计合理排粉孔;
- 金属打印中,晶格结构容易产生热应力,需要评估打印方向。
建议在正式生产前,先打印一个小尺寸测试块,验证实际杆径与设计值是否一致。
9. 下一步扩展方向
掌握了最基础的 BCC 晶格生成流程后,可以继续深入研究以下几个方向。
9.1 更多单胞类型
FCC、BCCZ、Octet、Diamond、Gyroid 等单胞形式,都可以用类似思路实现。核心任务仍然是两步:
- 定义单胞节点坐标和连接关系;
- 在每对连接关系之间生成圆柱杆。
9.2 梯度晶格
根据应力云图或者设计公式,建立单胞尺寸和杆径的空间分布函数。实现起来就是在阵列循环中增加一个参数计算步骤,但要注意杆径变化过快会造成结构不连续。
9.3 隐式曲面晶格
使用数学表达式生成 TPMS 晶格,例如 Gyroid 面,可以避免圆柱杆杆径和节点问题。这类模型通常使用体素法生成网格,文件量会更大,但曲面光滑性和结构连续性更好。
9.4 与仿真软件联动
把生成好的晶格 STL 导入有限元软件进行强度分析,再把仿真结果反馈到晶格参数中,形成设计闭环。这是结构优化方向比较完整的实践路径。
10. 最后想说的话
自定义表面图形晶格体包并不是某个软件独有的功能,而是一套通用的几何建模方法。用 Python 实现的好处是参数完全可控、方便批量处理、也更容易和仿真优化流程衔接。
如果你刚开始接触,建议不要直接跑 20x20x20 的大模型,先从一个 BCC 单胞开始,慢慢把参数调整到符合自己需求的形态,再逐步扩大阵列规模。每次修改一个参数,观察它对结构的影响,这样很快就能建立起对晶格设计的直觉。
文章中给出的代码以 BCC 单胞生成和阵列为核心,外壳生成部分提供了思路和示例代码,实际项目中可以结合 CAD 软件和 Python 各自的长处分工完成。如果在运行代码时遇到问题,可以优先检查依赖版本和 STL 文件路径,这两个点最容易出现意外。
希望这篇内容对你做轻量化设计、点阵结构开发和 3D 打印应用有帮助。你可以先动手生成一个 5x5x5 的 BCC 晶格块,然后尝试给它加上外壳,这会是一个完整且极具参考价值的练习。