[Challenge #02 – Analysis] How Many AGVs Are Enough?

🧪 [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.