欢迎光临
我们一直在努力

AP-08 AUTOSAR AP数据序列化与CMake工程构建

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 思维导图图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:序列化方案对比分析图2:序列化方案对比分析

方案编码格式优点缺点适用场景
SOME/IP 二进制 AP原生支持,车载友好 跨平台支持有限 车内ECU通信
protobuf 二进制 跨平台、多语言、向前兼容 需要额外集成 跨平台服务
FlatBuffers 二进制 零拷贝解析,性能最优 工具链复杂 游戏、高性能
JSON 文本 人类可读、调试方便 体积大、解析慢 日志、调试
CDR 二进制 CORBA兼容 效率一般 传统分布式系统

三、protobuf序列化实战

3.1 .proto接口定义

图3:protobuf消息定义示例图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编译与代码生成流程图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序列化架构总览图6:AP序列化架构总览

4.1 SOME/IP协议概述

图10: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字节流序列化格式图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项目目录结构图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配置详解图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依赖关系图图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最佳实践

  • 使用out-of-source构建:始终在源码目录外创建build目录,避免污染源码
  • 避免全局变量:使用target_*命令设置目标属性,避免CMAKE_CXX_FLAGS等全局变量
  • 明确依赖传递性:合理使用PRIVATE/PUBLIC/INTERFACE标记依赖传递
  • 使用生成器表达式:处理配置相关的差异化构建需求
  • 模块化设计:将相对独立的功能模块拆分到子目录
  • 7.2 序列化性能优化

  • 预分配缓冲区:对于频繁序列化的消息,预分配缓冲区避免重复分配
  • 零拷贝解析:FlatBuffers支持直接解析,避免数据拷贝
  • 对象池:对于大量短生命周期对象,使用对象池减少分配开销
  • 批量序列化:将多个小消息合并为批量消息,减少协议头开销
  • 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),继续深入学习自适应平台的核心技术。

    赞(0)
    未经允许不得转载:171主机测评 » AP-08 AUTOSAR AP数据序列化与CMake工程构建
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址