diff --git a/public/examples/1678-2026-robot.png b/public/examples/1678-2026-robot.png new file mode 100644 index 0000000..93850ef Binary files /dev/null and b/public/examples/1678-2026-robot.png differ diff --git a/public/examples/581-2026-robot.png b/public/examples/581-2026-robot.png new file mode 100644 index 0000000..c7e87d5 Binary files /dev/null and b/public/examples/581-2026-robot.png differ diff --git a/public/examples/6328-2026-robot.png b/public/examples/6328-2026-robot.png new file mode 100644 index 0000000..f41779f Binary files /dev/null and b/public/examples/6328-2026-robot.png differ diff --git a/public/examples/hub-shift-tracker.png b/public/examples/hub-shift-tracker.png new file mode 100644 index 0000000..84a7598 Binary files /dev/null and b/public/examples/hub-shift-tracker.png differ diff --git a/public/examples/io-layer.png b/public/examples/io-layer.png new file mode 100644 index 0000000..4802143 Binary files /dev/null and b/public/examples/io-layer.png differ diff --git a/public/examples/logging.png b/public/examples/logging.png new file mode 100644 index 0000000..905e014 Binary files /dev/null and b/public/examples/logging.png differ diff --git a/src/config/sidebarConfig.ts b/src/config/sidebarConfig.ts index 478ac46..835a13d 100644 --- a/src/config/sidebarConfig.ts +++ b/src/config/sidebarConfig.ts @@ -202,6 +202,19 @@ export const sidebarSections: Record = { }, ], + // Examples section + '/examples': [ + { + label: 'Examples', + items: [ + { + label: 'Overview', + slug: 'examples', + }, + ], + }, + ], + // Best Practices section '/best-practices': [ { diff --git a/src/content/docs/examples/1678-2026-code.mdx b/src/content/docs/examples/1678-2026-code.mdx new file mode 100644 index 0000000..b997363 --- /dev/null +++ b/src/content/docs/examples/1678-2026-code.mdx @@ -0,0 +1,10 @@ +--- +title: "1678's 2026 Code" +description: In-depth code analysis of 1678's 2026 robot code +--- + +## Links + +* [Robot code repository](https://github.com/frc1678/C2026-Public) +* [1678 Citrus Circuits 2026 CAD and Robot Code Release](https://www.chiefdelphi.com/t/1678-citrus-circuits-2026-cad-and-robot-code-release/521535) +* [1678 Citrus Circuits | Behind the Bumpers | FRC REBUILT](https://www.youtube.com/watch?v=wsAJGi3yxSk) diff --git a/src/content/docs/examples/581-2026-code.mdx b/src/content/docs/examples/581-2026-code.mdx new file mode 100644 index 0000000..1bda2eb --- /dev/null +++ b/src/content/docs/examples/581-2026-code.mdx @@ -0,0 +1,9 @@ +--- +title: 581 2026 Code +description: In-depth code analysis of 581's 2026 robot code +--- + +## Links + +* [Robot code repository](https://github.com/team581/frc-2026) +* [581 Blazing Bulldogs 2026 CAD and Code Release](https://www.chiefdelphi.com/t/581-blazing-bulldogs-2026-cad-and-code-release/521762) diff --git a/src/content/docs/examples/6328-2026-code.mdx b/src/content/docs/examples/6328-2026-code.mdx new file mode 100644 index 0000000..1115a8b --- /dev/null +++ b/src/content/docs/examples/6328-2026-code.mdx @@ -0,0 +1,82 @@ +--- +title: 6328 2026 Code +description: In-depth code analysis of 6328's 2026 robot code +--- + +## Links + +* [Robot code repository](https://github.com/Mechanical-Advantage/RobotCode2026Public) +* [FRC 6328 Mechanical Advantage 2026 Build Thread](https://www.chiefdelphi.com/t/frc-6328-mechanical-advantage-2026-build-thread/509595) +* [6328 Mechanical Advantage | Behind the Bumpers | FRC REBUILT Robot](https://www.youtube.com/watch?v=-mxl5Mrm7RQ) + +## Logging + +AdvantageKit is a logging framework developed by Mechanical Advantage for deterministic log replay. +It is based on the idea that the goal of the robot code is to convert inputs into outputs. + +![Logging](/examples/logging.png) + +> Instead of logging a limited set of values from the user code, AdvantageKit records all of the data flowing into the robot code. +> Every sensor value, button press, and much more is logged every loop cycle. +> After a match, these values can be replayed to the robot code in a simulator. +> Since every input command is the same, all of the internal logic of the code is replayed exactly. +> This allows you to log extra fields after the fact, or modify pipelines to see how they would have functioned during the match. +> This technique means that logging is more than just a tool for checking on specific issues; it's also a safety net that can be used to verify how any part of the code functions. + +## IO Layer + +The IO layer is a design pattern for subsystems recommended for simulation in AdvantageKit’s log replay framework because it reduces the chance of interacting with hardware that doesn’t exist. + +![IO layer diagram](/examples/io-layer.png) + +> Data logging of inputs should occur between the control logic and hardware interface - this ensures that any control logic can be replayed in the simulator. +> We suggest restructuring the subsystem such that hardware interfacing occurs in a separate object (we call this the "IO" layer). +> The IO layer includes an interface defining all methods used for interacting with the hardware along with one or more implementations that make use of vendor libraries to carry out commands and read data. + +## Hub shift tracker + +`HubShiftUtil.java` is a utility class to get consistent match times and accurate alliance shift tracking to time the shooting to start just before the alliance shift starts and stop just before it ends based on the ball's time of flight. +This is important to reduce wasted effort and the amount of uncounted balls in inactive periods. + +![Hub shift tracker](/examples/hub-shift-tracker.png) + +## SOTM with drum shooter + +Mechanical Advantage developed shoot on the move on their turreted Alpha Bot. +After transitioning to a drum shooter, they were able to adapt their shoot on the move code by applying the turret angles to the drivebase and limiting the angular velocity of the drivebase to account for its relatively sluggish movement due to the weight it has to carry. + +## Hood anti-decapitation trench bounds + +TrenchBounds.java facilitates anti-decapitation for the shooter hood by checking if the hood translation is within any of the four trench bounds. The Bounds utility record class takes two X and two Y coordinates and has a contains method that returns whether a Translation2d is within the Bounds. + +{/* rli:ignore */} +```java +public static BooleanSupplier contains(Supplier translation) { + return () -> + blueLeftTrench.contains(translation.get()) + || blueRightTrench.contains(translation.get()) + || redLeftTrench.contains(translation.get()) + || redRightTrench.contains(translation.get()); +} +``` + +## Energy Management + +* `BatteryEstimator.java` uses a single RC Thevenin model with Peukert correction. +The SOC (state-of-charge) is determined from open-loop coulomb counting, which is then used to determine OCV, R0, and RP. +VP (polarization voltage) is determined using RC model and corrected from battery terminal voltage measurements using Kalman gain. +The model structure is based on [this paper](https://doi.org/10.1016/j.ifacol.2022.06.031). +Battery parameters are for MK Powered battery based on [this post](https://www.chiefdelphi.com/t/detailed-frc-battery-comparison-for-2026/508077/22) fitted using data from logs. +* `BatteryLogger.java` logs current, power, and energy usage. +* `BreakerModel.java` is a thermal model of the main breaker using Miner's-rule damage accumulation. +Niceness (0–1) scales down the effective trip threshold so the model starts throttling before the actual trip point, providing headroom. +* `CurrentLimits.java` defines current limits for subsystems. +* `FinanceDepartment.java` calculates battery budgets and orchestrates `BatteryEstimator`, `BatterLogger`, `BreakerModel`, and `CurrentLimits` objects. + +## A variety of Gradle plugins + +[Gversion](https://plugins.gradle.org/plugin/com.peterabeles.gversion) auto-generates a version class file using Git and Gradle and outputs metadata that can be logged with AdvantageKit. + +[Spotless](https://plugins.gradle.org/plugin/com.diffplug.spotless) is a code formatter that helps ensure that the style of code written is consistent throughout the entire codebase. + +[Project Lombok](https://projectlombok.org/) is a library for code generation that relies on annotations. diff --git a/src/content/docs/examples/index.mdx b/src/content/docs/examples/index.mdx new file mode 100644 index 0000000..afaf21e --- /dev/null +++ b/src/content/docs/examples/index.mdx @@ -0,0 +1,70 @@ +--- +title: Examples +description: A programming reference featuring FRC robot code repositories, best practices, and innovative approaches +--- + +export const examples = [ + { + title: '6328 2026 Code', + href: '/examples/6328-2026-code/', + image: '/examples/6328-2026-robot.png', + imageAlt: '6328 2026 robot render', + category: '', + description: 'IO layer, hub shift tracker, sotm with drum shooter, hood anti-decapitation trench bounds, battery state estimator, and a variety of Gradle plugins', + }, + { + title: "1678's 2026 Code", + href: '/examples/1678-2026-code/', + image: '/examples/1678-2026-robot.png', + imageAlt: '1678 2026 robot render', + category: '', + description: 'Team library, custom targeting system, and customer power analyzer', + }, + { + title: '2910 2026 Code', + href: '/examples/581-2026-code/', + image: '/examples/581-2026-robot.png', + imageAlt: '581 2026 robot render', + category: 'Best Practices', + description: 'Advanced software practices, automation, and team programming documentation', + }, +]; + +
+
+

Repository examples

+
+

Steal from the best, invent the rest.

+ -Mike Corsetto, Team 1678 +
+

+ Don't be afraid to take inspiration from robot code that already solved hard programming problems. + This library collects code analysis and repositories so teams can study real examples, compare approaches, and build better code faster. +

+
+
+ +
+
+

Code analysis

+

In-depth Examples

+

These examples go beyond the source code and explain why the code was designed the way it was.

+
+ + +
diff --git a/src/plugins/remark-image-attributes.ts b/src/plugins/remark-image-attributes.ts index c11efb2..3974558 100644 --- a/src/plugins/remark-image-attributes.ts +++ b/src/plugins/remark-image-attributes.ts @@ -79,12 +79,12 @@ export function remarkImageAttributes() { node.data.hProperties = node.data.hProperties || {}; node.data.hProperties.class = `img-wrapper img-align-${align}`; } else if (children.length === 1) { - // Standalone image without attributes - still wrap and center + // Standalone image without attributes - still wrap and left-align node.data = node.data || {}; node.data.hName = 'div'; node.data.hProperties = node.data.hProperties || {}; node.data.hProperties.class = - 'img-wrapper img-align-center'; + 'img-wrapper img-align-left'; } } } diff --git a/src/starlightOverrides/Header.astro b/src/starlightOverrides/Header.astro index 47496c9..6c83532 100644 --- a/src/starlightOverrides/Header.astro +++ b/src/starlightOverrides/Header.astro @@ -12,6 +12,7 @@ const navLinks = [ { href: '/', label: 'Home' }, { href: '/learning-course/', label: 'Learning Course' }, { href: '/educators-guide/introduction/', label: "Educator's Guide" }, + { href: '/examples/', label: 'Examples' }, { href: '/best-practices/overview', label: 'Best Practices' }, { href: '/resources/docs', label: 'Other Resources' }, // TODO: update to '/resources/overview' after merging pr #104 { href: '/contribution/', label: 'Contribution' }, diff --git a/src/styles/global.css b/src/styles/global.css index b10c513..dea0e145 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -93,6 +93,11 @@ text-align: center; } +.sl-markdown-content .expressive-code figure { + margin: 1.5rem 0; + text-align: left; +} + .sl-markdown-content figure:not(.content-figure) img, .sl-markdown-content figure:not(.content-figure) video { max-width: 100%; @@ -125,25 +130,13 @@ Hero Section ========================================================================== */ -img[src*='tilt-book-white'], -img[src*='tilt-book-green'], -.hero-image img, -[class*='hero'] img, -section[class*='hero'] img { +[class*='hero'] img { filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.25)); transition: filter 0.3s ease; } -:root[data-theme='dark'] img[src*='tilt-book-white'], -:root[data-theme='dark'] .hero-image img { - filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.25)) - drop-shadow(0 0 40px rgba(255, 255, 255, 0.1)); -} - -.hero-actions a, [class*='hero'] a[class*='button'], -.hero a, -section[class*='hero'] a { +.hero a { background-color: var(--accent-color) !important; color: #ffffff !important; font-family: 'Inter', var(--sl-font), system-ui, sans-serif !important; @@ -154,33 +147,25 @@ section[class*='hero'] a { overflow: hidden; } -.hero-actions a:hover, [class*='hero'] a[class*='button']:hover, -.hero a:hover, -section[class*='hero'] a:hover { +.hero a:hover { background-color: var(--accent-color-hover) !important; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(163, 113, 247, 0.4); } -.hero-actions a:active, [class*='hero'] a[class*='button']:active, -.hero a:active, -section[class*='hero'] a:active { +.hero a:active { transform: translateY(0); box-shadow: 0 2px 8px rgba(163, 113, 247, 0.3); } /* Arrow animation on hover */ -.hero-actions a svg, -[class*='hero'] a[class*='button'] svg, -section[class*='hero'] a svg { +[class*='hero'] a[class*='button'] svg { transition: transform 0.3s ease !important; } -.hero-actions a:hover svg, -[class*='hero'] a[class*='button']:hover svg, -section[class*='hero'] a:hover svg { +[class*='hero'] a[class*='button']:hover svg { transform: translateX(4px); } @@ -455,11 +440,6 @@ html.lightbox-open [data-pagefind-ignore='all'] { transform: translateX(4px); } -/* Card grid spacing */ -.card-grid { - gap: 1.5rem !important; -} - /* Light mode adjustments */ :root[data-theme='light'] .sl-link-card { background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%) !important; @@ -529,103 +509,9 @@ html.sidebar-collapsed .sidebar-collapsed-toggle-wrapper { } /* ========================================================================== - Image Alignment and Figures + Centered Content (:::center directive) ========================================================================== */ -/* Centered (default) */ -.centered-image { - display: flex; - justify-content: center; - width: 100%; - margin: 1rem 0; -} - -.centered-image img { - display: block; -} - -.centered-figure { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - margin: 1rem 0; -} - -.centered-figure img { - display: block; -} - -.centered-figure figcaption { - margin-top: 0.5rem; - font-style: italic; - text-align: center; - color: var(--sl-color-gray-3); -} - -/* Left aligned */ -.left-image { - display: flex; - justify-content: flex-start; - width: 100%; - margin: 1rem 0; -} - -.left-image img { - display: block; -} - -.left-figure { - display: flex; - flex-direction: column; - align-items: flex-start; - width: 100%; - margin: 1rem 0; -} - -.left-figure img { - display: block; -} - -.left-figure figcaption { - margin-top: 0.5rem; - font-style: italic; - text-align: left; - color: var(--sl-color-gray-3); -} - -/* Right aligned */ -.right-image { - display: flex; - justify-content: flex-end; - width: 100%; - margin: 1rem 0; -} - -.right-image img { - display: block; -} - -.right-figure { - display: flex; - flex-direction: column; - align-items: flex-end; - width: 100%; - margin: 1rem 0; -} - -.right-figure img { - display: block; -} - -.right-figure figcaption { - margin-top: 0.5rem; - font-style: italic; - text-align: right; - color: var(--sl-color-gray-3); -} - -/* Centered content block (:::center directive) */ .centered-content { text-align: center; } @@ -702,7 +588,237 @@ html.sidebar-collapsed .sidebar-collapsed-toggle-wrapper { text-align: center; } -.code { - margin-left: 0; - margin-right: auto; +.content-panel:has(+ .content-panel .examples-hero) { + display: none; +} + +/* ========================================================================== + Examples Page (full-width layout) + ========================================================================== */ + +.main-frame:has(.examples-hero) .right-sidebar-container { + display: none; +} + +.main-pane:has(.examples-hero) { + --sl-content-width: 100% !important; + width: 100%; +} + +.examples-hero { + display: grid; + gap: clamp(1.25rem, 4vw, 3rem); + align-items: center; + max-width: none; + margin: 1.5rem 0 2rem; + padding: clamp(1.25rem, 4vw, 2rem); + border: 1px solid var(--sl-color-gray-5); + border-radius: 8px; + background: linear-gradient( + 135deg, + color-mix(in srgb, var(--sl-color-gray-6) 68%, transparent), + color-mix(in srgb, var(--sl-color-accent) 8%, transparent) + ); +} + +.examples-hero-copy { + display: grid; + align-content: center; + gap: 1.1rem; + max-width: 42rem; +} + +.examples-hero .examples-eyebrow { + margin: 0; + color: var(--accent-color); + font-size: var(--sl-text-sm); + font-weight: 700; + letter-spacing: 0; + text-transform: uppercase; +} + +.examples-hero blockquote { + margin: 0; + border: 0; + padding: 0; +} + +.examples-hero blockquote p { + max-width: 17ch; + color: var(--sl-color-white); + font-size: clamp(2.1rem, 4.3vw, 4.05rem); + font-weight: 800; + letter-spacing: 0; + line-height: 0.95; +} + +.examples-hero cite { + display: block; + margin-top: 0.75rem; + color: var(--sl-color-gray-3); + font-style: normal; + font-weight: 700; +} + +.examples-hero p { + max-width: 60ch; + margin: 0; + color: var(--sl-color-gray-2); + font-size: var(--sl-text-lg); +} + +.examples-hero .hero-copy { + max-width: 62ch; +} + +/* ========================================================================== + Examples Page (featured cards) + ========================================================================== */ + +.mechanism-eyebrow, +.category-label { + margin: 0; + color: var(--sl-color-accent-high); + font-size: var(--sl-text-sm); + font-weight: 700; + letter-spacing: 0; + text-transform: uppercase; +} + +.category-label { + color: var(--accent-color); +} + +.mechanism-section { + margin-block: 2.5rem; +} + +.section-heading { + display: grid; + gap: 0.35rem; + margin-bottom: 1rem; +} + +.section-heading h2 { + margin: 0; + color: var(--sl-color-white); + line-height: 1.05; +} + +.section-heading p { + max-width: 64ch; + margin: 0; + color: var(--sl-color-gray-3); +} + +.section-heading .mechanism-eyebrow { + color: var(--accent-color); + line-height: 1.2; +} + +.featured-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + grid-auto-rows: 1fr; + align-items: stretch; + gap: 1rem; +} + +.featured-card { + position: relative; + display: grid; + grid-template-rows: auto 1fr; + height: 100%; + margin: 0 !important; + border: 1px solid var(--sl-color-gray-5); + border-radius: 8px; + background: var(--sl-color-black); + transition: + border-color 160ms ease, + transform 160ms ease, + background-color 160ms ease; +} + +.featured-card-link { + color: inherit; + text-decoration: none; +} + +.featured-card-link::after { + content: ''; + position: absolute; + inset: 0; + z-index: 1; +} + +.featured-card:hover, +.featured-card:focus-within { + border-color: var(--accent-color-hover); + background: color-mix( + in srgb, + var(--sl-color-black) 92%, + var(--accent-color-hover) + ); + transform: translateY(-2px); +} + +.featured-card:focus-within, +.featured-card-link:focus-visible { + outline: 2px solid var(--accent-color-hover); + outline-offset: 3px; +} + +.featured-image { + overflow: hidden; + height: clamp(11rem, 18vw, 15rem); + padding: clamp(0.75rem, 1.5vw, 1.1rem); + border-bottom: 1px solid var(--sl-color-gray-5); + background: var(--sl-color-gray-7); + box-sizing: border-box; + border-radius: 8px 8px 0 0; +} + +.featured-image img { + display: block; + width: 100%; + height: 100%; + max-width: none; + object-fit: contain; + transition: + opacity 160ms ease, + transform 160ms ease; +} + +.featured-card:hover img { + transform: scale(1.02); +} + +.featured-body { + display: grid; + gap: 0.6rem; + padding: 1rem; +} + +.featured-body h3, +.featured-body h3 a { + margin: 0; + color: var(--sl-color-white); + text-decoration: none; +} + +.featured-body p { + margin: 0; + color: var(--sl-color-gray-3); + line-height: 1.5; +} + +.featured-body .category-label { + color: var(--accent-color); + line-height: 1.2; +} + +@media (max-width: 58rem) { + .featured-grid { + grid-template-columns: 1fr; + } }