一、需求描述
为了在一个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场景中进行可视化展示。