🧪 [Challenge #02 – Analysis] How Many AGVs Are Enough?
A simulation-driven search for the balance between productivity and efficiency
⚙️ Modeling Process Overview
1️⃣ Layout Setup & Base Configuration
- Flow: Start → B zone (Station B) → A zone (Station A) → End
- AGV Routing:
- Each station is paired with a Marker for pickup/drop-off.
- AGVs follow a closed-loop route via Track/Line objects.
- AGVs return to the AGVPool after completing each job.
- AMR Configuration:
- Turning radius: 0m (pivot rotation)
- Capacity: 1
- Receives only one command per trip
- Always returns to AGVPool after the job
2️⃣ Logic Development
- Job generation:
- Source generates jobs when downstream stations in B are available.
- Jobs are pushed to a sequence queue based on availability (B_1, B_2, B_3).
--Source exit ctrl
waituntil B_1.set = false or B_2.set = false or B_3.set = false -- Waituntil B1 Station Empty
if B_1.set = false
B_1.set := true
elseif B_2.set = false
B_2.set := true
else
B_3.set := true
end
seq.push(?.name) -- Set Seq queue
seq.push("")
waituntil A_1.set = false or A_2.set = false or A_3.set = false -- B only
seq.push(?.name)
seq.push("")
- Execution:
- AGV waits in AGVPool until a job is ready.
- Based on the command string, AGV picks up the MU and moves it to the corresponding station.
- When the station in B completes processing, the MU is moved to the next available A zone station.
- After unloading, the AGV always returns to the AGVPool.
-- param attribute: string, oldValue, newValue: string
-- param attribute: string, oldValue: string
param newValue: string
var A :object
waituntil AGVPool.NumIdleAGVs > 0
var agv := agvPool.getIdleAGV
seq.pop
switch newValue
case ""
case "Source"
agv.destination := O_M
waituntil agv.destinationwasreached
wait loadunloadTIme
source.mu.move(agv)
if B_1.empty = true
agv.destination := B_1_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(B_1)
elseif B_2.empty = true
agv.destination := B_2_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(B_2)
else
agv.destination := B_3_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(B_3)
end
agv.destination := AGVPool
waituntil agv.destinationwasreached
case "A_1"
agv.destination := A_1_M
waituntil agv.destinationwasreached
wait loadunloadTIme
A_1.mu.move(agv)
agv.destination := Marker
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(Conveyor)
agv.destination := AGVPool
waituntil agv.destinationwasreached
A_1.set := false
case "A_2"
agv.destination := A_2_M
waituntil agv.destinationwasreached
wait loadunloadTIme
A_2.mu.move(agv)
agv.destination := Marker
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(Conveyor)
agv.destination := AGVPool
waituntil agv.destinationwasreached
A_2.set := false
case "A_3"
agv.destination := A_3_M
waituntil agv.destinationwasreached
wait loadunloadTIme
A_3.mu.move(agv)
agv.destination := Marker
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(Conveyor)
agv.destination := AGVPool
waituntil agv.destinationwasreached
A_3.set := false
case "B_1"
if A_1.set = false
A_1.set := True
A := A_1
elseif A_2.set = false
A_2.set := True
A := A_2
else
A_3.set := True
A := A_3
end
agv.destination := B_1_M
waituntil agv.destinationwasreached
wait loadunloadTIme
B_1.mu.move(agv)
if A = A_1
agv.destination := A_1_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_1)
elseif A = A_2
A_2.set := True
agv.destination := A_2_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_2)
else
A_3.set := True
agv.destination := A_3_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_3)
end
B_1.set := false
agv.destination := AGVPool
waituntil agv.destinationwasreached
case "B_2"
if A_1.set = false
A_1.set := True
A := A_1
elseif A_2.set = false
A_2.set := True
A := A_2
else
A_3.set := True
A := A_3
end
agv.destination := B_2_M
waituntil agv.destinationwasreached
wait loadunloadTIme
B_2.mu.move(agv)
if A = A_1
A_1.set := True
agv.destination := A_1_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_1)
elseif A = A_2
A_2.set := True
agv.destination := A_2_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_2)
else
A_3.set := True
agv.destination := A_3_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_3)
end
B_2.set := false
agv.destination := AGVPool
waituntil agv.destinationwasreached
case "B_3"
if A_1.set = false
A_1.set := True
A := A_1
elseif A_2.set = false
A_2.set := True
A := A_2
else
A_3.set := True
A := A_3
end
agv.destination := B_3_M
waituntil agv.destinationwasreached
wait loadunloadTIme
B_3.mu.move(agv)
if A = A_1
A_1.set := True
agv.destination := A_1_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_1)
elseif A = A_2
A_2.set := True
agv.destination := A_2_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_2)
else
A_3.set := True
agv.destination := A_3_M
waituntil agv.destinationwasreached
wait loadunloadTIme
agv.mu.move(A_3)
end
B_3.set := false
agv.destination := AGVPool
waituntil agv.destinationwasreached
end
agv.isidle := true
- Important Behavior:
- Job sequencing and state flags (set := true/false) are used to reserve stations.
- AGV logic ensures MUs flow only when downstream space is available.
3️⃣ Experiment Design
- Tool: Experiment Manager
- Independent Variable: Number of AGVs (1 to 5)
- Fixed Conditions:
- Processing time: 60 sec (both B and A zones)
- Simulation period: 7 days
- Source interval: fixed
- Key Metrics:
- Daily production (Units Per Day - UPD)
- AGV Idle Time
- Station Waiting Time
- AGV Utilization
📊 Simulation Summary
AGVsDaily Output (UPD)Comment
1 | 225 | Severe bottleneck |
2 | 433 | Improved, but still under target |
3 | 645 | ✅ Target achieved |
4 | 623 | ✅ Slight drop, higher idle time |
5 | 728 | ✅ Max capacity reached, some idle overload |
Note: Results may vary depending on actual simulation conditions.
🔍 Key Insights
- With 1 AGV, a clear bottleneck and long waiting times occur.
- 2 AGVs significantly improve flow, but still fall short of the goal.
- 3 AGVs achieve the daily target of 600 units with balanced performance.
- 4 AGVs lead to a slight increase in idle time, with limited production gain.
- 5 AGVs reach the system’s practical maximum capacity, but show diminishing returns and increased inefficiency.
📌 Conclusion
- ✅ Optimal number of AGVs: 3
- This configuration strikes the best balance between investment and performance.
- Deploying more than 3 AGVs results in marginal gains and higher idle time.
- If maximum capacity is the business priority, 5 AGVs may be justified — but with clear trade-offs.
'Plant Simulation > Challenge' 카테고리의 다른 글
[Challenge #02 – 해설편] AGV는 몇 대가 적당했을까? (0) | 2025.07.27 |
---|---|
[Challenge #02] AGV는 몇 대가 적당할까? (0) | 2025.07.20 |
[Challenge #02] What is the appropriate number of AGVs? (0) | 2025.07.20 |
[Challenge #01 – Solution] How Can We Improve Our First Simulation Model? (0) | 2025.07.18 |
[Challenge #01 – 해설편] 처음 만든 시뮬레이션, 어떻게 개선할 수 있을까? (0) | 2025.07.16 |