Engineering portfolio
Austin Zhai
Computer Engineering · University of British Columbia
Experience
Advanced UAV Tech
I worked on a 5-person team building a drone that inspects warehouse pallets for damage at DHL sites. A pilot flew it down the aisles filming the racking, and that footage ran through a computer vision pipeline the team trained, which flagged which pallets were damaged and how they were stacked. My focus was the electronics: I sized and built the power system, assembled and wired the full stack onto a 20 inch carbon fiber airframe, and tuned the flight controller and optical flow sensor so it holds position indoors with no GPS. I also designed and printed the landing legs in Onshape when nothing off the shelf fit our motors and frame. Alongside the build I ran the project day to day and was the point of contact with DHL's staff and project managers.
The problem
Warehouses check pallets for damage by eye, one at a time, and a large DHL site holds thousands of them. We wanted to make that faster: fly an aisle once, and let software do the looking. I ran the meetings with DHL's staff and project managers, so what they needed came back to the team through me, and I kept the five of us pointed at the same milestones from there.
- Damage gets spotted by whoever happens to walk past it
- Pallets are stacked well above head height, so the top rows are the hardest to check
- A drone can film a whole aisle, top shelf included, in a single pass

Prototype
We started with a drone the company already owned, one of its light-show quads built on a DJI F450 frame. It was never going to be the final build. It was there to answer a single question.
- The question: how does a drone hold still indoors, where there is no GPS to lock onto?
- The answer: an MTF-01 optical flow module, a downward camera and laser rangefinder that tracks movement against the floor
- Once it was fitted and tuned the drifting stopped, and everything after this was designed around it


Power system
The battery decides how long a flight lasts, and how long a flight lasts decides how much of a warehouse you can cover in one go. So I sized the power system from measured numbers instead of guessing at a battery.
- One 4S LiPo fed all four motor controllers, the flight controller and the video transmitter
- I worked the flight time out from measured current draw before we bought anything
- Roughly 40 soldered connections across the power and signal wiring, every cable cut to length and heat-shrunk
- We trialled a 4-in-1 ESC stack part way through and ended up back on four separate ESCs
A projection, not a measurement. The airframe was never put on a scale and it has never been flown down to empty. The 0.8 is there because a LiPo below roughly 20% charge is off limits.


Final airframe
The prototype could not carry everything we needed, so we moved onto a bigger 20 inch carbon fiber frame. I assembled and wired this one up myself, and designed the parts that did not exist off the shelf.
- Pixhawk V6X flight controller, with the MTF-01 optical flow module mounted underneath
- No commercial landing leg fitted our combination of motors and frame, so I modelled our own in Onshape around the real parts and printed them
- They were hollowed out in a hex pattern to save weight without losing stiffness, and they are the legs holding the drone up in the photos below
- Then tuned until it held position steadily indoors



Flying it
A pilot flew the drone down the aisle and filmed the racking. That sounds like the easy part, but it was the part everything downstream depended on.
- If the drone drifts or wobbles, the video blurs
- A blurred frame is a pallet the software cannot read

Getting the footage off the drone
The footage came off the drone live. It carried a tiny FPV camera and video transmitter, and the picture arrived on a laptop while the drone was still in the air.
- An all-in-one camera and video transmitter weighing 4.7 g, about a thumbnail at 18.7 × 13 mm
- It transmitted to a receiver plugged into a laptop, which captured the feed in OBS
- That recording is what got fed through the software afterwards

Teaching it to recognise a damaged pallet
Before software can spot damage, someone has to show it thousands of examples. We labelled every training photo by hand in Roboflow, drawing a shape around each pallet and tagging it twice.
- Tag one: is this pallet damaged, or undamaged?
- Tag two: is it stacked flat, or in a pyramid?
- The two shapes sit on top of each other, which would make the model draw two boxes around every pallet
- So we built its training set from the damage shapes only, and saved the stacking tags for a later step

Training the models
Three models, trained in a notebook and chained together. The first one finds the pallets; the other two look at each pallet it found.
- Model 1 draws a box around every pallet in the frame
- Model 2 decides whether that pallet is damaged
- Model 3 decides whether it is stacked flat or in a pyramid
- Damaged pallets are rare in the training data, so we tuned model 2 to catch as many as it could and tolerate a few false alarms. Missing a damaged pallet costs more than double-checking a good one.


DroneScan, the app clients actually use
None of it is worth much if a warehouse manager cannot run it, so the team wrapped the whole pipeline in an app called DroneScan. You hand it the footage from a flight and it hands back a report.
- Drop in photos or a whole video, and it pulls the frames out for you
- Every pallet comes back outlined, marked damaged or clear, with a confidence score
- Filter a long list down to just the damaged ones

Galaxy Instrumentation and Controls Inc
This was my first real exposure to industrial automation: the PLCs, SCADA systems, and operator terminals that keep a factory floor running. I worked on CHEP's global rollout of upgraded Factory Management Systems, doing pre-migration verification across roughly 25 plants a week. At each site I connected to the plant server and baselined every layer beneath it, validating communication paths to about 7 Allen-Bradley PLCs, capturing HMI terminal configs in ThinManager, cross-checking device diagnostics in AVEVA SCADA, and confirming the automated pallet inspection systems were still hitting their timing spec. I wrote the verification runbook myself and automated the repetitive parts in Python. Learning to read a controls stack top to bottom, and to tell a network fault apart from a protocol one, is what I took away from it.
TELUS Digital
I evaluated AI-generated geolocation data across English and French, helping train the models behind mapping products used by millions. That meant assessing 80+ search queries weekly across POI accuracy, search relevance, routing quality, and autocomplete, applying structured rubrics while maintaining a ~90% accuracy rate. The bilingual angle was the differentiator: I caught multilingual discrepancies that monolingual reviewers missed, and those signals fed back into model retraining.
Hydroficient
I secured an IoT sensor pipeline for a simulated 500-room hotel water management system. I designed and stress-tested a 5-layer defense stack (TLS encryption, mutual TLS device authentication, HMAC message signing, timestamp validation, and sequence counters) against attack classes I built myself: eavesdropping, sensor spoofing, and replay attacks. Per-device certificates brought unauthorized broker access to zero, and I trained an Isolation Forest anomaly detection model wired into a real-time Streamlit dashboard so non-technical staff could monitor security without touching a terminal.
UBC Sailbot
UBC Sailbot builds a fully autonomous sailing robot: no remote control, no crew. I worked on the operations side, connecting electrical, mechanical, and software subteams to keep the project moving. I managed timelines, organized technical documentation for cross-team integration, and owned the outward-facing work: sponsor packages, website content, and video production. A social campaign I planned end-to-end grew average engagement by ~80%, supporting a roster of 18 active sponsors.
Independent Haircutting Business
This started as a hobby, but once I noticed how many friends were walking around with bad haircuts, the demand clicked and I turned it into a real business. I handled everything: client acquisition, pricing, scheduling, payments, and follow-up. An Excel-based booking and client system pushed retention to ~85% and let referrals run on autopilot. By the time I wrapped up to focus on school, I had served 50+ clients across 350+ appointments and generated over $7,000 in revenue. The biggest lesson: the scariest part of starting something is just starting.
Projects · Hardware · 2026
Smart Alarm
An ESP32 bedside alarm clock that tracks your sleep with an onboard accelerometer and wakes you during light sleep instead of at a fixed time. I designed the whole thing: firmware, sleep-staging model, schematic, and PCB.
Everything runs on the ESP32 itself. No phone, no cloud, no app.
Part 1 · Firmware and interface
A ten-screen menu system covering alarm, smart alarm, sleep data, weather, and settings, all driven by one rotary encoder and one button. The home face pulls NTP time and a 5-day OpenWeatherMap forecast, and repaints only the digits that actually changed, so the display never flickers. Alarm, smart-wake, and snooze settings live in EEPROM and reload on boot.


Part 2 · Sleep tracking and on-device ML
An MPU-6050 clipped to the mattress logs motion to an SD card at 2 Hz. Every 30 seconds the device pulls 21 statistical features off a ring buffer, scores that window as light or deep sleep, and writes it back out, so the morning hypnogram survives a reboot. I trained a Random Forest on eight hand-labelled nights, roughly 420,000 raw samples, and exported it to C to run entirely on-chip.

Part 3 · Schematic and PCB
The first version lived on two breadboards with the display taped to the front. Once the pinout stopped changing I redrew it in Altium as a two-layer board, with every peripheral broken out to a labelled header so modules stay swappable, and had it fabricated. Same firmware, considerably less wire.




Highlights
- Rings early at the first stretch of light sleep inside a window you choose, at one of three sensitivity levels, and falls back to the exact alarm time if that moment never comes
- The Random Forest hit 65% accuracy but only 0.19 recall on deep sleep, so I did not ship it as the displayed stage. Mattress motion alone barely separates deep from light: median peak deviation was 140 versus 142
- Shipped an actigraphy-style sleep-cycle model instead, using ~90 minute cycles where any movement forces light sleep. It produces 20 to 27% deep sleep, in line with published norms, and the Random Forest vote is still logged every window so the comparison stays honest
- Roughly 1,100 lines of firmware plus a 1.4 MB generated classifier, which is why the board needs the 3 MB huge_app partition instead of the default 1.3 MB one
- A 150 ms debounce plus a 400 ms hard lockout on the back button, and an external pull-up on the input-only encoder pin. Both problems surfaced on the breadboard and went straight into the schematic
- SD files open once on entering sleep mode and close once on exit. An earlier version reopened them on every write and corrupted the card, so that rule is now enforced by the architecture
Projects · Software · 2026
Portfolio Vision
A full-stack ETF portfolio decomposer that breaks any portfolio down into its true underlying holdings, with live prices, sector and geographic exposure, and saved user accounts.
Built first as a Python CLI, then rebuilt entirely as a production web application deployed at portfoliovision.online.
Part 1 · Python CLI
Full-featured terminal decomposer with recursive ETF unwrapping, mixed USD/CAD currency detection, tabular holdings output with sector and geographic breakdowns, and a captured/untracked coverage summary. Handles circular references and depth capping.

Part 2 · Web App
Production web application with live stock prices via a Yahoo Finance proxy, Google OAuth and OTP email authentication via Supabase, saved portfolios with per-user row-level security, a USD/CAD display toggle with real-time Frankfurter API conversion, and Logo.dev company logo resolution. Deployed on Vercel with analytics.

Highlights
- Recursive ETF decomposition across 500+ tickers with configurable depth cap and circular reference protection
- Live price fetching across US and Canadian exchanges (TSX, NEO, CSE) through a Vercel serverless proxy to Yahoo Finance, removing API key requirements and exchange restrictions
- Google OAuth and OTP email auth via Supabase with row-level security policies per user
- Real-time USD/CAD conversion via Frankfurter API with per-position and display-level currency toggling
- 400+ page views in the first week of launch, tracked via Vercel Analytics
- Deployed end-to-end on Vercel with a custom domain and environment-based secrets management
Projects · Hardware · 2026
PocketSense
A pocket-sized room monitor on a custom PCB I designed from scratch. Temperature, humidity, pressure, UV, and WiFi signal on a small OLED, navigated with three buttons and running off AA batteries.
Breadboard, then schematic, then board, then bring-up. I wrote the firmware, drew the schematic, laid out the PCB, and soldered it by hand.
Part 1 · Breadboard and firmware
Nothing got designed into copper until it worked on a breadboard first. The whole firmware was written and proven on a dev board before a single schematic symbol was placed. The sensor and the display share one I2C bus, which is exactly the kind of conflict you want to rule out while it is still a jumper wire away from fixable, and the UV sensor sits on a pin that stays readable while the WiFi radio is running. The interface itself is deliberately small: five screens, three buttons, and a state machine that fits in a couple of variables.

Part 2 · Schematic and PCB
Drawn in Altium as four blocks: power, reset, boot, and the programming header. Three AA cells feed a linear regulator through a slide switch that breaks the positive rail, so the board is genuinely dead when it is off. The layout is four layers, with solid ground and power planes inside and a clear keepout around the module antenna. DRC came back clean, and I checked the Gerbers in the fab viewer before ordering.



Part 3 · Assembly
Boards came back from JLCPCB and I soldered every part by hand, surface mount first while the board was still flat and nothing tall was in the way. The switch that shipped had the wrong pin count for the footprint, so instead of reordering I probed it with a meter and found the three pins that line up. Before any voltage went near it I checked for a short between power and ground. No beep, safe to power.



Part 4 · UART bring-up
There is no auto-reset circuit on this board, so getting into the bootloader is manual: hold boot, tap reset, release, all inside the second or so while the uploader is trying to connect. It works. The chip answers, reports its ID, and the flasher starts, which means the reset circuit, the boot circuit, the crossed serial lines, and the regulator are all doing their jobs. Then the transfer dies, and the meter says why. The regulator is putting out about 3.0V instead of 3.3V, so the moment the ESP32 pulls real current the cells sag and the board browns out mid-upload. That is a supply problem, not a layout one.

Highlights
- Five sensor screens on one small OLED, all driven by three buttons. No phone, no app, no cloud
- The bug that almost shipped: two of the buttons were wired to physical pins 17 and 18 on the module symbol, which are internal flash pins, not the GPIOs of the same number. Caught it against the datasheet pin table before the board went out
- The antenna keepout took a second pass. Altium keepouts only block routing on the outer layers, so clearing the internal planes underneath meant placing fills on them directly
- The firmware compiles to about 927 KB, 70% of the default partition, which is a lot of program space for something that draws five screens
- Every stage was mine end to end: firmware on a breadboard, schematic and four-layer layout in Altium, Gerbers out to the fab, and every part on the board soldered by hand
- One step from done. The upload browns out on battery sag, and the fix is fresh cells or a bench supply rather than a respin. The real lesson: a regulator with a 1.3V dropout on a 4.5V pack has almost no headroom, and under a WiFi current burst almost none becomes none
Projects · Software · 2026
Personal Website
Designed, built, and deployed austinzhai.com end to end. A React single-page site with a dark theme, scroll-triggered reveals, and a projects page that prints as a clean resume.
Full CI/CD through GitHub and Vercel, with the domain and DNS set up from scratch.
Languages: JavaScript, HTML, CSS
Frameworks: React, Tailwind CSS, Vite
Tools: Git, GitHub, Vercel, Domain Setup
Highlights
- End-to-end ownership: design, build, deploy, domain setup
- Sub-100KB JS bundle with under 1s LCP on cold load
- Projects page prints as a clean, resume-style document via dedicated print CSS
- Continuous deployment through a GitHub to Vercel pipeline

Additional projects
Servo Sonar Radar · 2026
A servo-mounted ultrasonic sensor sweeps 180° to detect objects across three modes: continuous sweep, detection-triggered pause, and manual joystick control. Detected objects are visualized on a real-time radar display rendered in Processing.


Smart Gate Access System · 2026
An ESP32-based access control system combining RFID card scanning and keypad password entry to control a servo-actuated gate, with an I2C LCD display for real-time status feedback and LED/buzzer alerts for granted and denied access events.

Stock Predictor · 2026
A Random Forest classifier that predicts next-day S&P 500 direction (up or down) from historical price and volume data pulled via yfinance. Trained and backtested in a Jupyter notebook, reaching a precision score of about 0.58 on held-out data.
Hangman · 2026
A command-line implementation of the classic word-guessing game. Players choose a difficulty, then guess letters or the full word with 6 lives. Draws from a curated word list per difficulty and tracks guessed letters to prevent duplicates.
Random Password Manager · 2026
A command-line Python password manager that stores, retrieves, and deletes credentials locally. A built-in generator guarantees variety across uppercase, lowercase, digits, and symbols on every run. Data persists via a local JSON file.