AUTOSAR OS模块详解(三) Alarm

AUTOSAR OS模块详解(三) Alarm

本文主要介绍AUTOSAR OS的Alarm,并对基于英飞凌Aurix TC3XX系列芯片的Vector Microsar代码和配置进行部分讲解。

文章目录

  • AUTOSAR OS模块详解(三) Alarm
    • 1 简介
    • 2 功能介绍
      • 2.1 触发原理
      • 2.2 工作类型
      • 2.3 Alarm启动方式
      • 2.4 Alarm配置
      • 2.5 代码分析
        • 2.5.1 主要数据结构
        • 2.5.2 Alarm初始化阶段
        • 2.5.3 Alarm启动阶段
        • 2.5.4 Alarm运行阶段
      • 3 小结

1 简介

在上一篇Autosar OS模块详解中,我们介绍了OS Counter,其作为OS时间Tick管理的重要元素,为OS提供了重要的时间基准,并驱动OS进行时间调度。

而Counter仅为OS注入时间驱动力,却不负责管理OS中各个任务或者其他定时事件,比如我们要设置某个Task的周期为10ms,或者有一些定时事件,比如100ms之后触发某个event,从概念上我们不能直接让Counter管理这些定时业务,毕竟它的职责仅仅是驱动OS,就像发动机仅仅负责动力,而不负责车辆的档位。

因此我们还需要一个中间层,来管理所有的定时业务。在Autosar OS中,这个元素称为Alarm,它就像车辆的变速器,来管理不同时间频率的定时事件。在Autosar OS中,Counter通过累加来驱动Alarm,Alarm根据自身的设定参数周期地激活Task或者触发事件。

在这里插入图片描述

对于不同的周期事件,我们只需要设置多个Alarm,每个Alarm驱动所关联的业务,这样的方式实现了代码的抽象和业务分层,具备良好的架构体系。

2 功能介绍

2.1 触发原理

在Autosar OS中,Alarm顾名思义,就是闹钟,用于实现定时业务。在前面的Counter模块中我们已经介绍了,Counter在每个Tick会累加所有关联的Alarm,然后判断Alarm的状态,如果Alarm到期,则会执行相应的Job,也就是下文会提到的工作类型。

2.2 工作类型

在Autosar OS中,Alarm有4种工作类型,用户可配置不同的类型,并关联不同的Task、Event等。

Os_AlarmActionSetEvent

该工作类型用来设置某个Task中的一个Event,关于Event我们后面会介绍到,它是扩展任务中的关键元素,Event触发后该任务进入就绪状态,并且不同的Event关联不同的Runable。

Os_AlarmActionActivateTask

该工作类型用于激活某个Task,用于基础任务。基础任务是一种不带Event的普通任务,该任务每次运行都需要被激活,运行完成之后会自动Terminate,也就是挂起。

Os_AlarmActionIncrementCounter

前文提到过,并非每个Counter都需要关联到独立的硬件,也可以使用软件Counter。使用软件Counter的时候就是通过其他硬件Counter中的Alarm进行累加,使软件Couter得以产生连续的Tick。

Os_AlarmActionCallback

该工作类型为调用相应的CallBack函数,需要注意的是,这种工作类型只能在SC1下使用。该工作类型使用不多,一般情况下很少会将业务逻辑的执行与OS内部Counter、Alarm等元素进行直接关联,而是通过Task或者Event激活等进行业务逻辑的设置。

2.3 Alarm启动方式

Alarm的启动有两种方式,一种是绝对启动(SetAbsAlarm),另一种是相对启动(SetRelAlarm)。

绝对启动

如果以Counter的值作为一个时间轴的话,绝对启动就是Alarm的触发在Counter的初始时刻,所有通过绝对启动的Alarm的启动时刻相等。在Vecotr Os中,所有的Alarm启动之后下一次Counter就会立即执行一次,因此所有的Alarm会在第一个Tick统一执行。

当存在1ms、5ms、10ms三个Alarm时,如果使用绝对启动,其在Counter Tick的时间轴上的触发关系如下图所示。可以看到当Alarm的最小公倍数Tick数出现时,所有的Alarm都会同时触发,此时系统的瞬态负载较高。

在这里插入图片描述

相对启动

相对启动在设置Alarm启动时,可以添加一个Offset,这个Offset的分辨率是Os的Tick。我们可以通过设置Alarm的Offset将Alarm的触发错开,相应的Task的执行也会因此错开。

同样是1ms、5ms、10ms三个Alarm,我们将10ms的Alarm添加一个2ms的Offset,将5ms的Alarm添加一个1ms的Offset,则其在Counter Tick时间轴上的触发如下图所示,我们可以看到,此时最多两两之间发生碰撞,系统的瞬态峰值负载得到了降低。

在这里插入图片描述

另外值得一提的是,如果Os Tick的分辨率是0.5ms,则可将这三个Alarm通过设置1.5ms、2.5ms的Offset的方式全部错开。但是分辨率增加也意味着Os运行开销的增大,需要根据项目实际情况进行斟酌。

2.4 Alarm配置

其实在Vector工具中,Alarm是不需要配置的,工具会根据Task内部配置的Rbl的周期,自适应生成对应的Alarm,相对启动的Offset也是通过配置Task中Rbl的Offset来实现的。这里我们以一个Bsw任务中的10ms Alarm为示例,介绍Alarm各配置的含义。

在这里插入图片描述

OsAlarmCounterRef

如上一章所述,Counter是Alarm的驱动源,因此每个Alarm都需要关联一个Counter,一般情况下是Alarm所在核内的Counter。

OsAlarmAction

如前所述,Alarm具有四种Action,一般最常使用的是OsAlarmSetEvent和OsAlarmActivateTask。这里由于Bsw Task分配了多个周期的Rbl,属性为Extended Task,因此需要使用SetEvent的形式进行调度。

OsAlarmTask

每个Alarm的动作都需要对应一个Task。

OsAlarmCallbackName

SC1类型的Os中可以使用Os_AlarmActionCallback类型,因此如果是该类型的Alarm这里可以配置Callback函数。

OsAlarmCounter

当Alarm的工作类型为Os_AlarmActionIncrementCounter时,这里就需要配置一个Counter,以实现软件Counter的激活。

OsAlarmEvent

当Alarm的工作类型为Os_AlarmActionSetEvent时,这里配置对应的需要设置的Event即可。

OsAlarmAlarmTime

Alarm的Offset,但是实际上Offset是根据对应Task中分配的Rbl的Offset来计算的,因此这里不用配置。

OsAlarmAutostartType

启动类型,绝对启动还是相对启动,这个一般由工具计算也不需要配置。

OsAlarmCycleTime

Alarm的周期,如果是一次性的Alarm,这里配置为0,但是这项配置其实也是工具自动计算的,不用配置。

OsAlarmAppModeRef

Os的启动是有启动模式的,这里可以配置为:在哪种启动模式下,该Alarm自动启动,即初始化时启动。这项配置一般不用配置,Rte启动时会统一启动所有Alarm。

OsAlarmAccessingApplication

Alarm的访问权限配置,一般情况下Alarm的激活都是由Os来执行,权限也是由Os自动分配,少数比较特别的Alarm用户或者运行时的RTE会操作,因此Os无法识别所有访问源,因此这里有时候需要手动配置权限。如果执行Os服务时报权限访问的错误,一般是这种权限配置的地方有缺失。

在这里插入图片描述

前文提到Alarm可以配置Offset以错开激活时机,在Vector工具中,这是通过Task中实现的。如上图中将所有5ms的Rbl都配置了1ms的Offset,则该5ms的Alarm就会由工具自动计算出Offset。

2.5 代码分析

此处以Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms为例,分析Vector Os中关于Alarm的代码执行逻辑。

2.5.1 主要数据结构

Alarm中的主要数据结构为Os_AlarmSetEventConfigType、Os_AlarmActivateTaskConfigType、Os_AlarmIncrementCounterConfigType和Os_AlarmCallbackConfigType,分别对应Alarm的四种工作类型,此处我们的Alarm工作类型为设置Event,因此我们重点介绍Os_AlarmSetEventConfigType类型。

在Os_Alarm_Lcfg.c文件中我们可以找到配置生成的每个Alarm的数据类型:

/*! Alarm configuration data: Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms */
CONST(Os_AlarmSetEventConfigType, OS_CONST) OsCfg_Alarm_Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms =
{
  /* .Alarm = */
  {
    /* .Job                   = */
    {
      /* .Dyn      = */ OS_ALARM_CASTDYN_ALARM_2_JOB(OsCfg_Alarm_Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms_Dyn),
      /* .Counter  = */ OS_COUNTER_CASTCONFIG_TIMERPFRT_2_COUNTER(OsCfg_Counter_SystemTimer),
      /* .Callback = */ Os_AlarmActionSetEvent
    },
    /* .Autostart             = */
    {
      /* .AlarmTime        = */ 0uL, /* 0.0 sec */
      /* .Cycle            = */ 0uL, /* 0.0 sec */
      /* .ApplicationModes = */ OS_APPMODE_NONE,
      /* .AlarmMode        = */ OS_ALARMMODE_ABSOLUTE
    },
    /* .AccessingApplications = */ (OS_APPID2MASK(OsApplication_NonTrusted_Core0) | OS_APPID2MASK(OsApplication_NonTrusted_Core1) | OS_APPID2MASK(OsApplication_NonTrusted_Core2) | OS_APPID2MASK(OsApplication_NonTrusted_Core3) | OS_APPID2MASK(SystemApplication_OsCore0) | OS_APPID2MASK(SystemApplication_OsCore1) | OS_APPID2MASK(SystemApplication_OsCore2) | OS_APPID2MASK(SystemApplication_OsCore3)),  /* PRQA S 0410 */ /* MD_MSR_Dir1.1 */
    /* .OwnerApplication      = */ &OsCfg_App_OsApplication_NonTrusted_Core0
  },
  /* .Task  = */ &OsCfg_Task_Default_BSW_Async_Task_Core0,
  /* .Mask  = */ Rte_Ev_Cyclic2_Default_BSW_Async_Task_Core0_1_5ms
};

我们可以看到,配置生成的数据为OsCfg_Alarm_前缀加上Alarm配置名。 这里我们可以看到所有的配置值都在这个结构体里,我们重点关注其中的.Alarm.job.Dyn,这个指针指向的结构体中有一个ExpirationTimestamp成员,是Alarm下一个需要被激活的Counter Tick值,也就是“闹钟”的设定值。

2.5.2 Alarm初始化阶段

关于Os的初始化,在本系列Counter章节中已经介绍过,这里就不赘述了,与Counter相同,Alarm的初始化也是在Os_AppInit中进行的,其调用栈如下图所示。

在这里插入图片描述

在初始化阶段,仅仅是将Alarm的状态切换为OS_ALARMSTATE_CANCELED,并没有其他操作。

2.5.3 Alarm启动阶段

Alarm的启动是在EcuM的StartupTwo阶段完成的,关于EcuM的启动时序,这里不做过多介绍,后续聊到EcuM模块时再做解读。

StartupTwo运行在Default_Init_Task中,这是一个系统自带的初始化Task,优先级较高。在StartupTwo阶段,有两个地方会启动Alarm,SchM_Init和Rte_Start。SchM_Init中主要初始化EcuM等Bsw模块相关联的Alarm,而Rte_Start则主要初始化用户定义的相关的Rbl的Alarm。其时序图如图所示。

在这里插入图片描述

我们从中可以看到,Os_AlarmSetRelAlarm是最终实施的主要服务函数,我们从该函数解读SetRelAlarm的实现,这里忽略掉其中的错误检查。

OS_FUNC_ATTRIBUTE_DEFINITION(OS_LOCAL_INLINE Os_StatusType, OS_CODE, OS_ALWAYS_INLINE, Os_AlarmSetRelAla
(
  P2CONST(Os_AlarmConfigType, AUTOMATIC, OS_CONST) Alarm,
  TickType Increment,
  TickType Cycle
))
{
  Os_StatusType status;
  
  if(ErrorCheck...){}
  else
  {
    P2VAR(Os_AlarmType, AUTOMATIC, OS_VAR_NOINIT) alarmDyn;

    alarmDyn = Os_AlarmGetDyn(Alarm);                                                                   

    /* #20 Set alarm's state to SET. */
    alarmDyn->State = OS_ALARMSTATE_SET;                                                                

    /* #30 Set alarm's cycle time to Cycle. */
    alarmDyn->Cycle = Cycle;                                                                            

    /* #40 Tell counter to execute alarm's job in Increment ticks. */
    Os_JobAddRel(&Alarm->Job, Increment);                                                               

    status = OS_STATUS_OK;
  }
  return status;
}

这里alarmDyn的数据指向的是OsCfg_Alarm_Rte_Al_TE2_Default_BSW_Async_Task_Core0_1_5ms_Dyn变量,大家能够很容易注意到其命名是有规律的。

首先,该函数将Alarm的状态切换到OS_ALARMSTATE_SET,表示该Alarm已经启动。

然后设置该Alarm的周期为Cycle,这里我们是5ms的Alarm,周期为5。

然后通过Os_JobAddRel将Alarm添加到其关联的Counter中,这里的Increment就是Offset,如果没有偏移的话Increment为1,表示第一次Counter Tick就会激活该Alarm,这里我们设置了1ms的Offset,因此Increment=2,表示第二次Counter Tick才会激活该Alarm。(Counter的逻辑是先累加再判断,因此第一次工作时Counter Tick值为1)

这里的Os_JobAddRel,也就是Os_CounterAddRelJob,是Counter内部的实现函数,我们观察其代码。

FUNC(void, OS_CODE) Os_CounterAddRelJob
(
  P2CONST(Os_CounterConfigType, AUTOMATIC, OS_CONST) Counter,
  P2CONST(Os_JobConfigType, AUTOMATIC, OS_CONST) Job,
  Os_TickType Offset
)
{
  Os_TickType now;
  Os_TickType newExpTime;
  P2CONST(Os_PriorityQueueConfigType, AUTOMATIC, OS_CONST) jobQueue;

  jobQueue = &Counter->JobQueue;

  /* #10 Get the current counter value (now). */
  now = Os_CounterGetPhysicalValue(Counter);                            

  /* #20 Set job's expiration time to mod(now + Offset). */
  newExpTime = Os_TimerAdd(
      Counter->Characteristics.MaxAllowedValue,
      Counter->Characteristics.MaxCountingValue,
      now,
      Offset);

  Os_JobSetExpirationTimestamp(Job, newExpTime);                        

  /* #30 Enqueue the given job in counter's job queue. */
  Os_PriorityQueueInsert(jobQueue, Job);                                

  /* #40 If the top element of the queue has changed: */
  if(Job == Os_PriorityQueueTopGet(jobQueue))                           
  {
    /* #50 Update timer's compare value. */
    Os_CounterSetCompareValue(Counter, newExpTime);                     
  }
}

首先通过Os_CounterGetPhysicalValue计算出Counter的当前值,一般启动时该值都是0。然后通过Os_TimerAdd结合当前值和Offset,计算“闹钟”的定时时刻ExpirationTimestamp,也就是时间轴上的下一个触发点。这里需要最大值来判断溢出。这里now=0,Offset=2,计算出结果为ExpirationTimestamp=2。

然后通过Os_JobSetExpirationTimestamp函数,将计算出的ExpirationTimestamp设置为超时值,在Counter的中断中,就是通过比较Counter的Tick值和ExpirationTimestamp,来判断该Alarm是否需要激活。

然后通过Os_PriorityQueueInsert来将Alarm插入到Counter的Job队列中,这里使用了完全二叉树进行优先级排序,时间轴上未来最近的一个Alarm具有最高优先级,感兴趣的读者可以自行研究,这里不进行算法层面的探讨。

最后一步是判断是否更新了待激活的Alarm,比如当前Counter待激活的时刻为Tick=5,现在插入的Alarm需要在Tick=2时激活,那么就需要更新Counter的Compare值,因为Counter并不是每一次Timer中断都会工作,而是按需执行。

到这里就完成了Alarm的启动,只需要等待Counter中断去执行了。

2.5.4 Alarm运行阶段

前篇我们提到,在Counter中的Os_CounterWorkJobs函数,会轮询任务队列中的所有Alarm,如果该Alarm的ExpirationTimestamp与Counter Tick的值吻合,则执行该Alarm的Callback,同时将该Alarm从Job队列中取出。这部分逻辑不再赘述,读者如果有疑问可以阅读前篇Counter详解。

这里我们在运行阶段重点介绍Alarm在的Callback中的动作,这是Counter激活Alarm之后由Alarm实施的主要业务。我们的Alarm选取的是5ms的SetEvent,因此其Callback是Os_AlarmActionSetEvent。

在这里插入图片描述

如果是周期Alarm,在Os_CounterReloadJob中,该Alarm会被重载,会根据Counter的当前Tick值加上周期,算出最新的ExpirationTimestamp,以设置下一个周期的触发;如果是一次性Alarm,则在Os_AlarmCancelOrReload中Alarm的状态会被设置为OS_ALARMSTATE_CANCELED,以表示关闭该Alarm。

然后执行Os_EventSetInternal以进行Event的设置,来触发对应Task中的Rbl执行。该部分我们在后面的Event文章内容中进行详细介绍,这里就一概而过。

3 小结

本文承接上篇Counter,介绍了Autosar Os中的Alarm机制,解释了Vector工具中的Alarm配置项,并根据Vector代码详细解读了其初始化、启动、运行等阶段,结合详细的时序图,介绍了其由Counter激活后的执行逻辑。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【0x04】HCI_Connection_Request事件详解

目录 一、事件概述 二、事件格式及参数 2.1. HCI_Connection_Request 事件格式 2.2. BD_ADDR 2.3. Class_Of_Device 2.4. Link_Type 三、主机响应 3.1. ACL链接类型 3.2. SCO或eSCO链接类型 四、应用场景 4.1. 设备配对场景 4.2. 蓝牙文件传输场景 4.3. 蓝牙物联网…

洛谷题目:P2742 [USACO5.1] 圈奶牛Fencing the Cows /【模板】二维凸包 题解 (本题较难)

题目传送门:P2742 [USACO5.1] 圈奶牛Fencing the Cows /【模板】二维凸包 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 另:由于一些文章的疏忽,导致一些错别字,代码错误,公式错误导致大家的理解和误导,…

Qt中的按钮组:QPushButton、QToolButton、QRadioButton和QCheckBox使用方法(详细图文教程)

💪 图像算法工程师,专业从事且热爱图像处理,图像处理专栏更新如下👇: 📝《图像去噪》 📝《超分辨率重建》 📝《语义分割》 📝《风格迁移》 📝《目标检测》 &a…

2025-1-21 SUCTF 2025 crypto signin

今年充满期待,上线一看两道题,一道看名字应该是跟环相关的,估计做不出来,还有一道签到题,没做出来,遗憾下线 文章目录 signin signin from Crypto.Util.number import * from secret import flagbit_lengt…

C语言之图像文件的属性

🌟 嗨,我是LucianaiB! 🌍 总有人间一两风,填我十万八千梦。 🚀 路漫漫其修远兮,吾将上下而求索。 图像文件属性提取系统设计与实现 目录 设计题目设计内容系统分析总体设计详细设计程序实现…

【Linux】华为服务器使用U盘安装统信操作系统

目录 一、准备工作 1.1 下载UOS官方系统 1.2制作启动U盘 1.3 服务器智能管理系统iBMC 二、iBMC设置U盘启动 一、准备工作 1.1 下载UOS官方系统 服务器CPU的架构是x86-64还是aarch64),地址:统信UOS生态社区 - 打造操作系统创…

macOS如何进入 Application Support 目录(cd: string not in pwd: Application)

错误信息 cd: string not in pwd: Application 表示在当前目录下找不到名为 Application Support 的目录。可能的原因如下: 拼写错误或路径错误:确保你输入的目录名称正确。目录名称是区分大小写的,因此请确保使用正确的大小写。正确的目录名…

python麻辣香锅菜品推荐

1.推荐算法概述 推荐算法出现得很早,最早的推荐系统是卡耐基梅隆大学推出的Web Watcher浏览器导航系统,可以根据当的搜索目标和用户信息,突出显示对用户有用的超链接。斯坦福大学则推出了个性化推荐系统LIRA.AT&T实验室于1997年提出基于协作过滤的个性化推荐系统…

利用大型语言模型在量化投资中实现自动化策略

“Automate Strategy Finding with LLM in Quant investment” 论文地址:https://arxiv.org/pdf/2409.06289 摘要 这个新提出的量化股票投资框架,利用大型语言模型(LLMs)与多智能体系统相结合的方法,通过LLMs从包括数…

JAVA:Spring Boot 实现责任链模式处理订单流程的技术指南

1、简述 在复杂的业务系统中,订单流程往往需要一系列的操作,比如验证订单、检查库存、处理支付、更新订单状态等。责任链模式(Chain of Responsibility)可以帮助我们将这些处理步骤分开,并且以链式方式处理每一个操作…

(开源)基于Django+Yolov8+Tensorflow的智能鸟类识别平台

1 项目简介(开源地址在文章结尾) 系统旨在为了帮助鸟类爱好者、学者、动物保护协会等群体更好的了解和保护鸟类动物。用户群体可以通过平台采集野外鸟类的保护动物照片和视频,甄别分类、实况分析鸟类保护动物,与全世界各地的用户&…

算法专题(三):二分查找

本篇还是像之前一样,以举例子的形式向大家讲解!每道题的题目均是传送门!点击跳转对应题! 目录 一、二分查找 1.1 题目 1.2 思路 1.3 代码实现 总结(模版) 朴素版: 二、在排序数组中查找…

C# OpenCvSharp 部署文档矫正,包括文档扭曲/模糊/阴影等情况

目录 说明 效果 模型 项目 代码 下载 参考 C# OpenCvSharp 部署文档矫正,包括文档扭曲/模糊/阴影等情况 说明 地址:https://github.com/RapidAI/RapidUnDistort 修正文档扭曲/模糊/阴影等情况,使用onnx模型简单轻量部署&#xff0c…

Excel 技巧15 - 在Excel中抠图头像,换背景色(★★)

本文讲了如何在Excel中抠图头像,换背景色。 1,如何在Excel中抠图头像,换背景色 大家都知道在PS中可以很容易抠图头像,换背景色,其实Excel中也可以抠简单的图,换背景色。 ※所用头像图片为百度搜索&#x…

吴恩达深度学习——神经网络介绍

文章内容来自BV11H4y1F7uH,仅为个人学习所用。 文章目录 什么是神经网络引入神经网络神经元激活函数ReLU隐藏单元 用神经网络进行监督学习监督学习与无监督学习举例 什么是神经网络 引入 已经有六个房子的数据集,横轴为房子大小,纵轴为房子…

xctf-comment(Intruder,git恢复,SQL注入,Hex解码)

这题是2018年网鼎杯真题,考察 Burp Suite 的 Intruder 模块去找用户密码,使用 githacker 恢复代码(githack不行),代码审计发现SQL二次注入,尝试SQL注入读取文件内容,读取的是/home/www/.bash_hi…

分布式系统通信解决方案:Netty 与 Protobuf 高效应用

分布式系统通信解决方案:Netty 与 Protobuf 高效应用 一、引言 在现代网络编程中,数据的编解码是系统设计的一个核心问题,特别是在高并发和低延迟的应用场景中,如何高效地序列化和传输数据对于系统的性能至关重要。随着分布式系…

C++《AVL树》

在之前的学习当中我们已经了解了二叉搜索树,并且我们知道二叉搜索树的查找效率是无法满足我们的要求,当二叉树为左或者右斜树查找的效率就很低下了,那么这本篇当中我们就要来学习对二叉搜索树进行优化的二叉树——AVL树。在此会先来了解AVL树…

ToDesk设置临时密码和安全密码都可以当做连接密码使用

ToDesk 在各领域办公都已经是非常常见了 为了安全 ToDesk 设置了连接密码,想连接 需要输入远程码和连接密码 我们刚打开 系统默认给我们用的是临时密码,安全性确实很强 和定时Tokey一样,固定时间切换。 但是 如果我们要经常连接这个电脑&a…

LLMs(大型语言模型)的多智能体:Auto-GPT

LLMs(大型语言模型)的多智能体:Auto-GPT 是指在一个系统中集成多个具有不同能力、角色和任务的智能体,这些智能体能够相互协作、沟通和交互,以共同完成复杂的任务或解决复杂的问题。每个智能体都可以被视为一个独立的实体,具有自己的策略、目标和知识库,通过相互之间的…