This project implements an intelligent simulation and optimization system for managing a robotic warehouse (Silo). The main objective is to orchestrate the inbound (InputManager) and outbound (OutputManager) flow of tens of thousands of boxes through a network of shuttles, minimizing travel times and maximizing pallet extraction efficiency.
The simulator is accompanied by a 3D Visualizer (developed in React and react-three-fiber) that reads an event trace (trace.json) to render the behavior of the algorithms in real time.
---
The physical warehouse has the following matrix topology:
- Aisles: 4 (numbered 1 to 4).
- Levels (Y / Shuttles): 8 height levels per aisle, giving us 32 independent shuttles.
- Sides: 2 per aisle (left and right).
- Column Depth (X): 60 positions along the aisle.
- Slot Depth (Z): 2 positions (Z1: directly accessible, Z2: blockable by Z1).
Throughout development, we iterated and solved key logistics and scalability problems. The process and adopted strategies are detailed below.
Problem: Initially, we treated all boxes equally or used hardcoded lists (Zones A, B, C). This was inefficient because cities with extremely high demand caused shuttles to waste time traveling to the end of the aisle.
Solution: - The system now reads the injection CSV file (test_boxes_50000_50ciudades.csv) before simulating and calculates the frequencies of each destination.
- We automatically select the top group of destinations that make up 65% of the total box volume and label them as
HOT. The rest areCOLD. - Placement rules:
- HOT: They have access to the full length of the warehouse (
X=1..60), but they sort their searches drastically prioritizing the ultra-fast access zone (HOT_FAST_RANGE = X: 1..10). - COLD: They are strictly relegated to the back of the warehouse (
REST_RANGE = X: 11..60).
- HOT: They have access to the full length of the warehouse (
Problem: The initial slot search algorithm traversed the matrix iterating for y in range(1, 9). This caused the lower levels (Y=1) to be saturated, while the upper shuttles did nothing, creating a massive bottleneck.
Solution:
- We created the
_orden_y_preferidos()function. Instead of searching sequentially, it sorts the 8 levels in real time by looking at:- The overall historical usage of each shuttle (
self.shuttle_load). - The number of boxes that that specific destination already has on that shuttle (
self.dest_shuttle_load).
- The overall historical usage of each shuttle (
- Result: After injecting 50,000 boxes, the standard deviation of the usage of the 32 shuttles is practically 0 (perfect distribution of ~1,560 boxes per shuttle).
Problem: Using depth Z=2 means that if we place a box there, and then a different box enters Z=1, the back box will be held hostage and require an expensive relocation maneuver during the extraction phase.
Solution:
- The
InputManagerapplies a restrictive and intelligent strategy: it only places boxes inZ=2ifZ=1is empty, or if the box already inZ=1is from the same destination (or if the box inZ=1has a lower estimated extraction time). - Even so, if an unavoidable block occurs during extraction time, the
OutputManagerhas implemented the_resolver_bloqueo_z_si_necesariofunction, which sends a shuttle to extract theZ=1box, takes it to the nearest free slot, and then frees theZ=2box, penalizing the simulation's metrics.
Problem: When dispatching the 12-box pallets, we needed the shuttles to take the minimum amount of time possible and not step on each other's toes. Solution:
- When a destination accumulates
$\ge 12$ boxes, it enters the extraction queue (Ready Queue). - The
_seleccionar_12_mejoresfunction groups all available boxes by shuttle. - It selects a maximum of 1 candidate box per shuttle (so that shuttles operate simultaneously, in parallel), looking for boxes that minimize the heuristic cost:
coste = abs(shuttle.pos_x - caja.x) + caja.x. - This algorithm extracts the optimal groups in record time by making the most of the trips.
Problem: The classic parser of the Box class deduced the destination_id assuming it was embedded in the central digits of the box_id (from character 7 to 15). However, the final dataset (test_boxes_50000_50ciudades.csv) separated the destination into an independent column and injected random numbers into the box_id. This caused the InputManager to crash as it perceived thousands of independent destinations (exhausting the columns the AI reserves per destination).
Solution:
- We implemented an on-the-fly overwrite in
generate_trace.py. We extract the correctdestinationcolumn, applyzfill(8), and dynamically reassemble acorrected_box_id. This achieves full compatibility without modifying the original object-oriented structure.
-
Generate the trace (Backend): Processes the CSV and runs the logistics simulation, dumping the results for the frontend.
python generate_trace.py
(This will generate visualizer/public/trace.json with all actions and report the load in the console).
-
Run the 3D Visualizer (Frontend):
cd visualizer npm install npm run devOpen the application in your browser. You will be able to see the 4 aisles and the 32 shuttles moving, and you will clearly identify how prioritization compacts the "HOT Cities" at the front of the racks.
-Pedro Javier Imbroda Castellano