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)
编译
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 # 前瞻行号
推荐启动
# 方式 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 写文件即可实时生效:
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 设置, 按默认格式输出。
解决:
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 |