1. Doel & validatiestatus
Dit platform simuleert en analyseert warehouse-operaties (picken, wegzetten, combitruck-rondes, slotting) op basis van echte WMS-data. Dit document is de technische onderbouwing: het legt elk rekenmodel uit zodat resultaten richting klanten verdedigbaar en narekenbaar zijn.
| Aspect | Status |
|---|---|
| Geautomatiseerde tests | 85 tests via node --test "tests/*.test.mjs" — elke engine heeft handmatig narekenbare testgevallen (zie §12) |
| Kwaliteitsbewaking | Aparte bewakingstests dwingen de projectafspraken af: engines blijven puur (geen UI-code), elke parameter is gedocumenteerd, dit document vermeldt het werkelijke testaantal, en de deploy-map is identiek aan de bron |
| Browserverificatie | Functionele end-to-end-test (tests/ui-test.html) die alle modules aanklikt en resultaten controleert |
| Reproduceerbaarheid | Alle steekproeven en mockdata gebruiken een vaste seed (mulberry32) — zelfde invoer geeft altijd exact dezelfde uitkomst |
| Scheiding logica / weergave | Alle rekenmodellen zijn pure modules in js/engines/ zonder UI-afhankelijkheden — afzonderlijk testbaar en herbruikbaar |
| Tijd-consistentie | De animatie speelt exact de berekende tijd af (tijdlijn = rijtijd + handelingstijden); vastgelegd met een consistentietest |
2. Data & coördinatensysteem
Alle berekeningen werken op drie databestanden die uit WMS-exports gegenereerd worden.
| Bestand | Inhoud | Bron |
|---|---|---|
warehouse_locations.json | 37.760 locaties: code, gang, blok, verdieping, positie + 3D-coördinaten | generate_layout.py ← plattegrond-xlsx |
analysis_data.json | Per locatie: ABC-klasse, voorraadleeftijd, zone, categorie (momentopname voorraad) | generate_analysis.py ← voorraadexport |
simulation_data.json | 9.480 chronologische mutaties (pick/put-away) over 6 dagen: tijdstip, locatie, artikel | generate_simulation.py ← mutatie-export |
Locatiecode → 3D-positie
Locatieformat GG-BB-VA (gang–blok–verdieping+positie). De afbeelding naar meters:
Gangen lopen dus in de X-richting; het gangpadcentrum van een gang wordt in de engines afgeleid uit de locatiedata zelf (gemiddelde van min/max Z per gang), zodat ook afwijkende klantlayouts kloppen zonder hardcoded maten.
Bron: generate_layout.py · js/engines/routing.js (constructor) Test: routing.test.mjs — "gangcentrum wordt afgeleid uit de locaties"3. Afstandsberekening (routeringsmodel)
Alle afstanden in het platform — picksimulatie, wegzetten, combitruck, slottingscore, what-if — komen uit één routeringsengine. Er bestaat dus geen "tweede waarheid".
Het gangpadmodel
Een picker of truck beweegt uitsluitend:
- binnen een gangpad in de X-richting;
- tussen gangpaden via een kopgang (links of rechts om de stellingen heen), een doorlopende dwarsgang (indien geconfigureerd) of een doorsteekopening (§4).
Diagonaal door stellingen "snijden" is per constructie onmogelijk: het routenetwerk is een portaalgraaf waarin alleen die verbindingen bestaan. Kortste paden worden berekend met Dijkstra; portaal-naar-portaal-afstanden worden één keer berekend en daarna hergebruikt (exact, geen benadering).
Testwarehouse: 3 gangen (Z = 10, 5, 0), stellingen X 0–20, kopgangen op X = −2 en X = +22. Van (x=2, gang 1) naar (x=8, gang 2):
via links : |2−(−2)| + |10−5| + |8−(−2)| = 4 + 5 + 10 = 19 m ← kortste via rechts: |2−22| + |10−5| + |8−22| = 20 + 5 + 14 = 39 m met extra dwarsgang op X=10: 8 + 5 + 2 = 15 mVerticaal: tijd, geen meters
Een pick op verdieping 5 kost geen extra loopafstand maar wel extra tijd (instelbaar per niveau). In KPI's waar hoog/laag eerlijk moet meewegen (slottingscore, what-if) wordt die tijd omgerekend naar meter-equivalenten — zie §10.
4. Doorsteekdetectie
In veel warehouses zitten doorrijopeningen ónder de stellingen waar een truck van gang kan wisselen. Het platform detecteert ze automatisch uit de layoutdata — geen handmatige invoer, dus ook geen vergeten of verzonnen openingen.
Detectieregel
Dit dekt twee fysieke situaties: (a) een doorrijopening onder de stellingen — verdieping 1–3 ontbreekt, verdieping 4+ loopt door; (b) gebieden waar simpelweg geen stellingen staan.
Tussen locatie 23-22-1D (x≈43) en 23-28-1A (x=52): blokken 23–26 van gang 22 én 23 hebben alleen verdieping 4–6 in de data — verdieping 1–3 is vrij. De detectie vindt hier een doorsteek op x 44–52, precies de opening die in de praktijk gebruikt wordt. In totaal vindt de detectie 32 doorsteken in de demolayout.
Elke gedetecteerde doorsteek is in de 3D-view zichtbaar (groen vlak) en per stuk uit te schakelen (rood) als hij in werkelijkheid niet gebruikt mag worden. Uitgeschakelde doorsteken doen niet mee in het routenetwerk.
Bron: js/engines/openingen.js Tests: openingen.test.mjs — incl. integratietest op de echte layout (doorsteek 23↔22 bij x 44–52)5. Picklogica — orderpicker (4 algoritmen)
De picksimulatie vergelijkt vier routeringsstrategieën op exact dezelfde picklijst. De gerapporteerde afstand van de heuristieken is de werkelijk gelopen route (expliciet pad, inclusief volledige gangtraversen) — niet een optimistische som van hemelsbrede afstanden.
| Algoritme | Werking | Wanneer sterk |
|---|---|---|
| S-vorm | Elke gang met picks volledig doorlopen, afwisselend van kopgang wisselend (slingerend). Laatste gang: goedkoopste afsluiting (keren of doorlopen) inclusief terugweg naar het depot. | Veel picks per gang |
| Grootste opening | Per gang worden picks gesplitst bij de grootste "opening" (gat tussen opeenvolgende picks of naar een kopgang): het deel vóór de opening wordt vanaf links bediend, het deel erna vanaf rechts via keerlussen. De verste gang wordt doorlopen om de andere kopgang te bereiken. | Weinig picks per gang |
| Gecombineerd | Per gang de goedkoopste keuze: volledig doorlopen (kost ganglengte, wisselt van kant) of keren bij de verste pick (kost 2 × diepte, blijft aan dezelfde kant). | Gemengde dichtheid |
| Optimaal (2-opt) | Nearest-neighbour-startvolgorde, verbeterd met 2-opt op de exacte netwerkafstanden (incl. doorsteken). Geen gang-heuristiek: vrije volgorde. | Referentie / bovengrens van haalbare winst |
Testwarehouse §3, depot op (x=10, gang 3). Picks: (x=5, gang 2) en (x=15, gang 1):
heen : depot → linkerkopgang 12 + naar gang 2 5 + pick op x=5 7 + doorlopen naar rechts 17 = 41 m terug: naar gang 1 5 + pick op x=15 7 + keren 7 + terug naar depotgang 10 + naar depot 12 = 41 m totaal = 82 m (keren was hier goedkoper dan doorlopen: 82 vs 92)Dat "Optimaal" hier wint komt vooral doordat het als enige de 32 doorsteken mag gebruiken — de drie heuristieken zijn klassiek gedefinieerd op kopgangen. Het verschil kwantificeert dus direct de waarde van de doorsteken.
6. Combitruck & dual cycling
Full-pallet werkmodel: een combitruck werkt taken strikt per gang af (minimaal gangwisselen). Per gang liggen twee instelbare punten: het drop-off punt (gepickte pallets) en het pick-up punt (inslagvoorraad).
Dual cycling (interleaving)
De besparing ontstaat door een inslagrit en een pickrit te koppelen: vol de gang in (inslag), leeg alleen het stukje tussen inslag- en picklocatie, vol terug (pick).
Eén gang, drop-off x=1, pick-up x=3. Inslag naar x=18, pick op x=15. Aanrijden depot→gang 27 m, terug 25 m:
dual : 3→18 (15) + 18→15 (3) + 15→1 (14) = 32 m → totaal 27+32+25 = 84 m enkel: inslag 2×15 (30) + omrijden 2 + pick 2×14 (28) = 60 m → totaal 112 m besparing dual cycling = 28 m (= 2 × min(diepte_inslag, diepte_pick) bij gelijke punten)De referentie "enkele cycli" gebruikt dezelfde gangvolgorde en aanrijroutes — alleen het werk ín de gang verschilt. De vergelijking meet dus zuiver het effect van dual cycling, niets anders.
Meerdere trucks & werkzones
- Zones zijn vrij definieerbare gangbereiken (bv. zone A = gang 1–5) en per truck toewijsbaar.
- Gang-integriteit: alle taken van één gang gaan altijd naar dezelfde truck — het per-gang-werkmodel blijft intact. Balancering gebeurt op taakaantal.
- Taken in gangen die geen enkele truck mag bedienen worden niet stilletjes herverdeeld maar gerapporteerd als "niet uitgevoerd" — een bewust signaal dat de zonedekking niet klopt.
- Doorlooptijd-KPI = tijd van de traagste truck (parallelle uitvoering), naast de totale rijmeters.
Schaduwscenario's (A/B-vergelijking van de inrichting)
Een scenario is een vastgelegde momentopname van de inrichting: drop-off/pick-up punten, doorsteken aan/uit, kopgangen, dwarsgangen, zones en truckaantal. Bij een scenariorun draaien alle scenario's op exact dezelfde takenlijst — alleen de inrichting verschilt. Het verschil in rijmeters en doorlooptijd is daardoor zuiver toe te schrijven aan de inrichtingswijziging, niet aan toeval in de taken.
Visueel rijdt scenario 1 ("Huidig") ondoorzichtig mét pallets en stapels; schaduwscenario's rijden semi-transparant mee in hun eigen kleur (baken op het dak + bord S2·T1 enz.). De vergelijkingstabel markeert het snelste scenario en toont het verschil in tijd en meters t.o.v. Huidig.
Bron: js/engines/combitruck.js (simuleerScenario) Tests: combitruck.test.mjs — 84/112/28-narekening, drop-off-garantie per gang, gangvolgorde, zoneverdeling (6 tests), besparing ≥ 0, tijdmodel, scenario's (punten aan taakkant korter; doorsteek open korter; 2 trucks halveren doorlooptijd; identieke taken per scenario)7. Wegzetlogica (orderpicker-variant)
De wegzetsimulatie wijst inkomende pallets toe aan lege locaties volgens een instelbare strategie en vergelijkt het resultaat altijd met willekeurig wegzetten op exact dezelfde palletlijst.
Strafpuntenmodel
Elke strategie is een optelsom van strafpunten + afstand als doorslag. Nieuwe klantregels zijn één extra strafterm — geen herontwerp.
ABC-banden
Gangen worden op depotafstand verdeeld: de dichtstbijzijnde abcZoneA (25%) van de gangen = A-band, de volgende abcZoneB (35%) = B-band, de rest = C-band. Golden zone = instelbare verdiepingen (standaard 1–2).
Zonekwaliteitsscore
8. Tijdmodellen
Elke tijdschatting is opgebouwd uit rijden/lopen + handelingstijden + heftijd. Geen verborgen factoren.
De 3D-animatie is een afspeling van ditzelfde tijdmodel: de animatieklok telt gesimuleerde seconden (rij-items = afstand/snelheid, pauze-items = handelingsduur). Visualisatie en gerapporteerde tijden kunnen daardoor per constructie niet van elkaar afwijken.
Tests: combitruck.test.mjs — "tijdmodel telt handling, heffen en drop/pickup mee" + "rijtijd + actieduur = berekende tijd"9. Heatmap
De heatmap kleurt elke locatie op intensiteit 0–1, berekend uit de mutatiestroom en/of de voorraadmomentopname.
| Weergave | Ruwe waarde per locatie |
|---|---|
| Pickfrequentie | aantal out-mutaties op die locatie |
| Wegzetfrequentie | aantal in-mutaties |
| Verblijftijd | voorraadleeftijd in dagen (uit analysedata) |
| ABC-klasse | vaste schaal A=1,0 · B=0,62 · C=0,3 · deadstock=0,08 |
| Gecombineerd | picks + wegzet + ABC-schaal |
Tijdens de mutatie-replay wordt alleen geteld tot de huidige replaypositie — de heatmap "groeit" dus live en correct mee met de afgespeelde dagen.
Bron: js/engines/heatmap.js Tests: heatmap.test.mjs — bereik [0,1], ordening, dynamische update, kleurschaaleinden10. Analyse-KPI's & what-if
Het analysepaneel is gebouwd op één centraal uitgangspunt: locaties zijn dynamisch per pallet. Een gepickte pallet wordt niet vanzelf vervangen door hetzelfde artikel. Daarom werkt geen enkele KPI met "de vaste locatie van een SKU".
10.1 Reiskosten van een locatie
De tweede term rekent heftijd om naar meter-equivalenten zodat een snelle locatie op verdieping 5 niet ten onrechte "goedkoop" lijkt. Voorbeeld: verdieping 5, 8 s/niveau, 1,2 m/s → 4 × 8 × 1,2 = +38,4 m-equivalent.
10.2 Pickkosten van een SKU (dynamisch!)
Dit is de empirische voetafdruk van de huidige wegzetregels — gemeten uit de werkelijke mutaties, geen aanname. In de demodata wordt topartikel ID-2001 bijvoorbeeld over 189 verschillende locaties gepickt; precies daarom zou een "vaste locatie"-model hier onzin opleveren.
10.3 Slottingefficiëntiescore
Dat laatste is essentieel: als alle picks achterin gebeuren terwijl vóórin leegstaat, hoort de score laag te zijn. Een eerdere versie die alleen bezochte locaties meenam gaf een misleidende 99% en is daarom vervangen; de huidige score op de demodata is 38,4% — eerlijk, en het toont meteen het besparingspotentieel.
Test: analytics.test.mjs — "perfecte slotting geeft 100" én "lege goedkope locaties tellen mee in het ideaal"10.4 What-if: wegzetzone van een SKU wijzigen
Omdat locaties dynamisch zijn, is het enige dat je werkelijk kunt sturen de wegzetzone. Het what-if-model:
De batchfactor (standaard 0,5) corrigeert dat één pick in een batchroute geen volledige eigen retourrit kost. Bij stuks-/enkelorderpicken zet je hem op 1,0. Hij schaalt alle besparingen lineair en staat expliciet bij elk resultaat vermeld.
Inclusief capaciteitscheck: past de voetafdruk (aantal picklocaties) niet in de vrije ruimte van de doelzone, dan staat er een expliciete waarschuwing.
10.5 Herslottingadvies (top 10)
Zelfde rekenmodel als de what-if, maar capaciteitsbewust over meerdere SKUs: elke aanbeveling reserveert de voetafdruk van de SKU in de doelzone, zodat tien adviezen niet allemaal dezelfde "beste plek" claimen. Drempel: minimaal 5 m/dag besparing. Elke regel heeft de volledige onderbouwing als hover-tekst.
10.6 Congestie & pickpadtrend
- Congestie: aantal mutaties per gang per uur (matrix) — toont knelpunten op piektijden. Directe telling, geen model.
- Pickpadtrend: afstand tussen opeenvolgende picks per dag als proxy voor de gelopen route (de mutatiedata bevat geen rondenummers). Geschikt als trendindicator dag-op-dag, niet als absolute routelengte — zie §11.
11. Aannames & beperkingen (eerlijkheidsparagraaf)
Wat het model bewust níet doet — essentieel voor een verdedigbaar klantgesprek.
| Aanname / vereenvoudiging | Effect & mitigatie |
|---|---|
| Trucks/pickers hinderen elkaar niet fysiek in een gang | Congestie wordt apart geanalyseerd (gang×uur-matrix); wachttijden zitten niet in de tijdschatting |
| Pickpadtrend is een proxy (opeenvolgende picks) | Alleen voor dag-op-dag-vergelijking gebruiken; met rondenummers in de export wordt dit exact |
| Batchfactor is een instelling, niet gemeten | Per klant afstemmen op het pickproces (1,0 enkelorder, ~0,3–0,6 batch); met ordernummers in de data is hij meetbaar |
| Verwachte zonekosten = gemiddelde vrije locatie | Bewust conservatief (onderschat de besparing eerder dan overschat) |
| Inslagtaken in de demo zijn gegenereerd | De engine accepteert elke takenlijst — een echte inbound-lijst is direct aansluitbaar |
| Wegzet-vergelijking is t.o.v. wíllekeurig | Bij presentatie benoemen: 89,6% is t.o.v. de slechtst denkbare referentie |
| S-vorm/grootste-opening/gecombineerd gebruiken alleen kopgangen | Conform de klassieke definities; "Optimaal" gebruikt het volledige netwerk incl. doorsteken — het verschil kwantificeert de doorsteekwinst |
| 250 werkdagen/jaar voor projecties | Instelbaar (Instellingen → Analyse) |
| Heftijd lineair per verdieping | Instelbaar per klant; werkelijke hefcurves zijn niet-lineair maar het verschil is klein t.o.v. rijtijden |
12. Validatie & tests
85 geautomatiseerde tests dekken elke engine. De belangrijkste categorie zijn handmatig narekenbare gevallen: kleine voorbeelden waarvan de uitkomst op papier is uitgerekend en exact vastgelegd.
| Testbestand | Dekt | Voorbeelden van vastgelegde waarheden |
|---|---|---|
routing.test.mjs (12) | Afstanden, paden, netwerk | 4+5+10=19 m; dwarsgang 15 m; paden nooit diagonaal; symmetrie; kopgang uitschakelen → 39 m |
openingen.test.mjs (6) | Doorsteekdetectie | echte doorsteek 23↔22 op x 44–52 gevonden; vervalt bij hogere doorrijhoogte-eis |
picking.test.mjs (7) | 4 pickalgoritmen | S-vorm exact 82 m; elke pick precies één keer; optimaal ≤ s-vorm; splitsing grootste gat |
putaway.test.mjs (10) | Wegzetregels | A-pallets in A-band; bulk vanaf niveau 3; geen dubbele toewijzing; bezetting +12 na 12 pallets |
combitruck.test.mjs (17) | Dual cycling, zones, tijd, scenario's | 84/112/28 m exact; 109 s exact; picks eindigen op drop-off; gang-integriteit; tijdlijn = engine-tijd; scenario's vergelijken identieke taken |
kwaliteit.test.mjs (5) | Projectafspraken | engines puur (geen UI-code); alle modules importeerbaar; elke parameter gedocumenteerd; documentatie vermeldt werkelijk testaantal; deploy = bron |
heatmap.test.mjs (8) | Intensiteiten, kleurschaal | bereik [0,1]; drukste locatie het warmst; live-update verschuift waarden |
analytics.test.mjs (13) | KPI's, what-if | perfecte slotting = 100; lege locaties in ideaal; batchfactor lineair; advies capaciteitsbewust |
mockdata.test.mjs (7) | Testdata | ABC-volume 80/15/5 ±5%; deterministisch per seed |
Zelf valideren
Daarnaast: alle steekproeven zijn seeded — een demonstratierun is dus exact reproduceerbaar, en wijzigingen in resultaten zijn altijd herleidbaar tot wijzigingen in data of parameters, nooit tot toeval.
13. Parameters
Geen enkele rekenwaarde is hardcoded: alles staat in js/config.js (CONFIG_SCHEMA), is instelbaar via de UI (sidebar → Instellingen, met uitleg per veld) en gedocumenteerd in KLANT_CONFIG.md. De belangrijkste:
| Parameter | Standaard | Werkt door in |
|---|---|---|
| Loopsnelheid picker | 1,2 m/s | Tijden picksimulatie, verticale meter-equivalenten |
| Picktijd per orderregel | 12 s | Tijden picksimulatie |
| Rijsnelheid combitruck | 2,5 m/s | Alle combitruck-tijden en de animatie |
| Handelingstijden combitruck | 30/30/10 s | Pick, inslag, drop/pickup-punt (+ heftijd 10 s/niveau) |
| Doorrijhoogte doorsteken | 3 niveaus | Doorsteekdetectie → routenetwerk → alle afstanden |
| ABC-zonebanden | 25% / 35% | Wegzetregels, what-if, herslotting |
| Golden zone | verdieping 1–2 | Wegzetregels, herslotting |
| Batchfactor | 0,5 | Alle besparingen in what-if en herslotting (lineair) |
| Werkdagen per jaar | 250 | Jaarprojecties |
| Drop-off / pick-up punten, zones, doorsteek-schakelaars | per gang | Combitruck — visueel instelbaar in 3D |
Per-gang instellingen (punten, doorsteken aan/uit, zones, trucks) worden per browser bewaard in localStorage; de defaults staan in code en zijn per klantuitrol aanpasbaar volgens NABOUWGIDS.md.