{{ item.title }}
+ {% if item.excerpt %} +{{ item.excerpt | strip_html | truncate: 220 }}
+ {% endif %} +diff --git a/_config.yml b/_config.yml index 61677f5..84c1aef 100644 --- a/_config.yml +++ b/_config.yml @@ -8,14 +8,15 @@ # Basic Site Settings locale : "en-US" -site_theme : "default" -title : "Bryce Grant" -title_separator : "-" -name : &name "Your Name" -description : &description "personal description" -url : https://bryceag11.github.io # the base hostname & protocol for your site e.g. "https://[your GitHub username].github.io" or if you already have some other page hosted on Github then use "https://[your GitHub username].github.io/[Your Repo Name]" -baseurl : "" # the subpath of your site, e.g. "/blog" -repository : "bryceag11/bryceag11.github.io" +site_theme : "default" +title : "Bryce Grant" +title_separator : "-" +name : &name "Your Name" +description : &description "personal description" +url : https://bryceag11.github.io # the base hostname & protocol for your site e.g. "https://[your GitHub username].github.io" or if you already have some other page hosted on Github then use "https://[your GitHub username].github.io/[Your Repo Name]" +baseurl : "" # the subpath of your site, e.g. "/blog" +repository : "bryceag11/bryceag11.github.io" +show_masthead : false # Site Author - The following control what appear as part of the author content on the side bar. # If a field is blank the icon and link will not appear, otherwise it will be shown. diff --git a/_data/home_highlights.yml b/_data/home_highlights.yml new file mode 100644 index 0000000..3469d53 --- /dev/null +++ b/_data/home_highlights.yml @@ -0,0 +1,24 @@ +- title: "QUAN" + description: "Quaternion Approximate Networks" + media: "/assets/media/highlights/quan.mp4" + # poster: "/assets/media/highlights/quan.png" + media_type: "video" + links: + - label: "Read more" + url: "https://arxiv.org/abs/2509.05512" +- title: "Sheaf Interpretability" + description: "Gluing Neural Interpretations: Sheaf Cohomology, Hodge Decomposition, and Energies for Cross-Context Consistency" + media: "/assets/media/highlights/ThickGradientSheafFix.mp4" + # poster: "ThickGradientSheafFix.mp4" + media_type: "video" + # links: + # - label: "Project" + # url: "/projects/" +- title: "Quat Viz" + description: "Quaternion Visualizations" + media: "/assets/media/highlights/quan.mp4" + poster: "/assets/media/highlights/quan.png" + media_type: "video" + links: + - label: "Demo" + url: "https://cwru-aism.github.io/QUANpaper/" diff --git a/_data/home_publications.yml b/_data/home_publications.yml new file mode 100644 index 0000000..170743b --- /dev/null +++ b/_data/home_publications.yml @@ -0,0 +1,34 @@ +- title: "Quaternion Approximation Networks for Enhanced Image Classification and Oriented Object Detection" + authors: "Bryce Grant, Peng Wang" + venue: "IROS 2025 · Oral" + image: "/assets/media/highlights/quan.mp4" + summary: "Rotation-aware perception layers composed with quaternion algebra so we keep spatial intuition while training compact real-valued networks" + links: + - label: "Paper" + url: "https://arxiv.org/abs/2509.05512" + - label: "Project Page" + url: "https://cwru-aism.github.io/QUANpaper/" + - label: "Code" + url: "https://github.com/bryceag11/QUAN_ultralytics" +# - title: "Causal PointNet for Fixtures" +# authors: "Bryce Grant, Peng Wang" +# venue: "IROS Workshop 2023" +# summary: "Counterfactual interventions on point clouds to predict when fixtures fail and to stage new grasping strategies before cutting tooling." +# links: +# - label: "Paper" +# url: "/files/Causal_PointNet.pdf" +# - label: "Project" +# url: "/projects/" +# - label: "Code" +# url: "https://github.com/bryceag11" +# - title: "UR5 Assembly Coach" +# authors: "Bryce Grant, AISM Lab" +# venue: "In progress" +# summary: "A multimodal coach that narrates assembly steps and pushes haptic cues to the robot as students learn manufacturing tasks." +# links: +# - label: "Summary" +# url: "/portfolio/" +# - label: "Project" +# url: "/projects/" +# - label: "Code" +# url: "https://github.com/bryceag11" diff --git a/_data/home_sections.yml b/_data/home_sections.yml new file mode 100644 index 0000000..373c858 --- /dev/null +++ b/_data/home_sections.yml @@ -0,0 +1,38 @@ +projects: + title: "Projects" + intro: "Selected research and class projects." + cards: + - title: "Causal DenseFusion" + image: "/images/causal_pointnet.png" + meta: "Computer Vision · Causal Inference" + date: "2024" + description: "Refines 6D pose estimates using causal interventions and backdoor adjustments based on structural causal models. Improves robustness to viewpoint ambiguity and symmetry." + badges: ["PointNet", "Causal ML"] + links: + - label: "PDF" + url: "/files/Causal_PointNet.pdf" + - label: "Code" + url: "https://github.com/bryceag11/CausalDenseFusion" + - title: "Probabilistic Digital Twin" + image: "/images/ros_stack.png" + meta: "Robotics · Probabilistic Modeling" + date: "2024" + description: "ROS2-integrated Dynamic Bayesian Network for real-time fault detection in Universal Robots using Unscented Kalman Filtering to track friction, damping, and wear parameters." + badges: ["ROS2", "UKF", "DBN"] + links: + - label: "PDF" + url: "/files/PGM_URDT.pdf" + - label: "Code" + url: "https://github.com/bryceag11/JARVIS" + - title: "Obstructive Sleep Apnea Detection" + image: "/images/500x300.png" + meta: "Machine Learning · Healthcare" + date: "2024" + description: "Machine learning system for detecting obstructive sleep apnea events using physiological signal processing and deep learning techniques for real-time classification of respiratory disturbances." + badges: ["Deep Learning", "Signal Processing"] + links: + - label: "Code" + url: "https://github.com/bryceag11/OSA" + footer_link: + label: "Older Projects" + url: "/projects/" diff --git a/_data/home_updates.yml b/_data/home_updates.yml new file mode 100644 index 0000000..027dc07 --- /dev/null +++ b/_data/home_updates.yml @@ -0,0 +1,18 @@ +- date: "Oct 2025" + emoji: "🤖" + text: "Attending IROS in Hanzghou to present QUAN" + url: "https://cwru-aism.github.io/QUANpaper/" +- date: "Apr 2025" + emoji: "🧭" + text: "Awarded the NSF GRFP" + url: "https://www.research.gov/grfp/AwardeeList.do?method=loadAwardeeList" +- date: "Aug 2024" + emoji: "🎓" + text: "Started my PhD at Case Western Reserve University" +- date: "May - Aug 2024" + emoji: "🧠" + text: "PhD intern at HP focused on multi-agents, RAG, and anomaly detection" +- date: "May 2024" + emoji: "😼" + text: "Graduated from the University of Kentucky with a dual BS in Electrical & Computer Engineering" + diff --git a/_data/navigation.yml b/_data/navigation.yml index b1a819f..d92e73a 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -17,11 +17,13 @@ main: # - title: "Teaching" # url: /teaching/ - # - title: "Portfolio" - # url: /portfolio/ - - title: "Projects" - url: /projects/ - # - title: "Blog Posts" + # - title: "Portfolio" + # url: /portfolio/ + - title: "Projects" + url: /projects/ + - title: "Photography" + url: /photography/ + # - title: "Blog Posts" # url: /year-archive/ - title: "CV" diff --git a/_data/photography.yml b/_data/photography.yml new file mode 100644 index 0000000..76a8f88 --- /dev/null +++ b/_data/photography.yml @@ -0,0 +1,412 @@ +film: + - slug: fall-2019 + title: "Fall 2019" + location: "Lexington, Kentucky" + description: "My first dedicated 35mm project—a study in grain, late harvest light, and the cables that crisscross industrial yards." + camera: "Canon AE-1 · Kodak Portra 400" + coords: + lat: 38.0406 + lng: -84.5037 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#f59e0b", "#c026d3"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Switchyard lines at dusk." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Detail study in rust + glass." + - slug: rodchenko-inspiration + title: "Rodchenko Inspiration" + location: "Moscow, Russia" + description: "Pulled from my Russian Culture final project—recreating Rodchenko angles with modern architecture and brutalist stairs." + camera: "Pentax K1000 · Ilford HP5" + coords: + lat: 55.7558 + lng: 37.6173 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#334155", "#94a3b8"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Subway descent shot with a 28mm lens." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Diagonal skylines inspired by Rodchenko." + - slug: atl + title: "ATL" + location: "Atlanta, Georgia" + description: "Transit pylons, MARTA shadows, and the soft neon of the BeltLine." + camera: "Canon AE-1 · Cinestill 800T" + coords: + lat: 33.749 + lng: -84.388 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#fb7185", "#fcd34d"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Midtown traffic study." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Blue hour reflections." + - slug: maui + title: "Maui" + location: "Maui, Hawai'i" + description: "Salt-stained cameras, volcano hikes, and the sound of waves on 35mm." + camera: "Olympus XA · Kodak Ektar 100" + coords: + lat: 20.7984 + lng: -156.3319 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#0ea5e9", "#38bdf8"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Haleakalā sunrise." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Paia surf break." + - slug: berlin + title: "Berlin" + location: "Berlin, Germany" + description: "Textures from Kreuzberg to the Brandenburg Gate, mixing Bauhaus lines with candid portraits." + camera: "Canon AE-1 · Kodak Gold" + coords: + lat: 52.52 + lng: 13.405 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#2563eb", "#ef4444"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Tempelhofer Feld silhouettes." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Neukölln stairwells." + - slug: france + title: "France" + location: "Paris + Lyon" + description: "A study on cafe stillness, metro motion blur, and industrial coastlines." + camera: "Canon AE-1 · Fuji Pro 400H" + coords: + lat: 48.8566 + lng: 2.3522 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#f472b6", "#c084fc"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Pont Neuf long exposure." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Lyon rail yard." + - slug: poland + title: "Poland" + location: "Warsaw, Poland" + description: "Concrete, color blocking, and everyday poetry while documenting the Vistula." + camera: "Pentax K1000 · Ilford Delta 400" + coords: + lat: 52.2297 + lng: 21.0122 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#a855f7", "#6366f1"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Science Palace symmetry study." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Foggy Vistula morning." + - slug: ukraine + title: "Ukraine" + location: "Kyiv & Lviv" + description: "Documenting resilience through portrait sessions and architectural fragments." + camera: "Canon AE-1 · Kodak Tri-X" + coords: + lat: 50.4501 + lng: 30.5234 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#fb923c", "#fcd34d"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Golden hour in Podil." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Opera house scaffolding." + - slug: pnw + title: "PNW" + location: "Pacific Northwest" + description: "Foggy evergreen mornings, ferries, and the color of drizzle." + camera: "Nikon FM2 · Kodak Ultramax" + coords: + lat: 47.6062 + lng: -122.3321 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#0ea5e9", "#a7f3d0"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Puget Sound ferry ride." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Columbia River Gorge overlook." + +digital: + - slug: us-abandoned + title: "US Abandoned" + location: "Detroit · Appalachia" + description: "Infrared-inspired digital set exploring abandoned manufacturing sites." + camera: "Sony A7 IV" + coords: + lat: 42.3314 + lng: -83.0458 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#22d3ee", "#0f172a"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Packard plant atrium scan." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Coal tipple geometry." + - slug: haleakala + title: "Haleakalā National Park" + location: "Maui, Hawai'i" + description: "Volcanic crater sunrises and otherworldly landscapes above the clouds." + camera: "Sony A7R III" + coords: + lat: 20.7097 + lng: -156.1720 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#f97316", "#fde047"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Crater rim at dawn." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Silversword plants." + - slug: mount-rainier + title: "Mount Rainier National Park" + location: "Washington" + description: "Alpine meadows and glacial peaks in the Pacific Northwest." + camera: "Sony A7R III" + coords: + lat: 46.8800 + lng: -121.7269 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#60a5fa", "#e0f2fe"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Paradise meadows wildflowers." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Nisqually Glacier study." + - slug: crater-lake + title: "Crater Lake National Park" + location: "Oregon" + description: "The deepest lake in the US, ringed by volcanic cliffs and pristine blue waters." + camera: "Sony A7R III" + coords: + lat: 42.8684 + lng: -122.1685 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#3b82f6", "#1e40af"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Wizard Island reflection." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Rim drive panorama." + - slug: olympic + title: "Olympic National Park" + location: "Washington" + description: "Temperate rainforests, rugged coastline, and mountain wilderness." + camera: "Sony A7R III" + coords: + lat: 47.8021 + lng: -123.6044 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#10b981", "#065f46"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Hoh Rainforest moss." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Ruby Beach sea stacks." + - slug: banff + title: "Banff National Park" + location: "Alberta, Canada" + description: "Turquoise lakes, jagged peaks, and Canadian Rockies grandeur." + camera: "Sony A7R III" + coords: + lat: 51.4968 + lng: -115.9281 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#06b6d4", "#0e7490"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Moraine Lake blue hour." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Peyto Lake overlook." + - slug: glacier-np + title: "Glacier National Park" + location: "Montana" + description: "Crown of the Continent with alpine lakes and Going-to-the-Sun Road." + camera: "Sony A7R III" + coords: + lat: 48.7596 + lng: -113.7870 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#7dd3fc", "#0c4a6e"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Hidden Lake overlook." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Many Glacier sunrise." + - slug: sequoia + title: "Sequoia National Park" + location: "California" + description: "Giant sequoias and the largest trees on Earth towering in the Sierra Nevada." + camera: "Sony A7R III" + coords: + lat: 36.4864 + lng: -118.5658 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#92400e", "#44403c"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "General Sherman Tree." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Giant Forest canopy." + - slug: redwood + title: "Redwood National Park" + location: "California" + description: "Ancient coastal redwoods and fog-draped forest cathedrals." + camera: "Sony A7R III" + coords: + lat: 41.2132 + lng: -124.0046 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#14532d", "#15803d"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Fern Canyon walls." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Tall Trees Grove." + - slug: rocky-mountain + title: "Rocky Mountain National Park" + location: "Colorado" + description: "High alpine tundra, elk herds, and continental divide vistas." + camera: "Sony A7R III" + coords: + lat: 40.3428 + lng: -105.6836 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#818cf8", "#4f46e5"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Bear Lake reflection." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Trail Ridge Road summit." + - slug: china + title: "China" + location: "Shanghai & Shenzhen" + description: "Architecture, street food vendors, and ferry commutes captured during robotics exchange trips." + camera: "Fujifilm X-Pro3" + coords: + lat: 31.2304 + lng: 121.4737 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#14b8a6", "#115e59"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Bund skyline reflection." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Shenzhen manufacturing floors." + - slug: japan + title: "Japan" + location: "Tokyo & Kyoto" + description: "A mix of neon alleys, Shinkansen pans, and quiet temples." + camera: "Fujifilm X100V" + coords: + lat: 35.6762 + lng: 139.6503 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#f472b6", "#d946ef"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Shibuya crossing long exposure." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Arashiyama bamboo groove." + - slug: morocco + title: "Morocco" + location: "Marrakesh & Sahara" + description: "Dust storms, Atlas mountain roads, and sunrise dunes in rich desert palettes." + camera: "Sony A7 III" + coords: + lat: 31.6295 + lng: -7.9811 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#f97316", "#ea580c"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Djemaa el-Fna rooftop." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Merzouga dunes at dawn." + - slug: portugal + title: "Portugal" + location: "Lisbon & Porto" + description: "Azulejo tiles, tram lines, and golden hour over the Douro River." + camera: "Sony A7 IV" + coords: + lat: 38.7223 + lng: -9.1393 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#0ea5e9", "#f59e0b"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Lisbon tram 28 study." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Porto riverside sunset." + - slug: costa-rica-panama + title: "Costa Rica & Panama" + location: "San José · Panama City" + description: "Cloud forests, canal locks, and vibrant street markets between two oceans." + camera: "Fujifilm X-T4" + coords: + lat: 9.7489 + lng: -83.7534 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#10b981", "#059669"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Monteverde canopy walk." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Panama Canal transit." + - slug: belize-guatemala + title: "Belize & Guatemala" + location: "Belize City · Antigua" + description: "Mayan ruins, Caribbean coastlines, and volcanic landscapes in Central America." + camera: "Sony A7 III" + coords: + lat: 15.7835 + lng: -90.2308 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#14b8a6", "#0d9488"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Tikal sunrise through fog." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Belize barrier reef." + - slug: western-europe + title: "Western Europe" + location: "Amsterdam · Paris · Geneva · Berlin" + description: "Digital medium-format stitches dedicated to reflections, trains, and living with water across Europe's cultural capitals." + camera: "GFX 50R" + coords: + lat: 52.3676 + lng: 4.9041 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#7dd3fc", "#1d4ed8"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Canal mirror lines." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Eiffel Tower dawn." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Geneva lake symmetry." + - slug: central-eastern-europe + title: "Central & Eastern Europe" + location: "Prague · Kraków · Zakopane · Lviv · Kyiv · Rivne" + description: "Night walks with a wireframe aesthetic—mixing lidar captures with still photography across historic cities and mountain towns." + camera: "Sony A7S III" + coords: + lat: 50.0755 + lng: 14.4378 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#06b6d4", "#0f172a"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Charles Bridge lidar overlay." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Tatra Mountains twilight." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Kyiv golden domes." diff --git a/_includes/home/highlights.html b/_includes/home/highlights.html new file mode 100644 index 0000000..c6d8e25 --- /dev/null +++ b/_includes/home/highlights.html @@ -0,0 +1,48 @@ +{% assign highlights = site.data.home_highlights | default: [] %} +{% if highlights.size > 0 %} +
{{ highlight.description }}
+ {% endif %} +{{ item.excerpt | strip_html | truncate: 220 }}
+ {% endif %} +No publications to display yet. Add entries to the _publications collection.
{{ pub.venue }}
+ {% endif %} + {% if pub.links %} +Add entries to _data/home_publications.yml to populate this section.
{{ include.kicker | default: section.kicker }}
+ {% endif %} +{{ section.intro }}
+ {% endif %} +{{ card.date }}
+ {% endif %} +{{ card.description }}
+ {% endif %} ++ {{ update.emoji | default: '•' }} + {{ update.date }} + {% assign update_url = update.url %} + {% if update_url %} + {% unless update_url contains '://' %} + {% assign update_url = update_url | relative_url %} + {% endunless %} + {{ update.text }} + {% else %} + {{ update.text }} + {% endif %} +
+{{ include.collection_title | default: include.entry_type | capitalize }}
+{{ include.entry.location }}
+{{ include.entry.description }}
+ +The requested gallery could not be found. Please update _data/photography.yml.
Electrical Engineering PhD Student
+ +I am a 2nd year Ph.D. student at Case Western Reserve University where I’m advised by Prof. Peng “Edward” Wang. I received my dual B.S. in EE & CPE (Electrical and Computer Engineering) from the University of Kentucky in 2024. I’m currently funded by the NSF GRFP.
+I’m interested in building robotic systems that reason about pose, motion, and uncertainty through geometric-aware learning, causal inference, and sequential modeling.
+I’m currently working on semantic affordances and topology-informed interpretability for VLAs.
+ +
+ Updates
+ {% include home/updates.html %} +35mm & Digital Photography
+Outside of my research, I love urbex, adrenaline, and film photography. Explore this to see what I've captured. UNDER CONSTRUCTION
+{{ entry.description }}
+{{ entry.location }}
+{{ entry.description }}
+{{ entry.location }}
+
-Refines 6D pose estimates using causal interventions and backdoor adjustments based on structural causal models. Improves robustness to viewpoint ambiguity and symmetry.
-
-[PDF](/files/Causal_PointNet.pdf) | [Code](https://github.com/bryceag11/CausalDenseFusion)
-
----
-
-### Probabilistic Digital Twin
-
-Developed a ROS2-integrated Dynamic Bayesian Network for real-time fault detection in Universal Robots. Used Unscented Kalman Filtering to track friction, damping, and wear parameters.
-
-[PDF](/files/PGM_URDT.pdf) | [Code](https://github.com/bryceag11/PGM_URDT)
-
----
-
-### Diffusion Models in Robotics
-
-Explores how Denoising Diffusion Probabilistic Models can be used to synthesize robot action policies from noise, using a NoProp training method adapted for stochastic control.
-
-[PDF](/files/DDPMs.pdf) | [Code](https://github.com/bryceag11/NoProp)
-
----
-
-### Just a Rather Very Intelligent System (J.A.R.V.I.S.)
-J.A.R.V.I.S. is a social navigating robot designed to provide an immersive telepresence experience for remote users. As the navigation systems lead, I developed the robot's 3D SLAM, path planning, and autonomous navigation capabilities from the ground up
-
-[PDF](/files/FDR_Report.pdf) | [CODE](https://github.com/bryceag11/JARVIS.git)
-
-
-
+author_profile: false
+classes: wide
---
+
+
+Personal projects from 2023-2024
+ +
+ Explores Denoising Diffusion Probabilistic Models using a NoProp training method. Surveys their use in robotics.
+
+ Social navigating robot designed to provide an immersive telepresence experience for remote users. Led development of the robot's 3D SLAM system with servo-mounted LiDAR, path planning algorithms, and autonomous navigation capabilities. Achieved real-time mapping and dynamic obstacle avoidance in healthcare environments.
+
+ IoT hardware bridge for retrofitting legacy point-of-sale systems with cloud-based inventory tracking. Built custom firmware for ESP-32 microcontroller enabling real-time inventory synchronization. Co-founded ShopStock LLC and deployed system across multiple retail locations.
+${message}
`; + } + + function bootGlobe() { + const container = document.getElementById('photo-globe'); + if (!container || !window.PHOTOGRAPHY_DATA) { + console.log('Container or PHOTOGRAPHY_DATA not found'); + return; + } + + // Check if WebGL is supported first + if (!supportsWebGL()) { + showFallback(container, 'The interactive globe needs WebGL (browser graphics). The full photo list is still available below.'); + return; + } + + // More robust Three.js loading with timeout + const maxWaitTime = 10000; // 10 seconds max wait + const startTime = Date.now(); + + function checkAndInit() { + if (typeof window.THREE !== 'undefined') { + console.log('Three.js loaded successfully, initializing globe...'); + try { + initGlobe(container); + } catch (error) { + console.error('Error initializing globe:', error); + showFallback(container, 'Error initializing 3D globe. Please refresh the page.'); + } + } else if (Date.now() - startTime < maxWaitTime) { + // Keep trying for up to 10 seconds + setTimeout(checkAndInit, 100); + } else { + console.error('Three.js failed to load after 10 seconds'); + // Try loading Three.js one more time as last resort + if (!window.threeLoadAttempted) { + window.threeLoadAttempted = true; + const script = document.createElement('script'); + script.src = '/assets/js/vendor/three.min.js'; + script.onload = () => { + if (typeof window.THREE !== 'undefined') { + console.log('Three.js loaded via fallback'); + initGlobe(container); + } else { + showFallback(container, 'Unable to load 3D graphics library. Please check your connection and refresh.'); + } + }; + script.onerror = () => { + showFallback(container, 'Failed to load graphics support. Please refresh the page.'); + }; + document.head.appendChild(script); + } else { + showFallback(container, 'Loading graphics support failed. Please refresh the page.'); + } + } + } + + checkAndInit(); + } + + function initGlobe(container) { + const THREE = window.THREE; + const tooltip = document.getElementById('photo-globe-tooltip'); + const dims = getDimensions(container); + container.style.minHeight = `${dims.height}px`; + + const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false }); + renderer.setPixelRatio(window.devicePixelRatio || 1); + renderer.setSize(dims.width, dims.height); + container.appendChild(renderer.domElement); + + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera(45, dims.width / dims.height, 0.1, 100); + camera.position.z = 3.2; + + // Different lighting for each mode + const filmAmbient = new THREE.AmbientLight(0xffffff, 0.5); // Normal white light + const filmDirectional = new THREE.DirectionalLight(0xffffff, 0.6); // White light + filmDirectional.position.set(5, 3, 5); + + const digitalAmbient = new THREE.AmbientLight(0x6666ff, 0.6); // Brighter blue ambient + const digitalDirectional = new THREE.DirectionalLight(0x88aaff, 0.5); // Brighter moonlight + digitalDirectional.position.set(-5, 3, 5); + + // Create Earth globe with better geometry + const globeGeometry = new THREE.SphereGeometry(1, 128, 64); + + // Load Earth texture from online source + const loader = new THREE.TextureLoader(); + loader.crossOrigin = 'anonymous'; + + // Load day and night Earth textures + const earthDayUrl = 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg'; + const earthNightUrl = 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png'; + + const earthTexture = loader.load( + earthDayUrl, + () => console.log('Earth day texture loaded'), + undefined, + () => { + console.log('Failed to load online texture, using procedural fallback'); + createProceduralEarth(); + } + ); + + const nightTexture = loader.load( + earthNightUrl, + () => console.log('Earth night lights texture loaded'), + undefined, + () => { + console.log('Failed to load night lights texture'); + } + ); + + // Create day/night shader for both modes + const vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `; + + const fragmentShader = ` + uniform sampler2D dayMap; + uniform sampler2D nightMap; + uniform float sunLongitude; // Sun's current longitude based on UTC time + uniform float sepiaAmount; // 0.0 = no sepia, 1.0 = full sepia + + varying vec2 vUv; + + vec3 applySepia(vec3 color, float amount) { + // Sepia tone matrix + vec3 sepia; + sepia.r = dot(color, vec3(0.393, 0.769, 0.189)); + sepia.g = dot(color, vec3(0.349, 0.686, 0.168)); + sepia.b = dot(color, vec3(0.272, 0.534, 0.131)); + return mix(color, sepia, amount); + } + + void main() { + // Convert UV to longitude (-PI to PI) + // UV.x goes from 0 to 1, representing -180° to 180° + float longitude = (vUv.x - 0.5) * 2.0 * 3.14159265359; + + // Calculate angular difference from sun position + float angleDiff = longitude - sunLongitude; + + // Normalize angle difference to -PI to PI range + angleDiff = mod(angleDiff + 3.14159265359, 2.0 * 3.14159265359) - 3.14159265359; + + // Day when angular difference is within ±90° of sun position + float halfPi = 3.14159265359 / 2.0; + float dayAmount = smoothstep(halfPi + 0.3, halfPi - 0.3, abs(angleDiff)); + + // Sample both textures + vec3 dayColor = texture2D(dayMap, vUv).rgb; + vec3 nightColor = texture2D(nightMap, vUv).rgb; + + // Mix based on sun position + vec3 finalColor = mix(nightColor, dayColor, dayAmount); + + // Apply sepia if requested + finalColor = applySepia(finalColor, sepiaAmount); + + gl_FragColor = vec4(finalColor, 1.0); + } + `; + + // Calculate sun's longitude based on current UTC time + // At UTC noon (12:00), sun is at Prime Meridian (0° longitude) + // At UTC midnight (0:00), sun is at antimeridian (180° or -180° longitude) + function getSunLongitude() { + const now = new Date(); + const hours = now.getUTCHours() + now.getUTCMinutes() / 60; + // Sun moves 360° in 24 hours = 15° per hour + // At hour 0 (midnight UTC) -> 180° (PI radians) + // At hour 12 (noon UTC) -> 0° (0 radians) + const longitude = ((12 - hours) / 12) * Math.PI; + return longitude; + } + + // Film material with sepia shader + const filmShaderMaterial = new THREE.ShaderMaterial({ + uniforms: { + dayMap: { value: earthTexture }, + nightMap: { value: nightTexture }, + sunLongitude: { value: getSunLongitude() }, + sepiaAmount: { value: 0.6 } // Light sepia for film + }, + vertexShader: vertexShader, + fragmentShader: fragmentShader + }); + + // Digital material without sepia + const digitalMaterial = new THREE.ShaderMaterial({ + uniforms: { + dayMap: { value: earthTexture }, + nightMap: { value: nightTexture }, + sunLongitude: { value: getSunLongitude() }, + sepiaAmount: { value: 0.0 } // No sepia for digital + }, + vertexShader: vertexShader, + fragmentShader: fragmentShader + }); + + // Update sun position every hour for realistic day/night cycle + setInterval(() => { + if (filmShaderMaterial.uniforms && digitalMaterial.uniforms) { + const newLongitude = getSunLongitude(); + filmShaderMaterial.uniforms.sunLongitude.value = newLongitude; + digitalMaterial.uniforms.sunLongitude.value = newLongitude; + } + }, 120000); // 2 minutes for testing, change to 3600000 for 1 hour + + // Fallback procedural Earth function + function createProceduralEarth() { + const canvas = document.createElement('canvas'); + canvas.width = 2048; + canvas.height = 1024; + const ctx = canvas.getContext('2d'); + + // Draw realistic ocean + ctx.fillStyle = '#1e3a5f'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Draw more realistic continents + ctx.fillStyle = '#2d5016'; + + // Draw landmasses with more detail + // North America + ctx.beginPath(); + ctx.moveTo(300, 300); + ctx.quadraticCurveTo(400, 250, 500, 350); + ctx.quadraticCurveTo(450, 450, 350, 500); + ctx.quadraticCurveTo(250, 400, 300, 300); + ctx.fill(); + + // South America + ctx.beginPath(); + ctx.moveTo(450, 600); + ctx.quadraticCurveTo(500, 650, 480, 800); + ctx.quadraticCurveTo(430, 850, 400, 800); + ctx.quadraticCurveTo(400, 700, 450, 600); + ctx.fill(); + + // Africa + ctx.beginPath(); + ctx.moveTo(1000, 400); + ctx.quadraticCurveTo(1100, 350, 1050, 500); + ctx.quadraticCurveTo(1080, 700, 1000, 750); + ctx.quadraticCurveTo(950, 650, 950, 500); + ctx.quadraticCurveTo(980, 400, 1000, 400); + ctx.fill(); + + // Europe + ctx.beginPath(); + ctx.moveTo(1000, 300); + ctx.quadraticCurveTo(1100, 280, 1150, 350); + ctx.quadraticCurveTo(1100, 380, 1000, 350); + ctx.fill(); + + // Asia + ctx.beginPath(); + ctx.moveTo(1200, 300); + ctx.quadraticCurveTo(1500, 250, 1600, 400); + ctx.quadraticCurveTo(1550, 500, 1400, 450); + ctx.quadraticCurveTo(1250, 400, 1200, 300); + ctx.fill(); + + // Australia + ctx.beginPath(); + ctx.ellipse(1500, 750, 120, 70, 0, 0, Math.PI * 2); + ctx.fill(); + + const fallbackTexture = new THREE.CanvasTexture(canvas); + filmShaderMaterial.uniforms.dayMap.value = fallbackTexture; + filmShaderMaterial.uniforms.nightMap.value = fallbackTexture; + digitalMaterial.uniforms.dayMap.value = fallbackTexture; + digitalMaterial.uniforms.nightMap.value = fallbackTexture; + } + + const globe = new THREE.Mesh(globeGeometry, filmShaderMaterial); + scene.add(globe); + + // Add initial film lighting + scene.add(filmAmbient, filmDirectional); + + const markersGroup = new THREE.Group(); + scene.add(markersGroup); + + // Create pin geometry (bright red ball head with silver stem) + const markerHeadGeometry = new THREE.SphereGeometry(0.018, 16, 16); + const markerStemGeometry = new THREE.CylinderGeometry(0.002, 0.003, 0.06, 8); + + // Materials for pins - bright red head, silver stem + const markerHeadMaterial = new THREE.MeshStandardMaterial({ + color: 0xff0000, // Bright red + emissive: 0xff3333, + emissiveIntensity: 0.5, // Brighter glow + metalness: 0.2, + roughness: 0.3 + }); + + const markerStemMaterial = new THREE.MeshStandardMaterial({ + color: 0xc0c0c0, // Silver + emissive: 0xffffff, + emissiveIntensity: 0.1, + metalness: 0.95, + roughness: 0.05 + }); + + const markerMap = new Map(); + const markers = { film: [], digital: [] }; + + function latLngToVector3(lat, lng, radius = 1.01) { + const phi = (90 - lat) * (Math.PI / 180); + const theta = (lng + 180) * (Math.PI / 180); + const x = -(radius * Math.sin(phi) * Math.cos(theta)); + const z = radius * Math.sin(phi) * Math.sin(theta); + const y = radius * Math.cos(phi); + return new THREE.Vector3(x, y, z); + } + + function createMarker(entry, type) { + // Create a container group for positioning + const containerGroup = new THREE.Group(); + + // Create the pin group that will be animated + const pinGroup = new THREE.Group(); + + // Create marker head (red sphere at TOP of pin) + const markerHead = new THREE.Mesh(markerHeadGeometry.clone(), markerHeadMaterial.clone()); + markerHead.position.y = 0; // Head at origin of pin + + // Create marker stem (silver cylinder going into Earth) + const markerStem = new THREE.Mesh(markerStemGeometry.clone(), markerStemMaterial.clone()); + markerStem.position.y = -0.03; // Half the stem height + + // Add both to the pin group + pinGroup.add(markerHead); + pinGroup.add(markerStem); + + // Position pin so it's mostly inserted + pinGroup.position.y = -0.035; // Push pin down into globe (inserted position) + + // Add pin group to container + containerGroup.add(pinGroup); + + // Position the container at the correct location on surface + const coords = entry.coords || { lat: 0, lng: 0 }; + const position = latLngToVector3(coords.lat, coords.lng, 1.0); // On surface + containerGroup.position.copy(position); + + // Make the container face toward the Earth's core (origin) + containerGroup.lookAt(0, 0, 0); + + // Store data including the pin group for animation + containerGroup.userData = { + ...entry, + type, + head: markerHead, + pinGroup: pinGroup, + defaultY: -0.035, // Inserted position + hoveredY: 0.01 // Pulled out position + }; + + markersGroup.add(containerGroup); + markers[type].push(containerGroup); + markerMap.set(entry.slug, containerGroup); + } + + (window.PHOTOGRAPHY_DATA.film || []).forEach((entry) => createMarker(entry, 'film')); + (window.PHOTOGRAPHY_DATA.digital || []).forEach((entry) => createMarker(entry, 'digital')); + + let currentMode = 'film'; + function setMode(mode) { + currentMode = mode; + globe.material = mode === 'film' ? filmShaderMaterial : digitalMaterial; + + // Switch lighting and background based on mode + if (mode === 'film') { + scene.remove(digitalAmbient, digitalDirectional); + scene.add(filmAmbient, filmDirectional); + renderer.setClearColor(0x0a0a0a, 1.0); // Dark background + } else { + // Digital mode - real-time day/night Earth + scene.remove(filmAmbient, filmDirectional); + scene.add(digitalAmbient, digitalDirectional); + renderer.setClearColor(0x000000, 1.0); // Black space + } + + markers.film.forEach((marker) => (marker.visible = mode === 'film')); + markers.digital.forEach((marker) => (marker.visible = mode === 'digital')); + container.dataset.mode = mode; + document + .querySelectorAll('[data-photo-mode]') + .forEach((button) => button.classList.toggle('is-active', button.dataset.photoMode === mode)); + } + + setMode('film'); + + const buttons = document.querySelectorAll('[data-photo-mode]'); + buttons.forEach((button) => + button.addEventListener('click', () => { + setMode(button.dataset.photoMode); + }) + ); + + let rotationY = 0; + let rotationX = 0; + let autoRotate = 0.0015; + let isDragging = false; + let lastX = 0; + let lastY = 0; + + function animate() { + requestAnimationFrame(animate); + if (!isDragging) { + rotationY += autoRotate; + } + markersGroup.rotation.y = rotationY; + markersGroup.rotation.x = rotationX; + globe.rotation.y = rotationY; + globe.rotation.x = rotationX; + renderer.render(scene, camera); + } + + animate(); + + let clickStartTime; + let clickStartMarker; + + function onPointerDown(event) { + clickStartTime = Date.now(); + clickStartMarker = hoveredMarker; + isDragging = true; + lastX = event.clientX; + lastY = event.clientY; + } + + function onPointerMove(event) { + if (isDragging) { + const deltaX = event.clientX - lastX; + const deltaY = event.clientY - lastY; + rotationY += deltaX * 0.005; + rotationX += deltaY * 0.005; + rotationX = Math.max(Math.min(rotationX, Math.PI / 2.5), -Math.PI / 2.5); + lastX = event.clientX; + lastY = event.clientY; + } + handleHover(event); + } + + function onPointerUp() { + isDragging = false; + + // Check if this was a click (not a drag) + const clickDuration = Date.now() - clickStartTime; + if (clickDuration < 200 && clickStartMarker && clickStartMarker === hoveredMarker) { + // Navigate to the photo album + const slug = hoveredMarker.userData.slug; + const type = hoveredMarker.userData.type; + if (slug) { + const url = `/photography/${type}/${slug}/`; + window.location.href = url; + } + } + } + + renderer.domElement.addEventListener('pointerdown', onPointerDown); + renderer.domElement.addEventListener('pointerleave', () => { + if (!isDragging) { + autoRotate = 0.0015; + if (hoveredMarker) { + // Animate pin back down + if (hoveredMarker.userData.pinGroup) { + hoveredMarker.userData.pinGroup.position.y = hoveredMarker.userData.defaultY; + } + if (hoveredMarker.userData.head) { + hoveredMarker.userData.head.material.emissiveIntensity = 0.5; + } + hoveredMarker = null; + } + clearTooltip(); + } + }); + window.addEventListener('pointermove', onPointerMove); + window.addEventListener('pointerup', onPointerUp); + + window.addEventListener('resize', () => { + const { width, height } = getDimensions(container); + container.style.minHeight = `${height}px`; + camera.aspect = width / height; + camera.updateProjectionMatrix(); + renderer.setSize(width, height); + }); + + const raycaster = new THREE.Raycaster(); + const mouse = new THREE.Vector2(); + let hoveredMarker = null; + + function updateTooltip(marker) { + if (!tooltip || !marker) return; + const vector = marker.position.clone(); + vector.applyMatrix4(markersGroup.matrixWorld); + vector.project(camera); + const rect = renderer.domElement.getBoundingClientRect(); + + // Account for scroll position + const scrollX = window.pageXOffset || document.documentElement.scrollLeft; + const scrollY = window.pageYOffset || document.documentElement.scrollTop; + + const x = ((vector.x + 1) / 2) * rect.width + rect.left + scrollX; + const y = ((-vector.y + 1) / 2) * rect.height + rect.top + scrollY; + + tooltip.style.left = `${x}px`; + tooltip.style.top = `${y}px`; + tooltip.style.transform = `translate(-50%, -120%)`; + + // Enhanced tooltip with photo preview + const userData = marker.userData; + const coverImage = userData.cover ? `