# AGENTS.md — SmartCar Demo ## Project overview LoongArch64 embedded autonomous smart car using OpenCV + NanoDet V10 model. Runs on-device (Loongson board) with cross-compilation from x86 Linux. ## Build ```sh # On the build host (x86 Linux): mkdir build && cd build cmake .. make ``` - **Cross-compilation target**: LoongArch64 (`-march=loongarch64`) - **Toolchain**: `/opt/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.6` - **C++ standard**: 17 - **OpenCV path** (device-side): `/mnt/d/PPPProgram/smartcar/opencv_device/` - `cross.cmake` is included **before** `cmake_minimum_required` — this is intentional. ## Project structure | Directory | Purpose | |-----------|---------| | `src/` | Source → compiled into `common_lib` (static lib) | | `lib/` | Public headers only (no .cpp) | | `main/` | Entry point → executable `smartcar_demo1` | | `docs/` | Architecture docs (`ARCHITECTURE.md` is the design reference) | | `build/` | CMake build output (gitignored) | **Executable name**: `smartcar_demo1` (the CMake project name `smartcar_demo2` is different — don't assume they match). ## Excluded modules These source files exist but are **excluded from build**: - `vl53l0x.cpp` — laser ranging module, hardware not connected - `zebra_detect.cpp` — classic zebra-crossing detection, replaced by NanoDet model - `encoder.cpp` — referenced in exclusion list but file does **not** exist in repo ## Device-side operation The onboard binary is controlled via text files in the working directory — no CLI args. ```sh # On device: sh ctl.sh init # initialize GPIO/PWM pins sh ctl.sh start # start the demo sh ctl.sh stop # stop and zero PWM ``` `ctl.sh` sets default config values by writing to files like `./speed`, `./kp`, `./steer_gain`, etc. The running binary reads these files. `start.sh` uses `LD_PRELOAD=./gpio_fix_final.so` (GPIO workaround). ## Configuration system All config is via single-value text files in the working directory: - `./speed` (double) — target speed - `./start` (0/1) — motor enable switch - `./debug` (0/1) — when 1, reloads all config files every 7 frames - `./showImg` (0/1) — LCD display toggle - `./deadband`, `./steer_gain`, `./center_bias` — steering tuning - `./foresee` — look-ahead row for steering - `./zebrasee` — zebra crossing near-threshold - `./destfps` — target framerate - `./kp`, `./ki`, `./kd` — PID gains (currently **not used**; motor is open-loop) - `./mortor_kp`, `./mortor_ki`, `./mortor_kd` — motor encoder PID (spelling `mortor` is intentional) ## Architecture 9-step per-frame pipeline in `CameraHandler()` (`src/camera.cpp`): 1. Capture frame (640×480 MJPEG → BGR) 2. Optionally save image 3. Vision line tracking (80×60 downscale → HSV+Otsu → floodFill → mid_line[60]) 4. Model inference every 2nd frame (NanoDet V10, 4 classes: cone/red/green/zebra) 5. Zebra crossing state machine (NORMAL→STOP(4s)→COOLDOWN(5s)) 6. Steering servo control (PWM, deadband-filtered) 7. Motor control (open-loop duty cycle, curve-based slowdown in turns) 8. LCD rendering (`/dev/fb0` mmap) 9. FPS logging **Key detail**: Motor control is **open-loop** — `MotorController::updateSpeed()` and encoder PID exist in code but are never called from the main loop. Only `updateduty()` (direct PWM) is used. Currently **cones (cls=0), red lights (cls=1), and green lights (cls=2)** are detected by the model but ignored in decision logic. ## Style and conventions - C++ files have **no copyright headers** and **minimal comments** — comments are ascii-box-style block comments when present - Header guards use the form `#ifndef FILENAME_H_` / `#define FILENAME_H_` - Global state uses `extern` globals (e.g., `g_cfg`, `g_steer_deviation`, `g_boxes`) - `lib/` contains .h files only; `src/` contains .cpp files only - No unit tests, no CI, no linting — this is embedded code tested on-device