一、需求描述
为了在一个17米长、2.5米宽、2.5米高的集装箱中最有效地利用空间,需要计算和展示多种规格的圆柱、正方体、梯形和三角形的摆放方式。Python用于计算装载数量和位置,Three.js用于3D可视化展示。
常见的箱体尺寸
类型 | 小型 | 中型 | 大型 | 特大型 |
---|---|---|---|---|
纸箱 | 30 cm x 20 cm x 15 cm | 40 cm x 30 cm x 20 cm | 60 cm x 40 cm x 40 cm | 80 cm x 60 cm x 60 cm |
塑料箱 | 30 cm x 20 cm x 15 cm | 40 cm x 30 cm x 25 cm | 60 cm x 40 cm x 30 cm | 80 cm x 60 cm x 40 cm |
常见的柱状体尺寸
类型 | 小型 | 中型 | 大型 |
---|---|---|---|
桶状容器 | 高 50 cm,直径 30 cm(约 50 升) | 高 80 cm,直径 50 cm(约 200 升) | 高 100 cm,直径 60 cm(约 300 升) |
圆柱形包裹 | 高 50 cm,直径 20 cm | 高 100 cm,直径 30 cm | 高 200 cm,直径 40 cm |
集装箱尺寸
类型 | 内部尺寸 (长 x 宽 x 高) | 容量 |
---|---|---|
20 英尺集装箱 | 5.9 m x 2.35 m x 2.39 m | 33.2 立方米 |
40 英尺集装箱 | 12.03 m x 2.35 m x 2.39 m | 67.7 立方米 |
40 英尺高柜集装箱 | 12.03 m x 2.35 m x 2.69 m | 76.3 立方米 |
20 英尺冷藏集装箱 | 5.44 m x 2.29 m x 2.26 m | 28.4 立方米 |
40 英尺冷藏集装箱 | 11.56 m x 2.29 m x 2.26 m | 59.1 立方米 |
托盘尺寸
类型 | 尺寸 |
---|---|
标准托盘(欧标) | 120 cm x 80 cm |
标准托盘(中标) | 110 cm x 110 cm |
半托盘 | 80 cm x 60 cm |
这些表格列出了物流运输中常用的箱体、柱状体、集装箱和托盘的标准尺寸,以便于装载、运输和堆叠。
二、空间利用方案需求
-
Python端计算:
- 计算各形状的装载数量和位置。
- 提供API服务,供前端Three.js访问。
-
Three.js端展示:
- 获取Python端计算结果并生成3D形状和位置。
- 提供交互功能,如旋转、缩放和平移。
客户要求
40 英尺高柜集装箱装载如下物品:
纸箱:小型、中型、大型各153,17,7个
桶状:小型、中型、大型各85,32,29个
圆柱形包裹:小型、中型、大型各189,68,21个
//------------------基础尺寸数据------------------ // 箱体尺寸 const boxSizes = { small: { cardboard: { length: 0.3, width: 0.2, height: 0.15 }, plastic: { length: 0.3, width: 0.2, height: 0.15 } }, medium: { cardboard: { length: 0.4, width: 0.3, height: 0.2 }, plastic: { length: 0.4, width: 0.3, height: 0.25 } }, large: { cardboard: { length: 0.6, width: 0.4, height: 0.4 }, plastic: { length: 0.6, width: 0.4, height: 0.3 } }, extraLarge: { cardboard: { length: 0.8, width: 0.6, height: 0.6 }, plastic: { length: 0.8, width: 0.6, height: 0.4 } } }; // 柱状体尺寸 const cylinderSizes = { small: { barrelContainer: { height: 0.5, diameter: 0.3 } }, medium: { barrelContainer: { height: 0.8, diameter: 0.5 } }, large: { barrelContainer: { height: 1.0, diameter: 0.6 } }, cylindricalPackage: { small: { height: 0.5, diameter: 0.2 }, medium: { height: 1.0, diameter: 0.3 }, large: { height: 2.0, diameter: 0.4 } } }; // 集装箱尺寸 const containerSizes = { "20ft": { internalDimensions: { length: 5.9, width: 2.35, height: 2.39 }, capacity: 33.2 }, "40ft": { internalDimensions: { length: 12.03, width: 2.35, height: 2.39 }, capacity: 67.7 }, "40ftHighCube": { internalDimensions: { length: 12.03, width: 2.35, height: 2.69 }, capacity: 76.3 }, "20ftRefrigerated": { internalDimensions: { length: 5.44, width: 2.29, height: 2.26 }, capacity: 28.4 }, "40ftRefrigerated": { internalDimensions: { length: 11.56, width: 2.29, height: 2.26 }, capacity: 59.1 } }; // 托盘尺寸 const palletSizes = { standardEuro: { length: 1.2, width: 0.8 }, standardUS: { length: 1.2, width: 1.0 }, halfPallet: { length: 0.8, width: 0.6 } }; //------------------需求尺寸数据------------------ // 纸箱数量和尺寸 const paper_box = { 'small': { quantity: 153, size: boxSizes.small }, 'medium': { quantity: 17, size: boxSizes.medium }, 'large': { quantity: 7, size: boxSizes.large } }; // 桶状容器数量和尺寸 const barrel = { 'small': { quantity: 85, size: cylinderSizes.small.barrelContainer }, 'medium': { quantity: 32, size: cylinderSizes.medium.barrelContainer }, 'large': { quantity: 29, size: cylinderSizes.large.barrelContainer } }; // 圆柱形包裹数量和尺寸 const cylinder = { 'small': { quantity: 189, size: cylinderSizes.cylindricalPackage.small }, 'medium': { quantity: 68, size: cylinderSizes.cylindricalPackage.medium }, 'large': { quantity: 21, size: cylinderSizes.cylindricalPackage.large } }; // 输出示例 console.log("纸箱数量和尺寸:"); console.log(paper_box); console.log("\n桶状容器数量和尺寸:"); console.log(barrel); console.log("\n圆柱形包裹数量和尺寸:"); console.log(cylinder); console.log("\n箱体尺寸:"); console.log(boxSizes); console.log("\n柱状体尺寸:"); console.log(cylinderSizes); console.log("\n集装箱尺寸:"); console.log(containerSizes); console.log("\n托盘尺寸:"); console.log(palletSizes); //------------------用python计算上述体积,并选用合适的集装箱车辆------------------ import numpy as np # 箱体尺寸定义(单位:米) boxSizes = { 'small': { 'cardboard': { 'length': 0.3, 'width': 0.2, 'height': 0.15 }, 'plastic': { 'length': 0.3, 'width': 0.2, 'height': 0.15 } }, 'medium': { 'cardboard': { 'length': 0.4, 'width': 0.3, 'height': 0.2 }, 'plastic': { 'length': 0.4, 'width': 0.3, 'height': 0.25 } }, 'large': { 'cardboard': { 'length': 0.6, 'width': 0.4, 'height': 0.4 }, 'plastic': { 'length': 0.6, 'width': 0.4, 'height': 0.3 } } } # 桶状容器尺寸定义(单位:米) cylinderSizes = { 'small': { 'barrelContainer': { 'height': 0.5, 'diameter': 0.3 } }, 'medium': { 'barrelContainer': { 'height': 0.8, 'diameter': 0.5 } }, 'large': { 'barrelContainer': { 'height': 1.0, 'diameter': 0.6 } }, 'cylindricalPackage': { 'small': { 'height': 0.5, 'diameter': 0.2 }, 'medium': { 'height': 1.0, 'diameter': 0.3 }, 'large': { 'height': 2.0, 'diameter': 0.4 } } } # 集装箱尺寸定义(单位:米) containerSizes = { '20ft': { 'internalDimensions': { 'length': 5.9, 'width': 2.35, 'height': 2.39 }, 'capacity': 33.2 }, '40ft': { 'internalDimensions': { 'length': 12.03, 'width': 2.35, 'height': 2.39 }, 'capacity': 67.7 }, '40ftHighCube': { 'internalDimensions': { 'length': 12.03, 'width': 2.35, 'height': 2.69 }, 'capacity': 76.3 }, '20ftRefrigerated': { 'internalDimensions': { 'length': 5.44, 'width': 2.29, 'height': 2.26 }, 'capacity': 28.4 }, '40ftRefrigerated': { 'internalDimensions': { 'length': 11.56, 'width': 2.29, 'height': 2.26 }, 'capacity': 59.1 } } # 物品数量和尺寸定义 paper_box = { 'small': { 'quantity': 153, 'size': boxSizes['small'] }, 'medium': { 'quantity': 17, 'size': boxSizes['medium'] }, 'large': { 'quantity': 7, 'size': boxSizes['large'] } } barrel = { 'small': { 'quantity': 85, 'size': cylinderSizes['small']['barrelContainer'] }, 'medium': { 'quantity': 32, 'size': cylinderSizes['medium']['barrelContainer'] }, 'large': { 'quantity': 29, 'size': cylinderSizes['large']['barrelContainer'] } } cylinder = { 'small': { 'quantity': 189, 'size': cylinderSizes['cylindricalPackage']['small'] }, 'medium': { 'quantity': 68, 'size': cylinderSizes['cylindricalPackage']['medium'] }, 'large': { 'quantity': 21, 'size': cylinderSizes['cylindricalPackage']['large'] } } # 计算每种物品的尺寸列表(考虑翻转) item_sizes = [] for item_type, items in {'paper_box': paper_box, 'barrel': barrel, 'cylinder': cylinder}.items(): for size_key, item in items.items(): quantity = item['quantity'] size = item['size'] for _ in range(quantity): # 加入所有可能的尺寸及其翻转 for orientation in [(size['length'], size['width'], size['height']), (size['width'], size['length'], size['height']), (size['height'], size['length'], size['width']), (size['length'], size['height'], size['width']), (size['width'], size['height'], size['length']), (size['height'], size['width'], size['length'])]: item_sizes.append((item_type, orientation[0], orientation[1], orientation[2])) # 装箱算法示例(考虑翻转) def pack_items(item_sizes, container_length, container_width, container_height): container_volume = container_length * container_width * container_height packed_items = [] for item_type, length, width, height in item_sizes: if length <= container_length and width <= container_width and height <= container_height: packed_items.append((item_type, length, width, height)) container_volume -= length * width * height container_height -= height elif width <= container_length and length <= container_width and height <= container_height: packed_items.append((item_type, width, length, height)) container_volume -= width * length * height container_height -= height elif height <= container_length and length <= container_width and width <= container_height: packed_items.append((item_type, height, length, width)) container_volume -= height * length * width container_height -= width return packed_items # 装箱 packed_items = pack_items(item_sizes, containerSizes['40ftHighCube']['internalDimensions']['length'], containerSizes['40ftHighCube']['internalDimensions']['width'], containerSizes['40ftHighCube']['internalDimensions']['height']) # 输出装箱结果 for item_type, length, width, height in packed_items: print(f"Packed {item_type} (L: {length}, W: {width}, H: {height})") # 计算剩余空间 remaining_volume = containerSizes['40ftHighCube']['internalDimensions']['length'] * \ containerSizes['40ftHighCube']['internalDimensions']['width'] * \ containerSizes['40ftHighCube']['internalDimensions']['height'] print(f"Remaining volume in container: {remaining_volume} cubic meters")
复制
三、空间利用方案
假设:
- 优先使用正方体和长方体,因其堆叠效率较高。
- 考虑每种形状的摆放方式和尺寸,尽量减少空间浪费。
装载方案:
-
正方体:
- 优先使用较大尺寸的正方体(例如2.8米和3.0米),因为它们的装载效率高。
- 利用较小尺寸的正方体填补空余空间。
-
圆柱:
- 圆柱适合填补不规则的空间,特别是在较大的箱子之间。
-
梯形和三角形:
- 使用它们填补空间,确保集装箱顶部和底部的利用率。
装载数量计算
为了简化,假设每个形状可以合理堆叠在一起,以下是每种形状的最大装载数量:
序号 | 形状 | 尺寸(米) | 数量(个) |
---|---|---|---|
1 | 正方体 | 3.0 x 3.0 x 3.0 | 2(2层) |
2 | 正方体 | 2.8 x 2.8 x 2.8 | 3(2层) |
3 | 正方体 | 2.2 x 2.2 x 2.2 | 5(2层) |
4 | 正方体 | 1.4 x 1.4 x 1.4 | 12(2层) |
5 | 圆柱 | 2.1 x 1.3 | 6 |
6 | 圆柱 | 1.7 x 2.5 | 5 |
7 | 圆柱 | 0.8 x 1.8 | 12 |
8 | 梯形 | 2.3 x 1.8 x 0.7 | 8 |
9 | 梯形 | 1.5 x 2.9 x 2.1 | 4 |
10 | 三角形 | 2.8 x 1.4 | 7 |
11 | 三角形 | 1.1 x 2.5 | 9 |
这是一种近似的装载数量计算,实际装载时需要具体考虑每个形状的排列方式和空间填充效果。
四、技术方案
-
Python端:
- 使用Python进行装载优化运算,计算各形状的摆放数量和位置。
- 使用Pandas等数据处理库处理和存储形状和位置数据。
- 使用Flask等框架提供后端服务,供前端Three.js访问。
-
Three.js端:
- 使用Three.js创建3D场景,并根据Python传递的数据生成各形状和位置。
- 使用WebGL渲染3D场景,提供交互功能(如旋转、缩放、平移)。
步骤详解
1. Python端计算与数据处理
安装必要库:
pip install numpy pandas flask
复制
代码示例:
import numpy as np import pandas as pd from flask import Flask, jsonify # 定义集装箱的尺寸 container_length = 17 container_width = 2.5 container_height = 2.5 # 定义箱子的尺寸 boxes = [ {'shape': 'cube', 'size': (3.0, 3.0, 3.0)}, {'shape': 'cube', 'size': (2.8, 2.8, 2.8)}, {'shape': 'cube', 'size': (2.2, 2.2, 2.2)}, {'shape': 'cube', 'size': (1.4, 1.4, 1.4)}, {'shape': 'cylinder', 'size': (2.1, 1.3)}, {'shape': 'cylinder', 'size': (1.7, 2.5)}, {'shape': 'cylinder', 'size': (0.8, 1.8)}, {'shape': 'trapezoid', 'size': (2.3, 1.8, 0.7)}, {'shape': 'trapezoid', 'size': (1.5, 2.9, 2.1)}, {'shape': 'triangle', 'size': (2.8, 1.4)}, {'shape': 'triangle', 'size': (1.1, 2.5)} ] # 计算每个形状的装载数量(简化版) def calculate_loads(container_dims, boxes): container_volume = container_dims[0] * container_dims[1] * container_dims[2] loads = [] for box in boxes: box_volume = np.prod(box['size']) max_boxes = container_volume // box_volume loads.append({'shape': box['shape'], 'size': box['size'], 'count': int(max_boxes)}) return loads loads = calculate_loads((container_length, container_width, container_height), boxes) loads_df = pd.DataFrame(loads) app = Flask(__name__) @app.route('/api/loads') def get_loads(): return jsonify(loads_df.to_dict(orient='records')) if __name__ == '__main__': app.run(debug=True)
复制
2. Three.js端展示
安装Three.js: 在HTML文件中引用Three.js库,可以从CDN加载:
<!DOCTYPE html> <html> <head> <title>3D Container Visualization</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script> </head> <body> <script> // 创建场景、相机和渲染器 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); camera.position.z = 30; // 获取数据并创建形状 axios.get('/api/loads') .then(response => { const loads = response.data; loads.forEach(load => { const count = load.count; const size = load.size; for (let i = 0; i < count; i++) { let geometry; let material = new THREE.MeshBasicMaterial({color: Math.random() * 0xffffff}); let mesh; if (load.shape === 'cube') { geometry = new THREE.BoxGeometry(size[0], size[1], size[2]); } else if (load.shape === 'cylinder') { geometry = new THREE.CylinderGeometry(size[1], size[1], size[0], 32); } else if (load.shape === 'trapezoid') { // 自定义几何体(梯形) geometry = new THREE.BoxGeometry(size[0], size[1], size[2]); // 简化为立方体展示 } else if (load.shape === 'triangle') { geometry = new THREE.Geometry(); geometry.vertices.push( new THREE.Vector3(0, size[1], 0), new THREE.Vector3(-size[0] / 2, 0, 0), new THREE.Vector3(size[0] / 2, 0, 0) ); geometry.faces.push(new THREE.Face3(0, 1, 2)); } if (geometry) { mesh = new THREE.Mesh(geometry, material); mesh.position.set( Math.random() * 17 - 8.5, Math.random() * 2.5 - 1.25, Math.random() * 2.5 - 1.25 ); scene.add(mesh); } } }); // 渲染函数 function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate(); }) .catch(error => { console.error('Error fetching loads:', error); }); </script> </body> </html>
复制
运行与测试
-
运行Flask后端:
复制python your_flask_script.py -
打开HTML文件:
- 将上述HTML代码保存为
index.html
。 - 在浏览器中打开该文件。
- 将上述HTML代码保存为
这将启动Flask服务器,计算装载数据并提供API服务,Three.js前端将从API获取装载数据,并在3D场景中进行可视化展示。