1. 项目概述
这个项目是基于WuwuSama_Icar_Project的一个扩展工程框架,主要针对久久派(LoongArch64架构)开发板进行优化。作为一名长期从事嵌入式开发的工程师,我发现很多初学者在搭建交叉编译环境时都会遇到各种问题,特别是当项目需要集成多个第三方库时。这个框架通过CMake和Ninja构建系统,实现了以下核心功能:
- 动态库的灵活添加和管理
- 多可执行文件的并行编译输出
- 编译完成后自动部署到开发板
- 测试代码的模块化管理
我在实际使用中发现,这套框架特别适合需要频繁修改和测试的中小型嵌入式项目。它解决了传统嵌入式开发中常见的几个痛点:编译速度慢、部署流程繁琐、测试代码管理混乱等。
2. 环境准备与工具链配置
2.1 基础环境搭建
首先需要准备开发环境,这里我推荐使用Ubuntu 20.04或更高版本。安装必要的工具链:
bash复制sudo apt-get update
sudo apt-get install -y git cmake ninja-build
Ninja是一个小巧但高效的构建系统,相比传统的make,它在处理大型项目时能显著提升编译速度。实测在同样的硬件环境下,使用Ninja可以将编译时间缩短30%-40%。
2.2 交叉编译工具链配置
久久派开发板采用LoongArch64架构,需要配置对应的交叉编译工具链。工具链可以从龙芯官方获取,安装到/opt目录下:
bash复制sudo tar -xzf loongson-gnu-toolchain-13.2.tar.gz -C /opt/
配置环境变量,确保工具链可用:
bash复制export PATH=/opt/loongson-gnu-toolchain-13.2/bin:$PATH
验证工具链是否安装成功:
bash复制loongarch64-unknown-linux-gnu-gcc --version
2.3 第三方库交叉编译
项目依赖多个第三方库,包括OpenCV、jsoncpp、ncnn和libserial。这些库需要预先交叉编译好。以OpenCV为例,交叉编译步骤如下:
- 下载OpenCV源码
- 创建build目录并配置CMake:
bash复制mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../99pi.cmake \
-DCMAKE_INSTALL_PREFIX=../install \
-DBUILD_LIST=core,imgcodecs,imgproc,videoio \
..
- 编译并安装:
bash复制make -j$(nproc) && make install
其他库的编译过程类似,需要注意每个库可能有特定的编译选项需要配置。建议将编译好的库统一存放在项目的cross_lib目录下,便于管理。
3. 工程框架设计与实现
3.1 CMake预设配置
项目使用CMake的Presets功能来简化配置流程。在根目录创建CMakeUserPresets.json文件:
json复制{
"version": 8,
"configurePresets": [
{
"name": "loongarch64",
"displayName": "LoongArch64 Cross Compile",
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_TOOLCHAIN_FILE": "99pi.cmake",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
}
}
]
}
这个配置实现了:
- 使用Ninja作为构建系统
- 指定交叉编译工具链文件(99pi.cmake)
- 统一管理构建输出目录
- 支持Debug/Release等多种构建类型
3.2 工具链文件详解
99pi.cmake文件定义了交叉编译的关键参数:
cmake复制# 基本系统信息
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR loongarch64)
# 工具链路径
set(TOOLCHAIN_DIR /opt/loongson-gnu-toolchain-13.2)
# 编译器路径
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/bin/loongarch64-unknown-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/bin/loongarch64-unknown-linux-gnu-g++)
# 系统根目录设置
set(CMAKE_FIND_ROOT_PATH "${TOOLCHAIN_DIR}")
# 查找规则设置
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
这个配置确保了:
- 编译器能够正确识别目标架构
- 头文件和库文件的查找路径正确
- 不会误用宿主机的工具链
3.3 多可执行文件管理
传统嵌入式项目通常只有一个主程序,但在开发过程中我们经常需要测试各个模块的功能。这个框架通过以下方式支持多可执行文件:
- 在test目录下组织各个测试程序
- 每个测试程序有独立的源文件
- 共享核心库代码
CMake配置示例:
cmake复制# 设置可执行文件输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/out/bin)
# 添加测试子目录
add_subdirectory(test)
在test/CMakeLists.txt中,为每个测试程序创建独立的构建目标:
cmake复制add_executable(buzzer_test buzzer_test.cc ${COMMON_SOURCES})
target_link_libraries(buzzer_test PRIVATE ${COMMON_LIBS})
这种方式的好处是:
- 各测试程序互不干扰
- 可以单独编译和部署
- 便于持续集成测试
4. 自动化部署实现
4.1 编译后自动上传
传统开发流程中,编译完成后需要手动将程序复制到开发板,这个过程既繁琐又容易出错。本框架通过CMake的add_custom_command实现自动上传:
cmake复制add_custom_command(
TARGET main
POST_BUILD
COMMAND scp ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/main root@192.168.3.99:/root/app/
COMMENT "Auto deploying main to target board..."
)
这个功能需要注意:
- 开发板必须开启SSH服务
- 开发机和开发板需要在同一网络
- 首次连接需要确认主机密钥
4.2 动态库部署
嵌入式程序通常依赖多个动态库,这些库也需要部署到开发板。建议将库文件统一放在开发板的/opt目录下,并设置LD_LIBRARY_PATH:
bash复制export LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH
可以通过CMake自动打包库文件:
cmake复制add_custom_target(deploy_libs
COMMAND rsync -avz ${PROJECT_SOURCE_DIR}/cross_lib/ root@192.168.3.99:/opt/
DEPENDS main
)
5. 项目结构与代码组织
5.1 目录结构设计
合理的目录结构能大大提高项目的可维护性。本项目的核心结构如下:
code复制SmartCar_99Pai_OpenSource/
├── CMakeLists.txt
├── CMakeUserPresets.json
├── 99pi.cmake
├── cross_lib/ # 交叉编译的第三方库
│ ├── opencv/
│ ├── jsoncpp/
│ ├── ncnn/
│ └── libserial/
├── smartCar/ # 主程序代码
│ ├── include/ # 公共头文件
│ ├── wuwu_library/ # 核心功能实现
│ └── main.cc # 主程序入口
└── test/ # 测试程序
├── CMakeLists.txt
├── buzzer_test.cc
├── camera_test.cc
└── ...
这种结构的特点是:
- 第三方库与项目代码分离
- 头文件统一管理
- 测试代码与主程序分离但共享核心库
5.2 CMake模块化管理
大型项目的CMake配置容易变得臃肿。本框架采用模块化方式组织CMake脚本:
- 根CMakeLists.txt负责全局配置
- 每个子目录有自己的CMakeLists.txt
- 公共变量集中定义
例如,test/CMakeLists.txt专注于测试程序的构建:
cmake复制# 定义公共源文件
file(GLOB_RECURSE COMMON_SOURCES
../smartCar/wuwu_library/*.cc
../smartCar/code/*.cc
)
# 定义公共链接库
set(COMMON_LIBS
opencv_core opencv_imgproc
jsoncpp ncnn serial
)
# 添加各个测试程序
add_executable(buzzer_test buzzer_test.cc ${COMMON_SOURCES})
target_link_libraries(buzzer_test PRIVATE ${COMMON_LIBS})
6. 常见问题与解决方案
6.1 交叉编译问题排查
问题1:找不到交叉编译器
解决方案:
- 检查工具链路径是否正确
- 确认环境变量已设置
- 验证编译器是否可执行
问题2:链接时找不到库
解决方案:
- 检查库路径是否在link_directories中指定
- 确认库文件名是否正确
- 使用objdump查看程序依赖的库
bash复制loongarch64-unknown-linux-gnu-objdump -x program | grep NEEDED
6.2 部署问题排查
问题1:SCP上传失败
解决方案:
- 检查网络连接
- 确认开发板SSH服务正常运行
- 检查用户名和密码是否正确
问题2:程序运行时找不到动态库
解决方案:
- 确认库文件已部署到开发板
- 检查LD_LIBRARY_PATH设置
- 使用ldd查看库依赖关系
bash复制ldd /path/to/program
6.3 性能优化建议
- 使用ccache加速编译:
bash复制sudo apt-get install ccache
export CC="ccache loongarch64-unknown-linux-gnu-gcc"
export CXX="ccache loongarch64-unknown-linux-gnu-g++"
- 合理设置CMake的构建类型:
cmake复制set(CMAKE_BUILD_TYPE "Release")
- 启用LTO(链接时优化):
cmake复制set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
7. 扩展与定制
7.1 添加新的第三方库
当项目需要引入新的库时,建议按照以下步骤操作:
- 交叉编译库文件
- 将头文件和库文件放入cross_lib目录
- 在CMake中添加包含路径和链接选项
- 更新部署脚本
例如添加curl库:
cmake复制include_directories(${PROJECT_SOURCE_DIR}/cross_lib/curl/include)
link_directories(${PROJECT_SOURCE_DIR}/cross_lib/curl/lib)
target_link_libraries(main PRIVATE curl)
7.2 支持多种构建配置
通过CMake的option功能可以支持多种构建配置:
cmake复制option(ENABLE_TEST "Build test programs" ON)
option(ENABLE_DEBUG "Enable debug output" OFF)
if(ENABLE_TEST)
add_subdirectory(test)
endif()
这样可以通过命令行参数控制构建行为:
bash复制cmake --preset=loongarch64 -DENABLE_TEST=OFF
7.3 集成持续集成系统
这个框架可以方便地集成到CI系统中。以GitLab CI为例:
yaml复制build:
stage: build
script:
- cmake --preset=loongarch64
- cmake --build out/build/loongarch64
artifacts:
paths:
- out/bin/
8. 实际应用案例
8.1 智能小车控制程序
在这个框架基础上,我开发了一个智能小车控制程序,主要功能包括:
- 电机控制
- 传感器数据采集
- 摄像头图像处理
- 网络通信
通过模块化设计,各个功能可以独立开发和测试:
bash复制# 单独测试电机控制
./out/bin/motor_test
# 单独测试摄像头
./out/bin/camera_test
8.2 基于ncnn的目标检测
框架还集成了ncnn神经网络推理框架,实现了基于YOLOv5的目标检测:
cpp复制// yolov5n_ncnn_main.cc
ncnn::Net net;
net.load_param("yolov5n.param");
net.load_model("yolov5n.bin");
ncnn::Mat in = ncnn::Mat::from_pixels(image.data, ncnn::Mat::PIXEL_BGR, image.cols, image.rows);
ncnn::Extractor ex = net.create_extractor();
ex.input("images", in);
这个示例展示了如何将深度学习模型部署到嵌入式设备。
9. 开发心得与建议
经过多个项目的实践,我总结了以下几点经验:
-
保持构建系统简洁:CMake脚本应该尽可能简单明了,复杂的逻辑可以封装到模块中。
-
重视测试基础设施:好的测试框架能大大提高开发效率,特别是对于嵌入式系统。
-
自动化一切可能的工作:从编译到部署,自动化能减少人为错误。
-
文档要及时更新:特别是当项目结构发生变化时。
-
考虑内存限制:嵌入式设备资源有限,要注意内存使用情况。
对于想要使用这个框架的开发者,我的建议是:
- 先熟悉CMake和Ninja的基本用法
- 从简单的示例开始,逐步增加复杂度
- 充分利用预设配置功能
- 建立自己的库仓库,便于复用
这个框架已经在多个实际项目中得到验证,能够显著提高开发效率,特别是在需要频繁迭代的项目中。