Mini Automated Guided Vehicle

Small robots that move goods without a fixed line.

A mini AGV is a compact, self-driving vehicle that carries a light load from one point to another and hands it off on its own. Put a few on the floor, or a few hundred — the layout is software, not steelwork.

Carry

Takes on a light load at a pick-up point and holds it steady in transit.

Navigate

Works out its own route and keeps clear of people and other vehicles.

Hand off

Places, tips or lifts the load at the destination without an operator.

Recharge

Returns to a charging point on its own and rejoins the fleet when ready.

Handoffs0
Vehicles0
Stations0
FaultsNone
Illustration of fleet behaviour — not live data
/* ---------- palette + sizing (edit these) ---------- */ .magv{ --magv-height: 300px; --magv-deck: #CFD0CC; /* floor */ --magv-grid: #B4B6B1; /* floor markers, shadows */ --magv-accent: #E9A70B; /* the vehicles */ --magv-accent-d: #A0700A; /* vehicle detail + live figure */ --magv-ink: #16181A; --magv-ink-2: #5B6165; --magv-ink-3: #878C8F; --magv-line: #C4C6C1; /* borders */ --magv-surface: #F1F1EF; /* stats bar, pick-up station */ --magv-load: #C4B08B; /* the carried item */ --magv-load-2: #E2D6BC; /* its label */ --magv-station: #9EA3A0; --magv-station-2: #7E8481; --magv-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; --magv-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace; } /* dark palette — applied when the visitor prefers dark and scheme is auto */ @media (prefers-color-scheme: dark){ .magv[data-scheme="auto"]{ --magv-deck: #1F2326; --magv-grid: #333A3E; --magv-accent: #F5B92B; --magv-accent-d: #F5B92B; --magv-ink: #ECEDEA; --magv-ink-2: #9BA1A5; --magv-ink-3: #6E7579; --magv-line: #2E3335; --magv-surface: #1B1E21; --magv-load: #9C8A68; --magv-load-2: #C4B593; --magv-station: #41474A; --magv-station-2: #2C3134; } } /* ...or when you force it */ .magv[data-scheme="dark"]{ --magv-deck: #1F2326; --magv-grid: #333A3E; --magv-accent: #F5B92B; --magv-accent-d: #F5B92B; --magv-ink: #ECEDEA; --magv-ink-2: #9BA1A5; --magv-ink-3: #6E7579; --magv-line: #2E3335; --magv-surface: #1B1E21; --magv-load: #9C8A68; --magv-load-2: #C4B593; --magv-station: #41474A; --magv-station-2: #2C3134; } /* ---------- structure ---------- */ .magv, .magv *{ box-sizing: border-box; } .magv{ width: 100%; font-family: var(--magv-font); } .magv__frame{ border: 1px solid var(--magv-line); border-radius: 14px; background: var(--magv-deck); overflow: hidden; } .magv__canvas{ display: block; width: 100%; height: var(--magv-height); max-width: 100%; } .magv__bar{ display: flex; flex-wrap: wrap; align-items: center; border-top: 1px solid var(--magv-line); background: var(--magv-surface); } .magv__stat{ display: flex; align-items: baseline; gap: 9px; padding: 11px 20px; border-right: 1px solid var(--magv-line); font-family: var(--magv-mono); font-size: 11px; line-height: 1.4; letter-spacing: .12em; text-transform: uppercase; color: var(--magv-ink-3); } .magv__stat b{ font-size: 14px; font-weight: 600; letter-spacing: .02em; color: var(--magv-ink); font-variant-numeric: tabular-nums; } .magv__stat--live b{ color: var(--magv-accent-d); } .magv__note{ margin-left: auto; padding: 11px 20px; font-family: var(--magv-mono); font-size: 10px; line-height: 1.4; letter-spacing: .09em; text-transform: uppercase; color: var(--magv-ink-3); } @media (max-width: 760px){ .magv__note{ margin-left: 0; width: 100%; border-top: 1px solid var(--magv-line); } .magv__stat{ padding: 10px 14px; } } (function () { 'use strict'; function initAll() { var nodes = document.querySelectorAll('.magv'); for (var i = 0; i < nodes.length; i++) init(nodes[i]); } function init(root) { if (root.getAttribute('data-magv-ready') === '1') return; // don't double-init root.setAttribute('data-magv-ready', '1'); var cv = root.querySelector('.magv__canvas'); if (!cv || !cv.getContext) return; var ctx = cv.getContext('2d'); var elCount = root.querySelector('[data-magv="count"]'); var elBots = root.querySelector('[data-magv="bots"]'); var elStations = root.querySelector('[data-magv="stations"]'); var motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); var W = 0, H = 0, dpr = 1; var deckTop = 0, deckBot = 0, rw = 30, rh = 20; var lanes = [], stations = [], pickup = { x: 0, y: 0, w: 0, h: 0 }; var bots = [], done = 0; var rafId = null, last = 0, running = false; var C = {}; function readTokens() { var s = getComputedStyle(root); function g(n) { return s.getPropertyValue(n).trim(); } C = { deck: g('--magv-deck'), grid: g('--magv-grid'), accent: g('--magv-accent'), accentD: g('--magv-accent-d'), ink: g('--magv-ink'), ink2: g('--magv-ink-2'), ink3: g('--magv-ink-3'), line: g('--magv-line'), surface: g('--magv-surface'), load: g('--magv-load'), load2: g('--magv-load-2'), station: g('--magv-station'), station2: g('--magv-station-2'), mono: g('--magv-mono') || 'monospace' }; } function rr(x, y, w, h, r) { r = Math.min(r, w / 2, h / 2); ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); } function layout() { var bandH = Math.max(38, Math.min(58, H * 0.19)); var pad = 9; var pickW = Math.max(58, Math.min(92, W * 0.085)); deckTop = bandH + pad; deckBot = H - bandH - pad; rw = Math.max(22, Math.min(34, W * 0.031)); rh = rw * 0.66; lanes = []; var n = 4, usable = deckBot - deckTop; for (var i = 0; i < n; i++) lanes.push(deckTop + usable * (i + 0.5) / n); var startX = pickW + 16; var avail = W - startX - 12; var gap = Math.max(9, W * 0.012); var target = Math.max(50, Math.min(76, W * 0.062)); var count = Math.max(3, Math.round((avail + gap) / (target + gap))); var cw = (avail - gap * (count - 1)) / count; stations = []; for (var s = 0; s < 2; s++) { for (var j = 0; j < count; j++) { stations.push({ x: startX + j * (cw + gap), w: cw, y: s === 0 ? pad : H - bandH - pad, h: bandH - pad, side: s === 0 ? 'top' : 'bot', id: (s === 0 ? 'A' : 'B') + (j + 1), fill: Math.random() * 0.55, flash: 0, swap: 0 }); } } pickup = { x: 7, y: deckTop, w: pickW - 14, h: deckBot - deckTop }; if (elStations) elStations.textContent = String(stations.length); } function assign(b) { var ahead = []; for (var i = 0; i b.x + rw) ahead.push(stations[i]); } var pool = ahead.length ? ahead : stations; var c = pool[(Math.random() * pool.length) | 0]; b.station = c; b.targetX = c.x + c.w / 2; b.edgeY = c.side === 'top' ? deckTop + rh * 0.62 : deckBot - rh * 0.62; return c; } function respawn(b, seed) { b.lane = (Math.random() * lanes.length) | 0; b.homeY = lanes[b.lane]; b.y = b.homeY; b.speed = 50 + Math.random() * 32; b.tipT = 0; b.state = 'carry'; if (seed) { b.x = pickup.x + pickup.w + Math.random() * Math.max(40, W - pickup.w - 60); b.alpha = 1; } else { b.x = pickup.x + pickup.w * 0.55; b.alpha = 0; } assign(b); if (seed && b.x > b.targetX) b.state = 'return'; } function build() { var want = Math.max(6, Math.min(14, Math.round(W / 82))); bots = []; for (var i = 0; i = b.targetX) { b.x = b.targetX; b.state = 'approach'; } } else if (b.state === 'approach') { var dy = b.edgeY - b.y; b.y += (dy > 0 ? 1 : -1) * Math.min(Math.abs(dy), 78 * dt); if (Math.abs(dy) = 1) { b.tipT = 1; b.state = 'return'; done++; b.station.flash = 1; b.station.fill += 0.11; if (b.station.fill >= 1) { b.station.fill = 0; b.station.swap = 1; } } } else { b.x += b.speed * 1.3 * dt; var d2 = b.homeY - b.y; b.y += (d2 > 0 ? 1 : -1) * Math.min(Math.abs(d2), 64 * dt); if (b.x > W + 40) respawn(b, false); } if (b.state === 'return' && b.x > W - 56) b.alpha = Math.max(0, (W + 24 - b.x) / 80); else b.alpha = Math.min(1, b.alpha + dt * 2.4); } function drawStation(c) { var openH = 6, top = c.y; ctx.fillStyle = C.station; rr(c.x, top, c.w, c.h, 4); ctx.fill(); if (c.fill > 0.02) { ctx.fillStyle = C.station2; var fh = (c.h - openH - 4) * c.fill; rr(c.x + 3, top + c.h - 3 - fh, c.w - 6, fh, 3); ctx.fill(); } ctx.fillStyle = C.station2; if (c.side === 'top') rr(c.x, top + c.h - openH, c.w, openH, 3); else rr(c.x, top, c.w, openH, 3); ctx.fill(); if (c.flash > 0.01) { ctx.strokeStyle = C.accent; ctx.globalAlpha = c.flash; ctx.lineWidth = 2; rr(c.x - 1, top - 1, c.w + 2, c.h + 2, 5); ctx.stroke(); ctx.globalAlpha = 1; } if (c.swap > 0.01) { ctx.fillStyle = C.accent; ctx.globalAlpha = c.swap * 0.35; rr(c.x, top, c.w, c.h, 4); ctx.fill(); ctx.globalAlpha = 1; } if (c.w > 40) { ctx.fillStyle = C.ink3; ctx.font = '600 9px ' + C.mono; ctx.textAlign = 'center'; ctx.textBaseline = c.side === 'top' ? 'top' : 'bottom'; ctx.fillText(c.id, c.x + c.w / 2, c.side === 'top' ? top + 5 : top + c.h - 5); } } function drawBot(b) { var t = b.state === 'tip' ? b.tipT : 0; var dir = b.station && b.station.side === 'top' ? -1 : 1; var tilt = t > 0 ? Math.sin(Math.PI * Math.min(1, t * 1.25)) * 0.34 * dir : 0; var carrying = b.state === 'carry' || b.state === 'approach' || b.state === 'tip'; var pw = rw * 0.6, ph = rh * 0.52, pOff = 0, pAlpha = 1; if (b.state === 'tip') { var e = t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; pOff = e * (rh * 0.5 + 30) * dir; pAlpha = 1 - Math.max(0, (t - 0.5) / 0.5); } ctx.save(); // shadow ctx.fillStyle = C.grid; ctx.globalAlpha = b.alpha * 0.35; ctx.beginPath(); ctx.ellipse(b.x, b.y + rh * 0.62, rw * 0.5, rh * 0.2, 0, 0, Math.PI * 2); ctx.fill(); ctx.globalAlpha = b.alpha; // chassis (tilts when handing off) ctx.save(); ctx.translate(b.x, b.y); ctx.rotate(tilt); ctx.fillStyle = C.accent; rr(-rw / 2, -rh / 2, rw, rh, rh * 0.34); ctx.fill(); ctx.fillStyle = C.accentD; ctx.globalAlpha = b.alpha * 0.32; rr(-rw / 2 + 2.5, -rh / 2 + 2, rw - 5, rh - 4, rh * 0.26); ctx.fill(); ctx.globalAlpha = b.alpha; ctx.fillStyle = C.ink; ctx.globalAlpha = b.alpha * 0.7; rr(rw / 2 - 6, -1.4, 3.4, 2.8, 1.4); ctx.fill(); ctx.globalAlpha = b.alpha; ctx.restore(); // the load if (carrying) { ctx.globalAlpha = b.alpha * pAlpha; ctx.fillStyle = C.load; rr(b.x - pw / 2, b.y - ph / 2 + pOff, pw, ph, 2); ctx.fill(); ctx.fillStyle = C.load2; rr(b.x - pw / 2 + 2, b.y - ph / 2 + pOff + 1.5, pw * 0.42, ph * 0.4, 1); ctx.fill(); } ctx.restore(); } function draw() { ctx.clearRect(0, 0, W, H); ctx.fillStyle = C.deck; ctx.fillRect(0, 0, W, H); // floor markers ctx.strokeStyle = C.grid; ctx.lineWidth = 1; ctx.globalAlpha = 0.75; ctx.beginPath(); for (var gx = pickup.x + pickup.w + 15; gx < W; gx += 30) { for (var gy = deckTop + 4; gy < deckBot; gy += 30) { ctx.moveTo(gx - 2.5, gy); ctx.lineTo(gx + 2.5, gy); ctx.moveTo(gx, gy - 2.5); ctx.lineTo(gx, gy + 2.5); } } ctx.stroke(); ctx.globalAlpha = 1; // lane edges ctx.strokeStyle = C.grid; ctx.beginPath(); ctx.moveTo(0, deckTop - 3); ctx.lineTo(W, deckTop - 3); ctx.moveTo(0, deckBot + 3); ctx.lineTo(W, deckBot + 3); ctx.stroke(); // pick-up station ctx.fillStyle = C.surface; rr(pickup.x, pickup.y, pickup.w, pickup.h, 6); ctx.fill(); ctx.strokeStyle = C.line; ctx.stroke(); ctx.fillStyle = C.accent; rr(pickup.x + pickup.w - 4, pickup.y + 6, 3, pickup.h - 12, 1.5); ctx.fill(); ctx.save(); ctx.translate(pickup.x + pickup.w / 2 - 2, pickup.y + pickup.h / 2); ctx.rotate(-Math.PI / 2); ctx.fillStyle = C.ink2; ctx.font = '600 9px ' + C.mono; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('PICK-UP', 0, 0); ctx.restore(); for (var i = 0; i < stations.length; i++) drawStation(stations[i]); for (var j = 0; j < bots.length; j++) drawBot(bots[j]); } function frame(now) { if (!running) return; var dt = Math.min(0.05, (now - last) / 1000 || 0); last = now; for (var i = 0; i < bots.length; i++) step(bots[i], dt); for (var j = 0; j 0) stations[j].flash = Math.max(0, stations[j].flash - dt * 2.2); if (stations[j].swap > 0) stations[j].swap = Math.max(0, stations[j].swap - dt * 1.6); } draw(); if (elCount) { var txt = done.toLocaleString('en-US'); if (elCount.textContent !== txt) elCount.textContent = txt; } rafId = requestAnimationFrame(frame); } function start() { if (running || motionQuery.matches || !W) return; running = true; last = performance.now(); rafId = requestAnimationFrame(frame); } function stop() { running = false; if (rafId) cancelAnimationFrame(rafId); rafId = null; } function staticFrame() { for (var i = 0; i < bots.length; i++) { var b = bots[i]; b.x = pickup.x + pickup.w + 30 + (i / bots.length) * (W - pickup.w - 90); b.alpha = 1; if (i === 2) { b.state = 'tip'; b.tipT = 0.62; b.y = b.edgeY; } } done = 1284; draw(); if (elCount) elCount.textContent = done.toLocaleString('en-US'); } function resize() { var r = cv.getBoundingClientRect(); if (!r.width) return; dpr = Math.min(window.devicePixelRatio || 1, 2); cv.width = Math.round(r.width * dpr); cv.height = Math.round(r.height * dpr); ctx.setTransform(dpr, 0, 0, dpr, 0, 0); W = r.width; H = r.height; readTokens(); layout(); build(); if (motionQuery.matches) staticFrame(); else draw(); } var rt; window.addEventListener('resize', function () { clearTimeout(rt); rt = setTimeout(resize, 140); }); // keep canvas colours in step with the palette var darkQuery = window.matchMedia('(prefers-color-scheme: dark)'); function repaint() { readTokens(); if (!running) draw(); } if (darkQuery.addEventListener) darkQuery.addEventListener('change', repaint); if (window.MutationObserver) { new MutationObserver(repaint).observe(root, { attributes: true, attributeFilter: ['data-scheme'] }); } if (motionQuery.addEventListener) { motionQuery.addEventListener('change', function () { if (motionQuery.matches) { stop(); staticFrame(); } else { start(); } }); } document.addEventListener('visibilitychange', function () { if (document.hidden) stop(); else start(); }); // only animate while it is on screen if ('IntersectionObserver' in window) { new IntersectionObserver(function (entries) { if (entries[0].isIntersecting) start(); else stop(); }, { threshold: 0.02 }).observe(cv); } function boot() { resize(); if (!motionQuery.matches) start(); } if (document.fonts && document.fonts.ready) document.fonts.ready.then(boot).catch(boot); else boot(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initAll); } else { initAll(); } })();

What is a Mini Automated Guided Vehicle

Somewhere between a conveyor and a forklift. Two things define the category, and both follow from the size.

Small enough to work in numbers

A mini AGV is built to be one of many rather than one of a few. Because each unit is light and inexpensive relative to fixed handling equipment, capacity is added by adding vehicles — not by rebuilding the system.

Coordinated by a fleet controller

Individual vehicles are simple. The intelligence sits in software above them, which assigns jobs, keeps traffic moving and decides when a vehicle should go and charge.

Guided, but not constrained

The vehicle follows a route rather than a rail. Change the destinations and the traffic pattern changes with them, usually without anyone picking up a tool.

Light loads, many destinations

The economics work best where items are small and the number of possible destinations is high. Heavier or bulkier work stays with larger AGVs, tow tractors and lift trucks.

HOW

Four moves, repeated all shift.

Whatever the application, every vehicle on the floor is doing one of these at any given moment. There is no line to stop — only traffic to route around.

Take the load

A load is placed on the vehicle, or the vehicle collects it, and a destination is assigned at the same moment.

01

Find a path

It plans a route to the destination while the fleet controller keeps it clear of other vehicles and of people on foot.

02

Hand it over

It confirms it is at the right place, then releases the load — tipping, rolling, lowering or simply waiting to be unloaded.

03

Rejoin the flow

It heads back for the next job, or to a charging point if it needs one. The cycle is a loop, not a line — which is the whole idea.

04
WHERE

Anywhere small things go to many places.

The pattern repeats across industries: high destination count, modest item weight, and demand that will not sit still long enough to justify fixed equipment.

Warehousing & fulfilment

Moving totes, cartons and picked items between storage, pack and despatch without a person walking the distance.

Returns processing

Work that arrives unsorted and unpredictable, where destinations need remapping by the shift rather than by the installation.

Hospitals & labs

Carrying samples, supplies and records between departments — short, frequent trips that otherwise fall to trained staff.

Parcel & post

Sorting items to route, round or driver at depots where the destination map changes far more often than the building does.

Manufacturing supply

Feeding parts and kits to assembly stations, and clearing finished work away, on a rhythm set by the line itself.

Retail back-of-house

Replenishment between stockroom and shop floor in buildings never designed around material handling.

LOAD HANDLING

What sits on top decides what the fleet is for.

The chassis is largely the same across the category. The handling attachment is what makes it a sorter, a shuttle or a shelf mover.

Tilt tray

The top surface tips to one side and the item slides off. Common wherever items are being sorted to a large number of destinations.

Lift deck

The whole body rises to pick up a shelf, rack or trolley, carries it, and sets it down elsewhere.

Roller or belt top

Powered rollers move the load on and off sideways. Suits totes and cartons transferring to and from conveyor.

Flat deck or tow

The load is placed on the vehicle, or trailed behind it. The simplest option, and the one that most often needs a person at each end.

Mini AGV
Mini AGV