Codex实战:二维板材排料优化与SVG可视化
先问一个问题:你见过定制家具厂或木工房的排料过程吗?师傅通常拿着一沓打印出来的板件清单,用纸片在电脑屏幕前面反复摆放,或者干脆对着大板尺寸在纸面画草稿,试试这块能不能塞进去、那块要不要旋转一下方向。这种“纸片试排”的方式在板件少的时候还能应付,一旦到了十几个非标尺寸的柜体板同时下料,排出来的结果往往很浪费,边角料一大块一大块地剩在那边,最后只能当废料处理。
我最近把这个问题交给 Codex 跑了一遍,效果让我有点意外:12 块柜体板,输入尺寸、设定好锯缝和板材规格,Codex 生成的启发式排料程序能自动完成摆放,并在 SVG 可视化结果里直观展示每一张大板的切割位置,边角料被集中挤压到剩余空间里。整个过程不复杂,但完整走了一遍之后,你会发现 AI 干预现实工作的方式,并不是替你做木工,而是替你把“计算最优排布”这件事变成了一段可运行、可校验的代码。本文就把这个过程完整拆解出来,适合对 AI Agent 编程感兴趣的开发者,也适合想了解“排料优化”这个经典工业问题怎么落地的朋友。
1. 先看问题:木工排料到底难在哪
1.1 现场为什么还在“纸片试排”
板式家具的生产流程里,排料是连接订单和生产之间的关键步骤。客户的订单到了工厂,拆单员会把柜体拆成一块一块的板件:侧板、顶板、层板、门板、背板,每一块都有长、宽、数量,甚至还有纹理方向要求。板材供应商给到工厂的是标准规格的大板,常见的有 1220mm × 2440mm、1220mm × 2800mm 等。所谓“排料”,就是决定这些小板件在整张大板上怎么摆放,使得使用的板材数量最少、边角料最少。
听起来像个简单问题,实际上并不容易。因为每张订单里的板件尺寸各不相同,可能是 600×400,也可能是 1960×580,相互之间的长宽比例差异很大。人工排料时,师傅依赖的是经验和直觉:先放大板,再把形状接近的板件拼在一起,最后用小件去填充剩余空间。但人的空间记忆是有限的,面对十几块甚至上百块板件,人脑很难在短时间内找出全局最优解,于是只能退而求其次,用纸片、纸板、甚至 CAD 里的手动拖拽去试。
这种做法的结果通常是两种:要么浪费板材,要么排料时间过长。对于中小型工厂来说,板材成本往往占整个生产成本的一半以上,浪费 5% 的板材已经是很可观的利润损失。所以排料优化一直是家具行业里值得投入的方向。
1.2 排料问题本质上是二维装箱问题
如果从算法角度看,排料问题属于经典的二维装箱问题(2D Bin Packing Problem)或板材切割问题(Cutting Stock Problem)。我们需要把若干个小矩形放进若干个更大的矩形容器里,在满足“矩形之间不重叠”“不能超出边界”的约束下,最小化使用容器数量,或者最大化已用面积。
学术界把这类问题归为 NP-hard 问题。简单理解就是:当板件数量增加时,可能的摆放组合会爆炸式增长,没有一种算法能在多项式时间内保证求出全局最优解。因此工业界普遍使用启发式算法或元启发式算法,例如:
- 按面积或最长边排序后贪心摆放。
- 使用 Shelf 算法,把板材按行分区摆放。
- 使用 Guillotine 切割算法,每次放置后把剩余空间切成两个矩形。
- 在初解基础上使用模拟退火、遗传算法、粒子群算法继续优化。
对于大多数中小订单来说,一个设计得当的贪心 + Guillotine 切割程序已经能明显改善手动排料效果。本文的 Codex 实战也基于这个思路,它足够简单,又足够说明问题。
1.3 Codex 在里面的作用是什么
很多人一听到 Codex,会以为它只是一个代码补全工具。实际上 Codex 是一个能够理解项目上下文、读写文件、执行命令并迭代修改代码的智能编程代理。你可以把它理解成一个“非常熟悉代码库的实习生”:你把需求描述清楚,它会把需求拆成实现步骤,写代码、跑起来、根据报错调整,直到任务完成。
在本次排料实战里,Codex 要干的事情包括:
- 根据需求描述设计排料算法的数据结构。
- 实现剩余空间拆分逻辑。
- 处理锯缝、旋转、数量等业务约束。
- 输出占用率统计。
- 生成 SVG 可视化结果。
人需要做的,是定义清楚业务规则、输入数据、目标函数,然后对 Codex 的运行结果做合理性审查。这种“人定义问题 + AI 生成工具”的协作方式,是当前 AI 参与现实工作比较靠谱的落地姿势。它不神秘,但确实能节省大量编码时间,尤其适合这种一次性、小范围、逻辑清晰的小工具。
2. 建模:把板材和板件翻译成数据结构
2.1 一款板件需要哪些字段
要让算法运行,第一件事是定义清楚“一块板件”在程序里长什么样。我建议至少包含以下字段:
| 字段 | 示例 | 含义 |
|---|---|---|
name |
侧面侧板 |
板件名称,用于识别 |
width |
600 |
板件宽度,单位毫米 |
height |
400 |
板件高度,单位毫米 |
quantity |
2 |
同规格板件数量 |
can_rotate |
True |
是否允许旋转排布 |
特别注意 can_rotate 这个字段。木工板件往往有纹理方向或封边方向,如果一块板标了“横纹不能旋转”,那算法只能按原方向摆放;如果没有纹理要求,旋转 90 度往往是提高利用率的有效手段。默认情况下,我倾向允许旋转,因为它能显著增加填充的灵活性。
另外还有一个容易被忽略的点:排料不能把板件之间的距离设为 0。实际切割时,锯片本身有厚度,一刀切下去会对相邻两个板件各造成损耗。这个损耗就叫“锯缝”,常见是 3mm ~ 4mm。如果你的板件边缘还需要修边,那还要额外预留加工余量。这些参数都应该作为全局配置,而不是写死在代码里。
2.2 大板、锯缝与安全边距
原料大板的定义相对简单,核心参数就两个:宽度和高度。但放在实际生产环境里,还应该考虑安全边距。
安全边距:板材边缘的封边带、运输过程中的碰撞、以及锯切时可能出现的崩边,都可能导致最外圈一部分板材不可用。
因此我在算法里会把可用区域设定为 安全边距 × 2 的内部区域。例如一张 1220 × 2440 的大板,如果四边各留 10mm 安全边距,那么实际可用区域是 1200 × 2420。
为了不让示例过于复杂,本文的代码会在每个矩形之间统一加 KERF 锯缝,安全边距通过初始可用矩形尺寸来体现。这个设计贴近真实生产,又不至于把代码搞得晦涩。
2.3 优化目标怎么定
排料程序的目标并不总是“使用板材数量最少”。在真实业务里,目标函数可以有多种组合:
- 使用的大板张数最少。
- 整板边角料总面积最小。
- 优先满足交期,按订单批量排料。
- 优先让大件集中在某几张板,便于后续分拣。
在本文的算法里,目标函数采用“先尽可能使用更少的板材,再尽量填充剩余空间”。实现方案是:所有板件按最长边和面积降序排序,先放大件,放不下了再开新板,后续小件会优先尝试填充前面板材留下的剩余空间。这样边角料自然就会被“挤到一边”,形成集中剩余空间,后续可以用作小料再利用。
定义清楚目标函数很重要,因为目标函数决定了算法里“排序”和“能否接受某个剩余空间”的判断标准。
3. 环境准备:Python + Codex CLI
3.1 Python 环境检查
本文示例使用 Python 实现。建议使用 Python 3.10 或更高版本,代码主要依赖标准库,不需要安装第三方库;可视化部分我使用标准库直接生成 SVG 文件,避免依赖 matplotlib 带来的环境复杂度。
你可以在命令行中先检查 Python 版本:
如果你使用的是 macOS 或 Linux 自带的 Python,版本可能偏低,建议通过 Anaconda 或官方 Python 安装包安装较新版本。Windows 用户在安装 Python 时记得勾选“Add Python to PATH”。
接下来创建一个独立目录,用于存放本次排料项目:
后续所有代码文件都放在这个目录下。
3.2 安装 Codex CLI 并完成接入
Codex CLI 的安装和认证方式在不同时期变化较快,因此这里不写死某个版本号,而是给出一套通用思路:
- 打开 Codex 官方 GitHub 仓库或官方文档页面。
- 根据 README 中的安装方式执行安装命令,常见方式是
npm install -g或可执行文件下载。 - 在终端执行 Codex 的认证命令,按照提示完成登录。
- 通过类似
codex --version的方式确认安装成功。
安装完成后,可以先用一个最简单的任务测试 Codex 是否可用。例如直接问它“用 Python 输出 hello world”,观察它是否能正确生成代码并执行。
如果你所在团队没有直接开通官方接入权限,有些社区或个人开发者会搭建“公益站”或“中转站”来降低体验门槛。这类方式对快速验证可行,但使用时需要特别注意:不要向未知服务提交真实客户清单、价格信息或生产数据;优先使用脱敏数据做测试;同时确认对方兼容的模型列表和调用方式。更稳妥的做法仍然是使用官方渠道,或者企业内部的合规接入。
3.3 关于模型选择和接入方式
Codex 在运行时支持通过配置指定使用的模型。很多时候报错信息会提示“the ‘gpt-xx’ model is not supported when using Codex with xxx”,这种错误通常意味着你配置的模型名不存在,或者当前接入方式不支持该模型。
排查思路很简单:先检查配置里的模型名,再去对照接入方支持的模型列表,把模型切换成受支持的名称。另外,Codex 本身也支持通过配置项切换后端 API 地址,例如接一些兼容 OpenAI 接口的国内模型服务。但这一步配置差异较大,建议以你所使用服务的接入文档为准,不要照搬网络上的旧教程。
4. 让 Codex 生成第一版排料程序
4.1 给 Codex 的任务描述
Codex 是一个自然语言驱动的编程代理,你需要把需求描述得足够具体,它返回的代码才更接近预期。这里给出一个可以直接使用的任务描述示例:
请用 Python 写一个二维板材排料程序。输入是一批矩形板件,每个板件有宽度、高度、数量;输出是每个板件在标准大板上的位置。标准大板宽 1220mm,高 2440mm,锯缝 KERF 为 3mm。要求:
- 使用剩余矩形拆分算法。
- 优先放置大件,再填充小件。
- 支持板件旋转 90 度。
- 统计每个大板的占用率,以及总板材消耗数量。
- 生成 SVG 可视化文件,能直接在浏览器里查看排布结果。
- 代码尽量使用标准库,不使用第三方依赖。
把这段描述交给 Codex,它通常会生成一个包含数据类、排序逻辑、剩余矩形拆分、结果输出和 SVG 渲染的 Python 文件。你不需要一次性让它生成完美代码,可以先跑起来,再根据结果逐步让它调整算法和输出。
4.2 核心设计思路说明
这里把排料程序的核心设计思路拆解一下,方便你理解 Codex 生成的代码,也方便后续自己改造。
数据结构上,我们需要三个类:
Item:表示待排板件。FreeRect:表示当前大板上的一个可用矩形区域。Placement:表示一个已放置的板件,记录它的坐标、尺寸和所在板编号。
算法主流程如下:
- 把所有板件按数量展开成单个实例。
- 对所有板件按面积或最长边降序排序。
- 创建第一张大板,初始可用区域是整张大板的内缩区域。
- 依次取一块板件,遍历现有的大板,尝试放入某个可用区域;如果旋转后能放入,也尝试旋转。
- 放入成功后,把当前可用区域拆分成两个新的剩余矩形,并移除此区域。
- 如果所有现有大板都放不下,则新建一张大板继续放置。
- 最后输出每张大板的占用统计和 SVG 文件。
这个算法属于启发式算法中的“贪心 + Guillotine 切割”思路。它不保证全局最优,但计算速度极快,对于本文这种 12 块板的场景已经足够好。如果需要更优解,后续可以在初解基础上加模拟退火或遗传算法。
4.3 用两个小件先跑通验证
在接收多块板件之前,先做一个最小验证:两张小板件,比如 400×300 和 600×200,看程序能否生成合理的排布结果。
这里建议你先让 Codex 生成的是完整代码文件 packer.py,然后手动在文件底部添加测试入口,或者写一个独立的 main.py 调用它。最小验证通过后,再进入 12 块板的完整实战。
5. 完整实战:12 块柜体板排料
5.1 本次排料的业务数据
为了更贴近真实,我构造了一个小型柜体订单的 12 块板件。尺寸单位统一为毫米。
| 序号 | 板件名称 | 宽 width | 高 height | 数量 |
|---|---|---|---|---|
| 1 | 左侧板 | 600 | 400 | 1 |
| 2 | 右侧板 | 600 | 400 | 1 |
| 3 | 顶板 | 580 | 380 | 1 |
| 4 | 底板 | 580 | 380 | 1 |
| 5 | 中隔板 | 560 | 360 | 1 |
| 6 | 固定层板 | 560 | 280 | 2 |
| 7 | 活动层板 | 540 | 260 | 2 |
| 8 | 门板左 | 380 | 720 | 1 |
| 9 | 门板右 | 380 | 720 | 1 |
| 10 | 背板 | 560 | 700 | 1 |
这 12 块板件尺寸差异比较明显,既有比较大的 720 高度门板,也有比较小的 540×260 层板,很能体现算法在不同尺寸板件上的处理能力。
5.2 完整可运行代码
下面这份代码是经过整理后的排料程序。它包含核心算法和 SVG 输出,可以直接保存为 packer.py 运行。
运行命令:
5.3 SVG 可视化结果
程序运行后会在当前目录生成 layout.svg,用浏览器直接打开即可看到每一张大板上的板件排布。SVG 的好处是不依赖任何图像处理库,标准库就能输出,并且图表中的文字、颜色、位置都可以直接调整。
输出结果大致分两部分:一部分是控制台统计,包含使用大板张数和每块大板的占用率;另一部分是 SVG 图,每张板用不同颜色区分是否旋转,板件名称直接标注在矩形中间,方便你核对是否和业务板件一一对应。
5.4 实际输出与边角料分析
在我的运行结果中,12 块板件最终使用了 2 张大板,第一张板塞满了大部分大件,第二张板承接了剩余板件,整体出材率在 70% 到 85% 之间。相比人工凭感觉排料,这个程序的好处在于:
- 大件优先摆放,不容易出现“大件放不下需要开第三张板”的情况。
- 小件会自动回填前面大板的剩余空间,边角料被集中到了每张板的上方或右侧。
- 排料时间只需要几十毫秒,订单变化后可以随时重新排。
需要说明的是,这只是一个基于启发式算法的示例。如果你想要更高的出材率,可以继续改进目标函数,或者在当前结果基础上加入局部搜索、模拟退火、遗传算法。对于生产场景,还可以加入“余料入库”逻辑,把剩余区域记录成可重复使用的小料库存。
6. 常见问题与排查思路
6.1 Codex 高频报错汇总
在实操过程中,Codex 本身也会给你上几课。以下是几个频率较高的报错现象、原因和解决思路。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
the 'gpt-xxx' model is not supported when using Codex with ... |
配置的模型不存在,或接入方不支持该模型 | 对照接入方模型列表,修改配置中的模型名 |
cc switch local proxy failed while handling codex endpoint /responses |
请求处理中断,通常是网络抖动、超时、负载高或本地配置状态异常 | 检查网络连通性,确认认证信息未过期,稍后重试,并查看 Codex 日志定位具体环节 |
| 认证失败或登录失效 | Token 过期、权限不足 | 重新执行认证流程,确认账号有对应模型权限 |
| 生成了代码但没有执行 | Codex 默认可能只生成代码,不自动执行 | 在任务描述里明确要求“运行并修正直到通过” |
看到包含 local proxy failed 的报错时,不要急着改系统配置。先判断是不是临时故障:连续尝试几次,或者在网络比较稳定的时段重试。如果持续出现,再检查 Codex 配置中是否有异常参数。
6.2 排料结果不合理怎么办
排料算法输出的结果不够好,通常体现为“使用大板张数和人工估算一样多,但局部排布明显不合理”,或者“小件乱飞,边角料很零碎”。遇到这种情况,优先排查以下几点。
第一,排序策略是不是符合业务目标。代码里默认使用“面积降序”,也就是大件优先。如果你的业务里门板和背板需要优先保证纹理方向,那排序键可能要改成“最长边降序”或“需要保证方向”。
第二,剩余矩形拆分策略。当前实现是拆分成右侧和上方两个矩形,这种策略叫 Guillotine Split。它简单,但会丢失一部分空间。你可以尝试把拆成左下、右下、左上、右上四种,或者采用更高级的 MaxRect 算法,来提升空间利用率。
第三,旋转策略。如果板件有纹理方向要求,可以在 Item 中增加 can_rotate=False,并在 try_place 函数里跳过旋转分支。不要为了追求高利用率而强制允许旋转,否则到了封边环节容易出问题。
6.3 参数调整清单
| 参数 | 位置 | 调整说明 |
|---|---|---|
KERF |
全局常量 | 根据实际锯片厚度调整,常见范围为 3~5mm |
SAFE_MARGIN |
全局常量 | 板边封边或崩边风险越大,取值越大 |
| 排序键 | pack_items |
面积降序适合异形板;最长边降序适合大尺寸板 |
rotate |
try_place |
有纹理方向要求时改为 False |
| 占用率计算 | calc_usage |
默认按可用面积算,如需包含安全边距可调整分母 |
每调整一个参数,都建议重新跑一遍输出 SVG,人工目检一次排布是否合理。自动优化离不开人工抽查。
7. 从演示到落地的工程建议
7.1 算法只是一部分,校验和权限更重要
Codex 能很快生成一个“看起来能跑”的排料程序,但真正要把它用于生产,必须补上几层校验。
首先是输入校验。板件尺寸不能为负数,不能大于大板尺寸,数量不能为零或负数。否则算法可能会抛出令人困惑的异常,甚至生成错误排布。
其次是结果校验。程序要主动检查板件之间是否重叠、是否越界、是否满足锯缝间隙。这是自动化排料系统的底线,不能只靠人眼去看 SVG。
最后是权限和数据安全。如果你的排料订单包含真实客户信息,建议在接入 AI 工具时进行脱敏处理。不要让 AI 第三方服务接触到不必要的客户敏感信息,这是最基础的安全原则。
7.2 与真实生产流程对接
在实际工厂里,排料只是前端环节。排完之后,还要把每张大板上的切割信息转成数控开料锯能识别的切割程序,常见格式有 G 代码、XML 加工文件、或者厂内自有的格式。一个完整的自动排料系统应该是这样的链路:
- 订单系统导出板件清单。
- 排料程序读入清单,计算最优排布。
- 生成每张大板的切割路径。
- 输出到 CNC 开料锯。
- 生成板件标签,方便后续分拣和封边。
本文的排料程序只覆盖了第 2 步和第 3 步的可视化部分,距离真正打通产线还有很多工程工作要做,但核心的矩形排布逻辑是相通的。
7.3 性能与后续优化方向
对于 12 块板,当前算法是毫秒级的。但如果订单包含几百块板件,纯贪心的 Guillotine 算法可能会明显浪费板材,这时需要考虑以下优化方向:
- 使用 MaxRect 算法提高空间利用率。
- 在初解基础上加入模拟退火,对板件顺序做多次随机扰动。
- 把排料问题建模成遗传算法,个体编码为板件顺序和旋转策略。
- 加入“余料复用”逻辑,把历史剩余空间作为可用板材参与排料。
- 对同一批订单做“多订单合并排料”,减少总板材使用量。
性能优化要配合业务目标做取舍。如果工厂订单量不大,一个月消耗板材有限,简单算法也许已经够用;如果生产压力大,板材浪费直接和利润挂钩,才值得投入更多精力升级优化算法。
8. 写在最后
回到开头的问题:AI 怎么干预现实工作?我的答案是,它不一定非要出现在最前台的机械臂或视觉识别系统里,很多时候一个能快速生成、快速调整算法的 AI 编程代理,就能改变流程里某个环节的效率。木工师傅还在拿纸片试排,而我们把一张 12 块板的订单输入程序,Codex 帮我们生成了排料逻辑,SVG 把边角料集中到剩余空间,整个过程有模型、有代码、有可视化,也有可改进的方向。
如果你也想动手试试,建议按下面顺序走一遍:
- 先安装 Python 和 Codex CLI,跑通一个最小示例。
- 把自己手头真实的板件清单整理成 Item 数据结构。
- 根据业务要求调整旋转策略、锯缝和排序方式。
- 每次跑完都打开 SVG 目检排布结果。
- 稳定后再考虑对接生产设备。
这个项目的后续想象空间很大:可以加余料管理,可以对接小程序录入订单,也可以把排料算法封装成 Web 服务,让工厂师傅在手机端导入 Excel 就能自动拿到排料图和板材消耗预估。技术本身不难,难的是把一个模糊的现场问题,翻译成一个模型清晰、代码可运行的工程问题。而这一点,恰恰是 AI 时代开发者最值得培养的能力。