# 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` |