当前位置: 首页 > news >正文

9-SOFA_Collision Model(碰撞模型)与 Collision Pipeline(碰撞流水线)

11-collision-pipeline.scn

<?xml version="1.0"?> <!-- Step 1: add the global collision pipeline --> <Node name="root" dt="0.01" gravity="0 0 0"> <Node name="plugins"> <RequiredPlugin pluginName="Sofa.Component.AnimationLoop"/> <RequiredPlugin pluginName="Sofa.Component.Collision.Detection.Algorithm"/> <RequiredPlugin pluginName="Sofa.Component.Collision.Detection.Intersection"/> <RequiredPlugin pluginName="Sofa.Component.Collision.Geometry"/> <RequiredPlugin pluginName="Sofa.Component.Collision.Response.Contact"/> <RequiredPlugin pluginName="Sofa.Component.Constraint.Projective"/> <RequiredPlugin pluginName="Sofa.Component.IO.Mesh"/> <RequiredPlugin pluginName="Sofa.Component.LinearSolver.Iterative"/> <RequiredPlugin pluginName="Sofa.Component.Mapping.Linear"/> <RequiredPlugin pluginName="Sofa.Component.Mass"/> <RequiredPlugin pluginName="Sofa.Component.ODESolver.Backward"/> <RequiredPlugin pluginName="Sofa.Component.SolidMechanics.FEM.Elastic"/> <RequiredPlugin pluginName="Sofa.Component.StateContainer"/> <RequiredPlugin pluginName="Sofa.Component.Topology.Container.Constant"/> <RequiredPlugin pluginName="Sofa.Component.Topology.Container.Dynamic"/> <RequiredPlugin pluginName="Sofa.Component.Visual"/> <RequiredPlugin pluginName="Sofa.GL.Component.Rendering3D"/> </Node> <DefaultAnimationLoop computeBoundingBox="false"/> <VisualStyle displayFlags="showVisualModels showBehaviorModels showCollisionModels"/> <!-- Global collision pipeline --> <CollisionPipeline/> <BruteForceBroadPhase/> <BVHNarrowPhase/> <MinProximityIntersection name="Proximity" alarmDistance="0.5" contactDistance="0.25"/> <CollisionResponse name="Response" response="PenalityContactForceField"/> <MeshGmshLoader name="meshLoaderCoarse" filename="mesh/liver.msh"/> <MeshOBJLoader name="meshLoaderFine" filename="mesh/liver-smooth.obj"/> <Node name="Liver"> <EulerImplicitSolver/> <CGLinearSolver iterations="200" tolerance="1e-09" threshold="1e-09"/> <TetrahedronSetTopologyContainer name="topo" src="@../meshLoaderCoarse"/> <TetrahedronSetGeometryAlgorithms template="Vec3d" name="GeomAlgo"/> <MechanicalObject template="Vec3d" name="MechanicalModel" showObject="1" showObjectScale="3"/> <TetrahedronFEMForceField name="FEM" youngModulus="1000" poissonRatio="0.4" method="large"/> <MeshMatrixMass massDensity="1" topology="@topo"/> <!-- 老师视频旧名称是 FixedConstraint --> <FixedProjectiveConstraint indices="1 3 50"/> <Node name="Visual"> <OglModel name="VisualModel" src="@../../meshLoaderFine"/> <BarycentricMapping name="VisualMapping" input="@../MechanicalModel" output="@VisualModel"/> </Node> </Node> </Node>

一个完整物体可以具有三种表示:
Liver

├── Mechanical Model
│ 负责力学计算和变形

├── Visual Model
│ 负责显示外观

└── Collision Model
负责碰撞检测

  • 力学模型保存真正参与求解的状态;
  • 视觉模型可以使用光滑表面;
  • 碰撞模型由适合检测的几何图元组成。

三个表示不一定使用同一套网格,但需要通过 Mapping 同步。

1.Collision Pipeline 的整体流程

SOFA 的碰撞流水线负责调度碰撞重置、碰撞检测和碰撞响应;这一节主要按“粗检测—窄检测—响应”的直观顺序讲。

碰撞过程分成:
Broad Phase
宽阶段、粗检测

Narrow Phase
窄阶段、精细定位

Intersection + Response
确认接触并产生响应

也可以概括成:
先找可能碰撞的物体

再找可能接触的局部区域

检查具体图元是否接触

创建接触响应

2.CollisionPipeline

<CollisionPipeline/>

CollisionPipeline 的作用是组织碰撞系统中各个阶段按照正确顺序执行。

可以类比成AnimationLoop
DefaultAnimationLoop
仿真总负责人
↓ 发出“开始处理碰撞”的命令
CollisionPipeline
碰撞子流程负责人

调用粗检测、窄检测、接触响应

也就是:
AnimationLoop 管整个时间步
CollisionPipeline 管时间步里的碰撞部分

3.BruteForceBroadPhase

第一种算法组件:

<BruteForceBroadPhase/>

它先比较不同碰撞物体的整体包围盒:

物体A包围盒 物体B包围盒
┌────────┐ ┌────────┐
│ A │ │ B │
└────────┘ └────────┘
如果包围盒相距很远:直接排除,不再做更细的检查;
如果包围盒相交或非常接近:把这一对物体交给 Narrow Phase。

4.BVHNarrowPhase

第二个组件:

<BVHNarrowPhase/>

BVH是:Bounding Volume Hierarchy 包围体层次结构

假设整个 liver 外面有一个大盒子:
┌──────────────────┐
│ Liver │
└──────────────────┘
仅凭这个盒子只能知道可能发生碰撞,不能知道到底是 liver 的哪个区域,于是 BVH 会继续分层,它通过从大盒子逐步进入小盒子,快速定位接触大概发生在哪个局部区域。

官方说明,BVHNarrowPhase会遍历碰撞图元的层次结构,继续排除不可能相交的图元对,最后把剩下的候选对交给相交检测方法。

4.MinProximityIntersection

第三个检测组件:

<MinProximityIntersection name="Proximity" alarmDistance="0.5" contactDistance="0.25"/>

BVH 负责精确定位可能接触的位置,而MinProximityIntersection最终检查:
primitive against primitive
图元对图元

例如:
球体 against 三角形
点 against 三角形
线段 against 线段

4.1.alarmDistance

alarmDistance="0.5"
当两组碰撞图元的距离小于0.5时:认为它们已经足够接近 ,需要继续进行接触检查。
它相当于警戒距离。

4.2.contactDistance

contactDistance="0.25"
距离小于0.25时:真正创建一个接触 DetectionOutput

所以关系是:
距离 > 0.5
忽略

0.25 < 距离 < 0.5
进入警戒和精细检测

距离 < 0.25
创建接触

5.CollisionResponse

发现两个物体接触了,下一步怎么处理?

<CollisionResponse name="Response" response="PenalityContactForceField"/>

这里讲的是penalty method(惩罚法),SOFA 组件的历史拼写是 PenalityContactForceField,该组件的作用就是用排斥弹簧处理接触。

两种碰撞响应:Penalty method 惩罚法、Lagrange multiplier constraint 拉格朗日乘子约束法

这个教程现在使用DefaultAnimationLoop,因此先集中讲惩罚法;约束型响应需要另一套约束求解流程。

惩罚法怎样产生力:

如果两个物体发生穿透:
物体A ─────┐
│ 穿透距离 d
物体B ─────┘

惩罚法根据穿透程度和接触刚度,产生排斥力:

其中:

  • :接触违反或穿透的程度;
  • :接触刚度;
  • :把两个物体推开的接触力。

惩罚法通过力来减少穿透,但不能严格保证时间步结束时穿透一定等于零。
接触刚度太小时,物体可能明显穿进去;接触刚度太大时,又可能导致数值问题。
因此它是一种近似处理,而不是严格的几何不可穿透约束。惩罚法是DefaultAnimationLoop下当前使用的响应方式.

6.碰撞算法可以替换

Broad Phase——BruteForceBroadPhase

Narrow Phase——BVHNarrowPhase、DirectSAPNarrowPhase

Intersection method——MinProximityIntersection、LocalMinDistance

这些组件可以根据项目需要替换,SOFA 用户文档中可以查到每种算法的细节。

我们当前使用的是这一组:

<BruteForceBroadPhase/> <BVHNarrowPhase/> <MinProximityIntersection .../>

最终流程是:
碰撞对象对

包围盒筛选

BVH局部筛选

具体图元成对检查

创建接触

7.全局流水线代码放在哪里

需要在:

<DefaultAnimationLoop/>

后面增加五行代码:

<CollisionPipeline/> <BruteForceBroadPhase/> <BVHNarrowPhase/> <MinProximityIntersection name="Proximity" alarmDistance="0.5" contactDistance="0.25"/> <CollisionResponse name="Response" response="PenalityContactForceField"/>

这些组件放在root中,因为它们管理的是整个场景中的碰撞流程,不是只属于某一个 liver。

所以要区分:

  • root 下的 CollisionPipeline → 整个场景共用的碰撞算法
  • 每个物体中的 Collision 子节点 → 该物体自己的碰撞表示

11-collision-model.scn

<?xml version="1.0"?> <!-- Step 2: add a collision representation to Liver --> <Node name="root" dt="0.01" gravity="0 0 0"> <Node name="plugins"> <RequiredPlugin pluginName="Sofa.Component.AnimationLoop"/> <RequiredPlugin pluginName="Sofa.Component.Collision.Detection.Algorithm"/> <RequiredPlugin pluginName="Sofa.Component.Collision.Detection.Intersection"/> <RequiredPlugin pluginName="Sofa.Component.Collision.Geometry"/> <RequiredPlugin pluginName="Sofa.Component.Collision.Response.Contact"/> <RequiredPlugin pluginName="Sofa.Component.Constraint.Projective"/> <RequiredPlugin pluginName="Sofa.Component.IO.Mesh"/> <RequiredPlugin pluginName="Sofa.Component.LinearSolver.Iterative"/> <RequiredPlugin pluginName="Sofa.Component.Mapping.Linear"/> <RequiredPlugin pluginName="Sofa.Component.Mass"/> <RequiredPlugin pluginName="Sofa.Component.ODESolver.Backward"/> <RequiredPlugin pluginName="Sofa.Component.SolidMechanics.FEM.Elastic"/> <RequiredPlugin pluginName="Sofa.Component.StateContainer"/> <RequiredPlugin pluginName="Sofa.Component.Topology.Container.Constant"/> <RequiredPlugin pluginName="Sofa.Component.Topology.Container.Dynamic"/> <RequiredPlugin pluginName="Sofa.Component.Visual"/> <RequiredPlugin pluginName="Sofa.GL.Component.Rendering3D"/> </Node> <DefaultAnimationLoop computeBoundingBox="false"/> <VisualStyle displayFlags="showVisualModels showBehaviorModels showCollisionModels"/> <!-- Global collision pipeline --> <CollisionPipeline/> <BruteForceBroadPhase/> <BVHNarrowPhase/> <MinProximityIntersection name="Proximity" alarmDistance="0.5" contactDistance="0.25"/> <CollisionResponse name="Response" response="PenalityContactForceField"/> <!-- Mechanical volumetric mesh --> <MeshGmshLoader name="meshLoaderCoarse" filename="mesh/liver.msh"/> <!-- Fine surface mesh, reused by visual and collision representations --> <MeshOBJLoader name="meshLoaderFine" filename="mesh/liver-smooth.obj"/> <Node name="Liver"> <EulerImplicitSolver/> <CGLinearSolver iterations="200" tolerance="1e-09" threshold="1e-09"/> <TetrahedronSetTopologyContainer name="topo" src="@../meshLoaderCoarse"/> <TetrahedronSetGeometryAlgorithms template="Vec3d" name="GeomAlgo"/> <MechanicalObject template="Vec3d" name="MechanicalModel" showObject="1" showObjectScale="3"/> <TetrahedronFEMForceField name="FEM" youngModulus="1000" poissonRatio="0.4" method="large"/> <MeshMatrixMass massDensity="1" topology="@topo"/> <FixedProjectiveConstraint indices="1 3 50"/> <!-- Visual representation --> <Node name="Visual"> <OglModel name="VisualModel" src="@../../meshLoaderFine"/> <BarycentricMapping name="VisualMapping" input="@../MechanicalModel" output="@VisualModel"/> </Node> <!-- Collision representation --> <Node name="Collision"> <MeshTopology name="CollisionTopology" src="@../../meshLoaderFine"/> <MechanicalObject template="Vec3d" name="StoringForces"/> <TriangleCollisionModel name="CollisionModel" contactStiffness="3"/> <BarycentricMapping name="CollisionMapping" input="@../MechanicalModel" output="@StoringForces"/> </Node> </Node> </Node>

8.给 Liver 增加 Collision 子节点

前面为了渲染,我们建立过:

<Node name="Visual"> ... </Node>

现在按照同样思路,再建立:

<Node name="Collision"> ... </Node>

场景结构变成:
Liver

├── MechanicalModel

├── Visual
│ └── VisualModel

└── Collision
└── CollisionModel

这次创建子节点不是为了 rendering,而是为了 collision detection。

9.Collision 子节点里的四个组件

<Node name="Collision"> <MeshTopology src="@../../meshLoaderFine"/> <MechanicalObject name="StoringForces"/> <TriangleCollisionModel name="CollisionModel" contactStiffness="3"/> <BarycentricMapping name="CollisionMapping" input="@../" output="@StoringForces"/> </Node>
9.1.MeshTopology
<MeshTopology src="@../../meshLoaderFine"/>

它定义碰撞表面的点、边、三角形连接关系。

这里使用 meshLoaderFine,也就是前面红色、较平滑的表面网格。

../../是层级寻址:向上回溯两级父节点,再找到meshLoaderFine这个节点。

因此当前例子是:

  • Mechanical Model——使用 liver.msh 的四面体体网格
  • Visual Model——使用 liver-smooth.obj
  • Collision Model——也使用 liver-smooth.obj

并没有要求碰撞网格必须和视觉网格相同,只是这个例子选择复用同一个细表面网格。

9.2.MechanicalObject name="StoringForces"
<MechanicalObject name="StoringForces"/>

它保存 Collision 子节点的状态。

SOFA 规则: 每个力学 / 碰撞计算,都需要一个MechanicalObject存顶点坐标、速度、受力。

Visual 节点里没有额外的MechanicalObject,为什么 Collision 节点里却需要一个?我们将在11小节进行解释。

9.3.TriangleCollisionModel

SOFA 内置碰撞算法:基于三角形面片碰撞检测。

<TriangleCollisionModel name="CollisionModel" contactStiffness="3"/>

它表示把 MeshTopology 中的三角形 作为碰撞检测图元。

  • 读取上面MeshTopology的肝脏三角面;
  • 场景里其他物体碰到肝脏时,计算穿透、挤压;
  • contactStiffness="3"接触刚度,数值越大碰撞反弹越硬。 算出的碰撞力会作用在StoringForces这套质点上。在12小节会详细解释。

SOFA 还支持 PointCollisionModel、LineCollisionModel、SphereCollisionModel、CubeCollisionModel、TriangleCollisionModel。但我们当前只选择了三角形。官方文档确认,CollisionModel 就是由某类碰撞图元组成的列表,而contactStiffness主要用于惩罚法接触响应。

9.4.BarycentricMapping

功能: 用重心坐标插值,把主肝脏模型每一步的形变位置、位移,实时同步复制到碰撞副本StoringForces

<BarycentricMapping name="CollisionMapping" input="@../" output="@StoringForces"/>
input="@../"

表示回到上一级Liver节点,找到上层的力学状态。

它等价于更明确地写:

input="@../MechanicalModel"

输出:

output="@StoringForces"

表示映射到 Collision 子节点的MechanicalObject

9.5.完整数据流

关系是:

上层主节点:Liver/MechanicalModel(肝脏真实形变质点)
↓ 数据传递靠 BarycentricMapping
当前节点:Collision/StoringForces(碰撞专用副本质点)
↓ 碰撞力作用在这套质点上
TriangleCollisionModel(三角碰撞检测器)

完整数据流分两步:

  • 正向映射(形变同步)Liver 主模型形变 → BarycentricMapping 插值 → StoringForces 同步位置 保证碰撞网格和肝脏外形完全一致。
  • 反向受力传递(碰撞反馈)TriangleCollisionModel 算出碰撞接触力 → 先存到 StoringForces 再通过映射反向传回主 Liver/MechanicalModel,让肝脏感受到碰撞挤压、产生形变反馈。

碰撞模型通常需要通过 Mapping 和父节点的力学表示连接。

10.MeshTopology 与 SetTopologyContainer 的区别

MeshTopology 和前面学过的:

<TetrahedronSetTopologyContainer/>

一样,都保存拓扑关系。

  • 几何(坐标):每个点(x,y,z)在哪,是位置,会动;
  • 拓扑(Topology):点和点怎么连、拼成什么面 / 体,是连接关系。

这里强调一个主要区别:

TetrahedronSetTopologyContainer 属于可更新的拓扑容器,配合相应 Modifier,可以支持拓扑变化,例如:切割、撕裂、增加或删除拓扑元素。

MeshTopology 在这里被作为静态拓扑使用:加载完成后拓扑保持不变。也就是说,节点位置可以跟随 Mapping 移动;但是点、边、三角形的连接关系不改变;不用于切割或者撕裂。

所以选择原则是:

  • 拓扑永远不改变 → 可以使用 MeshTopology
  • 需要切割、撕裂或动态更新拓扑 → 使用动态 SetTopologyContainer 和 Modifier

它和其他拓扑容器一样,可以通过:

src="@../../meshLoaderFine"

从 Loader 读取所有拓扑信息。

11.为什么 Collision 里需要 MechanicalObject

<Node name="Collision"> <MeshTopology src="@../../meshLoaderFine"/> <MechanicalObject name="StoringForces"/> <TriangleCollisionModel name="CollisionModel" contactStiffness="3"/> <BarycentricMapping name="CollisionMapping" input="@../MechanicalModel" output="@StoringForces"/> </Node>

Visual 节点里没有额外的MechanicalObject,为什么 Collision 节点里却需要一个?

11.1.Visual Model 是完全被动的

视觉模型只负责显示:
Mechanical Model
↓ Mapping
Visual Model
力学模型算出新位置后,Mapping 把位置传给视觉模型;视觉模型不计算接触力,也不需要把任何物理信息返回给力学模型。所以它基本是单向的。

11.2. Collision Model 不只是接收位置,还要返回力

碰撞模型首先也要跟随力学模型运动:
Mechanical Model
↓位置、速度
Collision Model

但发生碰撞后,碰撞模型会得到:接触位置、接触法向、两物体之间的距离、穿透距离、惩罚接触力。这些信息最终必须返回给力学模型:
Collision Model
↑接触力
Mechanical Model

这仍然由 Mapping 完成。向下传播状态,向上传播力。


碰撞模型的位置由力学模型位置​ 映射得到。

而碰撞力返回力学模型时,通常按 Mapping 的转置关系传递:

其中:

  • ​:碰撞模型上计算出的接触力;
  • ​:返回到力学节点上的力;
  • :Mapping 的雅可比矩阵。
11.3.StoringForces
<MechanicalObject name="StoringForces"/>

仿真时间步中,碰撞检测通常发生在物理求解之前。

流程是:
先检测碰撞

根据穿透距离创建接触力

但此时物理方程还没求解

暂时保存这些力

随后力学求解器使用这些力

因此需要一个 Collision 节点中的机械状态,用来承接碰撞模型的位置,也承接接触响应产生的力。我们把它命名为 StoringForces ,意思是“存储接触力”。
但要注意,这只是我们给它取的名字,不是特殊组件名称,它本质上仍然是普通的 <MechanicalObject/> 。

随后:

<BarycentricMapping input="@../MechanicalModel" output="@StoringForces"/>

会自动把 Collision 上的力反向传播给父节点的MechanicalModel

12.contactStiffness

<TriangleCollisionModel name="CollisionModel" contactStiffness="3"/>

contactStiffness 用于前面讲过的 Penalty method 惩罚法

两个物体发生了穿透,假设球体进入肝脏表面:
肝脏表面 ─────────────


部分进入表面

两个碰撞模型之间存在一个穿透距离

惩罚法会在接触位置建立一根虚拟弹簧:

物体A ←────虚拟弹簧────→ 物体B

排斥力近似为 :

其中:

  • :穿透距离;
  • contactStiffness
  • :把两个物体推开的接触力。

因此:

contactStiffness="3"

表示惩罚弹簧的刚度系数是 3。

13.碰撞处理完整步骤

假设:球体和一个三角形之间发生了 1 mm 的穿透。

第一步:Broad Phase 检查整体包围盒

SOFA 为每个碰撞对象计算包围盒。

包围盒通常与世界坐标的轴对齐:

先检查两个大盒子是否相交。如果物体真的已经发生穿透,那么它们的包围盒通常也会相交,因此进入下一阶段。

Broad Phase 的目的只是快速判断这一对物体是否可能碰撞,不是判断具体哪个三角形发生接触。

第二步:Narrow Phase 定位局部区域

接下来,BVH Narrow Phase 用逐层细分的包围盒,定位可能发生接触的小区域。

整个物体 ↓ 较大的局部区域 ↓ 更小的局部区域 ↓ 具体球体与三角形

第三步:Proximity Intersection 确认穿透距离

MinProximityIntersection检查具体图元,例如:Sphere against Triangle 球体 对 三角形

它确认:球体穿入三角表面 1 mm,即

第四步:Collision Response 创建虚拟弹簧

碰撞响应根据 穿透距离 d + contactStiffness k 创建惩罚弹簧。

这里举的数值是:contactStiffness = 3 穿透距离 = 1 mm

在国际单位制中,因此

如果刚度单位是 N/m,那么力就是

这个力暂时保存在 Collision 节点中,再通过 Mapping 传回 Mechanical Model,随后由力学求解器使用。

第五步:惩罚法不是直接移动节点

这里要特别纠正了一个容易产生的误解:发现穿透 1 mm 后,不是直接把球往外移动 1 mm。

SOFA 在这一阶段只是给节点施加排斥力,随后由质量、材料刚度、时间步长、速度、求解器 共同决定节点下一步实际移动多少。

所以流程不是:检测到穿透1 mm → 直接修正位置1 mm

而是:
检测到穿透1 mm → 生成接触力 → 求解物理方程 → 物体在接触力作用下运动

13.1.惩罚法不能保证完全不穿透

因为它只是施加力,并不强制满足,时间步结束后,可能仍有少量穿透。

contactStiffness太小:排斥力不足 → 物体继续穿入

contactStiffness太大:排斥力突然非常大 → 仿真抖动,求解困难,甚至不稳定

此外它还受以下因素影响:两个物体相撞的速度、时间步长、物体质量、材料刚度、求解器参数。

所以contactStiffness对调参非常敏感。

http://www.jsqmd.com/news/1250332/

相关文章:

  • AI学术写作助手:从智能大纲到文献矩阵
  • 【单片机毕业设计推荐】 基于 STM32 的智能婴儿监护床控制系统设计与实现 ,基于 STM32 的多传感婴儿看护装置设计(012203)
  • Day35|混合检索+重排序:RAG 召回率翻倍的正确姿势
  • 上海黄金回收正规资质筛查指南,普通人快速辨别靠谱渠道 - 日常比对手册
  • TVA驱动的具身智能迭代逻辑(17)
  • Docker进阶——多容器编排和机器人仿真环境搭建
  • AI 协作完整流程:从任务输入到可验证产出
  • labelme免费使用(不是下载exe文件)
  • CNAS认证测试报告:软件企业质量信任的基石
  • 业务智能体实战笔记:分层消除不确定性(五·终)|完全脱离 LLM 的确定性判定 + 评测闭环
  • 绮涛精密机械(上海)有限公司:长三角进口高端数控机床一站式解决方案服务商 - 品牌优选官
  • 2026宜宾本地家装行业装修公司靠谱口碑推荐,婚房全包装修/全屋适老化整装/出租房全屋简装高性价比落地方案 - 资讯速览
  • 网安必备的基础小知识
  • 抖音团购如何进入豆包AI推荐答案:本地生活商家的GEO获客指南
  • LLM到Agent的技术演进与核心组件解析
  • 免费查重网站哪个靠谱?2026年红黑榜实测,踩坑3次后的真心话
  • TDengine 2026 全面升级,中小企业全功能永久免费
  • 如何让智谱清言生成word文档?AI导出鸭苹果版将智谱清言的Markdown/LaTeX/Mermaid本地解析并转为标准docx,格式分毫不差。
  • 伟肯 NXI00136 水冷制动单元
  • TVA驱动的具身智能迭代逻辑(4)
  • 递归,分治,DFS,回溯的总结
  • 【finetuning】路由器微调案例分析
  • 【我的第一篇博客】
  • 财务大数据处理的基本流程是怎样的?财务大数据在风险管控中有什么用?
  • 椰林海鲜码头企业新愿景是什么?:守善求真务实 - 17328623207
  • CSharp: Flyweight Pattern
  • 图表库:折线图、柱状图、饼图绘制库(246)
  • 功率预测正在“改写利润表”:雅砻江大模型之后,新能源场站真正要补的不是一个算法
  • TVA驱动的具身智能迭代逻辑(16)
  • EUV 光源的工作机理