UBC 2030: Sci & Eng

Reuben Lavin

Currently building a navigation helmet for blind and low-vision users.

3D model of my assistive navigation helmet.

About

I graduated from West Point Grey Academy in Vancouver and study Science at the University of British Columbia, with a planned transfer into Engineering after first year. I'm drawn to robotics: the place where physics, mathematics, and software meet a moving piece of hardware.

Outside of academics I'm an MMA black belt and assistant instructor, a varsity tennis player, and a jazz band leader.

Next up: the field walk test of the assistive helmet, and the 360° dewarp’s first accuracy measurement on a real site.

Education

  1. Sept 2026–present

    University of British Columbia

    Bachelor of Science · Engineering transfer planned after first year

    • Foundational coursework in calculus, physics, chemistry, and computer science
    • Continuing self-directed work in embedded systems and robotics
  2. 2020–2026

    West Point Grey Academy

    Graduated June 2026 · Honor Roll

    • AP Computer Science · AP Statistics · AP Calculus BC · AP English Language · AP Human Geography
    • AP Scholars Award · Merit Award · Alfred Lu Spirit Award · Citizenship Award
    • Music Program Hall of Fame · Varsity Tennis
    • Core Connectors Mental Health Literacy Certificate · Adam's Apples Foundation
  3. 2018–2023

    Kumon Math

    Graduated full program · Outstanding Academics recognition

Certifications

  • Google AI Professional certificate badge

    Google AI Professional Certificate

    Google Career Certificates · March 2026 · 7 courses + umbrella credential

Projects

Field testing
Live depth streamed from the helmet over WiFi
Exploded view animation of the 3D-printed sensor pod, showing the two time-of-flight boards and the camera in their mounts.
The sensor pod, exploded. Both depth boards sit at a designed ±22.5°
BNO085 IMU wired on a breadboard during bring-up
BNO085 bring-up
First helmet rig: breadboard and sensors mounted on a batting helmet
First wearable rig
Assistive Tech · Sensor Fusion · Case Study

Assistive Navigation Helmet

A helmet that answers what the white cane cannot: what is at head height, what is approaching, and where exactly the thing you want is. It is not a mobility aid and not a substitute for a cane, a guide dog, or O&M training.

Two $6 ST VL53L8CX time-of-flight sensors sit on a bike helmet, yawed ±22.5° and seam-abutted into a ~90° × 45° depth field at 15 Hz, next to a fisheye camera and a BNO085 IMU. An ESP32-S3 streams depth and attitude over WiFi to a laptop in a backpack, which fuses them: segmentation and tracking name what is in frame, and the depth grid ranges it. Output is speech, spatialised earcons, and three coin motors at the temples and forehead. An assistant called Iris answers to its name, and a phone app shows the annotated feed.

The design rule the whole system rests on: CV names things; the depth grid never misses things. A detection’s range is the minimum over the depth zones it claims, never the mean, and any near zone that no detection claims is still announced as an obstacle. The classifier is allowed to be ignorant; it is not allowed to be the reason something goes unreported.

An 8 mm error that no rigid transform could remove

A residual that survives rotation, translation and scale is telling you the forward model is wrong, not the parameters.

Solving the depth-to-camera transform left a systematic ~8 mm residual against a sensor noise floor of 3.1 mm. Two signatures identified it: the error was per-pose rather than per-zone, and its size tracked how far the calibration board was tilted. A lateral ray error is invisible on a face-on board and leaks in proportionally as you tilt it. Root cause: a zone’s reported distance is a signal-weighted average over its cone, and the illuminator is brighter on the inner side of the outer zones, so their effective centre ray is pulled inward — fitted at ±13.4° where the geometry says ±16.9°. Two earlier measurements had been fighting over 34° versus 45° for a week; both were right, because one measured the zone’s bounds and the other its signal-weighted centroid.

Distance is perpendicular, not slant

A radially symmetric residual is a geometry-model signature, not a hardware defect.

Residuals showed corners −14 mm and centre +14 mm across poses. I chased a warped board, a radial sensor bias and crosstalk before testing the obvious: the sensor reports perpendicular distance, and I was treating it as slant range along the zone ray. On the same 23 poses, the slant reading gives 12.02 mm plane rms and a false 36 mm dome; the perpendicular reading gives 3.83 mm, which is noise. The same mistake was live in the firmware’s row-cosine table, biasing the outer rows about 8% while the haptics were running off it.

The research said the product was 30× too chatty

Chatter is the most cited reason people abandon devices like this, so silence is the default state.

The first callout engine narrated every 2 seconds. Reading the primary sources, including the actual constants in Microsoft Soundscape’s open-source callout generator, said that was badly wrong: never repeat an object inside 60 s, and treat silence as the resting state. No shipped product speaks distances while you walk, because proximity belongs in a repetition rate, and there is a two-item recall ceiling while walking. Person detection, which I had assumed was the point, is the least-wanted feature in user studies. So routine narration was deleted, distance moved into the tempo of a tick that speeds up as time-to-contact falls, and head-height obstacles became the product.

  • Joint calibration ships at 5.5 mm rms across 39 poses and 1.15° from CAD, against a sensor noise floor of 3.1 mm
  • Camera intrinsics at 0.30 px rms over 19 views; measured field of view 119.58° × 63.12°, which replaced an estimate that was 10° out
  • A frame-convention error put the solve 179° from the CAD prior. Image-right is the wearer’s right, which is −X. Naming the observer rather than the side removed a whole class of sign errors
  • Black foam at 756 mm read all 16 zones at 9.4% reflectance; a white wall at 958 mm lost 3 zones to specular glare. Visible colour does not predict near-infrared behaviour
  • Every alert is logged, and hushing the audio within ten seconds of one counts as a vote that it was a false positive — directives excluded, since hushing during “stop” is panic, not a judgment

Where it stands: the full stack is built and runs end to end — dual depth, IMU, camera, fusion, speech, haptics, voice control and the phone app. Measured on the field laptop: ~10 fps segmentation with tracking, and a 104 ms median open-vocabulary door scan over 20 runs. The safety loop runs no neural inference at all; it is geometry at sensor rate. The false-positive instrumentation is built and verified synthetically, but no number from a real walk exists yet — the field walk test and the demo video are the next step.

C · ESP-IDFESP32-S3VL53L8CX ToF ×2BNO085 IMUFisheye cameraPython · OpenCVYOLO26n-seg · ByteTrackYOLO-WorldVosk voiceHapticsPWA
Top: a synthetic equirectangular frame with four coloured great circles, visibly bent. Bottom: the same circles after dewarping, dead straight.
The test: great circles go in bent, come out straight to 0.82 px RMS
The same scene rendered at several fields of view and scaled to equal size, showing where narrowing stops recovering real detail.
Narrowing the view only helps until the camera runs out of detail
Computer Vision · UBC Research · Case Study

360° Camera Dewarping

A 360° camera flattens the world the way a map flattens the globe, and object detectors trained on ordinary photographs fall apart on the result. This re-projects the panorama into virtual pinhole cameras on the GPU, so any off-the-shelf detector works on it unmodified.

An equirectangular frame stretches horizontally by 1/cos(latitude): 1.0× at the horizon, 2.0× at 60°, 3.9× at 75°, and at the pole a single point smears across an entire row. It also has a seam, where the left and right edges are the same physical place, so a person walking past is cut in half and rendered at opposite ends of the image. Published trackers show the cost: one recent method scores 73.4 HOTA on ordinary video and 18.2 on panoramic footage. Built for a construction-site safety project in UBC Civil Engineering's Smart Structures group under the guidance of Prof. Tony Yang and PhD researcher Fan Xie, where the camera has to watch a whole site at once but the detector still needs to see normal pictures.

The map is computed once, not every frame

The virtual cameras never move, so dewarping is a gather, not a computation.

Each virtual view uses a gnomonic tangent-plane projection: build a ray for every output pixel, rotate it into world space, convert to longitude and latitude, and read the source pixel there. Because the views are fixed, that mapping is identical on every frame, so it is computed once at startup into a lookup table and each frame becomes a single batched grid_sample. Four 640 px views take 10.6 ms on a laptop GTX 1650 and 37 MB of VRAM, and the output stays on the GPU so the detector never waits for a copy back.

Checking it geometrically instead of by eye

“It looks right” is not evidence. A gnomonic projection maps every great circle to a straight line, so that can be tested.

Synthetic frames containing known great circles are pushed through the real pipeline, and the output lines are fitted by total least squares. Nothing is compared against the library's own forward maths, so an error in that maths cannot hide inside the test. Great circles come out straight to 0.82 px RMS, angular scale is isotropic to 1.0000, and known angles land within 0.65 px of prediction, all on a 640 px view.

Three things that were wrong

Hardware decode was slower than software, and the first “optimal” field of view was measuring the wrong thing.

Turning on hardware decode took a frame from 21 ms to 54 ms, because the capture path forces a GPU-to-CPU copy back. Anti-aliasing was destroying real detail to suppress aliasing that measured 37 dB down, so it now defaults off. And the first field-of-view answer, 90°, came from Laplacian variance, which rewards aliasing rather than detail; measuring the frequency content instead put the answer at 70–75°, and a second independent method agreed.

  • The sampling grid stays in fp32: at fp16 the error reaches about 2 px, worst exactly at the seam
  • The viewer reports two resolution numbers side by side, where the real detail ends and where the pixel grid ends, because the second one flatters the camera

Where it stands: the dewarp is built and verified geometrically, and runs live on the camera at 32.7 fps with no dropped frames. Detection accuracy on site has not been measured yet; the improvement is expected from the geometry and from the literature, not demonstrated on this camera. That experiment is the next step.

Code: the repository is private for now. This is work for UBC Civil Engineering, and it opens up once cleared.

PythonPyTorch · CUDAOpenCVPyAV · RTSPYOLO11Projective geometry
A walkthrough of the build, hardware, and app
Embedded · Wearable · Case Study

Sunrise Wake-Up Mask

Loud alarms wake you with a jolt. This is a sleep mask that wakes you the way morning light does: a strip of LEDs behind the fabric fades up like a real sunrise over your chosen window, with haptic motors easing in at the end. You set the wake time, color, and length from a small desktop app over USB.

An ESP32-C6 drives a cut strip of WS2812 addressable LEDs behind a foam diffuser, with coin haptic motors switched by a 2N3904 transistor. A 1000 mAh LiPo powers it and charges over USB. The firmware takes its settings over USB serial — no reflashing to change an alarm — and a companion desktop app (a Web Serial page wrapped in Electron) sends the wake time, color, and sunrise curve. The firmware and app were written with Claude Code; the hardware, wiring, and the debugging below are mine.

The bug that reset the whole board

Motors on the logic rail browned out the MCU mid-buzz.

The haptic motors ran off the same 3.3 V rail as the ESP32-C6. At full power they pulled enough current to sag that rail past the brown-out threshold, and the chip reset mid-sequence — which froze the LED strip on its last frame, because WS2812s hold their last color the instant the data signal stops. The tell was a single serial line: Brownout detector was triggered. At low duty the motors didn't draw enough to spin at all; at high duty they crashed the board. With the case already sealed, the fix was firmware: clamp the motor duty to a ceiling that stays under the brown-out point, so the lights and motors run together without taking the brain down.

Coin motors that won't start, but will keep going

A full-power kick breaks stiction, then it settles to a low hum.

Ramping the motor's PWM up from zero never worked: a coin motor needs a sharp push to overcome static friction, and a slow ramp just creeps through values too weak to ever start it, so the motor stayed silent through the whole sunrise. The fix is a kickstart — a brief full-power pulse on the rising edge that breaks the motor free, then drops to the low level it can sustain once it's already spinning. That one trick is what lets the buzz arrive gently instead of snapping from silence to full.

A clock it can't forget

It re-learns the time over WiFi, so the alarm survives a power cut.

An ESP32 loses track of time the moment it loses power, which is fatal for an alarm. The mask connects to WiFi and syncs the real time over SNTP, then stores the alarm in non-volatile memory. The app sends the wake time as an absolute timestamp, so the device itself needs no notion of time zones — the laptop already knows your local time and just hands over the exact moment to fire. Unplug it, plug it back in, and within seconds it has resynced and the alarm still goes off when it should.

  • Addressable LEDs run from the battery rail, not the regulated 3.3 V: at low voltage the blue and green channels starve and the color shifts
  • Settings are sent, not flashed: the firmware is a small state machine that takes JSON over USB, so a new alarm is one serial message, not a reflash
  • Every dead end and fix, including a 10-bit HDR export that wouldn't play and a swapped data/ground line that killed the strip, is written up in the repo's devlog

Where it stands: finished. The full sunrise-and-haptic wake sequence runs on hardware, configured live from the desktop app, with WiFi time sync working.

C · ESP-IDF 5.4ESP32-C6WS2812B LEDsHapticsFreeRTOSWiFi · SNTPWeb SerialElectron
Bare-hand pinch and turn, captured in-headset on a Quest 3
AR / VR · Cultural Heritage · Case Study

Spindle Whorl AR

Coast Salish spindle whorls are carved wooden discs, but museum archives mostly hold them as flat photographs. This Meta Quest 3 mixed-reality app rebuilds them as 3-D objects floating in your real room: reach out, pinch, pick one up, and turn the carving in your hands.

Six whorls hang in a 150° arc at reach height through passthrough. Pinch thumb and index to grab the nearest disc, move and turn it freely, open your hand to release; ungrabbed discs spin slowly so the art stays visible. Each disc is gently domed with a real centre bore, so you can look through the hole at your own room.

The hand-tracking dead end

Meta's legacy hand API delivers nothing on OpenXR. Unity's XR Hands does, with 25/40 mm pinch hysteresis.

On the OpenXR backend, Meta's legacy OVRHand API silently delivers nothing: IsTracked stays false while the headset is plainly tracking hands. The working path is Unity's XRHandSubsystem, reading thumb-tip and index-tip poses directly. Pinch detection uses hysteresis (grab below 25 mm, release above 40 mm) so a disc doesn't stutter in and out of your grip at the threshold.

Geometry from code, not the modeller

A domed annulus with a real bore, generated in C#, photos aligned to it per disc.

The original Blender export had UVs on its top face only, so photos didn't wrap and the disc backs were blank. The disc is now generated procedurally in C#: a biconvex annulus with a real bore and inscribed UVs on both faces. A Python + OpenCV pipeline strips each source photo's painted-checkerboard background (keeping only background connected to the image border, so the carved interior survives), and a measured per-disc UV offset lands each artifact's painted hole exactly on the geometric bore.

Making it run on real hardware

Headless builds, and an adb launch sequence that beats the Quest's focus race.

On-device, the floor-origin setting was silently ignored, leaving discs a metre overhead; fixed at runtime through XRInputSubsystem. Builds run headless from the command line, and a scripted adb launch sequence beats the Quest's home-panel focus race that leaves sideloaded apps visible but never focused. Written with Claude Code; tested and verified end to end on borrowed school hardware.

  • One rule throughout: no AI-generated stand-in art. Placeholders are unlabeled UV grids, and AI only cleans real photographs; it never invents Coast Salish designs

Where it stands: finished at v0.5. A working hand-tracked six-whorl gallery on a Quest 3, with headless builds and a verified deploy pipeline.

Unity 6Meta Quest 3Passthrough AROpenXRXR HandsC#Python · OpenCV

More builds

Sentinel occupancy dashboard
AI · Computer Vision

Sentinel: AI Occupancy Monitor

Real-time people counting via YOLOv8-Pose tripwire over an IP camera, with a live Streamlit dashboard.

View project →
Robotics

Autonomous RC Car

Self-navigating car using ultrasonic sensors: a full end-to-end hardware and firmware build.

View project →
Aerospace · Hardware

RC Plane Build

Designed and assembled an RC airplane from scratch: airframe, control surfaces, and radio tuning.

View project →
Claude Monitor CRT dashboard
Embedded · Dashboards

Claude Monitor: Live Pi Usage Dashboard

Wall-mounted 7" Raspberry Pi screen showing live Claude Code usage scraped from the CLI dialog.

View project →
Product · Open Source

Bullseye: Marketplace Deal Scorer

Open-source Windows app that scores Facebook Marketplace listings against real eBay sold comps in real time.

View project →
cc-discord-remote on phone
Automation · Windows API

cc-discord-remote: Drive Claude Code from Discord

Phone-controlled Claude Code: a Discord bot that attaches to a live terminal via Win32 console APIs and tails session JSONL for responses.

View project →

Skills

Programming

  • C · ESP-IDF firmware
  • Python · CV & tooling
  • C# · Unity
  • Java · AP CS
  • SQL / SQLite

Hardware & Build

  • Microcontrollers (ESP32, Arduino, ESP-IDF)
  • Soldering & breadboarding
  • Sensor wiring (I²C devices)
  • RC platforms · servos · ESCs
  • Foam-board airframe construction

Tools & Libraries

  • Git & GitHub
  • VS Code
  • OpenCV · YOLOv8 · OpenVINO
  • Streamlit · Pandas
  • Windows & basic terminal

Activities & Leadership

Junior Software Developer · AI Operations

SentryDevices · Vancouver, BC · June–August 2026

First paid software role, part-time through the summer. The work is covered by a non-disclosure agreement, so the detail stays off this page; happy to talk about what I learned in general terms.

Black Belt & Assistant Instructor

Ken Shin Dojo · Kerrisdale Community Centre

Five years of kickboxing; youngest black belt and assistant instructor in Ken Shin Dojo's history (founded 2006). For the past year, lead 20+ students across three weekly kickboxing and jiu-jitsu classes, including students with physical and mental disabilities, work that demands additional patience and compassion.

Grad Committee Treasurer

Class of 2026 · West Point Grey Academy

Managed the budget, vendor payments, and fundraising bookkeeping for the graduating class.

Music Hall of Fame · Jazz Combo Founder

WPGA Music Program · Self-taught multi-instrumentalist

Inducted into the WPGA Music Hall of Fame for 12 years of commitment. Self-taught on drums, bass, piano, saxophone, and guitar. Founded the WPGA Jazz Combo (2023) and grew it from four to seven members across 10+ performances. Co-founder and rhythm-section leader of the WPGA R&B band. Performed for Special Olympics Canada.

Varsity Tennis

WPGA · Vancouver Lawn Tennis Club

Competitive player on WPGA's varsity team and member of Vancouver Lawn Tennis & Badminton Club.

UBC Physics Olympics · Bronze Medal

2026 · WPGA

Bronze medal in the lab event at the 2026 UBC Physics Olympics, a one-day inter-school competition covering physics theory and hands-on apparatus design.

Euclid Mathematics Contest

2026 · University of Waterloo CEMC

Participated in the 2026 Euclid Contest, the Centre for Education in Mathematics and Computing's senior-level Canadian math competition focused on proof-style problem solving.

Head of Technical Maintenance · Windsor Market

2021–2024

Primary on-call technician for a small commercial grocer for three years. Repaired industrial refrigeration, HVAC, electrical circuitry, and structural systems: hands-on STEM applied to facility infrastructure and operational safety. Also ran till, stock, and back-office operations.

Volunteer Work

Community

Kerrisdale Community Centre, Camp Jubilee restoration, WPGA productions, and concert venue merch sales.

Get in touch

Open to internships and to collaboration on embedded or computer-vision work.