1. 龙芯智能车到底是个什么项目,为什么值得折腾
第一次听说“龙芯智能车”这个概念,很多人脑子里冒出来的第一个问题就是:这不就是拿龙芯的芯片替换掉原来智能车上那颗单片机吗,能有多复杂?我一开始也这么想,直到真正把一块龙芯开发板、一套逐飞开源库和一份内核源码摆在桌面上,才发现这件事的工程量远比想象中大得多。简单来说,这个项目的核心目标是:用基于 LoongArch 架构的龙芯处理器作为主控,跑完整的 Linux 系统,在上面运行逐飞科技提供的开源智能车控制库,从而实现摄像头图像采集、赛道元素识别、电机舵机控制等一整套智能车功能。它解决的根本问题是——让智能车从传统的单片机裸机开发模式,升级到拥有完整操作系统、支持多任务调度、能跑复杂视觉算法的嵌入式 Linux 平台。
适合看这篇内容的人有三类:第一类是在准备全国大学生智能车竞赛、想尝试龙芯平台赛道的同学;第二类是对 LoongArch 架构感兴趣、想找一个完整项目来练手内核编译和交叉编译的嵌入式爱好者;第三类是做嵌入式 Linux 开发、需要一套从内核到应用层完整链路参考的工程师。不管你之前有没有接触过龙芯,只要你会基本的 Linux 命令、能看懂 C 语言,这篇内容里的步骤和思路你都能跟下来。
我踩过的第一个坑就是低估了“内核编译”这四个字的分量。在 x86 上编译内核,你随便找台 Ubuntu 机器,装几个依赖,make defconfig 然后 make -j 就完事了。但龙芯不一样,你需要交叉编译工具链、需要匹配的 LoongArch 内核源码树、需要针对智能车外设做设备树裁剪,还要把逐飞库依赖的各种驱动模块编进去。这一套流程走下来,没有一份靠谱的攻略,光靠官方文档能把你绕晕。所以我把整个从零搭建的过程拆成了几个核心环节,每个环节都讲清楚为什么这么做、怎么做、做完怎么验证。
2. 整体方案设计与核心思路拆解
2.1 为什么选龙芯加 Linux 而不是继续用单片机
传统智能车方案里,主流主控是各种单片机或者 RTOS 平台,比如常见的 32 位 MCU。这类方案的好处是实时性强、开发简单、功耗低。但缺点也很明显:算力有限,跑个简单的图像二值化就到头了,想上卷积神经网络或者复杂的赛道元素识别算法基本不可能。龙芯处理器虽然主频不算特别高,但它是完整的 64 位架构,有 MMU、有 Cache 层级、支持完整的 Linux 内存管理,这意味着你可以跑 OpenCV、可以跑轻量级推理框架、可以用多线程做图像处理和控制的并行。
另一个关键考量是生态延续性。逐飞开源库本身是跨平台的,它把底层硬件操作抽象成了一组统一的接口。你在单片机上用的那些摄像头驱动、电机 PWM 控制、编码器读取函数,在 Linux 平台上可以通过逐飞提供的 Linux 适配层来调用。这就意味着你不需要从零写一套控制逻辑,而是把精力集中在算法调优和系统集成上。从项目管理的角度看,这能省掉至少百分之六十的底层开发时间。
2.2 逐飞开源库在龙芯平台上的角色定位
逐飞开源库在这个项目里扮演的是“硬件抽象层加算法中间件”的角色。它向下封装了龙芯开发板上各种外设的寄存器操作,向上提供了统一的 API 给应用层调用。具体来说,它包含几个核心模块:摄像头采集模块负责从 MIPI 或者 DVP 接口读取图像数据并转换成灰度或 RGB 格式;图像处理模块提供二值化、边缘提取、中线拟合等常用算法;控制模块负责把计算出的偏差转换成舵机转向角和电机占空比;通信模块则处理调试串口、无线模块的数据收发。
这里有一个很重要的设计决策:逐飞库在 Linux 下是以字符设备驱动加用户态库的形式存在的。摄像头、电机这些外设的底层操作被写成了内核模块,编译进内核或者以 .ko 形式动态加载。用户态则通过 ioctl 或者 mmap 来和驱动交互。这样做的好处是既能保证实时性要求高的操作在内核态完成,又能让算法逻辑在用户态灵活修改,不用每次改个 PID 参数都重新编译内核。
2.3 内核编译在整个链路中的位置
很多人会问:我能不能直接用龙芯官方提供的预编译内核,跳过编译这一步?答案是:对于点亮开发板、跑个 Hello World 来说可以,但对于智能车项目来说不行。原因有三个:第一,官方内核通常没有开启智能车需要的外设驱动,比如特定的 PWM 控制器、正交编码器接口、摄像头传感器驱动;第二,官方内核的设备树是按照通用开发板配置的,引脚复用和时钟树没有针对智能车的 PCB 做优化;第三,逐飞库的某些内核模块需要特定的内核版本和配置选项才能编译通过。
所以内核编译不是可选项,而是必选项。你需要拿到龙芯官方的内核源码树,打上逐飞提供的补丁,用正确的 config 文件配置,然后用交叉编译工具链编译出 Image 和 dtb 文件,最后烧录到开发板上。这个过程听起来线性,但实际操作中每个环节都有坑,后面我会逐个拆解。
3. 核心细节解析与实操要点
3.1 交叉编译工具链的选择与验证
龙芯的 LoongArch 架构需要专用的交叉编译工具链。目前主流的选择是龙芯官方维护的 GCC 工具链,或者用 crosstool-ng 自己构建。我建议直接用官方预编译好的工具链,因为自己构建不仅耗时,还容易在 glibc 版本和内核头文件版本上出问题。
工具链下载下来之后,第一件事是验证它能不能正常工作。你可以写一个最简单的 C 程序,用交叉编译器编译,然后用 file 命令查看生成的二进制文件架构信息。如果显示的是 LoongArch 相关的标识,说明工具链没问题。这一步看似简单,但我见过太多人卡在这里,原因是环境变量没配好,或者下载的工具链版本和内核源码版本不匹配。
注意:工具链的 glibc 版本必须和根文件系统里的 glibc 版本一致或者兼容。如果你用的根文件系统是 Buildroot 生成的,里面用的是 musl libc,那你就需要用 musl 版本的交叉工具链,否则编译出来的程序在板子上跑不起来。
3.2 内核源码树的获取与补丁管理
龙芯内核源码可以从官方仓库获取,建议选择长期支持版本,比如 5.10 或者 6.1 系列。拿到源码后,不要急着编译,先做两件事:第一,创建一个独立的分支来管理你的修改,这样后续更新或者回滚都方便;第二,把逐飞提供的补丁文件按顺序打上去。补丁通常包含设备树修改、驱动添加、配置选项调整这几类。
打补丁的时候有个技巧:用git am而不是patch命令。git am会保留补丁的提交信息,方便你追溯每个修改的来源。如果某个补丁打失败了,git am会停下来让你手动解决冲突,解决完之后git am --continue继续。这比patch命令的体验好太多。
3.3 设备树配置的关键参数
设备树是 Linux 内核识别硬件的依据,对于智能车来说,设备树里需要描述的东西包括:摄像头接口的引脚复用和时钟频率、PWM 控制器的通道分配、编码器接口的 GPIO 中断配置、串口波特率和引脚映射。这些参数必须和你的硬件原理图一一对应,错一个引脚,对应的外设就工作不了。
我建议的做法是:先拿官方开发板的设备树文件作为基础,然后对照智能车 PCB 的原理图,逐个修改外设节点。修改的时候不要一次改太多,改一个外设就编译一次设备树,烧录进去用dmesg看内核有没有正确识别。比如你改了摄像头接口,烧录后dmesg | grep camera应该能看到摄像头驱动加载成功的日志。如果没有,就回去检查引脚复用寄存器的配置。
3.4 内核配置选项的取舍
内核配置是很多人头疼的地方,因为选项太多了。我的策略是:以逐飞提供的 defconfig 为基础,然后用make menuconfig做微调。必须确认开启的选项包括:对应的摄像头传感器驱动、PWM 子系统、GPIO 子系统、I2C 子系统、SPI 子系统、USB 主机支持、网络协议栈、文件系统支持。另外,为了调试方便,建议把CONFIG_DEBUG_INFO打开,这样后续用 gdb 调试内核模块的时候能看符号信息。
有一个容易忽略的选项是内核抢占模式。智能车对控制的实时性有要求,建议把抢占模式设置为CONFIG_PREEMPT,这样内核在大多数地方都可以被抢占,能降低控制环路的延迟。但注意不要用CONFIG_PREEMPT_RT,因为实时补丁和某些外设驱动可能有兼容性问题,而且配置起来更复杂。
4. 实操过程与核心环节实现
4.1 环境搭建与依赖安装
我用的宿主机是 Ubuntu 20.04,这个版本比较稳定,各种依赖包也齐全。首先安装必要的工具:
sudo apt update sudo apt install build-essential git flex bison libssl-dev \ libncurses-dev bc rsync kmod cpio python3-dev \ device-tree-compiler u-boot-tools这些包分别对应:编译工具链、Git 版本管理、词法语法分析器、SSL 库、菜单配置界面、BC 计算器、文件同步工具、内核模块工具、cpio 归档工具、Python 开发头文件、设备树编译器、U-Boot 工具。缺一个都可能在编译过程中报错。
然后把交叉编译工具链解压到/opt目录下,并添加到 PATH 环境变量:
export PATH=/opt/loongarch-toolchain/bin:$PATH export CROSS_COMPILE=loongarch64-unknown-linux-gnu- export ARCH=loongarch这三个环境变量很关键:PATH 让系统能找到编译器,CROSS_COMPILE 指定编译器前缀,ARCH 告诉内核构建系统目标架构是 LoongArch。建议把这些写到~/.bashrc里,免得每次开新终端都要重新设置。
4.2 内核源码获取与补丁应用
从官方仓库克隆内核源码:
git clone --depth=1 -b loongarch-5.10 https://github.com/loongson/linux.git cd linux git checkout -b smartcar然后应用逐飞补丁。假设补丁文件放在~/patches目录下:
git am ~/patches/0001-add-camera-driver.patch git am ~/patches/0002-add-pwm-driver.patch git am ~/patches/0003-update-devicetree.patch如果某个补丁打失败,Git 会提示冲突文件。手动编辑冲突文件,解决完之后:
git add <冲突文件> git am --continue补丁全部应用成功后,用git log --oneline看一下提交历史,确认每个补丁都正确合入。
4.3 内核配置与编译
先用逐飞提供的 defconfig 生成基础配置:
make loongson_smartcar_defconfig然后打开菜单配置做微调:
make menuconfig在菜单里重点检查这几个地方:Device Drivers 下面的 Camera 相关选项、PWM 相关选项、GPIO 相关选项;Kernel Features 下面的 Preemption Model 选择 Preemptible Kernel;File Systems 下面确保 ext4 和 squashfs 都选上。
配置完成后开始编译:
make -j$(nproc) Image dtbs modules-j$(nproc)表示用所有 CPU 核心并行编译,能大幅缩短编译时间。编译完成后,在arch/loongarch/boot/目录下会生成Image文件,在arch/loongarch/boot/dts/目录下会生成.dtb文件,内核模块则分散在各个子目录里。
4.4 烧录与启动验证
把编译好的 Image 和 dtb 文件拷贝到 TF 卡或者通过 tftp 传输到开发板。具体的烧录方式取决于你的开发板型号,常见的有两种:一种是通过 USB 烧录工具把镜像写到 eMMC 或者 SPI Flash;另一种是把 TF 卡插到读卡器上,用 dd 命令写入。
烧录完成后上电启动,通过串口终端观察启动日志。如果一切正常,你会看到内核解压信息、设备树加载信息、各个驱动初始化信息,最后进入登录提示符。这时候用dmesg | grep -i error检查有没有报错,用ls /dev/video*确认摄像头设备节点是否创建成功,用ls /sys/class/pwm/确认 PWM 控制器是否注册。
4.5 逐飞库的交叉编译与部署
内核跑起来之后,下一步是编译逐飞库。逐飞库通常提供一个 CMake 或者 Makefile 构建系统,你需要修改里面的交叉编译配置,指定 CROSS_COMPILE 和 sysroot 路径。sysroot 就是根文件系统的路径,里面包含了目标平台的头文件和库文件。
编译完成后,把生成的动态库和可执行文件拷贝到开发板的根文件系统里。如果是通过网络文件系统挂载根文件系统的方式,直接拷贝到 NFS 共享目录就行。如果是烧录到本地存储,需要用 scp 或者 U 盘拷贝。
部署完成后,运行逐飞库提供的测试程序,比如摄像头采集测试、电机转动测试。如果摄像头能出图、电机能转,说明整个链路已经打通了。
5. 常见问题与排查技巧实录
5.1 内核编译报错速查表
| 报错信息 | 可能原因 | 解决方法 |
|---|---|---|
No rule to make target 'Image' | ARCH 环境变量没设置 | 执行export ARCH=loongarch |
CROSS_COMPILE not found | 工具链路径不对 | 检查 PATH 和 CROSS_COMPILE 变量 |
fatal error: openssl/ssl.h | 缺少 libssl-dev | sudo apt install libssl-dev |
dtc: command not found | 缺少设备树编译器 | sudo apt install device-tree-compiler |
undefined reference to ... | 库版本不匹配 | 检查工具链和 sysroot 的 glibc 版本 |
5.2 设备树配置错误的排查思路
设备树配错了,最典型的表现是驱动加载了但设备不工作。比如摄像头驱动dmesg里显示 probe 成功,但/dev/video0打不开。这时候你需要检查设备树里摄像头节点的status属性是不是okay,reg属性里的 I2C 地址对不对,pinctrl引用的引脚组有没有定义。
另一个常见问题是引脚复用冲突。比如你把某个引脚配成了 PWM 输出,但另一个驱动又把它配成了 GPIO 输入,结果就是两个驱动都工作不正常。排查方法是查看/sys/kernel/debug/pinctrl/下面的引脚状态,确认每个引脚只被一个功能占用。
5.3 逐飞库运行时的典型问题
逐飞库在 Linux 下运行时,最常见的问题是权限不足。比如操作 GPIO 需要 root 权限,或者访问/dev/video0需要 video 组的权限。解决方法有两种:一种是用sudo运行程序,另一种是配置 udev 规则,给特定设备节点设置合适的权限和所属组。
还有一个问题是实时性不达标。如果你发现控制环路有抖动,先检查内核抢占模式是不是PREEMPT,然后检查有没有其他高优先级任务在抢 CPU。可以用chrt命令把智能车控制程序设置为实时调度策略,比如chrt -f 50 ./smartcar,这样能显著降低控制延迟。
5.4 实操心得与避坑建议
第一个心得:每次只改一个变量。不管是改设备树、改内核配置还是改逐飞库的代码,每次只改一个地方,然后编译、烧录、验证。如果一次改多个地方,出了问题你根本不知道是哪个改动导致的。
第二个心得:保留可回滚的版本。内核源码用 Git 管理,根文件系统用镜像备份,逐飞库的代码也用 Git 管理。每次验证通过的版本打一个 tag,这样出了问题可以快速回滚到上一个稳定版本。
第三个心得:串口日志是你的最好朋友。把内核的 loglevel 调高,让dmesg输出尽可能多的信息。遇到问题先看日志,大部分错误日志里都有明确的提示。如果日志不够详细,可以在驱动代码里加printk,重新编译驱动模块加载测试。
第四个心得:不要忽视电源问题。智能车上的电机启动瞬间电流很大,可能导致龙芯开发板供电不稳,表现为系统随机重启或者外设工作异常。建议给核心板和控制板分别供电,或者在电源输入端加一个大电容做缓冲。
6. 性能调优与后续扩展方向
6.1 图像处理流水线的优化
摄像头采集回来的原始图像数据量很大,如果每帧都做完整的二值化和边缘提取,CPU 占用率会很高。优化的思路是:把图像处理拆成多个阶段,用流水线的方式并行处理。比如第一帧做采集的时候,第二帧做二值化,第三帧做中线拟合。在 Linux 下可以用多线程实现,一个线程负责采集,一个线程负责处理,线程之间用环形缓冲区传递数据。
另一个优化点是降低图像分辨率。智能车赛道识别不需要很高的分辨率,把摄像头输出配置成 QVGA 或者更低,能大幅减少数据量和处理时间。如果摄像头支持硬件二值化或者 ROI 裁剪,一定要用上,这比软件处理快得多。
6.2 控制环路的实时性保障
控制环路的实时性直接决定了智能车能不能跑高速。在 Linux 下保障实时性的手段包括:使用PREEMPT内核、把控制线程设置为实时调度策略、用mlockall锁定内存防止换页、关闭不需要的中断和后台服务。实测下来,经过这些优化,控制环路的抖动可以控制在几百微秒以内,对于智能车来说足够了。
6.3 从龙芯智能车延伸到其他应用
这套龙芯加 Linux 加逐飞库的架构,其实不只能用来做智能车。你把摄像头换成其他传感器,把电机控制换成其他执行器控制,就能快速搭建出各种嵌入式视觉控制项目。比如自动分拣系统、智能云台、巡线机器人等等。核心思路是一样的:用 Linux 提供算力和多任务能力,用逐飞库提供硬件抽象和算法中间件,你只需要关注业务逻辑。
我在实际项目中发现,龙芯平台的生态虽然不如 ARM 那么丰富,但基本的开发工具和库都很齐全。而且 LoongArch 架构的文档比较完善,遇到问题查手册基本都能解决。对于想学习嵌入式 Linux 完整开发链路的同学来说,龙芯智能车是一个非常好的练手项目,因为它涵盖了从内核编译、设备树配置、驱动开发到应用层算法实现的全部环节。