AUTOSAR AP数据序列化与CMake工程构建
📖 AUTOSAR AP实战指南系列 · 文章导航
| AP-01 | AUTOSAR AP开篇 | ✅已发布 |
| AP-02 | ara*框架全景解析 | ✅已发布 |
| AP-03 | SOME/IP协议实战 | ✅已发布 |
| AP-04 | ara::com通信管理 | ✅已发布 |
| AP-05 | ara::exec状态管理 | ✅已发布 |
| AP-06 | ara::log日志框架 | ✅已发布 |
| AP-07 | ara::persistency持久化 | ✅已发布 |
| AP-08 | 数据序列化与CMake | 📍本文 |
| AP-09 | C++17在AP中的应用 | ⏳待发布 |
| AP-10 | 功能安全与信息安全 | ⏳待发布 |
| AP-11 | OTA更新机制 | ⏳待发布 |
| AP-12 | AP综合实战 | ⏳待发布 |
一、引言
图1:AP-08 数据序列化与CMake 思维导图
在智能汽车软件架构中,数据序列化与跨进程通信是核心基础设施。AUTOSAR Adaptive Platform(AP)提供了统一的服务接口定义和多种序列化方案,同时采用现代化的CMake构建系统来管理复杂的工程项目。深入理解这些技术,对于开发高性能的车载应用软件至关重要。
本文将系统讲解AUTOSAR AP中的数据序列化机制,包括SOME/IP、protobuf、FlatBuffers等主流方案的技术特点和使用场景;同时深入剖析CMake构建系统的核心概念、target模型和依赖管理,并通过完整实例帮助读者掌握构建配置技能。
二、数据序列化基础
2.1 序列化的本质
数据序列化是将内存中的数据结构转换为字节流的过程,以便于网络传输或持久化存储。在车载环境中,不同的ECU可能运行在不同的硬件平台上,使用不同的编程语言,数据序列化是实现异构系统间通信的桥梁。
序列化的核心挑战在于如何在紧凑性(传输效率)、兼容性(向前向后兼容)和易用性(开发体验)之间取得平衡。不同的序列化方案在这三个维度上各有侧重,适用于不同的应用场景。
从协议栈的角度看,数据序列化位于通信栈的应用层之下。当上层应用需要发送一条消息时,首先调用序列化库将数据结构编码为字节流,然后由传输层(如TCP/IP、SOME/IP)负责将字节流从一端传输到另一端,接收端则通过反序列化恢复原始数据结构。
2.2 AUTOSAR AP中的序列化需求
AUTOSAR AP服务于自动驾驶、座舱娱乐等高性能计算场景,这些场景的特点是计算资源丰富、软件架构复杂、实时性要求相对宽松。在这样的背景下,序列化方案的选择需要考虑以下因素:
首先是与ara::com通信框架的集成。ara::com是AP的核心通信中间件,定义了服务接口的抽象描述和通信模式。序列化需要与ara::com无缝配合,自动处理Proxy/Skeleton之间的数据传递。
其次是语言互操作能力。AP应用通常采用C++开发,但可能需要与Python、Java等语言实现的服务进行交互。序列化方案需要支持多语言的代码生成和解析。
第三是性能开销。在高频率通信场景下(如传感器数据),序列化反序列化的CPU开销和内存拷贝可能成为瓶颈,需要选择高效的序列化实现。
2.3 常见序列化方案对比
图2:序列化方案对比分析
| SOME/IP | 二进制 | AP原生支持,车载友好 | 跨平台支持有限 | 车内ECU通信 |
| protobuf | 二进制 | 跨平台、多语言、向前兼容 | 需要额外集成 | 跨平台服务 |
| FlatBuffers | 二进制 | 零拷贝解析,性能最优 | 工具链复杂 | 游戏、高性能 |
| JSON | 文本 | 人类可读、调试方便 | 体积大、解析慢 | 日志、调试 |
| CDR | 二进制 | CORBA兼容 | 效率一般 | 传统分布式系统 |
三、protobuf序列化实战
3.1 .proto接口定义
图3:protobuf消息定义示例
protobuf使用.proto文件定义消息和服务接口。这种IDL(接口定义语言)方式实现了接口与实现的分离,使得服务契约可以在多语言、多平台间共享。
一个典型的protobuf定义文件:
// vehicle_service.proto
syntax = "proto3";
package vehicle;
import "google/protobuf/timestamp.proto";
// 传感器数据类型定义
message SensorData {
uint32 sensor_id = 1; // 传感器ID
float temperature = 2; // 温度值
float pressure = 3; // 压力值
bytes raw_data = 4; // 原始数据
google.protobuf.Timestamp ts = 5; // 时间戳
}
// 车辆状态
message VehicleState {
uint32 vehicle_id = 1;
float speed = 2; // 车速 km/h
float steering_angle = 3; // 方向盘转角
bool engine_running = 4; // 发动机状态
repeated SensorData sensors = 5; // 多个传感器数据
}
// 服务定义
service VehicleService {
// 获取车辆状态
rpc GetVehicleState(google.protobuf.Empty) returns (VehicleState);
// 上报传感器数据(流式)
rpc StreamSensorData(stream SensorData) returns (google.protobuf.Empty);
// 订阅车辆状态变化
rpc SubscribeState(google.protobuf.Empty) returns (stream VehicleState);
}
3.2 protoc编译与代码生成
图4:protobuf编译与代码生成流程
protoc是protobuf的编译器,负责将.proto文件解析并生成目标语言的代码。对于C++开发,生成的代码包括头文件和实现文件,提供了完整的序列化/反序列化接口。
典型的编译命令:
# 安装protobuf编译器
apt-get install protobuf-compiler
# 安装C++运行时和插件
apt-get install libprotobuf-dev protobuf-compiler-grpc
# 生成C++代码
protoc –cpp_out=. \\
–grpc_out=. \\
–plugin=protoc-gen-grpc=`which grpc_cpp_plugin` \\
vehicle_service.proto
这会生成以下文件: – vehicle_service.pb.h – protobuf消息类的头文件 – vehicle_service.pb.cc – protobuf消息类的实现 – vehicle_service.grpc.pb.h – gRPC服务存根的头文件 – vehicle_service.grpc.pb.cc – gRPC服务存根的实现
3.3 序列化API使用
生成的C++类提供了简洁易用的序列化接口:
#include "vehicle_service.pb.h"
// 序列化示例
void serialize_example() {
// 创建消息对象
VehicleState state;
state.set_vehicle_id(1001);
state.set_speed(60.5f);
state.set_steering_angle(15.2f);
state.set_engine_running(true);
// 添加传感器数据
SensorData* sensor = state.add_sensors();
sensor->set_sensor_id(1);
sensor->set_temperature(25.3f);
sensor->set_pressure(101.325f);
// 序列化到字节流
std::string buffer;
if (state.SerializeToString(&buffer)) {
// buffer现在包含二进制数据,可以发送或存储
send_to_network(buffer);
}
}
// 反序列化示例
void deserialize_example(const std::string& buffer) {
VehicleState state;
if (state.ParseFromString(buffer)) {
// 访问字段
std::cout << "Vehicle ID: " << state.vehicle_id() << std::endl;
std::cout << "Speed: " << state.speed() << " km/h" << std::endl;
// 遍历repeated字段
for (const auto& sensor : state.sensors()) {
std::cout << "Sensor " << sensor.sensor_id()
<< ": " << sensor.temperature() << "C" << std::endl;
}
}
}
四、SOME/IP协议与序列化
图6:AP序列化架构总览
4.1 SOME/IP协议概述
图10:SOME/IP序列化协议栈
SOME/IP(Scalable Service-Oriented Middleware over IP)是AUTOSAR定义的面向服务的IP通信协议。它在车载以太网环境中广泛使用,是ara::com的默认传输机制。
SOME/IP的核心概念是服务(Service),每个服务由一组方法(Method)、事件(Event)和字段(Field)组成。方法类似于RPC调用,可以有请求-响应或fire-and-forget模式;事件是订阅发布模式下的数据推送;字段是既可读又可通知的数据项。
4.2 SOME/IP序列化格式
图5:SOME/IP字节流序列化格式
SOME/IP定义了紧凑的二进制序列化格式,包含固定的头部和可变长度的负载:
+—————-+—————-+—————-+—————-+
| Message ID | Length | Request ID | Interface |
| (32bit) | (32bit) | (32bit) | Version |
+—————-+—————-+—————-+—————-+
| Return Code | |
| (8bit) | Payload (variable) |
+—————-+ |
| [Payload data serialized by application] |
+——————————————————————–+
Message ID唯一标识服务接口中的方法或事件;Length表示后续字节数;Request ID用于匹配请求和响应;Interface Version和Return Code用于协议控制。
4.3 ara::com与序列化集成
ara::com对SOME/IP提供了原生支持。当开发者使用Adaptive Application Manager定义服务接口后,ara::com自动处理序列化相关的所有细节。
// ara::com服务实现示例
#include "ara/com/runtime.h"
#include "vehicle_serviceStub.h"
class VehicleServiceImpl : public VehicleServiceStub {
public:
// 方法实现
void GetVehicleState(
std::shared_ptr<ara::com::ara::com::Promise< VehicleState::ConstPtr>> promise) override
{
VehicleState::Ptr state = std::make_shared<VehicleState>();
state->set_vehicle_id(getCurrentVehicleId());
state->set_speed(readSpeedSensor());
promise->SetValue(std::move(state));
}
// 事件发送
void SendSensorUpdate(const SensorData& data) {
SensorUpdateEvent(data); // 触发事件通知订阅者
}
};
// 服务发布
int main() {
auto runtime = ara::com::Runtime::GetInstance();
auto service = runtime->CreateService<VehicleServiceImpl>();
service->Offer(); // 在网络上发布服务
}
五、CMake构建系统详解
5.1 CMake核心概念
CMake是跨平台的构建系统生成器,通过CMakeLists.txt文件描述构建过程,生成特定平台的原生构建文件(如Unix的Makefile、Windows的Visual Studio项目)。相比直接编写Makefile,CMake提供了更抽象、更易维护的构建配置方式。
CMake 3.15+引入的现代CMake采用target-centric(目标中心)的设计理念,每个构建产物(可执行文件、库)都是一个target,具有自己的属性(编译选项、链接库、include路径等)。这种设计使得依赖关系清晰明确,易于管理复杂的项目结构。
5.2 项目结构设计
图7:AUTOSAR AP项目目录结构
一个典型的AUTOSAR AP项目结构:
project/
├── CMakeLists.txt # 根构建文件
├── src/
│ ├── CMakeLists.txt
│ ├── main.cpp # 应用入口
│ ├── service_a.cpp
│ └── service_b.cpp
├── include/
│ ├── CMakeLists.txt
│ ├── service_a.h
│ └── service_b.h
├── proto/
│ ├── CMakeLists.txt
│ ├── vehicle_service.proto
│ └── sensor.proto
├── tests/
│ ├── CMakeLists.txt
│ ├── test_service_a.cpp
│ └── test_integration.cpp
└── build/ # 构建输出目录
5.3 根CMakeLists.txt详解
图8:根CMakeLists.txt配置详解
cmake_minimum_required(VERSION 3.15)
project(VehicleAPApp VERSION 1.0.0 LANGUAGES CXX)
# ============================================================
# 基础配置
# ============================================================
# C++17标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 输出配置
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# 编译选项
add_compile_options(
-Wall -Wextra -Wpedantic
$<$<CONFIG:Debug>:-g -O0 -DDEBUG>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
)
# ============================================================
# 依赖查找
# ============================================================
# AUTOSAR AP核心库 (使用ara-core或自定义路径)
find_package(ara-core REQUIRED)
# Protobuf
find_package(Protobuf REQUIRED)
# gRPC (可选)
find_package(gRPC CONFIG)
# Google Test (用于单元测试)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1
)
FetchContent_MakeAvailable(googletest)
# ============================================================
# 子目录构建
# ============================================================
add_subdirectory(proto)
add_subdirectory(src)
add_subdirectory(tests)
# ============================================================
# 安装配置
# ============================================================
install(DIRECTORY ${CMAKE_BINARY_DIR}/bin/
DESTINATION bin)
install(DIRECTORY ${CMAKE_BINARY_DIR}/lib/
DESTINATION lib)
5.4 target目标详解
现代CMake中,每个构建产物都是target,主要类型包括:
# 1. 可执行文件
add_executable(app_main
main.cpp
app_core.cpp
)
# 2. 静态库
add_library(service_lib STATIC
service_a.cpp
service_b.cpp
)
# 3. 共享库
add_library(sensor_plugin SHARED
plugin_entry.cpp
)
# 4. 接口库(仅包含头文件)
add_library(data_types INTERFACE)
target_include_directories(data_types INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
5.5 依赖管理
图9:CMake target依赖关系图
target_link_libraries是CMake依赖管理的核心命令:
# 链接到库,传递依赖
target_link_libraries(app_main
PRIVATE # app_main私有依赖,app_main的使用者不继承
service_lib
data_types
PUBLIC # 公有关联,app_main的使用者继承这些依赖
ara::com::services
INTERFACE # 接口依赖,仅传递,不编译使用
version_info
)
# 使用生成器表达式处理配置差异
target_link_libraries(app_main
$<$<CONFIG:Debug>:debug_helpers> # Debug配置额外链接
$<$<CONFIG:Release>:performance> # Release配置优化库
)
六、实战项目构建
6.1 Protobuf子目录构建
# proto/CMakeLists.txt
find_package(Protobuf REQUIRED)
# 定义proto文件列表
set(PROTO_FILES
vehicle_service.proto
sensor.proto
)
# 为每个proto文件生成代码
protobuf_generate(TARGET proto_lib PROTOS ${PROTO_FILES})
# 设置输出目录
set_target_properties(proto_lib PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)
# 安装proto文件(供其他模块使用)
install(FILES ${PROTO_FILES}
DESTINATION share/proto)
6.2 服务库构建
# src/CMakeLists.txt
add_library(service_lib STATIC
service_a.cpp
service_b.cpp
)
target_include_directories(service_lib
PUBLIC ${CMAKE_SOURCE_DIR}/include
PRIVATE ${CMAKE_SOURCE_DIR}/src
)
target_link_libraries(service_lib
PUBLIC
proto_lib
ara::com::foundation
PRIVATE
Boost::filesystem
)
6.3 应用可执行文件构建
# src/main.cpp
add_executable(vehicle_app main.cpp)
target_include_directories(vehicle_app
PRIVATE ${CMAKE_SOURCE_DIR}/include
)
target_link_libraries(vehicle_app
PRIVATE
service_lib
proto_lib
ara::com::ara::exec
PUBLIC
ara::core
)
# 代码签名(如果需要)
if(ENABLE_CODE_SIGNING)
custom_command(TARGET vehicle_app POST_BUILD
COMMAND ${CODE_SIGN_TOOL}
ARGS –input $<TARGET_FILE:vehicle_app>
–output $<TARGET_FILE:vehicle_app>.signed
–key ${SIGNING_KEY}
)
endif()
6.4 测试配置
# tests/CMakeLists.txt
include(GoogleTest)
add_executable(unit_tests
test_service_a.cpp
test_service_b.cpp
mocks/mock_ara_com.cpp
)
target_link_libraries(unit_tests
PRIVATE
service_lib
GTest::gtest_main
GTest::gmock
)
# 单元测试
gtest_discover_tests(unit_tests)
# 集成测试(需要运行时环境)
add_executable(integration_tests
test_integration.cpp
)
target_link_libraries(integration_tests
PRIVATE
service_lib
GTest::gtest_main
)
# 添加测试依赖:确保服务在测试前启动
add_dependencies(integration_tests vehicle_app)
6.5 完整构建流程
# 创建构建目录(out-of-source构建)
mkdir build && cd build
# 配置项目
cmake .. \\
-DCMAKE_BUILD_TYPE=Release \\
-DENABLE_CODE_SIGNING=ON \\
-Dara-core_ROOT=/path/to/ara-core-sdk
# 编译
cmake –build . –parallel 8
# 运行测试
ctest –output-on-failure
# 安装
cmake –install . –prefix /opt/vehicle-app
# 打包
cpack -G DEB # 生成Debian包
cpack -G RPM # 生成RPM包
七、最佳实践与常见问题
7.1 CMake最佳实践
7.2 序列化性能优化
7.3 常见问题解决
问题1:protoc找不到include的文件
# 使用-I指定include路径
protoc -I=proto –cpp_out=. \\
–cpp_out=. \\
proto/main.proto
问题2:CMake找不到Protobuf
# 明确指定Config文件位置
find_package(Protobuf REQUIRED
CONFIG
PATHS /path/to/protobuf/lib/cmake
)
问题3:链接时找不到符号 检查target_link_libraries的传递性设置,确保依赖库的依赖也正确传递。
八、总结
本文深入讲解了AUTOSAR AP中的数据序列化机制和CMake工程构建方法。数据序列化是车内服务通信的基础,选择合适的序列化方案需要综合考虑性能、兼容性和开发效率;CMake作为现代化的构建系统,提供了灵活强大的项目组织能力。
在实际项目中,建议遵循以下原则:优先使用AP原生的SOME/IP方案;需要跨平台时考虑protobuf;使用现代CMake的target模型组织代码;保持构建配置的可维护性。
下一篇文章我们将探讨C++17在AUTOSAR AP中的高级应用(AP-09),继续深入学习自适应平台的核心技术。
