Hard to imagine, just a year ago, Oznium.com was in a cramped, dark, messy, and animal infested warehouse. Bats (yes, found multiple bats!), snakes, and black widows. And the metal roof was crazy. Just the tiniest drizzle sounded like a hail storm inside. At least it was cheap (for California standards).
But now, we're in an amazing new Colorado warehouse (for the last 10 months). Radiant heat throughout, 3800 square feet, motorized roll up doors, and three bathrooms. In a beautiful little town, surrounded by mountains.
Been a whirlwind six weeks in Colorado with the latest round of improvements. Here's the details:
Pull up an order on the computer. Memorize the order's items. Walk around, picking the products. Bring them back to the packing table. Pack and ship. Go to next order / repeat process again.
On the iPad, tap a button to "create a new pick list". An algorithm assigns each customer order to a unique barcoded & numbered bin on the pick cart. We use three different bins sizes, and the algorithm uses dimensional product info to assign bins.
Since the computer knows the exact location of each product in our warehouse, it maps a pick route from start to finish. There's no back-tracking.
The iPad now displays the first pick "step". For example, it tells the user to pick a quantity of 3 from inventory location C-5-E and put a quantity of 1 into pick bin 17, and a quantity of 2 into pick bin 22.
The user taps the "next" button, and moves onto the next step.
Whenever the user gets to a product that needs a barcode label (mostly items that are cut from a spool), there's a Zebra GX420d wireless printer on the pick cart that instantly spits out a barcode label. Along with our logo and phone number, the label shows the product name, quantity, and of course a scannable barcode. The label printer has an auto peeler mechanism to save time.
If for some reason the user wants to print a label manually, they can simply tap a button on the iPad.
// set random filename $file_path = "/temp/print/zpl_".rand(1,10000).".zpl"; // save the incoming zpl data to this file $fh = fopen($myFile, "w") or die("can't open file"); fwrite($fh, $_REQUEST["zpl"]); fclose($fh); // set printer name $printer_name = "gx420d_hk1"; exec("/Applications/MAMP/htdocs/print.sh ".$printer_name." ".$file_path); // Then delete the file once its printed. exec("rm ".$file_path);
#!/bin/bash clear lpr -P $1 -r -E -l $2
An Axis 212 network camera with fish-eye lens is mounted to a custom welded aluminum arm which positions the camera directly over the pick cart. It gives us a perfect view of the picker, the pick bins and either side of aisle / inventory bins. We archive video of the pick process. This helps us analyze each motion to figure out ways to optimize even further. It also provides an audit trail if there's a problem with an order.
We obviously need to power the label printer and camera, so we use a $100 battery backup / power supply that gives us about an hour of run-time. We just plug it in whenever we're not picking orders.
Once the pick list is complete, the user brings the cart to the packing and shipping table.
Each bin is labeled on all sides with a barcode, and a number (1 to 30). We simply scan a bin, and the order pops up on the computer screen. We empty the contents of the bin, scan them with our wireless barcode scanner (to verify everything is correct), and then pack them. We place the package on the scale, and scan the barcode on the package which tells the computer exactly what type of package it is (for example, large priority mail box, flat rate priority envelope, padded mailed, etc.)
Some packages don't have barcodes. We automatically snap photos from three angles, and feed these photos to proprietary image recognition software. It figures out exactly which package is on the scale with almost perfect accuracy. This process is still in beta, but I've gotten it down to about six-tenths of second.
We record which package was used to ship the order for a variety of reasons. The main reason is to automate re-ordering of packaging supplies. We keep inventory levels of all packaging supplies.
Our standard shipping labels are 4" x 6". We re-arrange the label layout a bit so we can fit a 3/4" bright green color bleed on the top of the label with our logo and phone number pre-printed. Its an inexpensive and easy way to add a little more branding to our packages. 95% of our labels print with this, while certain international labels print on a larger format laser printer. We've got an auto-peeler, so the label pops out pre-peeled, all ready to be applied.
Small barcode labels automatically come out whenever the picker gets to a product that needs a label. Or they can be generated manually by tapping a button on the iPad.
To optimize print spool speed, and print quality, we send raw ZPL code to the printer. An image file (PDF, PNG, etc) could be 60k, whereas the ZPL text is just 1k. We don't have to worry about resolution.
We used to run a convoluted method to send XML files to Endicia/Dazzle on our shipping PC. And we used to have a messy ODBC link with UPS Worldship. Now we generate shipping labels using Endicia's Label Server API, and UPS Shipping API. This gives us a deeper level of control, and makes things faster / more reliable.
It can take anywhere from one second, to sometimes 15 seconds to get the label back from Endicia's Label Server API. One second isn't too bad, but every bit of time savings helps. We try to pre-generate as many labels as possible (where we are pretty sure of the weight and shipping method). We run this process in the background, and cache the labels. If a cached label exists when the order is shipped, the label is instantly fed to the printer. If we mis-calculated the weight or method, and the cached label isn't used, we'll just automatically refund/cancel it.
We ship worldwide, and most countries already support paperless invoices, so its just one less thing we have to worry about printing and affixing to the package. We can just put a single 4x6 label on the box.
We've got a handful of aisles, each named with a letter (A, B, C…)
Each aisle has columns, named with a number (1, 2, 3…) Just like street numbers, one side is even, and the other side is odd.
Each column has six rows, named again with a letter (A through F)
So location C-3-D would be on aisle C, column 3, and the 4th bin up.
Location C-2-D would be across from C-3-D.
If we have excess inventory that doesn't all fit in a bin, we have back-stock locations too.
Logically, you'd group ducks with ducks, and trees with trees. It works OK to group similar products together, but once you give every part number its own unique inventory location, it doesn't really matter where they are placed. We find that, by having similar products physically separated, and placed in seemingly random places, it greatly improves picking accuracy. The pickers now don't even think about what product they are picking, they just go to a certain location and pick from that bin.
If you have yellow ducks and orange ducks right next to each other, eventually a yellow duck will find its way into the orange duck bin by mistake. Perhaps a friend comes into your warehouse and wants to look at a duck. They grab the yellow duck, look at it, and then carelessly put it back into the orange duck bin.
Now imagine, if you had yellow ducks next to green shrubberies, and on the complete opposite side of the warehouse, had orange ducks next to green trees. Your friend is much less likely to put that yellow duck back into the bin of trees. And if he did, it would be pretty obvious.
We generate a list of all our part numbers, sorted by the most commonly picked items. Using this list, we generate inventory locations for each product. We put the most popular items on the first aisle, and the least popular items furthest away on the last aisle.
We are using 10 x 2MP Vivotek cameras, 2 x 10MP Arecont cameras, 1 Axis 215 PTZ dome camera, and 1 Axis 212 fish-eye camera.
I'm hardly ever in the warehouse (so far only 6 weeks this year), so its really good to have piece of mind while I'm away. I can check in to make sure things are staying clean and organized. We can use the recorded video as an audit trail if there's a problem with an order to pinpoint exactly what went wrong. And hopefully prevent it in the future.
This one's really cool. We've got five cameras pointed at our shipping table from various angles. Our system automatically snaps a handful of photos while the customer's order is being packed. We make these instantly available to the customer, so they can see how their exact order was packed (on the My Account page).
We've made a custom ship table that conveniently holds the most popular packaging supplies. The packing table also houses:
24" LCD Screen
12" x 12" Stainless Steel Doran Scale, with wifi adapter
2 x Direct Thermal Label Printers
2 x Wireless Laser Printers
1 x Symbol Barcode Scanner
1 x Wireless Honeywell 2D Barcode Scanner
Handful of routers and power backups
Really complicated to set up. Their tech support didn't even know what Linux was… So I was pretty much on my own. Using a DIGI Connect RS232 Serial to Wifi adapter, I gave the adapter/scale its own IP address. Played with a bunch of configuration settings, and finally figured out a way to communicate with the scale from a custom perl script.
The scale averages weights 12 times a second, and sends current weight to my perl script 3 times a second. The perl script maintains a constant connection with the scale, and re-connects automatically if needed.
I save the last stable weight reading to a file, which can then be used from my backend systems. I've made some triggers available too, for example, I can have the perl script "ping" a certain URL if the weight changes (and motion stops). This will be useful for further automating the auto image recognition system. The user could place a package on the scale. As soon as the scale has a stable weight, the perl script would ping a URL to begin the image snaps / recognition process. And about 1 second later, an appropriate shipping label would automatically print.
Rather than be overwhelmed with a massive inventory count at year end, we try to count a few items per day, each day of the year. This way we can count each product at least twice a year, and it doesn't take more than 15 or 20 minutes a day. A system keeps track of who counted what, and when. It then queues up the most urgent part numbers that need to be counted next.
Its a brand new implementation, and so far working really well. Before, we didn't have barcodes on anything, and stupid mistakes were common. For example, we'd accidentally send the customer a blue duck instead of a red duck.
I've experimented with a handful of symbologies and originally liked the 2D datamatrix style because it didn't take up too much space on a label. But it seems the linear Code 128 is overall the best choice. Readability with our scanners is quick. We aren't encoding much data, and can fit the barcode into about 1/4" x 1 1/2"
We have full control over the design process, manufacture 95-something percent of our products, and are only one selling them. We don't have to worry about UPC codes. We've made up our own simple data format:
x = quantity in pack
y = unique part number (auto increment database field, so far from 1 to 1500 or so)
z = unique purchase order id
An example could be: 10-556-345
Which would mean there's 10 red duck beaks in the package and they are from purchase order 345.
We use the purchase order to track supplier defects. For example, we can see if defects are coming from a certain purchase order / manufacturing batch.
I've thought about using a unique serial number for each label, where no two barcode labels would be the same. This would allow us to track a few more things, but I don't think its really necessary now. From what I understand, Zappos does this, and they call it a "license plate".
The entire backend system runs through a browser (rather than a desktop app). Of course, a barcode scanner just plugs in to a USB port and simulates a keyboard. If you scan a barcode, the data is "typed" into the computer.
To handle barcode scanning in our web interface, I programmed our scanners to send an open bracket "[" + the actual barcode data + a closing bracket "]".
The system automatically displays a confirmation indicator next to each line item on the order as it is scanned. If partial quantity is scanned, it shows up orange. If the exact amount is scanned, it shows up green, and if too much is scanned, it shows up red.
It obviously won't let the user proceed if there's an error. If a completely wrong product is scanned, we do an AJAX call with the part number scanned, and get the name back.
I whipped up a super beta version of this just to test the concept. It works fine.
Basically, the picker can wear a headset, and receive voice directed commands.
Something like: "Go to location E-40-D and pick 2 quantity. Put them in bin 12."
Then with voice recognition, they can say, "OK", and it'd move on to the next step. Or they could say "repeat"…
Each inventory bin will have an LED light that will be individually controllable from our web server. When the picker is on a certain step, the bin will light up.
And the bin on the pick cart that they are supposed to put the product into will light up.
And when they are back at the shipping table, we will use a dimensioning algorithm to suggest a box/envelope size to be used for the order. A light next to those boxes/envelopes will turn on, thus visually directing them to the appropriate package.
With some training and optimization, the algorithm will be able to make better decisions than the shippers. So we won't waste oversized boxes.
Think of the sensors on the bottom of your garage door. One is an infrared emitter, the other is an infrared receiver. Same thing your TV remote uses.
In the same device that holds the LED to light up the bin, there will be infrared emitters/receivers.
As soon as someone sticks their hand into a bin, the infrared beam will be broken. We'll be able to tie this in with the pick system. If for example, they accidentally grabbed something from the wrong bin, or put something into the wrong bin, a buzzer would go off, and the bin could flash. When they grab from the correct bin, and place into the correct bin, the lights would turn off, and the next step would appear.
Off to hang out in Europe for a bit. Literally threw a few t-shirts in a bag and hopped on the next flight to Frankfurt :) Not sure where I'll end up, but thinking somewhere warm, like driving the coast of France / Spain / Italy.
Update: So far, spent a night in Frankfurt, then drove down to Zürich for a few nights to enjoy my all-time favourite spa, at the Dolder Grand (one of my top five hotels).
Then hit the tiny car-free town of Zermatt, high in the Swiss alps, right below the Matterhorn. Went paragliding off the mountain and caught some sweet thermal updrafts. Spent like 25 minutes flying around, and got cool video from the iPhone (a bit worried I'd drop it though!).
Note, best place to stay in Zermatt: Hotel Omnia, perched on a giant rock. Feeling like James Bond, you enter through a narrow cave down below, through a set of air-lock doors, and up a 5-story elevator in the granite.
Then drove through France, straight to Barcelona, for jamón ibérico de bellota. Its the best ham in the world. From Spanish pigs who are fed a strict acorn diet, and then cured for two years. Lots of awesome architecture and fun culture. Picasso & Gaudi.
Spent a night in Andorra. Its a tiny country in the Pyrenee mountain range, wedged between France and Spain. Quite a strange place. Infamous as an inexpensive shopping mecca. The main town, Andorra la Vella, reminds me a bit more of Singapore than a European town (because of the shopping, modern buildings, and lights). Yet tons of natural beauty. Takes 30 minutes to drive across the country. Enjoyed some peaceful hiking around high-mountain lakes.
St Tropez is really fun. Yachts, celebrities, and fashionable people. Yet despite the glitz, it feels quite relaxed, almost stuck in a place in time. Awesome clubs (26 euro drinks are not uncommon), and awesome dining.
Alain Ducasse's, "Spoon", serves a unique bubble gum flavoured ice cream for desert.
An hour's drive into the countryside, through curvy vineyard backroads, is Chez Bruno, famous for its truffle creations. Foie gras stuffed pidgeon with truffles. Mmmm.
A trendy Morrocan restaurant tucked down a narrow cobblestone alley, serves heaping portions of couscous and lamb. Washing it down with Morrocan mint tea, I felt as if I were actually in Marrakesh.
Gotta love a place that serves pool-side breakfast until noon. And restaurants that don't even start dinner until 9:30.
I could definitely see renting a villa and hanging in St Tropez for a few months.
But… au revoir France! Decided to hit Bali. Saw a picture of a beach there and almost died, it was so beautiful. Booked a flight at 10 am, and was on the plane less than six hours later. Gotta love last-minute travel decisions. And its always good to leave a place before you're tired of it - so you still have good memories. 20 hours of flights: Nice - Dubai - Kuala Lumpur - Denpassar.
Iranian caviar, and '00 Dom on Emirates - Not a bad way to travel :)
Will probably stay in Bali for at least a month, maybe two or three. Not sure where to after Bali. Cape Town and Hawaii are on my radar though.