文章目录
前言
背景介绍
问题描述
分析排查
解决方案
总结归纳
前言
见《研发日记,Matlab/Simulink避坑指南(六)——字节分割Bug》
见《研发日记,Matlab/Simulink避坑指南(七)——数据溢出钳位Bug》
见《研发日记,Matlab/Simulink避坑指南(八)——else if分支结构Bug》
见《研发日记,Matlab/Simulink避坑指南(九)——可变数组应用Bug》
见《【研发日记】Matlab/Simulink避坑指南(十)——移位溢出Bug》
背景介绍
最近在一个项目中,使用Simulink写一段嵌入式代码,功能是把一个信号的当前值和上一周期的值都同时拿来使用。乍一看感觉挺简单,想着直接用一个Delay模块就能搞定了,但是就这么一个小应用让我栽了跟头,在整个工程中排查了几个小时才找到问题。下面就来分享一下这一小段个人经历。
一开始的模型搭建大概是下面这个样子:
问题描述
按照上述示例,预想应该可以正常运行,但是实际运行时却跟预想的不一样,示例如下:
我的系统运行周期是10ms,希望看到的是Out2输出一个慢10ms的信号,但是看到运行结果是慢了100ms。
分析排查
使用前段时间解锁的Debug技能《【研发日记】Matlab/Simulink技能解锁(一)——在Simulink编辑窗口Debug》,来调试一下上述模型,示例如下:
使用Step Forward单步运行,打上Signal Value Lable,可以看到Out2的输出值并不是每10ms都更新,而是累积运行10步后才更新,更新的数据是100ms前Out1的值。
这时候我们把模型的Sample Time打开,看到的结果是信号源模块和Delay模块居然都是100ms,示例如下:
信号源模块的100ms没有问题,但是Delay模块我是想让他10ms的,打开Delay模块的配置看到Sample Time设置的是-1,我们是想让他继承系统运行的10ms周期的,示例如下:
根据上述结果推测,Delay模块没有继承系统的10ms周期,而是继承了信号源模块的100ms周期,所以Out2的输出值输出值出现了错误,问题应该就出在这里。
解决方案
根据上面的分析,调整Delay模块的Sample Time,直接把它写成固定的10ms,示例如下:
重新运行程序,问题不再出现,Bug修复完工,示例如下:
至此,这个使用Delay模块做信号延迟的应用就算是没问题了,生的代码示例如下:
#include "DelayPeriod.h"#include "DelayPeriod_private.h"/* Block signals (default storage) */B_DelayPeriod_T DelayPeriod_B;/* Block states (default storage) */DW_DelayPeriod_T DelayPeriod_DW;/* External outputs (root outports fed by signals with default storage) */ExtY_DelayPeriod_T DelayPeriod_Y;/* Real-time model */static RT_MODEL_DelayPeriod_T DelayPeriod_M_;RT_MODEL_DelayPeriod_T *const DelayPeriod_M = &DelayPeriod_M_;static void rate_scheduler(void);/* * This function updates active task flag for each subrate. * The function is called at model base rate, hence the * generated code self-manages all its subrates. */static void rate_scheduler(void){ /* Compute which subrates run during the next base time step. Subrates * are an integer multiple of the base rate counter. Therefore, the subtask * counter is reset when it reaches its limit (zero means run). */ (DelayPeriod_M->Timing.TaskCounters.TID[1])++; if ((DelayPeriod_M->Timing.TaskCounters.TID[1]) > 9) {/* Sample time: [0.1s, 0.0s] */ DelayPeriod_M->Timing.TaskCounters.TID[1] = 0; }}real_T rt_urand_Upu32_Yd_f_pw_snf(uint32_T *u){ uint32_T hi; uint32_T lo; /* Uniform random number generator (random number between 0 and 1) #define IA 16807 magic multiplier = 7^5 #define IM 2147483647 modulus = 2^31-1 #define IQ 127773 IM div IA #define IR 2836 IM modulo IA #define S 4.656612875245797e-10 reciprocal of 2^31-1 test = IA * (seed % IQ) - IR * (seed/IQ) seed = test < 0 ? (test + IM) : test return (seed*S) */ lo = *u % 127773U * 16807U; hi = *u / 127773U * 2836U; if (lo < hi) { *u = 2147483647U - (hi - lo); } else { *u = lo - hi; } return (real_T)*u * 4.6566128752457969E-10;}/* Model step function */void DelayPeriod_step(void){ /* Outport: '<Root>/Out2' incorporates: * Delay: '<Root>/Delay' */ DelayPeriod_Y.Out2 = DelayPeriod_DW.Delay_DSTATE; if (DelayPeriod_M->Timing.TaskCounters.TID[1] == 0) { /* UniformRandomNumber: '<Root>/Uniform Random Number' */ DelayPeriod_B.UniformRandomNumber = DelayPeriod_DW.UniformRandomNumber_NextOutput; /* Outport: '<Root>/Out1' */ DelayPeriod_Y.Out1 = DelayPeriod_B.UniformRandomNumber; /* Update for UniformRandomNumber: '<Root>/Uniform Random Number' */ DelayPeriod_DW.UniformRandomNumber_NextOutput = rt_urand_Upu32_Yd_f_pw_snf (&DelayPeriod_DW.RandSeed) * 2.0 + -1.0; } /* Update for Delay: '<Root>/Delay' */ DelayPeriod_DW.Delay_DSTATE = DelayPeriod_B.UniformRandomNumber; rate_scheduler();}
总结归纳
那么这个代码调试过程,发现的问题可以积累下来这么几条小经验以供自己将来使用,也供广大网友参考:
1、Delay模块对信号的延迟1个时间单位,这个时间长度的绝对值取决于Delay模块运行周期。比如它在模型顶层10ms运行一次,那么就是延时10ms。如果在使能子模块中100ms运行一次,那么就是延时100ms.
2、各个模块中的Sample Time参数,如果写成固定值,那么系统运行时就会优先采用这个周期。如果写成-1,那么就是交给系统自动分配一个有继承关系的运行周期。
3、除了上述自动分配的Sample Time,Matlab/Simulink中有很多处自动分配的功能,这种自动化功能大大地方便了我们的建模开发工作,但是引入了很多不确定性。所以如果我们对一些自动分配的规则没有熟练掌握时,还是要尽量自己手动开发。
以上就是本人在解决Delay模块周期Bug时,一些个人理解和分析的总结,首先介绍了基本的项目背景,然后描述了问题的想象,最后分析排查了Bug原因,并给出了问题解决方案。
后续还会分享其他的,使用Matlab/Simulink进行研发时遇到的Bug,欢迎评论区留言、点赞、收藏和关注,这些鼓励和支持都将成文本人持续分享的动力。
上述例程使用的Demo工程,可以到笔者的主页查找和下载。
版权声明:原创文章,转载请注明出处与链接,违者必究!