# SmartCar Demo — 使用手册 & 踩坑记录

## 项目概述

龙芯 2K0300 智能车卖家 Demo，简易架构：`sysfs` 直接控制 GPIO/PWM + mmap 读编码器 + OpenCV 巡线。

**赛道**：蓝底黄线边界，灰度路面
**驱动**：前轮舵机转向 + 后轮双电机差速辅助

---

## 硬件信息

| 部件 | 型号/规格 | 说明 |
|------|----------|------|
| 主控 | 龙芯 2K0300 (LA264, 1.2GHz) | LoongArch64 |
| 摄像头 | USB UVC | 320×240 MJPG, `/dev/video0` |
| 显示器 | SPI LCD | `/dev/fb0`, RGB565, 160×128 (实际) |
| 舵机 | 标准 PWM 舵机 | pwmchip1/pwm0, 3ms 周期, 1.52ms 中位 |
| 电机驱动 | DRV8701E (双路) | pwmchip8/pwm1→右, pwmchip8/pwm2→左 |
| 编码器 | 硬件脉冲计数 | `/dev/mem` mmap 基址 `0x1611B000` |
| IMU | JY62 六轴陀螺仪 | `/dev/ttyS1`, 115200bps |
| ToF 测距 | VL53L0X | `/dev/stmvl53l0x_ranging` |
| 音箱 | wonderEcho | I2C-2, 地址 0x6E (有应答, 无驱动) |

### 关键引脚映射

| 功能 | GPIO / PWM | 备注 |
|------|-----------|------|
| 电机使能 | GPIO73 | 高有效, 1=启动, 0=紧急制动 |
| 左电机 IN1 (正转) | GPIO12 + pwmchip8/pwm2 | DRV8701 IN/IN 模式 |
| 左电机 IN2 (反转) | GPIO13 | **必须锁 0** |
| 右电机 PWM | pwmchip8/pwm1 | 方向脚 GPIO67 是哑脚 |
| 左编码器 | 通道0 + GPIO75 | 方向检测 |
| 右编码器 | 通道3 + GPIO72 | 方向检测, 极性 -1 |
| 舵机 | pwmchip1/pwm0 | 3ms, 1.31~1.73ms (±7 刻度) |

---

## 构建环境

### 交叉编译工具链

```
路径: /opt/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.6
编译器: loongarch64-linux-gnu-g++ (GCC 8.3)
架构: -march=loongarch64
```

### OpenCV

```
路径: /home/ilikara/loongson/opencv-4.11.0/loongson
版本: 4.11.0 (必须匹配设备上的 libopencv)
```

### 编译

```bash
cd smartcar1
mkdir -p build && cd build
cmake ..    # CROSS_COMPILE=1 在 cross.cmake 中设置
make -j$(nproc)
```

产物: `build/main/smartcar_demo` (约 90KB)

**Windows 上不可编译！** 必须在 Linux 下用交叉工具链。可在 WSL Ubuntu 中用 `g++ -std=c++17 -fsyntax-only` 做部分语法检查。

---

## 部署与启动

### 板端目录结构

```
/home/root/smartcar/
├── smartcar_demo         # 主程序
├── ctl.sh                # 启停脚本
├── gpio_fix_final.so     # GPIO74→73 重定向 (LD_PRELOAD)
├── start.sh              # 推荐启动脚本 (已内置 LD_PRELOAD)
├── showImg               # LCD 预览开关
├── start                 # 车控启停
├── speed                 # 目标速度 (占空比)
├── deadband              # 舵机死区
├── steer_gain            # 转向增益
├── kp / ki / kd          # 舵机 PID
├── mortor_kp / mortor_ki / mortor_kd  # 电机 PID
└── foresee               # 前瞻行号
```

### 推荐启动

```bash
# 方式 1: 使用包装脚本 (已固化 LD_PRELOAD)
sh /home/root/smartcar/start.sh

# 方式 2: 手动
LD_PRELOAD=/home/root/smartcar/gpio_fix_final.so /home/root/smartcar/smartcar_demo
```

---

## 运行时参数热调

**无需重新编译**，直接 `echo` 写文件即可实时生效：

```bash
echo 11  > /home/root/smartcar/speed        # 目标占空比 (0~100)
echo 8   > /home/root/smartcar/deadband      # 舵机死区 (像素)
echo 1.5 > /home/root/smartcar/steer_gain    # 转向增益
echo 3.5 > /home/root/smartcar/kp            # 舵机 P
echo 0.3 > /home/root/smartcar/ki            # 舵机 I
echo 2.0 > /home/root/smartcar/kd            # 舵机 D
echo 40  > /home/root/smartcar/foresee       # 前瞻行 (0~120, 越大看越远)
echo 1   > /home/root/smartcar/showImg       # 1=LCD 预览, 0=关闭
echo 1   > /home/root/smartcar/start         # 1=启动, 0=停止
echo 1   > /home/root/smartcar/saveImg       # 保存当前帧到 ./image/
```

---

## 踩坑记录 (关键！)

### 1. GPIO74 与 LCD 驱动冲突 — 程序无法启动

**现象**: `std::runtime_error: Failed to open GPIO value file`

**原因**: Demo 二进制硬编码 GPIO_EN=74, 但 GPIO74 被 LCD 驱动 `fb_st7735r` 占用 (内核级, 无法释放)。

**解决**: 编译 `gpio_fix_final.so`, 用 LD_PRELOAD 劫持 `open()` 系统调用, 将对 gpio74 的访问重定向到 gpio73。启动时必须带 `LD_PRELOAD`。

**源代码修复**: `control.cpp` 中 `GPIO mortorEN(73)` — 如果从源码编译, 直接改成 GPIO73, 无需 LD_PRELOAD。

### 2. 右电机方向硬件锁死 — 差速失效

**现象**: 右轮只能单向旋转, 代码写 GPIO67 无效。

**原因**: DRV8701 的右路方向脚 (原本应对应某个 GPIO) 在 PCB 上直接拉高/拉低了。代码中的 GPIO67 是哑脚, 实际不控制任何硬件。

**解决**: 物理剪线交换右电机电源线 (M+/M- 对调), 代码中 GPIO67 作为占位符不动。这样一来, 代码输出正占空比时电机实际反向。

### 3. GPIO13 实测映射 — 文档与实物不符

**文档称**: GPIO12=左IN1, GPIO13=右IN1。
**实测**: GPIO13 控制的是**左电机 IN2 (反转)**, 不是右电机方向!

**影响**: 若 GPIO13=1, 左电机 IN1=1 IN2=1 → 制动状态, 左轮抱死。

**解决**: 代码中显式 `leftIn2.setValue(0)` 把 GPIO13 锁 0, 确保左电机只有正转 (IN1=1, IN2=0)。

### 4. 摄像头分辨率不稳定

**现象**: 摄像头偶尔返回 640×480 而非 320×240。

**原因**: 某些 UVC 摄像头不响应 `CAP_PROP_FRAME_WIDTH/HEIGHT` 设置, 按默认格式输出。

**解决**: 
```cpp
cap.open(0, cv::CAP_V4L2);                               // 强制 V4L2 后端
cap.set(cv::CAP_PROP_FRAME_WIDTH, 320);                   // 设宽
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 240);                  // 设高
cap.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M','J','P','G')); // MJPG
```
即使摄像头不遵从, `resize()` 也能在软件层统一到 80×60。

### 5. 编码器必须 root 权限

**现象**: `ENCODER` 构造失败, mmap 报错。

**原因**: 编码器通过 `/dev/mem` 直接映射 PWM 硬件寄存器 (物理地址 `0x1611B000`), 需要 root。

**解决**: 必须以 root 运行程序。`sudo` 或直接以 root 登录。

### 6. IMU 端口纠正

**文档称**: IMU 在 `/dev/ttyS2`。
**实测**: IMU 在 `/dev/ttyS1` (115200bps)。

### 7. 网络热插拔导致断连

**现象**: 拔掉网线再插上, SSH 连不上。

**原因**: `/etc/network/interfaces` 中静态 eth0 配置与 connman 冲突。

**解决**: 删除 `/etc/network/interfaces` 中的 eth0 段, 让 connman 全权管理有线网络。修改后即使先开机后插网线也能自动获取 IP。

### 8. GCC 14.2.0 工具链 ABI 不兼容

**现象**: 用新版 GCC 14.2.0 (当前 WSL 环境) 编译的程序在设备上运行报 `GLIBCXX_3.4.30 not found`。

**原因**: GCC 14.2.0 的 libstdc++ ABI 与设备上的 glibc 2.28 / libstdc++ 6.0.25 不兼容。

**解决**: 
- **必须使用原始 GCC 8.3 rc1.6 工具链**。路径: `/opt/loongson-gnu-toolchain-8.3-x86_64-loongarch64-linux-gnu-rc1.6`
- 可用 C 编译器 (`gcc` 而非 `g++`) + 显式 `-march=la64v1.0` + 指定 `-Wl,-dynamic-linker=...` 编译纯 C 程序

### 9. 视觉管线分辨率选择

**Demo 内部处理**: 摄像头 320×240 → 缩放至显示分辨率 (160×120) → 再除以 `calc_scale=2` → 最终 **80×60** 做 HSV 二值化 + 洪泛填充 + 搜线。

**关键**: 80×60 是最优平衡点——再大 Otsu 不稳, 再小细节丢失。

### 10. 启动顺序 & 竞态

- GPIO 必须先 export + 设方向, 再启动程序
- 舵机 PWM 必须先设 period + enable, 才能写 duty_cycle
- 程序需要几秒初始化 IMU (校准 200 样本 × 5ms = 1 秒)

### 11. PWM 频率

| PWM | 频率 | 周期 | 用途 |
|-----|------|------|------|
| pwmchip8/pwm1,2 | 20kHz | 50000ns | 电机驱动 (DRV8701 需 ≥ 20kHz) |
| pwmchip1/pwm0 | 333Hz | 3000000ns | 舵机 (标准 3ms 周期) |

### 12. 算力实测 (2K0300 CPU)

| 精度 | 算力 | 备注 |
|------|------|------|
| FP32 | 0.43 GFLOPS | 无 SIMD, 纯标量 |
| INT8 | 0.44 GOPS | 几乎无加速, 与 FP32 持平 |
| INT4 | 0.24 GOPS | 比 INT8 更慢 (位操作开销) |

**结论**: 2K0300 没有 SIMD/NPU, 纯 CPU 推理极弱。模型需 ≤15M MACs 才能跑 30+ FPS。只适合微型 CNN (2-3 层, 32×32 输入, 8-16 通道) 做简单分类。

---

## Demo 程序列表

| Demo | 功能 |
|------|------|
| `main/` | **完整自动驾驶巡线** — CameraTimer + MortorTimer + 文件热调 |
| `demo1/` | 键盘 WASD 遥控 — 非阻塞终端 |
| `image_test/` | 离线图像处理调试器 — 方向键翻页, 7 种显示模式 |
| `udp_receive/` | UDP 遥控 (端口 8888) + Python 滑块 GUI |
| `encoder_demo/` | 编码器原始读数测试 |
| `jy62_demo/` | JY62 IMU 六轴数据读取 |
| `key_demo/` | 9 键 GPIO 状态面板 |
| `wonderEcho_demo/` | I2C smbus 读写实验 |
| `opencv_demo1/` | USB 摄像头 → framebuffer 直显 + FPS |
| `opencv_demo2/` | 3D 旋转立方体软件渲染 |
| `opencv_demo3/` | Haar 级联人脸检测 |
| `framebuffer_demo/` | 纯软件 8×8 字体 + 几何绘图 |
| `vl53l0x_test/` | VL53L0X 测距传感器 (ST 官方) |

---

## 文件说明

| 文件 | 用途 |
|------|------|
| `CMakeLists.txt` | 构建配置: `src/` → 静态库, 各 demo 独立链接 |
| `cross.cmake` | 交叉工具链切换 (`CROSS_COMPILE=1`) |
| `lib/` | 头文件: GPIO, PWM, PID, Motor, Encoder, Camera, Timer, serial 等 |
| `src/` | 实现文件: 每个 `.cpp` → 编译进 `libcommon_lib.a` |
