当前位置:   article > 正文

【研发日记】Matlab/Simulink软件优化(二)——通信负载柔性均衡算法

【研发日记】Matlab/Simulink软件优化(二)——通信负载柔性均衡算法

文章目录


前言

背景介绍

初始代码

优化代码

分析和应用

总结


前言

        见《【研发日记】Matlab/Simulink软件优化(一)——动态内存负荷压缩

背景介绍

        在一个嵌入式软件开发项目中,需要设计一个ECU节点的CAN网路数据发送,需求是在500k的通信波特率上,动态发送10到40帧报文,发送一轮的时间最长不能超过50ms。示例如下:

初始代码

        一开始算法开发的思路非常简单,就是设置一个50ms的任务,用for循环把要发送的数据装入CAN发送Buffer。示例如下:

        以上模型生成的代码如下:

  1. #include "untitled.h"
  2. #include "untitled_private.h"
  3. /* Block signals (default storage) */
  4. B_untitled_T untitled_B;
  5. /* Block states (default storage) */
  6. DW_untitled_T untitled_DW;
  7. /* Real-time model */
  8. static RT_MODEL_untitled_T untitled_M_;
  9. RT_MODEL_untitled_T *const untitled_M = &untitled_M_;
  10. /* Model step function */
  11. void untitled_step(void)
  12. {
  13. int32_T i;
  14. int32_T rtb_Gain;
  15. int32_T s6_iter;
  16. char_T *sErr;
  17. void *inputMsgRef;
  18. /* Outputs for Enabled SubSystem: '<Root>/Subsystem' incorporates:
  19. * EnablePort: '<S3>/Enable'
  20. */
  21. /* RelationalOperator: '<S1>/Compare' incorporates:
  22. * Constant: '<S1>/Constant'
  23. * UnitDelay: '<S2>/Output'
  24. */
  25. if (untitled_DW.Output_DSTATE == 0) {
  26. if (!untitled_DW.Subsystem_MODE) {
  27. /* Enable for Iterator SubSystem: '<S3>/For Iterator Subsystem' */
  28. /* Enable for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  29. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  30. LibReset(&untitled_DW.CANTransmit_CANTransmit[0U]);
  31. LibStart(&untitled_DW.CANTransmit_CANTransmit[0U]);
  32. if (*sErr != 0) {
  33. rtmSetErrorStatus(untitled_M, sErr);
  34. rtmSetStopRequested(untitled_M, 1);
  35. }
  36. /* End of Enable for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  37. /* End of Enable for SubSystem: '<S3>/For Iterator Subsystem' */
  38. untitled_DW.Subsystem_MODE = true;
  39. }
  40. /* Outputs for Iterator SubSystem: '<S3>/For Iterator Subsystem' incorporates:
  41. * ForIterator: '<S6>/For Iterator'
  42. */
  43. for (s6_iter = 0; s6_iter < 40; s6_iter++) {
  44. /* Gain: '<S6>/Gain' */
  45. rtb_Gain = s6_iter << 3;
  46. for (i = 0; i < 8; i++) {
  47. /* Selector: '<S6>/Selector' incorporates:
  48. * Constant: '<Root>/Constant'
  49. */
  50. untitled_B.Selector[i] = untitled_ConstP.Constant_Value[i + rtb_Gain];
  51. }
  52. /* S-Function (scanpack): '<S6>/CAN Pack' */
  53. /* S-Function (scanpack): '<S6>/CAN Pack' */
  54. untitled_B.CANPack.ID = 10U;
  55. untitled_B.CANPack.Length = 8U;
  56. untitled_B.CANPack.Extended = 0U;
  57. untitled_B.CANPack.Remote = 0;
  58. untitled_B.CANPack.Data[0] = 0;
  59. untitled_B.CANPack.Data[1] = 0;
  60. untitled_B.CANPack.Data[2] = 0;
  61. untitled_B.CANPack.Data[3] = 0;
  62. untitled_B.CANPack.Data[4] = 0;
  63. untitled_B.CANPack.Data[5] = 0;
  64. untitled_B.CANPack.Data[6] = 0;
  65. untitled_B.CANPack.Data[7] = 0;
  66. {
  67. (void) memcpy((untitled_B.CANPack.Data), &untitled_B.Selector[0],
  68. 8 * sizeof(uint8_T));
  69. }
  70. /* S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  71. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  72. /* S-Function (scanpack): '<S6>/CAN Pack' incorporates:
  73. * S-Function (svntcantransmit): '<S6>/CAN Transmit'
  74. */
  75. inputMsgRef = &untitled_B.CANPack;
  76. /* S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  77. LibOutputs_CANTransmit(&untitled_DW.CANTransmit_CANTransmit[0U],
  78. inputMsgRef, 1);
  79. if (*sErr != 0) {
  80. rtmSetErrorStatus(untitled_M, sErr);
  81. rtmSetStopRequested(untitled_M, 1);
  82. }
  83. }
  84. /* End of Outputs for SubSystem: '<S3>/For Iterator Subsystem' */
  85. } else {
  86. if (untitled_DW.Subsystem_MODE) {
  87. /* Disable for Iterator SubSystem: '<S3>/For Iterator Subsystem' */
  88. /* Disable for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  89. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  90. LibReset(&untitled_DW.CANTransmit_CANTransmit[0U]);
  91. if (*sErr != 0) {
  92. rtmSetErrorStatus(untitled_M, sErr);
  93. rtmSetStopRequested(untitled_M, 1);
  94. }
  95. /* End of Disable for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  96. /* End of Disable for SubSystem: '<S3>/For Iterator Subsystem' */
  97. untitled_DW.Subsystem_MODE = false;
  98. }
  99. }
  100. /* End of RelationalOperator: '<S1>/Compare' */
  101. /* End of Outputs for SubSystem: '<Root>/Subsystem' */
  102. /* Switch: '<S5>/FixPt Switch' incorporates:
  103. * Constant: '<S4>/FixPt Constant'
  104. * Constant: '<S5>/Constant'
  105. * Sum: '<S4>/FixPt Sum1'
  106. * UnitDelay: '<S2>/Output'
  107. */
  108. if ((uint8_T)(untitled_DW.Output_DSTATE + 1U) > 49) {
  109. untitled_DW.Output_DSTATE = 0U;
  110. } else {
  111. untitled_DW.Output_DSTATE++;
  112. }
  113. /* End of Switch: '<S5>/FixPt Switch' */
  114. }
  115. /* Model initialize function */
  116. void untitled_initialize(void)
  117. {
  118. {
  119. int32_T bitParams[4];
  120. char_T *sErr;
  121. /* Start for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  122. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  123. CreateHostLibrary("slhostlibcantransmit.dll",
  124. &untitled_DW.CANTransmit_CANTransmit[0U]);
  125. if (*sErr == 0) {
  126. bitParams[0U] = 1;
  127. bitParams[1U] = 4;
  128. bitParams[2U] = 3;
  129. bitParams[3U] = 1;
  130. LibCreate_CANTransmit(&untitled_DW.CANTransmit_CANTransmit[0U], "vector",
  131. "slvectorxlwrapper.dll", "Virtual", 0, 1, 1, 1,
  132. "canslconverter", "vectorxlplugin", 500000.0,
  133. &bitParams[0U], 0, 0, 0, 1.0, 0);
  134. }
  135. if (*sErr == 0) {
  136. LibStart(&untitled_DW.CANTransmit_CANTransmit[0U]);
  137. }
  138. if (*sErr != 0) {
  139. rtmSetErrorStatus(untitled_M, sErr);
  140. rtmSetStopRequested(untitled_M, 1);
  141. }
  142. /* End of Start for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  143. /* End of SystemInitialize for SubSystem: '<S3>/For Iterator Subsystem' */
  144. /* End of SystemInitialize for SubSystem: '<Root>/Subsystem' */
  145. }
  146. }
  147. /* Model terminate function */
  148. void untitled_terminate(void)
  149. {
  150. char_T *sErr;
  151. /* Terminate for Enabled SubSystem: '<Root>/Subsystem' */
  152. /* Terminate for Iterator SubSystem: '<S3>/For Iterator Subsystem' */
  153. /* Terminate for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  154. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  155. LibTerminate(&untitled_DW.CANTransmit_CANTransmit[0U]);
  156. if (*sErr != 0) {
  157. rtmSetErrorStatus(untitled_M, sErr);
  158. rtmSetStopRequested(untitled_M, 1);
  159. }
  160. LibDestroy(&untitled_DW.CANTransmit_CANTransmit[0U], 0);
  161. DestroyHostLibrary(&untitled_DW.CANTransmit_CANTransmit[0U]);
  162. /* End of Terminate for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  163. /* End of Terminate for SubSystem: '<S3>/For Iterator Subsystem' */
  164. /* End of Terminate for SubSystem: '<Root>/Subsystem' */
  165. }

        按照上述示例生成的代码,调试时监测到CAN网络上的瞬时负载率,在0%和100%之间来回跳变。0%和100%各自占用一段时间,两者的比例随着发送报文数量的多少变化。当报文数为最大的40帧时,100%瞬时负载率会持续10ms左右,如下图所示:

        分析上述网络通信的特点,100%瞬时负载率持续的10ms时间段里,肯定会有其他ECU节点也发出报文,这时候CAN网络就会自动根据ID的优先级分配谁先发,谁等待后发,即出现冲突抢占现象。在CAN网络中如果冲突抢占是偶发的,那就不会有太大影响,但是如果冲突抢占是持续的,那就不是我们希望看到的了。

优化代码

        根据对上述问题的分析,我们发现每个50ms周期里边都还有至少40ms是没有利用的,那么只要把前面拥挤的报文分散开到后面一部分,就能解决前面的问题了。然后如果还有剩余的时间没有利用,那么我们就柔性地缩短50ms的周期时长,提高数据发送的频率。这样既能解决前面的问题,又能把总线资源充分利用起来,用于提高我们网络通信的性能。示例如下:

        以上模型生成的代码如下:

  1. #include "untitled.h"
  2. #include "untitled_private.h"
  3. /* Named constants for Chart: '<S1>/Chart' */
  4. #define untitled_IN_a ((uint8_T)1U)
  5. #define untitled_IN_a1 ((uint8_T)2U)
  6. /* Block signals (default storage) */
  7. B_untitled_T untitled_B;
  8. /* Block states (default storage) */
  9. DW_untitled_T untitled_DW;
  10. /* Real-time model */
  11. static RT_MODEL_untitled_T untitled_M_;
  12. RT_MODEL_untitled_T *const untitled_M = &untitled_M_;
  13. /* Model step function */
  14. void untitled_step(void)
  15. {
  16. real_T rtb_Gain;
  17. int32_T i;
  18. char_T *sErr;
  19. void *inputMsgRef;
  20. /* Chart: '<S1>/Chart' incorporates:
  21. * Constant: '<Root>/Constant3'
  22. */
  23. if (untitled_DW.is_active_c3_untitled == 0U) {
  24. untitled_DW.is_active_c3_untitled = 1U;
  25. untitled_DW.is_c3_untitled = untitled_IN_a;
  26. untitled_B.FrameIndex = 0.0;
  27. } else if (untitled_DW.is_c3_untitled == untitled_IN_a) {
  28. untitled_DW.is_c3_untitled = untitled_IN_a1;
  29. untitled_B.FrameIndex++;
  30. } else {
  31. /* case IN_a1: */
  32. if (untitled_B.FrameIndex >= 39.0) {
  33. untitled_DW.is_c3_untitled = untitled_IN_a;
  34. untitled_B.FrameIndex = 0.0;
  35. }
  36. }
  37. /* End of Chart: '<S1>/Chart' */
  38. /* Gain: '<S1>/Gain' */
  39. rtb_Gain = 8.0 * untitled_B.FrameIndex;
  40. for (i = 0; i < 8; i++) {
  41. /* Selector: '<S1>/Selector' incorporates:
  42. * Constant: '<Root>/Constant2'
  43. */
  44. untitled_B.Selector[i] = untitled_ConstP.Constant2_Value[i + (int32_T)
  45. rtb_Gain];
  46. }
  47. /* S-Function (scanpack): '<S1>/CAN Pack' */
  48. /* S-Function (scanpack): '<S1>/CAN Pack' */
  49. untitled_B.CANPack.ID = 10U;
  50. untitled_B.CANPack.Length = 8U;
  51. untitled_B.CANPack.Extended = 0U;
  52. untitled_B.CANPack.Remote = 0;
  53. untitled_B.CANPack.Data[0] = 0;
  54. untitled_B.CANPack.Data[1] = 0;
  55. untitled_B.CANPack.Data[2] = 0;
  56. untitled_B.CANPack.Data[3] = 0;
  57. untitled_B.CANPack.Data[4] = 0;
  58. untitled_B.CANPack.Data[5] = 0;
  59. untitled_B.CANPack.Data[6] = 0;
  60. untitled_B.CANPack.Data[7] = 0;
  61. {
  62. (void) memcpy((untitled_B.CANPack.Data), &untitled_B.Selector[0],
  63. 8 * sizeof(uint8_T));
  64. }
  65. /* S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  66. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  67. /* S-Function (scanpack): '<S1>/CAN Pack' incorporates:
  68. * S-Function (svntcantransmit): '<S1>/CAN Transmit'
  69. */
  70. inputMsgRef = &untitled_B.CANPack;
  71. /* S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  72. LibOutputs_CANTransmit(&untitled_DW.CANTransmit_CANTransmit[0U], inputMsgRef,
  73. 1);
  74. if (*sErr != 0) {
  75. rtmSetErrorStatus(untitled_M, sErr);
  76. rtmSetStopRequested(untitled_M, 1);
  77. }
  78. }
  79. /* Model initialize function */
  80. void untitled_initialize(void)
  81. {
  82. {
  83. int32_T bitParams[4];
  84. char_T *sErr;
  85. /* Start for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  86. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  87. CreateHostLibrary("slhostlibcantransmit.dll",
  88. &untitled_DW.CANTransmit_CANTransmit[0U]);
  89. if (*sErr == 0) {
  90. bitParams[0U] = 1;
  91. bitParams[1U] = 4;
  92. bitParams[2U] = 3;
  93. bitParams[3U] = 1;
  94. LibCreate_CANTransmit(&untitled_DW.CANTransmit_CANTransmit[0U], "vector",
  95. "slvectorxlwrapper.dll", "Virtual", 0, 1, 1, 1,
  96. "canslconverter", "vectorxlplugin", 500000.0,
  97. &bitParams[0U], 0, 0, 0, 1.0, 0);
  98. }
  99. if (*sErr == 0) {
  100. LibStart(&untitled_DW.CANTransmit_CANTransmit[0U]);
  101. }
  102. if (*sErr != 0) {
  103. rtmSetErrorStatus(untitled_M, sErr);
  104. rtmSetStopRequested(untitled_M, 1);
  105. }
  106. /* End of Start for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  107. /* Enable for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  108. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  109. LibReset(&untitled_DW.CANTransmit_CANTransmit[0U]);
  110. LibStart(&untitled_DW.CANTransmit_CANTransmit[0U]);
  111. if (*sErr != 0) {
  112. rtmSetErrorStatus(untitled_M, sErr);
  113. rtmSetStopRequested(untitled_M, 1);
  114. }
  115. /* End of Enable for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  116. }
  117. }
  118. /* Model terminate function */
  119. void untitled_terminate(void)
  120. {
  121. char_T *sErr;
  122. /* Terminate for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  123. sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  124. LibTerminate(&untitled_DW.CANTransmit_CANTransmit[0U]);
  125. if (*sErr != 0) {
  126. rtmSetErrorStatus(untitled_M, sErr);
  127. rtmSetStopRequested(untitled_M, 1);
  128. }
  129. LibDestroy(&untitled_DW.CANTransmit_CANTransmit[0U], 0);
  130. DestroyHostLibrary(&untitled_DW.CANTransmit_CANTransmit[0U]);
  131. /* End of Terminate for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  132. }

        按照上述示例生成的代码,调试时监测到CAN网络上的瞬时负载率非常均匀地保持在25%左右。并且不管报文数量的如何变化,软件都能自动地柔性处理,既不会负载率过高,也不会总线资源浪费,同时又能将报文频率性能发挥到最大。如下图所示:

        分析上述网络通信的特点,已实现了项目中的需求,同时也利用通信负载柔性均衡算法把性能发挥到了最优。

分析和应用

        通信负载均衡,在不同的软件开发项目中重要性是不一样的。一种是实时性要求高的的应用(例如底盘控制),每一帧消息都要在准确的时间发送出去,不允许冲突抢占导致的延误。另一种是网络通信资源非常小的总线(例如低速CAN),单位时间内能发送的报文数量本来就比较少,所以更要仔细计算充分利用,要不然很容易因为负载不均衡导致报文阻塞。

        使用通信负载柔性均衡算法时,需要注意如下几点:

        1、不同波特率,理想负载率下,单位时间对应的报文数量需要仔细的计算,才能设定出最优的算法。例如:500k波特率,在25%理想负载率下,1ms对应的报文数量就是1帧。同理如果1M波特率,那么1ms对应的报文数量就是2帧。

        2、计算好最优的理论算法之后,还要更具自己处理器的性能,设定一个合适的控制粒度。例如:自己的软件最快可以1ms运算一圈,那么就可以1ms控制一次发送1帧或者2帧。如果自己的软件最快只能5ms运算一圈,那么同理就5ms控制一次发送5帧或者10帧。这里的控制粒度越小,负载均衡的效果也越好,但是并非所有的平台都能达到理论极限,只要在自己平台的基础上发挥到最优即可。

        3、对于有网络管理机制的应用场景,需求方可能不希望我们的50ms周期发生变化。例如,网络上的主ECU节点利用同步信号,控制着各个从ECU节点分别占用这50ms中的一小段。当我们自己节点的报文发送完之后,需要等着下一个50ms的到来,或者下一个同步信号的到来。这时候就要把FrameIndex的循环Limit固定成50ms,然后在Transmit模块上加一个使能条件FrameIndex < FrameNum。这样也能达到我们通信负载柔性均衡的目的,同时也满足主ECU节点的网络管理。

总结

        以上就是本人在嵌入式软件开发中处理通信负载率不均衡问题时,一些个人理解和分析的总结,首先介绍了它的背景情况,然后展示它的初始设计和优化设计,最后分析了通信负载均衡算法的注意事项和应用场景。

        后续还会分享另外几个最近总结的软件优化知识点,欢迎评论区留言、点赞、收藏和关注,这些鼓励和支持都将成文本人持续分享的动力。

        另外,上述例程使用的Demo工程,可以到笔者的主页查找和下载。


        版权声明:原创文章,转载和引用请注明出处和链接,侵权必究

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/707172
推荐阅读
相关标签
  

闽ICP备14008679号