乐于分享
好东西不私藏

新能源汽车零重力座椅应用层软件设计

新能源汽车零重力座椅应用层软件设计

1、概述

本文基于《新能源汽车零重力座椅控制策略开发要求》所述的相关需求,现将控制逻辑拆分到AUTOSAR CP 架构中的软件组件,并详细描述应用层软件的设计与实现过程。在后续的实际工作中,技术人员可直接据此创建SWC,配置RTE、操作系统任务并编写代码。

1.1设计原则

·每个SWC负责单一功能域。

·SWC间通过RTE的标准Sender/Receiver接口交互。

·10ms周期任务保证运动控制同步精度;50ms碰撞响应路径由OS中断事件实现。

·应用层代码不直接操作硬件,全部通过RTE访问BSW服务。

1.2开发环境

·AUTOSAR版本:CP 4.2.2

·OS:Scalable OS,符合 OSEK/VDX

·配置工具:EB tresos Studio/Vector DaVinci

·编译器:符合ISO C99

·微控制器:Infineon TC3xx或NXP S32K

2、软件架构与SWC划分

2.1 SWC总体设计

系统包含以下应用层SWC,部署于CDC上。

SWC名称

类型

功能描述

ZgSeatCtrl

Application SWC

零重力控制逻辑:状态机、条件判断、轨迹规划、运动协调、记忆管理、迎宾逻辑。

ZgSeatDiag

Application SWC

故障汇总、DTC管理、降级模式触发、防夹冗余检测。

ZgLinProxy

Application SWC /CDD

1.作为SCU的LIN数据映射代理:将RTE信号转换为LIN发送帧,接收帧解析为信号。

2.内部可封装LIN调度。

3.本例按Application SWC实现,通过LIN服务接口操作。

ZgHmiBridge

Application SWC

接收HMI软开关、记忆请求,反馈状态显示、警告信息。

说明ZgLinProxy在项目中常作为复杂驱动直接访问LIN硬件,但在AUTOSAR标准架构下,也可将LIN信号映射到BSW的PDU中,由Com/RTE直接为应用层提供信号接口。这里采用标准映射方式,将各LIN信号定义为系统信号,ZgSeatCtrl直接通过RTE发送TargetPositions、接收ActualPositions。本例不再单独保留ZgLinProxy SWC,以降低复杂度,所有必要的LIN信号将通过BSW层配置实现透传。

2.2修改后 SWC 列表

基于上述背景对SWC进行更新,如下:

SWC

描述

ZgSeatCtrl

零重力主控逻辑

ZgSeatDiag

诊断与故障处理

ZgHmiBridge

HMI交互适配

三个SWC间通过RTE接口交换数据,ZgSeatCtrl内部包含多个可运行实体。

3、接口定义

3.1部分信号定义

信号名

数据元素

类型

范围/分辨率

VehicleSpeed

VehicleSpeed

uint16

0..65535, 0.01 km/h

GearPosition

GearPosition

enum 

0=Dan,1=P,2=R,3=N,4=D

DriverDoorStatus

DoorStatus

uint8

0=Closed,1=Open

CrashSignal

CrashStatus

uint8

0=Normal,1=Crash

ZeroGravityRequest

ZgRequest

uint8

0=Idle,1=Activate,2=Deactivate

ManualSwitchUp

ManualSwitches

uint8 bitfields

BIT0:SlideFwd, BIT1:SlideBwd

SeatOccupied

Occupied

uint8

0=Empty,1=Occupied

ActualPos_Slide

Position_mm

sint16

0.1mm/bit

ActualPos_Recline

Angle_degx10

sint16

0.1°/bit

... 

TargetPos_Slide

Position_mm

sint16

0.1mm/bit

... 

ControlWord

CtrlWord

uint16

BIT0:EnableMotors, BIT1:Abort

StatusWord

StatusWord

uint16

BIT0:InPosition, BIT1:AntiPinchActive

FaultCode

FaultCodeType

uint32

DTC格式

DiagCommand

DiagCmd

uint8

清除故障等

3.2 接口

Sender-Receiver接口:

·I_VehicleData:包含VehicleSpeed,GearPosition,DoorStatus,CrashStatus;

·I_SeatFeedback:包含ActualPositions,StatusWord,MotorCurrents,Occupied;

·I_SeatCommands:包含TargetPositions,ControlWord;

·I_HmiControls:ZgRequest,ManualSwitches,MemoryRequest;

·I_HmiDisplay:ZgStateEnum,WarningMsgEnum,ActiveMemorySlot;

3.3 SWC与Port映射

ZgSeatCtrl:

·RPort VehicleDataI_VehicleData,接收车速、档位等;

·RPort SeatStatusI_SeatFeedback,接收实际位置和状态;

·PPort SeatCmdI_SeatCommands,发送目标位置和控制字;

·RPort HmiInputI_HmiControls,接收请求和手动开关;

·PPort HmiOutputI_HmiDisplay,发送状态给HMI;

·RPort DiagInfoI_Diag,接收诊断状态,用于决策。

ZgSeatDiag:

·RPort SeatStatusDiagI_SeatFeedback,接收位置/状态以检测防夹;

·RPort ZgStateDiag:从ZgSeatCtrl,接收当前运动状态;

·PPort DiagOutput:发送故障码给DEM,诊断事件;

·PPort DiagToZgCtrl:提供降级模式指示给ZgSeatCtrl。

ZgHmiBridge:

·RPort ZgDisplayIn:接收ZgStateEnum,WarningMsgEnum等;

·PPort HmiSignalOut:转换为HMI显示信号;

·RPort HardKeysIn:接收硬开关,本例中手动开关从SCU LIN来,已经包含在SeatStatus中,所以ZgSeatCtrl直接处理,ZgHmiBridge只负责屏幕显示。

注:不单独实现ZgHmiBridge,将HMI显示接口直接由ZgSeatCtrl通过RTE输出,这里不再进一步细化。

4、ZgSeatCtrl行为设计

4.1 Runnables 列表

ZgSeatCtrl作为 Application SWC,包含以下可运行实体,由不同事件触发。

可运行实体

触发事件

周期/条件

功能

ZG_Init

Init Event

ECU启动时执行一次

初始化状态、加载NVM记忆位置、标定参数

ZG_ReceiveVehicleSignals

DataReceivedEvent 

信号更新即触发

更新本地车速、档位等镜像,必要时触发状态转移

ZG_ReceiveSeatFeedback

DataReceivedEvent 

LIN数据更新后触发

更新位置反馈缓冲,检查到位、防夹标志

ZG_ProcessHMI

DataReceivedEvent 

开关/请求一次

处理零重力请求、手动开关、记忆命令,存入内部队列

ZG_StateMachine

TimingEvent

10ms

执行状态机跃迁、条件评估、轨迹规划更新、输出目标位置与控制字

ZG_DiagReport

TimingEvent

100ms

汇总故障上报、更新DTC状态

4.2 内部状态变量

在SWC内部定义静态变量,不通过RTE共享。

typedef enum {

ZG_IDLE= 0,

ZG_MOVING_TO_ZG,

ZG_ZG_ACTIVE,

ZG_MOVING_TO_NORMAL,

ZG_INTERRUPTED,

ZG_FAULT,

ZG_SAFE_STOP,

ZG_WELCOME_MOVING,

ZG_MANUAL_OVERRIDE

} ZgState_t;

typedef struct {

sint16Slide;// 0.1mm

sint16Cushion;// 0.1°

sint16Recline;// 0.1°

sint16Legrest;// 0.1mm

sint16Footrest;// 0.1°

} SeatPosition_t;

// 目标位置、轨迹阶段、记忆位置数组等

static ZgState_t zgState = ZG_IDLE;

static SeatPosition_t targetPos;

static SeatPosition_t actualPos;

static uint8 trajPhase; // 0=idle,1=phase1...

static uint32 trajTimer; // ms since movement start

static uint8 conditionsMet;

4.3 RTE 数据流向

·ZG_ReceiveVehicleSignals从RTE读取RPort_VehicleData所有信号,更新vehSpd,gear,door等全局镜像。

·ZG_ReceiveSeatFeedback读取RPort_SeatStatus,更新actualPos,statusWord

·ZG_ProcessHMI读取RPort_HmiInput,将命令暂存于环形缓冲,由状态机消费。

·ZG_StateMachine根据当前状态和缓冲命令,计算targetPosctrlWord,通过PPort_SeatCmd写出;同时更新PPort_HmiOutput状态。

5、Runnable设计与代码实现

5.1 ZG_StateMachine

该Runnable集成了状态机、进入条件判断、轨迹生成及运动控制。代码结构如下:

#include "Rte_ZgSeatCtrl.h"

#include "ZgSeatCtrl_Private.h"

/* 外部常量:标定配置可通过NVM加载,此处用宏 */

#define ZG_SLIDE_TARGET_MM-100// -10mm = -100 in 0.1mm

#define ZG_RECLINE_TARGET_DEG1250// 125.0°

#define ZG_CUSHION_TARGET_DEG150// 15.0°

#define ZG_LEGREST_PERCENT85// 85% 行程,预先转换为工程值

#define ZG_FOOTREST_TARGET_DEG0// 水平

/* 运动参数 */

#define MAX_SLIDE_SPEED200// 0.1mm/10ms = 20mm/s 对应每10ms 2.0mm=20 unit

#define MAX_RECLINE_SPEED50// 0.1°/10ms -> 5°/s

#define MAX_CUSHION_SPEED50

#define MAX_LEGREST_SPEED150// 15mm/s

#define PHASE1_DURATION_MS1500

#define PHASE2_DURATION_MS3000

/* 内部函数声明 */

static boolean ZG_CheckEntryConditions(void);

static void ZG_TrajectoryGenerator(void);

static void ZG_UpdateMotorCommands(void);

static void ZG_CheckMotionTimeout(void);

void ZG_StateMachine(void) {

/*读取输入信号已在其他Runnable中更新到镜像变量,这里直接使用镜像 */

booleanentryOk = ZG_CheckEntryConditions();

switch(zgState){

caseZG_IDLE:

if(pendingZgRequest == 1 && entryOk) {

zgState= ZG_MOVING_TO_ZG;

trajPhase= 1;

trajTimer= 0;

//记录起始位置用于轨迹规划

startPos= actualPos;

//设置零重力最终目标

zgTargetPos.Slide= ZG_SLIDE_TARGET_MM;

zgTargetPos.Recline= ZG_RECLINE_TARGET_DEG;

zgTargetPos.Cushion= ZG_CUSHION_TARGET_DEG;

zgTargetPos.Legrest= (ZG_LEGREST_PERCENT * maxLegrestTravel) / 100;

zgTargetPos.Footrest= ZG_FOOTREST_TARGET_DEG;

}

/*迎宾模式处理省略 */

break;

caseZG_MOVING_TO_ZG:

/*检查中断条件:手动开关操作、故障、碰撞等 */

if(crashStatus == 1) {

zgState= ZG_SAFE_STOP;

SetAbortCmd();

break;

}

if(faultHappened) {

zgState= ZG_FAULT;

SetAbortCmd();

break;

}

/*手动操作优先级最高:切换为手动,保存中断点 */

if(manualSwitchActive) {

zgState= ZG_MANUAL_OVERRIDE;

interruptedTarget= targetPos; /* 当前未完成的目标 */

SetAbortCmd();// 停止自动运动,释放电机给手动指令

break;

}

/*碰撞、开门等请参考设计;此处简化 */

//执行轨迹生成

ZG_TrajectoryGenerator();

//冗余防夹检测(在ZG_StateMachine内调用或由Diag监控)

if(ZG_RedundantAntiPinch()) {

zgState= ZG_INTERRUPTED;

DTC_Report(B1A04);

ReverseAndStop();

break;

}

//运动超时检查

if(ZG_CheckMotionTimeout()) {

zgState= ZG_FAULT;

DTC_Report(B1A05);

SetAbortCmd();

break;

}

//发出目标

ZG_UpdateMotorCommands();

//到位判断

if(AllAxisInWindow(targetPos, actualPos, tolerance)) {

zgState= ZG_ZG_ACTIVE;

trajPhase= 0;

SetMotorsDisable();// 保持位置,关闭使能以节能

}

break;

caseZG_ZG_ACTIVE:

if(pendingZgRequest == 2) { // 恢复请求

zgState= ZG_MOVING_TO_NORMAL;

//目标设置为记忆位置1或驾驶位置

targetPos= memoryPositions[memoryRecallSlot];

trajPhase= 1;

trajTimer= 0;

startPos= actualPos;

}

//行车自动退出逻辑

if(vehicleSpeed > 30 && gearPos != P) { // speed unit 0.01km/h: >0.3km/h

zgState= ZG_MOVING_TO_NORMAL;

targetPos= drivingPosition; // 预设的安全驾驶位置

trajPhase= 1; trajTimer = 0; startPos = actualPos;

}

break;

caseZG_MOVING_TO_NORMAL:

//类似MOVING_TO_ZG,轨迹调用了恢复运动算法

ZG_TrajectoryGenerator();

if(ZG_CheckMotionTimeout()) { ... }

ZG_UpdateMotorCommands();

if(AllAxisInWindow(...)) {

zgState= ZG_IDLE;

}

break;

caseZG_MANUAL_OVERRIDE:

//直接传递手动开关到目标位置(通过另一个函数处理)

//如果手动开关释放且用户再次请求继续,可恢复

if(!manualSwitchActive && continueRequest) {

zgState= ZG_MOVING_TO_ZG; // 从中断点继续?

trajPhase= determinePhase(currentActual);

startPos= actualPos;

}else if (manualSwitchActive) {

//手动目标由ManualProcessing直接输出到PPort_SeatCmd

ProcessManualMotion();

}

break;

/*其他状态类似 */

default:break;

}

/*更新HMI输出 */

Rte_Write_PPort_HmiOutput_ZgState((uint8)zgState);

Rte_Write_PPort_HmiOutput_Warning(warningMsg);

}

5.2 轨迹生成器

基于S曲线生成五个轴的目标位置,使用时间分段和二阶平滑,这里采用梯形速度加平滑。

static void ZG_TrajectoryGenerator(void) {

uint32t = trajTimer; // 当前时间,ms

trajTimer+= 10; // 增加10ms

if(zgState == ZG_MOVING_TO_ZG) {

//阶段1: 0~1500ms 滑轨+坐垫+腿托展开

if(t <= PHASE1_DURATION_MS) {

//滑轨:从初始位置到安全后位 (例-80mm)

targetPos.Slide= LinearInterp(startPos.Slide, SAFE_SLIDE_POS, t, PHASE1_DURATION_MS);

//坐垫倾角

targetPos.Cushion= LinearInterp(startPos.Cushion, ZG_CUSHION_TARGET_DEG, t, PHASE1_DURATION_MS);

//腿托展开部分

targetPos.Legrest= LinearInterp(startPos.Legrest, ZG_LEGREST_POS, t, PHASE1_DURATION_MS);

//靠背保持或者微动

targetPos.Recline= startPos.Recline;

//脚踏伺机

targetPos.Footrest= startPos.Footrest;

}

//阶段2: 1500~4500ms 靠背开始后仰,腿托继续,脚踏动作

elseif (t <= (PHASE1_DURATION_MS + PHASE2_DURATION_MS)) {

uint32t2 = t - PHASE1_DURATION_MS;

targetPos.Slide= SAFE_SLIDE_POS; // 保持

targetPos.Cushion= ZG_CUSHION_TARGET_DEG;

targetPos.Recline= LinearInterp(startPos.Recline, ZG_RECLINE_TARGET_DEG, t2, PHASE2_DURATION_MS);

targetPos.Legrest= LinearInterp(ZG_LEGREST_PARTIAL, ZG_LEGREST_POS, t2, PHASE2_DURATION_MS);

targetPos.Footrest= LinearInterp(startPos.Footrest, ZG_FOOTREST_TARGET_DEG, t2, PHASE2_DURATION_MS);

}

else{

//阶段3:精细到位,直接赋目标值

targetPos= zgTargetPos;

}

//TODO: 应用S曲线平滑速度约束,限制每周期增量

ApplySpeedLimit(&targetPos);

}

elseif (zgState == ZG_MOVING_TO_NORMAL) {

//恢复逆过程:先靠背回收,再滑轨前移

//分阶段实现

}

}

/* 辅助线性插值 */

static sint16 LinearInterp(sint16 start, sint16 end, uint32 curTime, uint32 totalTime) {

if(totalTime == 0) return end;

int32val = start + ( (int32)(end - start) * (int32)curTime ) / totalTime;

return(sint16)val;

}

5.3 运动命令输出与位置闭环

static void ZG_UpdateMotorCommands(void) {

//准备ControlWord: 使能电机,设置非回退

uint16ctrl = 0x0001; // Bit0 Enable

if(abortFlag) ctrl |= 0x0002;

Rte_Write_PPort_SeatCmd_ControlWord(ctrl);

Rte_Write_PPort_SeatCmd_TargetSlide(targetPos.Slide);

Rte_Write_PPort_SeatCmd_TargetCushion(targetPos.Cushion);

Rte_Write_PPort_SeatCmd_TargetRecline(targetPos.Recline);

Rte_Write_PPort_SeatCmd_TargetLegrest(targetPos.Legrest);

Rte_Write_PPort_SeatCmd_TargetFootrest(targetPos.Footrest);

}

5.4 进入条件评估

static boolean ZG_CheckEntryConditions(void) {

if(vehicleSpeed > 0) return FALSE;

if(gearPosition != 1) // P

returnFALSE;

if(doorStatus == 1) return FALSE;

if(crashStatus != 0) return FALSE;

if(scuFaultPresent) return FALSE;

//座椅占位等

returnTRUE;

}

5.5 冗余防夹检测

static boolean ZG_RedundantAntiPinch(void) {

//计算实际速度 = 当前位置变化 / 周期

staticSeatPosition_t lastActual;

SeatPosition_tspeed;

speed.Slide= actualPos.Slide - lastActual.Slide;

//....类似

//若指令目标速度为moveUp,但实际速度大幅低于预期则触发

if(expectedSlideSpeed > 10 && abs(speed.Slide) < (abs(expectedSlideSpeed)*0.3) ) {

if(lowSpeedCounter++ > 50) { // 500ms

lowSpeedCounter= 0;

returnTRUE;

}

}else {

lowSpeedCounter= 0;

}

lastActual= actualPos;

returnFALSE;

6、诊断SWC 

ZgSeatDiag周期100ms执行,监控SCU上报故障、通讯超时,检测冗余防夹事件并报告DEM,可调用Dem_SetEventStatus()

void ZgDiag_100ms(void) {

//读取SeatStatus的StatusWord

uint16status;

Rte_Read_RPort_SeatStatusDiag_StatusWord(&status);

if(status & SCU_FAULT_MASK) {

Dem_SetEventStatus(DemEventId_B1A00,DEM_EVENT_STATUS_FAILED);

}

//检查LIN超时(由BSW提供信号AliveCounter)

//防夹冗余检测如果发现在ZgSeatCtrl中触发,可通过内部接口报告,或此处独立计算。

}

7、建议

在OS中配置任务和调度:

·Task_10ms:周期10ms,调用ZG_StateMachine

·Task_5ms:扫描LIN接收信号,触发ZG_ReceiveSeatFeedback; 也可合并到10ms。

·Task_100ms: 调用ZG_DiagReport和部分HMI刷新。

·ISR级别:碰撞信号通过CAN中断映射到ZG_CrashCallback直接置标志,保证50ms内停车。

:虽然RTE在Data Received Event自动触发Runnable,但为了确定顺序,可将ZG_ReceiveVehicleSignalsZG_ProcessHMI配置在ZG_StateMachine之前运行的同一任务中或由RTE保证数据一致性。

8、代码集成与配置注意事项

·信号映射:在System Description中将CAN信号映射到I_VehicleData接口数据元素;LIN信号从PDU映射到I_SeatFeedback

·NVM配置:记忆位置和标定参数存储于NVM Block,通过NvM_ReadAll初始化时加载。

·标定参数可调:使用XCP/CCP或通过DID读取修改。

·复杂驱动:若LIN Tx直接由SCU要求,需确保ZgSeatCtrlPPort_SeatCmd最终触发LIN发送;BSW层面配置LinIf传输请求。


关注+