文章目录
前言
背景介绍
问题描述
分析排查
解决方案
总结归纳
前言
见《研发日记,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工程,可以到笔者的主页查找和下载。
版权声明:原创文章,转载请注明出处与链接,违者必究!