AI Skill Report Card

Simulating FSAE Vehicle Dynamics

A-83·Oct 3, 2026·Source: Web
14 / 15

Define every car as a parameter set, never as a branch in the simulation logic:

Python
# vehicles.py CR30 = { "name": "Cr30", "season": "2025-2026", "mass_kg": 245.0, "wheelbase_m": 1.550, "track_front_m": 1.230, "track_rear_m": 1.200, "cg_height_m": 0.265, "weight_dist_front": 0.46, "tire_model": "pacejka_hoosier_r25b_18x6", "aero": {"cl": -2.8, "cd": 0.95, "cop_front_frac": 0.45, "ref_area_m2": 1.1}, "powertrain": {"engine": "cbr600", "redline_rpm": 14000, "final_drive": 3.8}, "suspension": {"roll_stiffness_f_nm_deg": 450, "roll_stiffness_r_nm_deg": 380}, } CR31 = { **CR30, # inherit CR30 as baseline, override only what's changed "name": "CR31", "season": "2026-2027", "mass_kg": 238.0, "aero": {**CR30["aero"], "cl": -3.2, "cop_front_frac": 0.47}, } sim_result_cr30 = run_lap_sim(CR30, track="endurance_2026") sim_result_cr31 = run_lap_sim(CR31, track="endurance_2026")

The simulation engine (run_lap_sim, tire model, aero map, etc.) takes a parameter dict as input and contains zero if car == "CR30" conditionals.

Recommendation▾
Add a third example showing a bad/anti-pattern output (e.g., hardcoded if-branch) alongside the correct fix, to reinforce the good-vs-bad contrast more explicitly.
14 / 15
Progress:
- [ ] Extract all car-specific numbers into a parameter schema
- [ ] Build/confirm simulation engine reads only from the schema (no hardcoded constants)
- [ ] Define CR30 parameter set from current design data
- [ ] Define CR31 as CR30 + deltas (inheritance, not duplication)
- [ ] Validate CR30 sim against known test/endurance data (correlation check)
- [ ] Run CR31 sim, diff results against CR30 baseline
- [ ] Document every parameter source (CAD, test data, estimate, carryover)
- [ ] Version-control parameter files alongside sim code
  1. Schema first. Before touching simulation code, write down every physical/geometric/electronic parameter that could plausibly differ between model years: mass, CG, geometry, tire model, aero coefficients, powertrain, suspension rates, driver inputs. This schema is the contract between "car design" and "simulation code."

  2. One engine, many inputs. The lap sim, skidpad sim, acceleration sim, etc. must be pure functions of (vehicle_params, track_params, driver_params). If you find yourself writing if vehicle["name"] == "CR31", that's a signal a parameter is missing from the schema — add it instead of branching.

  3. Inherit, don't duplicate. CR31 is defined as CR30 plus explicit overrides. This makes design deltas visible by diffing the dict, and prevents silently stale parameters when CR30 data is corrected.

  4. Correlate before trusting. Validate the engine against CR30 using real test day / endurance data (if available) before relying on CR31 predictions, since CR31 has no physical twin yet.

  5. Track provenance. Tag each parameter with its source (measured, CAD estimate, carried over, assumed) so reviewers know confidence level — critical for CR31 since much of it is still being built.

  6. Diff, don't just report. When presenting CR31 results, always show delta vs CR30 (lap time, corner speeds, g-g diagram shift) so design changes are traceable to performance impact.

Recommendation▾
Include a minimal sketch of run_lap_sim's expected signature/return structure so the parametric contract is fully concrete, not just the input dicts.
15 / 20

Example 1: Input: "Suspension team dropped front roll stiffness from 450 to 400 Nm/deg on CR31 only." Output:

Python
CR31["suspension"]["roll_stiffness_f_nm_deg"] = 400 # was 450 (CR30), test change per Suspension lead 2026-03-01

Re-run run_lap_sim(CR31, track); report lateral g and balance shift vs previous CR31 run and vs CR30, not just an absolute number.

Example 2: Input: "We need skidpad time for both cars on the new tire compound." Output: Add "tire_model": "pacejka_hoosier_r25b_18x6_newcompound" as an override in both CR30_newcompound and CR31_newcompound derived dicts (don't mutate baseline dicts — keep baseline reproducible), run the same run_skidpad_sim() for each, output a table: Car | Tire | Skidpad Time | Δ vs baseline compound.

Recommendation▾
Consider trimming some repetition between Workflow and Best Practices/Pitfalls sections (e.g., inheritance and provenance points appear in multiple places) to tighten length.
  • Keep parameter files in version control (JSON/YAML/Python dataclasses), reviewed like code.
  • Use dataclasses or typed dicts with units in field names (mass_kg, not mass) to prevent unit-mismatch bugs across model years.
  • Write regression tests: feed CR30 params through the engine and assert outputs match last known-good values whenever engine code changes.
  • When CR31 lacks real data for a parameter, default to CR30's value and flag it explicitly (e.g., "cg_height_m": CR30["cg_height_m"], # TODO: CR31 CAD not finalized).
  • Separate "track/environment" params (surface mu, elevation, temperature) from "vehicle" params so the same car can be run across multiple events.
  • Hardcoding car identity in logic (if name == "Cr30") instead of adding a missing parameter — this guarantees the sim breaks or silently misbehaves for future cars (CR32, CR33...).
  • Copy-pasting CR30's full parameter block for CR31 and hand-editing — causes silent drift when CR30 data gets corrected later. Use inheritance/override instead.
  • Mixing units (mm vs m, deg vs rad) between the two cars' data sources — enforce one unit system in the schema.
  • Trusting CR31 outputs at face value — it has no on-track data yet; always present results as "relative to validated CR30 baseline," not absolute truth.
  • Letting the simulation engine know about model years at all — the engine should be car-agnostic; only the parameter files should know "CR30" or "CR31" exists.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
14/15
Workflow
14/15
Examples
15/20
Completeness
18/20
Format
14/15
Conciseness
13/15