中国汽车工程师之家--聚集了汽车行业80%专业人士 

论坛口号:知无不言,言无不尽!QQ:542334618 

本站手机访问:直接在浏览器中输入本站域名即可 

  • 353查看
  • 0回复

[Simulink] MBD实战之电机控制 第05期:把模型嵌入代码

[复制链接]


该用户从未签到

发表于 2-3-2024 09:18:05 | 显示全部楼层 |阅读模式

汽车零部件采购、销售通信录       填写你的培训需求,我们帮你找      招募汽车专业培训老师


全文约4864字,你将看到以下内容:


    集成式MBD开发方式

    把FOC算法模型嵌入代码

    写在最后


姗姗来迟的第五期(天太冷了,搭模型、写文章也需要毅力

)。

本期将是2021年的收官,也将是MBD实战之电机控制系列的最后一期。这一期将介绍如何将模型生成的代码集成到已有的代码中去——把算法模型嵌入你的代码之中去。

1 集成式的MBD开发方式

这个话题这前面的文章中提过了很多次,之所以频繁的提及这种集成的方法,是因为这是目前MBD(Model-Based Design,基于模型的设计)的主流开发方式。

具体而言,集成式MBD的开发方法是:软件代码手写一部分,模型生成一部分,然后再将两部分集成在一起,从而实现完整的软件开发。

手写代码和模型生成有着不同的分工:



    手写代码:适用于底层驱动、外设初始化、内存管理、中断管理、OS、Bootloader、协议栈等,还包括片外扩展IC的初始化,以及其他数学特征不明显的功能;

    模型生成:适用于数学算法、状态机、数据处理等。



Tips:为了便于区分,本文将手写的代码称为底层代码,而模型生成的代码称为模型代码。底层代码负责的模块并不一定全部都是必须的,例如OS(嵌入式中一般使用实时操作系统,比如FreeRTOS),很多控制可能不需要OS,只需要中断管理即可。

可以看出,底层代码负责的功能,并不适合使用模型来实现。上一期给出的完全由模型实现的电机控制框架,虽然实现了电机控制,但实际上忽略了很多实际项目中必须的模块,例如OS、Bootloader等,还有很多安全机制也没有考虑。

底层代码功能理论上也能通过模型来实现,但没必要、也不需要用模型的方式来实现。MathWorks官方也一直推荐仅使用Simulink/MATLAB搭建算法模型,像底层驱动、内存管理、OS、Bootloader、协议栈等等,还是通过手写代码的方式更为高效。

这一点我有直接咨询过MathWorks的工程师,他告诉我底层代码要通过模型来实现,其难点在于TLC语言的复杂性和硬件芯片的多样性(市面上的芯片太多,每一个都需要适配),这两点没有专业的团队,很难实现模型的高效性。

Tips:我有了解到,有的公司利用StateFlow实现了类似于OS的Task Manager,用于管理模型中的不同任务。其构成也是相当复杂,没有专门的团队是没办法开发和维护的。但也仅限于任务调度——我感觉像复杂的状态机,OS的很多功能是没办法用StateFlow实现的。

对于有复杂算法的软件,MBD代码生成就可以大显身手了,算法越复杂,优势就越显著。举一些例子,如:电机控制、电池管理系统(BMS)、路径规划、紧急制动(AEB)、自动泊车(APS)等算法,其算法实现都可以通过MBD的方式来开发。

用MBD来开发算法,简单总结有如下优势:



    便于算法实现和调整:这里指的并不是找Bug,算法的数学表达可能是固定的,但在特定的应用场景下,算法的局部需要特定调整,例如添加状态机实现不同状态的切换。借助模型的图形化,以及MATLAB/Simulink上的各种工具箱,这种调整是很直观,并且很容易实现。

    专注算法且算法独立:使用模型搭建算法,我们只需要专注算法实现的本身,不需要考虑底层驱动等因素;由于不考虑底层驱动,算法模型可以方便地实现移植。有人说算法的核心是数学,写代码往往并不太容易兼顾数学,但搭建模型可以。

    便利的仿真测试:算法的测试对于优化算法是非常强有力的途径,通过MBD,可以方便的实现MIL、SIL、PIL和HIL,根据测试结果,反馈到模型中,再对模型进行调整,这其中的效率是手写代码无法比拟的。


我自己是有完全手写代码实现电机控制软件所有环节的经验的,对比搭建MBD电机控制框架的开发过程,上述优点是非常明显的,我深有体会。这些优点对新手入门也是非常友好,特别是对非电子、计算机专业的人来说。

在使用集成式MBD开发项目时,软件实现一般需要两个角色:



    底层工程师:底层工程师需要对MCU和硬件电路设计很熟悉,他负责实现底层驱动、MCU初始化、OS、Bootloader、协议栈等。他使用手写代码的方式进行开发,在底层代码中给算法预留好接口,以供算法集成。

    算法模型工程师:算法模型工程师则负责搭建模型、测试算法,测试通过后就可以生成代码,然后集成到底层代码中去。



Tips:软件实现还可以有一个统筹底层代码和模型代码的架构工程师,给底层和模型设计需求;非软件的部分,一般有一位硬件工程师。

集成好了之后可能还需要进行测试,因为仿真毕竟和实际情况有差异。这时候再调整模型,或者调整底层代码,就可以做到彼此独立、互不干扰了。这一点在传统的开发中往往是一大痛点,而MBD可以很好的解决这个问题。

2 把FOC算法模型嵌入代码

依然以PMSM无感电机控制软件开发为例来介绍如何实现模型代码的集成,硬件平台是NXP的电机开发套件MCSPTE1AK144。

MBD实战之电机控制 第05期:把模型嵌入代码w2.jpg

电机开发套件MCSPTE1AK144 - From NXP

后文中提到的最新的模型和代码已经更新到了我的GitHub仓库中,下载模型可以访问下面这个链接:


GitHub仓库:autoMBD电机控制项目
https://github.com/TkungAI/autoMBD_Motor_Control


也可以在对话框中发送关键词“MBD电机控制”,即可收到链接信息,公众号里还有其他资源,发送关键词“资源”可以收到下载链接。

回到正题,如何开展集成式MBD开发,有以下三个工作环节:

1. 实现算法模型

首先要明确算法模型的需求,可以简单地概括为:



    算法的输入和输出是什么?

    算法的内部变量有哪些?

    算法要处理什么任务(状态机)?


这三个问题的解答也需要和底层工程师和硬件工程师一起协商、相互约束、相互补充,共同明确需求。下图是本次实现的电机控制算法模型(文件名FOC_Ctrl_CodeModel.slx):

MBD实战之电机控制 第05期:把模型嵌入代码w3.jpg

PMSM无感电机控制算法模型 - From autoMBD

这里给出我在本次的PMSM无感电机控制软件开发中,对上述三个问题的回答:



    输入和输出

    输入为5个ADC采样信号和2个电机控制信号,ADC采用数据用于FOC算法、目标转速的输入,电机控制信号用于控制电机的启停;输出为三相占空比Duty Cycle和LED信号,分别用来执行控制和显示控制状态(参见上图)。



    中间变量

    中间量不会输出,是在算法处理数据过程中产生的,可以体系算法的特征,比如处理后的电压信号、电流信号、无感算法估计的速度和位置、控制信号等。

    这部分通过Bus数据对象实现对数据结构体的定义。下图中的varFOC即包含了所有的中间量。


MBD实战之电机控制 第05期:把模型嵌入代码w4.jpg

中间变量和StateFlow Chart - From autoMBD



    状态机设计

    这部分也就是明确电机控制软件具有的功能、任务。

    在不同的阶段,算法执行的内容是不一样的,例如关闭电机的状态下,不需要跑FOC算法。本次项目中,只实现了简单启动、关闭和故障清楚的功能,状态相对简单,如下图所示:


MBD实战之电机控制 第05期:把模型嵌入代码w5.jpg

算法模型状态机 - From autoMBD

Tips:如果有看之前提出的电机控制框架模型,应该就能发现这里的状态机与之非常相似。实际上这里的状态机是对原版的更新和优化,把函数调用方式从Function Call换成了Simulink Function,在Chart内部即可调用函数,不需要把调用的函数模型建立在Chart外面了。具体状态机的讲解可以参考电机实战的03期和04期的内容。

要注意的是,在Run状态下,调用的FOC核心算法模型,其包含了独属于FOC算法的子状态机和中间变量FocInternalPara。这里将FOC算法部分和与非FOC算法部分进行了分别设计,增加算法的灵活性和移植性。

MBD实战之电机控制 第05期:把模型嵌入代码w6.jpg

FOC核心数学算法 - From autoMBD

MBD实战之电机控制 第05期:把模型嵌入代码w7.jpg

FOC核心算法模型子状态机 - From autoMBD

Tips:该状态机只负责FOC算法的状态控制,包括对齐、开环、追踪和无感闭环四个状态,具体内容可以参考电机实战的04期的内容。

算法模型的内容就介绍到这里,其他的细节还请下载模型后打开查看。模型创建好了就可以生成代码了:

MBD实战之电机控制 第05期:把模型嵌入代码w8.jpg

算法模型生成代码 - From autoMBD

这里生成的代码即集成时需要用到的代码。生成代码文件很多,其实是可以优化减少的,后续再开新的文章来讲解如何优化生成的代码。

2. 实现底层代码

Tips:底层代码的实现本质上就是手写代码的开发方式,只不过不做算法部分而已,所以需要有一定的编程基础。网络上有很多嵌入式开发的文章和案例,autoMBD的目标也不是介绍手写代码的开发,所以这里不会很细致的讲如何实现底层代码,仅从方法论的角度简单介绍一下。

可以发现,上述的算法模型中已经没有任何的硬件支持包的模块了,即已经不包含任何的底层驱动、外设初始化等等的功能,这部分由底层代码来实现。

要开发底层代码,需要用到集成开发环境IDE。对于S32K1系列芯片,NXP公司提供了一套免费的、专用于NXP汽车芯片的IDE工具——面向Arm?的S32 Design Studio(简称S32DS)。

MBD实战之电机控制 第05期:把模型嵌入代码w9.jpg

面向Arm?的S32 Design Studio - From NXP

打开S32DS后,窗口如下图所示:

MBD实战之电机控制 第05期:把模型嵌入代码w10.jpg

S32DS窗口 - From NXP

要配置底层驱动,需要用到S32K1专用图形配置工具——Processor Expert(上图中红色方框中的部分)。该工具能够实现对S32K1系列芯片的灵活配置。而底层驱动则需要使用S32K1 SDK,该SDK具有丰富的API供开发者使用,功能完善且强大。

关于Processor Expert和S32K1 SDK的使用,更多请参考NXP的技术文档。

本次项目中我也实现了一个基本的PMSM无感电机控制的底层代码,包括外设配置、MCU初始化和中断管理的内容,该DS工程位于仓库的这个位置:



    S32DS_Prjct -> FOC_Ctrl_MBD_Integration。



该底层代码实现的功能与上一期中的电机控制框架基本一致。

3. 模型代码的集成

到这里,模型代码和底层代码都已经实现,如何集成呢?实际上生成的代码中有一个ert_main.c的文件就给出了集成的实例,读者可以参考该文件。

模型代码集成的过程是比较简单的,主要分为以下几步:



    模型初始化:FOC_Ctrl_CodeModel_initialize()

    周期性运算Step函数:FOC_Ctrl_CodeModel_step()或rt_OneStep()

    终止(如果需要):????FOC_Ctrl_CodeModel_terminate()



Tips:调用并运行模型算法的是Step函数,即计算模型的一个步长。两个Step函数功能是一样的,rt_OneStep()在”模型Step“的基础上封装了一层错误检查,但该错误检查基本上起不了什么作用。

特别要注意的是周期性调用Step函数的周期,需要和算法的运算周期保持一致,不然可能会出现错误结果。

本次项目中,模型初始化在MCU初始化完成后调用;Step函数的调用在Motor_ISR中;模型算法不会终止,所以不需要Terminate函数。具体的代码如下:

/* Low level driver initialization */    MCU_Init();    GD3000_Init();    FMSTR_Init();
/* Initialize model */    FOC_Ctrl_CodeModel_initialize();
    MCU_Start();
/* Forever loop */while(1)    {        FMSTR_Poll();        mainCnt++;    }
voidMotor_ISR(void){/* Read ADC*/    ADC_DRV_GetChanResult(INST_ADCONV1, 0, &adcResult[0][0]); /* V_REFSH */    ADC_DRV_GetChanResult(INST_ADCONV1, 1, &adcResult[0][1]); /* Pot     */    ADC_DRV_GetChanResult(INST_ADCONV1, 2, &adcResult[0][2]); /* iA      */    ADC_DRV_GetChanResult(INST_ADCONV1, 3, &adcResult[0][3]); /* Temp    */    ADC_DRV_GetChanResult(INST_ADCONV2, 0, &adcResult[1][0]); /* V_REFSL */    ADC_DRV_GetChanResult(INST_ADCONV2, 1, &adcResult[1][1]); /* uDC     */    ADC_DRV_GetChanResult(INST_ADCONV2, 2, &adcResult[1][2]); /* iB      */    ADC_DRV_GetChanResult(INST_ADCONV2, 3, &adcResult[1][3]); /* iDC     */
/* Set model inputs here */    FOC_Ctrl_CodeModel_U.ADCinput[0] = adcResult[0][2];    FOC_Ctrl_CodeModel_U.ADCinput[1] = adcResult[1][2];    FOC_Ctrl_CodeModel_U.ADCinput[2] = adcResult[1][3];    FOC_Ctrl_CodeModel_U.ADCinput[3] = adcResult[1][1];    FOC_Ctrl_CodeModel_U.ADCinput[4] = adcResult[0][1];    FOC_Ctrl_CodeModel_U.FaultSwitch = faultSwitch;    FOC_Ctrl_CodeModel_U.MotorSwitch = motorSwitch;/* Calculate one-step for the modle */    rt_OneStep();/* Get model output here */    pwmDuty[0] = (uint16_t)(FULL_DUTY * FOC_Ctrl_CodeModel_Y.DUTY[0]);    pwmDuty[1] = (uint16_t)(FULL_DUTY * FOC_Ctrl_CodeModel_Y.DUTY[1]);    pwmDuty[2] = (uint16_t)(FULL_DUTY * FOC_Ctrl_CodeModel_Y.DUTY[2]);
/* Set PWM duty */    FTM_DRV_FastUpdatePwmChannels(INST_FLEXTIMER_PWM1, 3, pwmChannels, pwmDuty, true);
/* Clear FTM flag */    FTM_DRV_ClearStatusFlags(3, FTM_TIME_OVER_FLOW_FLAG|FTM_RELOAD_FLAG);}

可以看到模型的传参和返回结果是通过全局变量的方式实现的:



    输入全局变量:FOC_Ctrl_CodeModel_U

    输出全局变量:FOC_Ctrl_CodeModel_Y


这里是可以通过对算法模型进行设置的,可以改成传参的方式,本次项目使用的是默认方式。

前文中的算法模型的中间变量也是一个全局变量:



    模型中间变量:FOC_Ctrl_CodeModel_DW.varFOC



到这里模型代码的集成就算完成了,可以像手写代码的方式调试和下载代码了。

其实还有很多关于代码集成的细节和技巧没有提到,本次项目实现的模型代码完全是在默认设置下生成的。

Simulink是可以对生成的代码进行很细致的控制的,例如控制代码生成的位置、函数传参的方式、函数的复用等,还有代码如何优化,如何创建数据字典来管理模型数据。关于这些技巧和实践,我会在后续的文章中进行介绍,欢迎持续关注作者
MBD实战之电机控制 第05期:把模型嵌入代码w11.jpg


3 写在最后

先说一点题外话,我最近才了解到NXP新发布的S32K3系列芯片,其最新的硬件支持包MBDT有了一个较大的升级,它的底层模块已经实现了完全等同于S32K3专用底层配置工具的功能。这一升级使得底层驱动这一功能也可以在Simulink模型中实现。

现在的S32K1的MBDT底层模块由于功能很有限,很多情况下底层驱动的配置必须使用专用配置工具Processor Expert来配置才能满足需求。而S32K3的更新有望改变这一现状,有兴趣的可以了解一下S32K3芯片和它最新的硬件支持包MBDT。

MBD实战之电机控制 第05期:把模型嵌入代码w12.jpg

S32K3X4EVB-Q172 - From NXP

到本篇文章为止,我认为关于MBD的基础部分,应该已经写得差不多了,如果看完我的全部文章,应该能有所收获。

下一期我将对所有发布的文章,和GitHub仓库进行一次清理和总结。

明年autoMBD将会进入到下一个阶段,会尝试更有挑战的内容,例如TLC语言、MBD实现通信、MBD与OS的结合等。所以请持续关注autoMBD,更多技术分享不容错过。

2021年还剩下最后一周,在这里提前祝大家2022年新年快乐,希望各位读者能在2022年收获更多!

快速发帖

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|Archiver|汽车工程师之家 ( 渝ICP备18012993号-1 )

GMT+8, 22-11-2024 21:03 , Processed in 0.537508 second(s), 31 queries .

Powered by Discuz! X3.5

© 2001-2013 Comsenz Inc.