使用 Python 开源库 py3dbp 解决三维装箱问题

该文章已生成可运行项目,

本文介绍如何使用 py3dbp 解决三维装箱问题。文章包含了从基本概念、实战案例到最终生成动态GIF可视化的流程及代码实现。

在物流、仓储和制造业中,如何将不同尺寸的物品高效地装入一个有限的容器(如卡车、集装箱或箱子)是一个经典且极具挑战性的问题。这就是著名的三维装箱问题(3D Bin Packing Problem, 3D-BPP)。解决好这个问题能显著节约运输成本、提高空间利用率。

Python社区为我们提供了一个强大而简洁的工具——py3dbp。本文将带您深入了解这个库,并通过一个实际案例,展示如何从零开始解决一个装箱问题,并最终创建一个直观的动态装箱过程GIF。

一、py3dbp 核心概念

在使用之前,我们先了解 py3dbp 的三个核心组件:

  • Packer (装箱器):这是执行装箱算法的核心引擎。您可以把它想象成一个负责指挥的工人。
  • Bin (箱子/容器):代表您要装入物品的容器。它有明确的属性,如名称、长、宽、高和最大承重。在我们的案例中,这就是一辆货车。
  • Item (物品):代表需要被装入箱子的物品。它同样有名称、长、宽、高和重量等属性。

整个工作流程非常直观:创建箱子和一系列物品 -> 将它们都交给装箱器 -> 装箱器执行算法 -> 检视装箱结果。

二、实战案例:装载一辆货车

假设我们有一辆小型货车和一批不同规格的货物,我们的目标是尽可能多地将这些货物装入车厢。

  • 货车车厢尺寸 (Bin): 587cm (长) x 235cm (宽) x 270cm (高)。
  • 货物列表 (Items): 我们有多种不同尺寸和数量的箱子需要装载。
第1步:安装必要的库

您需要安装 py3dbp 用于核心计算,matplotlib 用于绘图,以及 imageio 用于将图片序列合成为GIF。

pip install py3dbp
pip install matplotlib
pip install imageio```
第2步:编写代码实现装箱

我们将遵循以下步骤编写代码:

  • 导入库。
  • 定义 Bin (货车) 和 Item (货物)。
  • 创建 Packer 并将箱子和物品添加进去。
  • 执行装箱算法并打印结果。
第3步:生成动态GIF可视化

静态的结果报告虽然清晰,但远不如一个动态图来得直观。我们将编写一个函数,它能一步步地展示每个箱子是如何被放入车厢的,并最终将这个过程保存为 GIF 文件。

这需要我们:

  • 编写一个函数,能绘制出任意数量物品在箱子中的3D视图。
  • 循环调用这个绘图函数,从1个物品开始,每次增加1个,并将每一帧保存为图片。
  • 使用 imageio 将所有图片帧合成为一个GIF。

下面是包含完整代码(计算+可视化)的脚本:

import os
import imageio
import numpy as np
from py3dbp import Packer, Bin, Item
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection

# --- Matplotlib 全局设置 ---
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False# 用来正常显示负号

def plot_packing_result(bin_obj, items_to_plot, title, save_path=None):
    """
    绘制指定物品在箱子中的3D视图 (已修正数据类型问题)
    """
    fig = plt.figure(figsize=(12, 10))
    ax = fig.add_subplot(111, projection='3d')

    # 为不同物品分配颜色
    num_items = len(items_to_plot)
    cmap = plt.get_cmap('viridis')
    colors = [cmap(i) for i in np.linspace(0, 0.9, num_items)] if num_items > 0else []
    color_map = {item.name: colors[i] for i, item in enumerate(items_to_plot)}

    # 绘制每个已装箱的物品
    for item in items_to_plot:
        item_d = float(item.depth)
        item_w = float(item.width)
        item_h = float(item.height)
        
        pos = item.position
        x, y, z = float(pos[0]), float(pos[1]), float(pos[2])
        
        vertices = [
            (x, y, z), (x + item_d, y, z), (x + item_d, y + item_w, z), (x, y + item_w, z),
            (x, y, z + item_h), (x + item_d, y, z + item_h), (x + item_d, y + item_w, z + item_h), (x, y + item_w, z + item_h)
        ]
        faces = [
            [vertices[0], vertices[1], vertices[2], vertices[3]], [vertices[4], vertices[5], vertices[6], vertices[7]],
            [vertices[0], vertices[1], vertices[5], vertices[4]], [vertices[2], vertices[3], vertices[7], vertices[6]],
            [vertices[1], vertices[2], vertices[6], vertices[5]], [vertices[0], vertices[3], vertices[7], vertices[4]]
        ]

        poly3d = Poly3DCollection(faces, facecolors=color_map.get(item.name), linewidths=1, edgecolors='k', alpha=0.85)
        ax.add_collection3d(poly3d)

    ax.set_xlim(0, float(bin_obj.depth))
    ax.set_ylim(0, float(bin_obj.width))
    ax.set_zlim(0, float(bin_obj.height))
    
    ax.set_xlabel('长度 (X轴 / depth)')
    ax.set_ylabel('宽度 (Y轴 / width)')
    ax.set_zlabel('高度 (Z轴 / height)')
    
    ax.view_init(elev=20, azim=-45)
    plt.title(title, fontsize=16)

    if save_path:
        plt.savefig(save_path)
        plt.close(fig)
    else:
        plt.show()

def create_animated_gif(bin_obj, output_filename='packing_animation.gif'):
    """
    创建并保存装箱过程的GIF动画
    """
    packed_items = bin_obj.items
    ifnot packed_items:
        print("分析:没有成功装入任何物品,无法创建GIF。请检查物品尺寸是否相对于容器过大。")
        return

    frame_folder = "gif_frames"
    ifnot os.path.exists(frame_folder):
        os.makedirs(frame_folder)

    filenames = []
    print(f"步骤1:为GIF生成 {len(packed_items) + 1} 帧图片...")
    
    for i in range(len(packed_items) + 1):
        frame_path = os.path.join(frame_folder, f"frame_{i:03d}.png")
        if i == 0:
            plot_packing_result(bin_obj, [], f"第 {i}/{len(packed_items)} 步: 空货车", save_path=frame_path)
        else:
            item_being_packed = packed_items[i-1]
            plot_packing_result(bin_obj, packed_items[:i], f"第 {i}/{len(packed_items)} 步: 装入 {item_being_packed.name}", save_path=frame_path)
        filenames.append(frame_path)

    print("步骤2:将所有图片帧合成为GIF文件...")

    with imageio.get_writer(output_filename, mode='I', duration=3.5) as writer:
        for filename in filenames:
            image = imageio.imread(filename)
            writer.append_data(image)
    
    print(f"成功!GIF动画已保存至: {output_filename}")
    
    for filename in filenames:
        os.remove(filename)
    os.rmdir(frame_folder)

# ================== 主程序入口 ==================
if __name__ == '__main__':
    packer = Packer()
    
    # 按照 (名称, 宽, 高, 深, ...) 的顺序创建 Bin
    truck = Bin('货车', 235, 270, 587, 1000.0)
    packer.add_bin(truck)
    
    # 按照 (名称, 宽, 高, 深, ...) 的顺序创建 Item
    packer.add_item(Item('大号箱子-1', 100, 100, 100, 15.0))
    packer.add_item(Item('大号箱子-2', 100, 100, 100, 15.0))
    packer.add_item(Item('中号箱子-1', 70, 60, 80, 10.0))
    packer.add_item(Item('中号箱子-2', 70, 60, 80, 10.0))
    packer.add_item(Item('中号箱子-3', 70, 60, 80, 10.0))
    packer.add_item(Item('小号箱子-1', 40, 30, 50, 5.0))
    packer.add_item(Item('小号箱子-2', 40, 30, 50, 5.0))
    packer.add_item(Item('小号箱子-3', 40, 30, 50, 5.0))
    packer.add_item(Item('小号箱子-4', 40, 30, 50, 5.0))
    packer.add_item(Item('扁平箱子-1', 150, 10, 120, 8.0))
    packer.add_item(Item('扁平箱子-2', 150, 10, 120, 8.0))
    packer.add_item(Item('瘦高箱子-1', 40, 200, 40, 12.0))
    packer.add_item(Item('瘦高箱子-2', 40, 200, 40, 12.0))
    packer.add_item(Item('超大号箱子', 300, 300, 300, 100.0))

    print("开始执行装箱算法...")
    packer.pack(bigger_first=True, distribute_items=False)

    packed_bin = packer.bins[0]
    print(f"\n计算完毕!")
    print(f"已装入物品数量: {len(packed_bin.items)}")
    print(f"未装入物品数量: {len(packed_bin.unfitted_items)}")
    
    create_animated_gif(packed_bin, 'packing_animation.gif')
三、运行与结果

将以上代码保存为 .py 文件并运行。程序会首先在控制台输出装箱结果,然后开始生成GIF的每一帧。完成后,您会在代码文件所在的目录下找到一个名为 packing_animation.gif 的文件。

这个GIF文件会像下面这样,动态地展示每一个箱子被依次放入货车的过程。

AI大模型学习福利

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获取

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。


因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

四、AI大模型商业化落地方案

因篇幅有限,仅展示部分资料,需要点击文章最下方名片即可前往获

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量

本文章已经生成可运行项目
在物流运输环节中,企业需将包装完成的货物装载至货车厢体内。为提升运输资源利用率,应尽可能充分利用车厢空间。理想状态下,车厢空间达到完全利用是最佳情况,然而实际操作中若空间利用率超过85%,即可视为较为高效的装载方案。 假设车厢为长方体结构,其尺寸参数分别为长度L、宽度W及高度H。待装载货物共计n件,每件货物同样为长方体,其中第i件货物的尺寸为li、wi、hi(所有货物总体积显著超过车厢容积)。设定以下前提条件:车厢具有8个顶点,将靠近驾驶室底部的一个顶点设为三维坐标系原点(0,0,0)。车厢六个平面中有五个处于封闭状态,包括四个侧立面及靠近驾驶室的端面,仅保留一个装卸作业面供工作人员操作。 核心求解目标包括两项:一是确定每件货物在车厢空间内的三维坐标定位,即各货物与车厢坐标原点相对应的顶点位置坐标;二是精确计算车厢空间的实际利用率。 本问题可分为基础要求与进阶要求两个层次: 基础要求层面: 1. 所有尺寸参数均为整数单位 2. 采用静态装载方案,即从n件货物中选取m件进行空间排布(无需考虑装载作业顺序) 3. 所有货物均采用水平放置方式,以最大接触面作为承重基面 4. 算法运算时限宽松,24小时内得出可行解即可接受 进阶要求部分...。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值