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

news2024/11/26 17:34:42

文章目录


前言

背景介绍

初始代码

优化代码

分析和应用

总结


前言

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

背景介绍

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

初始代码

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

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


#include "untitled.h"
#include "untitled_private.h"

/* Block signals (default storage) */
B_untitled_T untitled_B;

/* Block states (default storage) */
DW_untitled_T untitled_DW;

/* Real-time model */
static RT_MODEL_untitled_T untitled_M_;
RT_MODEL_untitled_T *const untitled_M = &untitled_M_;

/* Model step function */
void untitled_step(void)
{
  int32_T i;
  int32_T rtb_Gain;
  int32_T s6_iter;
  char_T *sErr;
  void *inputMsgRef;

  /* Outputs for Enabled SubSystem: '<Root>/Subsystem' incorporates:
   *  EnablePort: '<S3>/Enable'
   */
  /* RelationalOperator: '<S1>/Compare' incorporates:
   *  Constant: '<S1>/Constant'
   *  UnitDelay: '<S2>/Output'
   */
  if (untitled_DW.Output_DSTATE == 0) {
    if (!untitled_DW.Subsystem_MODE) {
      /* Enable for Iterator SubSystem: '<S3>/For Iterator Subsystem' */
      /* Enable for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
      sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
      LibReset(&untitled_DW.CANTransmit_CANTransmit[0U]);
      LibStart(&untitled_DW.CANTransmit_CANTransmit[0U]);
      if (*sErr != 0) {
        rtmSetErrorStatus(untitled_M, sErr);
        rtmSetStopRequested(untitled_M, 1);
      }

      /* End of Enable for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
      /* End of Enable for SubSystem: '<S3>/For Iterator Subsystem' */
      untitled_DW.Subsystem_MODE = true;
    }

    /* Outputs for Iterator SubSystem: '<S3>/For Iterator Subsystem' incorporates:
     *  ForIterator: '<S6>/For Iterator'
     */
    for (s6_iter = 0; s6_iter < 40; s6_iter++) {
      /* Gain: '<S6>/Gain' */
      rtb_Gain = s6_iter << 3;
      for (i = 0; i < 8; i++) {
        /* Selector: '<S6>/Selector' incorporates:
         *  Constant: '<Root>/Constant'
         */
        untitled_B.Selector[i] = untitled_ConstP.Constant_Value[i + rtb_Gain];
      }

      /* S-Function (scanpack): '<S6>/CAN Pack' */
      /* S-Function (scanpack): '<S6>/CAN Pack' */
      untitled_B.CANPack.ID = 10U;
      untitled_B.CANPack.Length = 8U;
      untitled_B.CANPack.Extended = 0U;
      untitled_B.CANPack.Remote = 0;
      untitled_B.CANPack.Data[0] = 0;
      untitled_B.CANPack.Data[1] = 0;
      untitled_B.CANPack.Data[2] = 0;
      untitled_B.CANPack.Data[3] = 0;
      untitled_B.CANPack.Data[4] = 0;
      untitled_B.CANPack.Data[5] = 0;
      untitled_B.CANPack.Data[6] = 0;
      untitled_B.CANPack.Data[7] = 0;

      {
        (void) memcpy((untitled_B.CANPack.Data), &untitled_B.Selector[0],
                      8 * sizeof(uint8_T));
      }

      /* S-Function (svntcantransmit): '<S6>/CAN Transmit' */
      sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);

      /* S-Function (scanpack): '<S6>/CAN Pack' incorporates:
       *  S-Function (svntcantransmit): '<S6>/CAN Transmit'
       */
      inputMsgRef = &untitled_B.CANPack;

      /* S-Function (svntcantransmit): '<S6>/CAN Transmit' */
      LibOutputs_CANTransmit(&untitled_DW.CANTransmit_CANTransmit[0U],
        inputMsgRef, 1);
      if (*sErr != 0) {
        rtmSetErrorStatus(untitled_M, sErr);
        rtmSetStopRequested(untitled_M, 1);
      }
    }

    /* End of Outputs for SubSystem: '<S3>/For Iterator Subsystem' */
  } else {
    if (untitled_DW.Subsystem_MODE) {
      /* Disable for Iterator SubSystem: '<S3>/For Iterator Subsystem' */
      /* Disable for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
      sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
      LibReset(&untitled_DW.CANTransmit_CANTransmit[0U]);
      if (*sErr != 0) {
        rtmSetErrorStatus(untitled_M, sErr);
        rtmSetStopRequested(untitled_M, 1);
      }

      /* End of Disable for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
      /* End of Disable for SubSystem: '<S3>/For Iterator Subsystem' */
      untitled_DW.Subsystem_MODE = false;
    }
  }

  /* End of RelationalOperator: '<S1>/Compare' */
  /* End of Outputs for SubSystem: '<Root>/Subsystem' */

  /* Switch: '<S5>/FixPt Switch' incorporates:
   *  Constant: '<S4>/FixPt Constant'
   *  Constant: '<S5>/Constant'
   *  Sum: '<S4>/FixPt Sum1'
   *  UnitDelay: '<S2>/Output'
   */
  if ((uint8_T)(untitled_DW.Output_DSTATE + 1U) > 49) {
    untitled_DW.Output_DSTATE = 0U;
  } else {
    untitled_DW.Output_DSTATE++;
  }

  /* End of Switch: '<S5>/FixPt Switch' */
}

/* Model initialize function */
void untitled_initialize(void)
{
  {
    int32_T bitParams[4];
    char_T *sErr;

    /* Start for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
    sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
    CreateHostLibrary("slhostlibcantransmit.dll",
                      &untitled_DW.CANTransmit_CANTransmit[0U]);
    if (*sErr == 0) {
      bitParams[0U] = 1;
      bitParams[1U] = 4;
      bitParams[2U] = 3;
      bitParams[3U] = 1;
      LibCreate_CANTransmit(&untitled_DW.CANTransmit_CANTransmit[0U], "vector",
                            "slvectorxlwrapper.dll", "Virtual", 0, 1, 1, 1,
                            "canslconverter", "vectorxlplugin", 500000.0,
                            &bitParams[0U], 0, 0, 0, 1.0, 0);
    }

    if (*sErr == 0) {
      LibStart(&untitled_DW.CANTransmit_CANTransmit[0U]);
    }

    if (*sErr != 0) {
      rtmSetErrorStatus(untitled_M, sErr);
      rtmSetStopRequested(untitled_M, 1);
    }

    /* End of Start for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
    /* End of SystemInitialize for SubSystem: '<S3>/For Iterator Subsystem' */
    /* End of SystemInitialize for SubSystem: '<Root>/Subsystem' */
  }
}

/* Model terminate function */
void untitled_terminate(void)
{
  char_T *sErr;

  /* Terminate for Enabled SubSystem: '<Root>/Subsystem' */
  /* Terminate for Iterator SubSystem: '<S3>/For Iterator Subsystem' */
  /* Terminate for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  LibTerminate(&untitled_DW.CANTransmit_CANTransmit[0U]);
  if (*sErr != 0) {
    rtmSetErrorStatus(untitled_M, sErr);
    rtmSetStopRequested(untitled_M, 1);
  }

  LibDestroy(&untitled_DW.CANTransmit_CANTransmit[0U], 0);
  DestroyHostLibrary(&untitled_DW.CANTransmit_CANTransmit[0U]);

  /* End of Terminate for S-Function (svntcantransmit): '<S6>/CAN Transmit' */
  /* End of Terminate for SubSystem: '<S3>/For Iterator Subsystem' */
  /* End of Terminate for SubSystem: '<Root>/Subsystem' */
}

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

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

优化代码

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

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

#include "untitled.h"
#include "untitled_private.h"

/* Named constants for Chart: '<S1>/Chart' */
#define untitled_IN_a                  ((uint8_T)1U)
#define untitled_IN_a1                 ((uint8_T)2U)

/* Block signals (default storage) */
B_untitled_T untitled_B;

/* Block states (default storage) */
DW_untitled_T untitled_DW;

/* Real-time model */
static RT_MODEL_untitled_T untitled_M_;
RT_MODEL_untitled_T *const untitled_M = &untitled_M_;

/* Model step function */
void untitled_step(void)
{
  real_T rtb_Gain;
  int32_T i;
  char_T *sErr;
  void *inputMsgRef;

  /* Chart: '<S1>/Chart' incorporates:
   *  Constant: '<Root>/Constant3'
   */
  if (untitled_DW.is_active_c3_untitled == 0U) {
    untitled_DW.is_active_c3_untitled = 1U;
    untitled_DW.is_c3_untitled = untitled_IN_a;
    untitled_B.FrameIndex = 0.0;
  } else if (untitled_DW.is_c3_untitled == untitled_IN_a) {
    untitled_DW.is_c3_untitled = untitled_IN_a1;
    untitled_B.FrameIndex++;
  } else {
    /* case IN_a1: */
    if (untitled_B.FrameIndex >= 39.0) {
      untitled_DW.is_c3_untitled = untitled_IN_a;
      untitled_B.FrameIndex = 0.0;
    }
  }

  /* End of Chart: '<S1>/Chart' */

  /* Gain: '<S1>/Gain' */
  rtb_Gain = 8.0 * untitled_B.FrameIndex;
  for (i = 0; i < 8; i++) {
    /* Selector: '<S1>/Selector' incorporates:
     *  Constant: '<Root>/Constant2'
     */
    untitled_B.Selector[i] = untitled_ConstP.Constant2_Value[i + (int32_T)
      rtb_Gain];
  }

  /* S-Function (scanpack): '<S1>/CAN Pack' */
  /* S-Function (scanpack): '<S1>/CAN Pack' */
  untitled_B.CANPack.ID = 10U;
  untitled_B.CANPack.Length = 8U;
  untitled_B.CANPack.Extended = 0U;
  untitled_B.CANPack.Remote = 0;
  untitled_B.CANPack.Data[0] = 0;
  untitled_B.CANPack.Data[1] = 0;
  untitled_B.CANPack.Data[2] = 0;
  untitled_B.CANPack.Data[3] = 0;
  untitled_B.CANPack.Data[4] = 0;
  untitled_B.CANPack.Data[5] = 0;
  untitled_B.CANPack.Data[6] = 0;
  untitled_B.CANPack.Data[7] = 0;

  {
    (void) memcpy((untitled_B.CANPack.Data), &untitled_B.Selector[0],
                  8 * sizeof(uint8_T));
  }

  /* S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);

  /* S-Function (scanpack): '<S1>/CAN Pack' incorporates:
   *  S-Function (svntcantransmit): '<S1>/CAN Transmit'
   */
  inputMsgRef = &untitled_B.CANPack;

  /* S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  LibOutputs_CANTransmit(&untitled_DW.CANTransmit_CANTransmit[0U], inputMsgRef,
    1);
  if (*sErr != 0) {
    rtmSetErrorStatus(untitled_M, sErr);
    rtmSetStopRequested(untitled_M, 1);
  }
}

/* Model initialize function */
void untitled_initialize(void)
{
  {
    int32_T bitParams[4];
    char_T *sErr;

    /* Start for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
    sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
    CreateHostLibrary("slhostlibcantransmit.dll",
                      &untitled_DW.CANTransmit_CANTransmit[0U]);
    if (*sErr == 0) {
      bitParams[0U] = 1;
      bitParams[1U] = 4;
      bitParams[2U] = 3;
      bitParams[3U] = 1;
      LibCreate_CANTransmit(&untitled_DW.CANTransmit_CANTransmit[0U], "vector",
                            "slvectorxlwrapper.dll", "Virtual", 0, 1, 1, 1,
                            "canslconverter", "vectorxlplugin", 500000.0,
                            &bitParams[0U], 0, 0, 0, 1.0, 0);
    }

    if (*sErr == 0) {
      LibStart(&untitled_DW.CANTransmit_CANTransmit[0U]);
    }

    if (*sErr != 0) {
      rtmSetErrorStatus(untitled_M, sErr);
      rtmSetStopRequested(untitled_M, 1);
    }

    /* End of Start for S-Function (svntcantransmit): '<S1>/CAN Transmit' */

    /* Enable for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
    sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
    LibReset(&untitled_DW.CANTransmit_CANTransmit[0U]);
    LibStart(&untitled_DW.CANTransmit_CANTransmit[0U]);
    if (*sErr != 0) {
      rtmSetErrorStatus(untitled_M, sErr);
      rtmSetStopRequested(untitled_M, 1);
    }

    /* End of Enable for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  }
}

/* Model terminate function */
void untitled_terminate(void)
{
  char_T *sErr;

  /* Terminate for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
  sErr = GetErrorBuffer(&untitled_DW.CANTransmit_CANTransmit[0U]);
  LibTerminate(&untitled_DW.CANTransmit_CANTransmit[0U]);
  if (*sErr != 0) {
    rtmSetErrorStatus(untitled_M, sErr);
    rtmSetStopRequested(untitled_M, 1);
  }

  LibDestroy(&untitled_DW.CANTransmit_CANTransmit[0U], 0);
  DestroyHostLibrary(&untitled_DW.CANTransmit_CANTransmit[0U]);

  /* End of Terminate for S-Function (svntcantransmit): '<S1>/CAN Transmit' */
}

        按照上述示例生成的代码,调试时监测到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工程,可以到笔者的主页查找和下载。


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1795371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

特征工程技巧—Bert

前段时间在参加比赛&#xff0c;发现有一些比赛上公开的代码&#xff0c;其中的数据预处理步骤值得我们参考。 平常我们见到的都是数据预处理&#xff0c;现在我们来讲一下特征工程跟数据预处理的区别。 数据预处理是指对原始数据进行清洗、转换、缩放等操作&#xff0c;以便为…

制造执行MES系统在光伏行业的应用

全球对可再生能源的需求不断增长&#xff0c;光伏能源作为一种清洁、可持续的能源形式&#xff0c;已经在广泛应用中受到了广泛关注。为满足工业领域的光伏能源需求&#xff0c;光伏制造执行系统(MES)作为一种集成化的技术解决方案&#xff0c;提供了更高效、更可靠的解决方案。…

海外仓系统推荐:中小型海外仓和家庭海外仓如何低投入高营收

随着海外仓行业整体竞争的加剧&#xff0c;海外仓的管理和经营成本可以说也是水涨船高。这对一些集团性质的大型海外仓影响不大&#xff0c;因为他们可以通过规模效应来摊薄成本。 但是对中小型海外仓和一些家庭海外仓来说&#xff0c;影响将是巨大的。一方面&#xff0c;海外…

GLM-4开源版本终于发布!!性能超越Llama3,多模态媲美GPT-4V,MaaS平台全面升级

今天上午&#xff0c;在 AI 开放日上&#xff0c;备受关注的大模型公司智谱 AI 公布了一系列行业落地数据&#xff1a; 根据最新统计&#xff0c;智谱 AI 大模型开放平台目前已拥有 30 万注册用户&#xff0c;日均调用量达到 4000 亿 Tokens。GPT-4o深夜发布&#xff01;Plus免…

【重学C语言】十八、SDL2 图形编程介绍和环境配置

【重学C语言】十八、SDL2 图形编程介绍和环境配置 **SDL2介绍**SDL 2用途SDL 在哪些平台上运行&#xff1f;下载和安装 SDL2安装 SDL2 clion 配置 SDL2 SDL2介绍 SDL2&#xff08;Simple DirectMedia Layer 2&#xff09;是一个开源的跨平台多媒体开发库&#xff0c;主要用于游…

Cortex系列详解

Cortex系列属于ARMv7架构(ARM公司在经典处理器ARM11以后的产品改用Cortex命名) 一、Cortex-A系列 “A”系列面向尖端的基于虚拟内存的操作系统和用户应用。 A 系列处理器适用于具有高计算要求、运行丰富操作系统以及提供交互媒体和图形体验的应用领域。 具体案例如:智能手…

2024百度之星 跑步

原题链接&#xff1a;码题集OJ-跑步 题目大意&#xff1a;一个n个人在绕圈跑&#xff0c;第i个人跑一圈的时间是i分钟&#xff0c;每二个人位置相同就会打一次招呼&#xff0c;如果同时来到终点&#xff0c;他们就会停下来&#xff0c;请问会打多少次招呼&#xff1f; 思路&a…

文件加密软件排行榜前五名|好用的五款文件加密软件分享

你的公司是否存在这些问题&#xff1a; 数据泄露事件常有发生&#xff0c;数据安全的重要性日益凸显&#xff0c;而文件加密软件则是保护数据安全的重要工具。 市场上存在众多文件加密软件&#xff0c;每款都有其独特的特点和优势。 本文将为您分享五款好用的文件加密软件&…

java通过ftp上传文件到服务器

依赖 <!-- https://mvnrepository.com/artifact/com.jcraft/jsch --><dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version></dependency> 代码 package com.cyz; impor…

Docker高级篇之Dockerfile解析

文章目录 1. DockerFile简介2. DockerFile的构建过程3. DockerFile的常用保留字4. 使用案例5. 虚悬镜像 1. DockerFile简介 DockerFile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像的指令和参数构成的脚本。 2. DockerFile的构建过程 DockerFile内容的基…

2.3 OpenCV随手简记(四)

阈值处理是很多高级算法底层处理的预方法之一。 自己求图像平均阈值&#xff1a; # -*- codingGBK -*- import cv2 as cv import numpy as np #求出图像均值作为阈值来二值化 def custom_image(image): gray cv.cvtColor(image, cv.COLOR_BGR2GRAY) cv.imshow("原来&qu…

精选网络安全书单:打造数字世界的钢铁长城!

目录 1.前言 2.书单推荐 2.1. 《内网渗透实战攻略》 2.2. 《Kali Linux高级渗透测试&#xff08;原书第4版&#xff09;》 2.3. 《CTF那些事儿》 2.4. 《权限提升技术&#xff1a;攻防实战与技巧》 2.5. 《数字政府网络安全合规性建设指南&#xff1a;密码应用与数据安全…

CentOS6系统因目录有隐含i权限属性致下属文件无法删除的故障一例

CentOS6服务器在升级openssh时因系统目录权限异常&#xff08;有隐含i权限属性&#xff09;&#xff0c;下属文件无法删除&#xff0c;导致系统问题的故障一例。 一、问题现象 CentOS6在升级openssh时&#xff0c;提示如下问题&#xff1a; warning: /etc/ssh/sshd_config c…

五个超实用的 ChatGPT-4o 提示词

GPT-4o 是 OpenAI 最近推出的最新人工智能模型&#xff0c;不仅具备大语言模型的能力&#xff0c;而且拥有多模态模型的看、读、说等能力&#xff0c;而且速度比 GPT-4 更快。下面我们就来介绍几个超实用的 GPT-4o 提示词&#xff0c;帮助大家更好地了解 GPT-4o 的功能和应用场…

Vuforia AR篇(五)— 地平面检测

目录 前言一、什么是地平面识别&#xff1f;二、使用步骤三、示例代码四、效果五、总结 前言 在增强现实&#xff08;AR&#xff09;应用程序的开发中&#xff0c;地平面识别是一项关键技术&#xff0c;它允许虚拟对象与现实世界的地面进行互动。Vuforia 是一个功能强大的 AR …

SpringBoot引入WebSocket依赖报ServerContainer no avaliable

1、WebSocketConfig 文件报错 Configuration EnableWebSocket public class WebSocketConfig {Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}2、报错内容 Exception encountered during context initialization - canc…

UFS协议—新手快速入门(二)【5-6】

目录 五、UFS协议栈 六、UFS技术演进与详解 1、UFS应用层 设备管理器 任务管理器 2、UFS传输层 3、UFS互联层 UFS协议—新手快速入门&#xff08;一&#xff09;【1-4】 五、UFS协议栈 UFS&#xff08;Universal Flash Storage&#xff09;协议是针对固态存储设备&…

德国西门子论未来质量管理 - 如何与明天相遇?

未来制造业的质量 -- 如何用软件方案满足质量要求 作者&#xff1a;Bill Butcher 翻译&编辑&#xff1a;数字化营销工兵 【前言】在Frost&Sullivan最近发表的一份白皮书中&#xff0c;他们讨论了制造业的质量投资。质量是制造过程的关键要素&#xff0c;但似乎比其他…

Ubuntu部署开源网关Apache APISIX

说明 系统&#xff1a;Ubuntu 24.04 LTSDocker版本&#xff1a;v26.1.3Docker Compose版本&#xff1a;v2.26.1 下载和配置 Ubuntu需要安装Docker和Docker Compose 下载apisix-docker仓库 git clone https://github.com/apache/apisix-docker.git修改docker-compose 配置e…

嵌入式学习记录6.6(拷贝构造/友元函数/常成员函数)

一.拷贝构造函数和拷贝赋值函数 1.1拷贝构造函数功能,格式 拷贝构造函数是一种特殊的构造函数&#xff0c;用来将一个类对象给另一个类对象初始化使用的。 1> 用一个类对象给另一个类对象初始化时&#xff0c;会自动调用拷贝构造函数。 2> 当一个类对作为函数的实参&…