0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于TPU-MLIR:详解EinSum的完整处理过程!

算能开发者社区 ? 2024-02-19 13:08 ? 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

EinSum介绍

EinSum(爱因斯坦求和)是一个功能强大的算子,能够简洁高效地表示出多维算子的乘累加过程,对使用者非常友好。

本质上, EinSum是一个算子族,可以表示多种基础操作,如矩阵乘法、Reduce。EinSum支持任意多的输入,只要计算中只包含点乘(element-wise)、广播(broadcast)、归约求和(reduction sum)都可以使用EinSum来表示。以下给出一种将EinSum计算等价表达的流程:

  1. 将输入的维度符号放入一个列表,移除重复元素后按升序排列;
  2. 对各输入维度执行转置操作,确保维度标识符按照升序对齐,实现维度对齐;
  3. 在缺失的维度上填充1(扩展维度),以便与第一步中定义的维度保持一致;
  4. 对所有输入执行广播点乘;
  5. 对那些不在输出标识符中的维度执行累加操作;
  6. 利用转置操作调整维度顺序,使其与输出标识符的顺序一致。

下图是以out = EinSum("ijk, lki-> li", in0, in1)为例,根据上述步骤进行等价转换。e5439836-cee4-11ee-9118-92fbcf53809c.png

TPU-MLIR转换

虽然使用上述流程可以完成对EinSum的计算转换,但如果严格按照该流程执行,会带来大量的Transpose和Reshape操作,这不仅会给TPU-MLIR的LayerGroup功能带来挑战,同时也难以显式地识别出如矩阵乘法这类操作,从而无法充分利用硬件加速单元。因此,TPU-MLIR并未直接采用上述流程进行转换。

接下来,我们将详细介绍EinSum的完整处理过程。

前端接口

以下示例代码摘自OnnxConverter.py文件,并附带了注释。代码整体结构简洁明了,我们可以看到,转换函数目前仅支持两个输入的常见情况。特别需要注意的是公式的归一化过程。由于EinSum的表达式可以使用任意非重复字符来表示下标,这虽然提高了可读性,但也导致同一操作有多种不同的表示方式。归一化操作就是将表达式字符重新映射,以字符'a'作为起始。例如,比如ij,jk->ik和dk,kv->dv都会映射为ab,bc->ac

#https://pytorch.org/docs/1.13/generated/torch.einsum.html?highlight=einsum#torch.einsum
defconvert_einsum_op(self,onnx_node):
assert(onnx_node.op_type=="Einsum")
equation=onnx_node.attrs.get("equation").decode()

#公式归一化
defnormalize_equation(equation_c):
equation=equation_c
new_equation=''
start='a'
translate_map={}
forsinequation:
ifs=='':
continue
elifnot((s>='a'ands<=?'z')or(s>='A'ands<=?'Z')):
translate_map[s]=s
elifsnotintranslate_map:
translate_map[s]=start
start=chr(ord(start)+1)
new_equation+=translate_map[s]
returnnew_equation
equation=normalize_equation(equation)
lhs=self.getOperand(onnx_node.inputs[0])#
#大多情况下rhs是Weight, self.getOp会先到Weight Map中查找;如果找不到,
#其会从Mutable Tensor中查找,然后返回对应的Value。
rhs=self.getOp(onnx_node.inputs[1])
new_op=top.EinsumOp(self.unranked_type,
[lhs,rhs],
mode=StringAttr.get(equation),
#设置loc信息,方便找到原图对应算子
loc=self.get_loc("{}_{}".format(onnx_node.name,onnx_node.op_type)),
#将该算子插入到当前的block中
ip=self.mlir.insert_point).output
#将输出放到MutableTensor列表中,供后面算子使用
self.addOperand(onnx_node.name,new_op)

内部转换

TPU-MLIR目前支持了几种常见的表达式,并根据不同的算子进行了优化转换。所有的变换最终都利用了硬件的矩阵乘法加速单元,从而实现了对算子的有效加速。以下是部分代码片段,该代码来自tpu-mlir/lib/Dialect/Top/Canonicalize/Einsum.cpp,并在原有基础上添加了注释。

structConvertEinsum:publicOpRewritePattern{
usingOpRewritePattern::OpRewritePattern;

LogicalResultmatchAndRewrite(EinsumOpop,
PatternRewriter&rewriter)constoverride{
//目前只支持输入个数为2或者输入0为Weight的情况
if(op.getInputs().size()!=2||module::isWeight(op.getInputs()[0])){
llvm_unreachable("Notsupportnow.");
//returnfailure();
}
autonone=module::getNoneOp(op);
automode=op.getMode().str();
autolhs=op.getInputs()[0];
autorhs=op.getInputs()[1];
autolshape=module::getShape(lhs);
autorshape=module::getShape(rhs);
std::stringlname=module::getName(lhs).str();
std::stringrname=module::getName(rhs).str();
std::stringname=module::getName(op.getOutput()).str();

std::vectoroperands;
std::vectorattrs;
if(mode=="a,b->ab"){
//外积操作:可看作[a,1]x[1,b]的矩阵乘法操作
//lhs->ReshapeOp():shape=[a]toshape[a,1]
rewriter.setInsertionPointAfter(lhs.getDefiningOp());
//
autonewType=RankedTensorType::get({lshape[0],1},module::getElementType(lhs));
autoloc=NameLoc::get(rewriter.getStringAttr(lname+"_to2dim"));
autolrsOp=rewriter.create(loc,newType,ValueRange{lhs});
operands.push_back(lrsOp);

//rhs->ReshapeOp():shape=[b]toshape[1,b]
rewriter.setInsertionPointAfter(rhs.getDefiningOp());
newType=RankedTensorType::get({1,rshape[0]},module::getElementType(rhs));
loc=NameLoc::get(rewriter.getStringAttr(rname+"_to2dim"));
autorrsop=rewriter.create(loc,newType,ValueRange{rhs});
operands.push_back(rrsop);
operands.push_back(none);
//用MatMulOp实现[a,1]x[1,b]=[a,b],并替换原来的EinSum操作
rewriter.setInsertionPoint(op);
automatmulOp=rewriter.create(op.getLoc(),op.getType(),operands,attrs);
op.replaceAllUsesWith(matmulOp.getOperation());
rewriter.eraseOp(op);
}elseif(mode=="abcd,cde->abe"){
//可以转换成矩阵乘法[a*b,c*d]x[c*d,e]->[a*b,e]->[a,b,e]
//lhs_reshape_rst=[lhs_shape[0]*lhs_shape[1],lhs_shape[2]*lhs_shape[3]]
rewriter.setInsertionPointAfter(lhs.getDefiningOp());
autonewType=RankedTensorType::get({lshape[0]*lshape[1],lshape[2]*lshape[3]},module::getElementType(lhs));
autoloc=NameLoc::get(rewriter.getStringAttr(lname+"_to2dim"));
autolrsOp=rewriter.create(loc,newType,ValueRange{lhs});
operands.push_back(lrsOp);
newType=RankedTensorType::get({rshape[0]*rshape[1],rshape[2]},module::getElementType(rhs));
if(module::isWeight(rhs)){
rhs.setType(newType);
operands.push_back(rhs);
}else{
rewriter.setInsertionPointAfter(rhs.getDefiningOp());
loc=NameLoc::get(rewriter.getStringAttr(rname+"_to2dim"));
autorrsop=rewriter.create(loc,newType,ValueRange{rhs});
operands.push_back(rrsop);
}
operands.push_back(none);
rewriter.setInsertionPoint(op);
newType=RankedTensorType::get({lshape[0]*lshape[1],rshape[2]},module::getElementType(op));
loc=NameLoc::get(rewriter.getStringAttr(name+"_matmul"));
automatmulOp=rewriter.create(loc,newType,operands,attrs);
autoorsOp=rewriter.create(op.getLoc(),op.getType(),ValueRange{matmulOp});
op.replaceAllUsesWith(orsOp.getOperation());
rewriter.eraseOp(op);
}elseif(mode=="abcd,bed->abce"){
rewriter.setInsertionPointAfter(rhs.getDefiningOp());
//转换过程
//batchmatmuldoesnotsupportbroadcast
//temporarysolution
//[h,k,c]->[1,h,k,c]->[b,h,k,c]
operands.push_back(lhs);

RankedTensorTypenewType;
//右操作数处理
if(autowOp=dyn_cast(rhs.getDefiningOp())){
//对于Weight来说,可以将数据复制,解决不支持广播问题,[b,e,d]->[a,b,e,d]
autostorage_type=module::getStorageType(rhs);
assert(storage_type.isF32()&&"Todo,supoortmoreweighttype");
autodata=wOp.read_as_byte();
uint8_t*dptr;
newType=RankedTensorType::get({lshape[0],rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
std::vector<float_t>new_filter(newType.getNumElements(),0);
dptr=(uint8_t*)new_filter.data();
//实际的数据复制过程
for(int32_ti=0;i0];i++){
autooffset=i*data->size();
memcpy(dptr+offset,data->data(),data->size());
}
autonew_op=top::create(op,"folder",new_filter,newType);
wOp.replaceAllUsesWith(new_op.getDefiningOp());
operands.push_back(new_op);
rewriter.eraseOp(wOp);
}else{
//对于普通tensor,先reshape成[1,b,e,d]再用tile算子翻倍数据为[a,b,e,d]

//Reshape操作
autoloc=NameLoc::get(rewriter.getStringAttr(rname+"_reshape"));
newType=RankedTensorType::get({1,rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
autorrsop=rewriter.create(loc,newType,ValueRange{rhs});

//Tile操作,各维tile倍数[a,1,1,1]
newType=RankedTensorType::get({lshape[0],rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
loc=NameLoc::get(rewriter.getStringAttr(rname+"_tile"));
attrs.push_back(rewriter.getNamedAttr("tile",rewriter.getI64ArrayAttr({lshape[0],1,1,1})));
autotileOp=rewriter.create(loc,newType,ValueRange{rrsop},attrs);
attrs.clear();
operands.push_back(tileOp);
}
operands.push_back(none);
//这里使用了右操作数转置的批量矩阵乘法算子,硬件可直接支持
//[a*b,c,d]*[a*b,e,d]^T->[a*b,c,e]
attrs.push_back(rewriter.getNamedAttr("right_transpose",rewriter.getBoolAttr(true)));
rewriter.setInsertionPoint(op);
automatmulOp=rewriter.create(op.getLoc(),op.getType(),operands,attrs);
op.replaceAllUsesWith(matmulOp.getOperation());
rewriter.eraseOp(op);
}elseif(mode=="abcd,ced->abce"){
//dumbimplementation
//转置lhs[a,b,c,d]->[a,c,b,d]
//trans_shape=[lhs_shape[0],lhs_shape[2],lhs_shape[1],lhs_shape[3]]
rewriter.setInsertionPointAfter(lhs.getDefiningOp());
autoloc=NameLoc::get(rewriter.getStringAttr(lname+"_trans"));
autonewType=RankedTensorType::get({lshape[0],lshape[2],lshape[1],lshape[3]},module::getElementType(lhs));
attrs.push_back(rewriter.getNamedAttr("order",rewriter.getI64ArrayAttr({0,2,1,3})));
autotranOp=rewriter.create(loc,newType,ValueRange{lhs},attrs);
attrs.clear();
operands.push_back(tranOp);

//复制或Tilelhs:[c,e,d]->[a,c,e,d]
rewriter.setInsertionPointAfter(rhs.getDefiningOp());
if(autowOp=dyn_cast(rhs.getDefiningOp())){
//Weight翻倍数据
autostorage_type=module::getStorageType(rhs);
assert(storage_type.isF32()&&"Todo,supoortmoreweighttype");
autodata=wOp.read_as_byte();
uint8_t*dptr;
newType=RankedTensorType::get({lshape[0],rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
std::vector<float_t>new_filter(newType.getNumElements(),0);
dptr=(uint8_t*)new_filter.data();
for(int32_ti=0;i0];i++){
autooffset=i*data->size();
memcpy(dptr+offset,data->data(),data->size());
}
autonew_op=top::create(op,"folder",new_filter,newType);
wOp.replaceAllUsesWith(new_op.getDefiningOp());
operands.push_back(new_op);
rewriter.eraseOp(wOp);
}else{
//rehshape+tile:[c,e,d]-reshape->[1,c,e,d]-tile->[a,c,e,d]
loc=NameLoc::get(rewriter.getStringAttr(rname+"_reshape"));
newType=RankedTensorType::get({1,rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
autorrsop=rewriter.create(loc,newType,ValueRange{rhs});
loc=NameLoc::get(rewriter.getStringAttr(rname+"_tile"));
attrs.push_back(rewriter.getNamedAttr("tile",rewriter.getI64ArrayAttr({lshape[0],1,1,1})));
newType=RankedTensorType::get({lshape[0],rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
autotileOp=rewriter.create(loc,newType,ValueRange{rrsop},attrs);
attrs.clear();
operands.push_back(tileOp);
}
operands.push_back(none);
//右操作数带转置批量矩阵乘法:[a*c, b, d]*[a*c, e, d]^T ->[a*c, b, e]->[a, c, b, e]
newType=RankedTensorType::get({lshape[0],lshape[2],lshape[1],rshape[1]},module::getElementType(op));
attrs.push_back(rewriter.getNamedAttr("right_transpose",rewriter.getBoolAttr(true)));
rewriter.setInsertionPoint(op);
loc=NameLoc::get(rewriter.getStringAttr(name+"_matmul"));
automatmulOp=rewriter.create(loc,newType,operands,attrs);
attrs.clear();
//[b,w,h,k]->[b,h,w,k]
attrs.push_back(rewriter.getNamedAttr("order",rewriter.getI64ArrayAttr({0,2,1,3})));
autotranBackOp=rewriter.create(op.getLoc(),op.getType(),ValueRange{matmulOp},attrs);
op.replaceAllUsesWith(tranBackOp.getOperation());
rewriter.eraseOp(op);
}elseif(mode=="abcd,abed->abce"||mode=="abcd,abde->abce"){
//lhs(abcd)*rhs(abed)^T->abce
//lhs(abcd)*rhs(abde)->abce
autonewType=RankedTensorType::get({lshape[0],lshape[1],lshape[2],rshape[2]},module::getElementType(op));
if(mode=="abcd,abde->abce"){
newType=RankedTensorType::get({lshape[0],lshape[1],lshape[2],rshape[3]},module::getElementType(op));
}
rewriter.setInsertionPoint(op);
rewriter.setInsertionPointAfter(rhs.getDefiningOp());
operands.push_back(lhs);
operands.push_back(rhs);
operands.push_back(none);
if(mode=="abcd,abed->abce"){
//rhs(abed)^T
attrs.push_back(rewriter.getNamedAttr("right_transpose",rewriter.getBoolAttr(true)));
}

autoloc=NameLoc::get(rewriter.getStringAttr(name));
automatmulOp=rewriter.create(loc,newType,operands,attrs);
op.replaceAllUsesWith(matmulOp.getOperation());
attrs.clear();
rewriter.eraseOp(op);

} elseif(mode=="abcd,cde->abce"){

//lhs:
//abcd->acbd(pemute)
//rhs:
//cde->1cde(reshape)
//acde->acde(tile)
//matmul:
//lhs(acbd)*rhs(acde)=result(acbe)
//result:
//acbe->abce(pemute)
//success!

rewriter.setInsertionPointAfter(lhs.getDefiningOp());
autoloc=NameLoc::get(rewriter.getStringAttr(lname+"_trans"));
autonewType=RankedTensorType::get({lshape[0],lshape[2],lshape[1],lshape[3]},module::getElementType(lhs));
attrs.push_back(rewriter.getNamedAttr("order",rewriter.getI64ArrayAttr({0,2,1,3})));
autotranOp=rewriter.create(loc,newType,ValueRange{lhs},attrs);
attrs.clear();
operands.push_back(tranOp);
rewriter.setInsertionPointAfter(rhs.getDefiningOp());
if(autowOp=dyn_cast(rhs.getDefiningOp())){

autodata=wOp.read_as_byte();
uint8_t*dptr;
newType=RankedTensorType::get({lshape[0],rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
std::vector<float_t>new_filter(newType.getNumElements(),0);
dptr=(uint8_t*)new_filter.data();
for(int32_ti=0;i0];i++){
autooffset=i*data->size();
memcpy(dptr+offset,data->data(),data->size());
}
autonew_op=top::create(op,"folder",new_filter,newType);
wOp.replaceAllUsesWith(new_op.getDefiningOp());
operands.push_back(new_op);
rewriter.eraseOp(wOp);
}else{
loc=NameLoc::get(rewriter.getStringAttr(rname+"_reshape"));
newType=RankedTensorType::get({1,rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
autorrsop=rewriter.create(loc,newType,ValueRange{rhs});
loc=NameLoc::get(rewriter.getStringAttr(rname+"_tile"));
attrs.push_back(rewriter.getNamedAttr("tile",rewriter.getI64ArrayAttr({lshape[0],1,1,1})));
newType=RankedTensorType::get({lshape[0],rshape[0],rshape[1],rshape[2]},module::getElementType(rhs));
autotileOp=rewriter.create(loc,newType,ValueRange{rrsop},attrs);
attrs.clear();
operands.push_back(tileOp);
}
operands.push_back(none);
newType=RankedTensorType::get({lshape[0],lshape[2],lshape[1],rshape[2]},module::getElementType(op));
rewriter.setInsertionPoint(op);
loc=NameLoc::get(rewriter.getStringAttr(name+"_matmul"));
automatmulOp=rewriter.create(loc,newType,operands,attrs);
attrs.clear();
attrs.push_back(rewriter.getNamedAttr("order",rewriter.getI64ArrayAttr({0,2,1,3})));
autotranBackOp=rewriter.create(op.getLoc(),op.getType(),ValueRange{matmulOp},attrs);
op.replaceAllUsesWith(tranBackOp.getOperation());
rewriter.eraseOp(op);

}else{
llvm_unreachable("Einsumnotsupportthismodenow");
}
returnsuccess();
}

总结

TPU-MLIR对EinSum的实现虽然不完全,但已经足够实用,能满足目前常见网络的需求。通过Converter直接表达式规范化,降低了编译器优化或模式分析的复杂性。在算子分析时,我们不仅需要在计算上实现等价变换,还需充分了解实际硬件的特性。针对不同硬件架构及其对算子的支持情况,需具体分析以找到最佳实现方法。此外,我们可以看到在工程实践中,人们更注重实用性和效率,在实现上不必追求完备,是要覆盖实际应用场景即可。EinSum的转换还有改进空间,我们也欢迎社区提出宝贵的建议并贡献代码。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 前端
    +关注

    关注

    1

    文章

    214

    浏览量

    18331
  • 代码
    +关注

    关注

    30

    文章

    4906

    浏览量

    71030
  • TPU
    TPU
    +关注

    关注

    0

    文章

    154

    浏览量

    21242
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    基于碳纳米材料的TPU导电长丝制备与性能研究

    HS-TGA-103热重分析仪(TG、TGA)是在升温、恒温或降温过程中,观察样品的质量随温度或时间的变化,目的是研究材料的热稳定性和组份。广泛应用于塑料、橡胶、涂料、药品、催化剂、无机材料
    的头像 发表于 07-11 10:21 ?141次阅读
    基于碳纳米材料的<b class='flag-5'>TPU</b>导电长丝制备与性能研究

    请问Einsum(opset7) 是否支持 OpenVINO??

    from srcpluginsintel_gpusrcpluginprogram_builder.cpp:249:Operation: /class_head/Einsum of type Einsum(opset7) is not supported ; CPU:Ex
    发表于 06-19 08:03

    边缘计算网关在水产养殖尾水处理中的实时监控应用

    ,某大型水产养殖企业决定引入先进的 YC-GR90-S工业智能网关 技术,对尾水处理过程进行远程监控和管理。 二、项目需求 设备远程监控: 需要实时监控尾水处理过程中各类设备的运行状态,如水泵、过滤器、曝气机等,确保设备正常运行
    的头像 发表于 06-06 14:36 ?157次阅读
    边缘计算网关在水产养殖尾水<b class='flag-5'>处理</b>中的实时监控应用

    TPU处理器的特性和工作原理

    张量处理单元(TPU,Tensor Processing Unit)是一种专门为深度学习应用设计的硬件加速器。它的开发源于对人工智能(AI)和机器学习应用的需求,尤其是深度学习中的神经网络计算。
    的头像 发表于 04-22 09:41 ?1606次阅读
    <b class='flag-5'>TPU</b><b class='flag-5'>处理</b>器的特性和工作原理

    Google推出第七代TPU芯片Ironwood

    在 Google Cloud Next 25 大会上,我们隆重推出第 7 代 Tensor Processing Unit (TPU) — Ironwood。这不仅是我们迄今为止性能最高、扩展性最佳的定制 AI 加速器,更是第一款专为推理而设计的 TPU
    的头像 发表于 04-16 11:20 ?682次阅读
    Google推出第七代<b class='flag-5'>TPU</b>芯片Ironwood

    谷歌第七代TPU Ironwood深度解读:AI推理时代的硬件革命

    谷歌第七代TPU Ironwood深度解读:AI推理时代的硬件革命 Google 发布了 Ironwood,这是其第七代张量处理单元 (TPU),专为推理而设计。这款功能强大的 AI 加速器旨在
    的头像 发表于 04-12 11:10 ?1932次阅读
    谷歌第七代<b class='flag-5'>TPU</b> Ironwood深度解读:AI推理时代的硬件革命

    Imagination D系列GPU:关于2D 双速率纹理处理

    实现景深、光晕、模糊等效果。大多数这些后处理过程都是以纹理采样为主的过滤效果,它们对算术逻辑单元(ALU)的要求不高,但受限于纹理处理单元(TPU)的吞吐率。解决这
    的头像 发表于 02-08 14:28 ?440次阅读
    Imagination D系列GPU:关于2D 双速率纹理<b class='flag-5'>处理</b>

    TPU编程竞赛系列|第九届集创赛“算能杯”火热报名中!

    第九届全国大学生集成电路创新创业大赛(以下简称“集创赛”)正式开始报名。算能在处理器应用方向特别设立了“TPU赋能的边缘计算架构优化与创新应用设计”赛题,诚邀各校参赛队伍充分发挥TPU的算力优势
    的头像 发表于 02-06 13:41 ?1214次阅读
    <b class='flag-5'>TPU</b>编程竞赛系列|第九届集创赛“算能杯”火热报名中!

    使用ADS1274 ADC进行前端信号采集,前端信号调理过程中是否还需要设计AA Filter?

    本人打算使用ADS1274 ADC进行前端信号采集,信号带宽大概为1Khz 至 11Khz,使用ADC的快速采样模式,外部振荡器频率为32.768MHz。 现在遇到的问题是,不知道前端信号调理过程
    发表于 01-22 08:18

    光缆用tpu外护套用在哪些型号光缆上

    光缆用TPU(热塑性聚氨酯)外护套因其耐磨、抗拉、柔性好以及优良的防潮和阻燃性能,被广泛应用于多种型号的光缆上,特别是需要较高机械保护和恶劣环境适应性的光缆。以下是一些可能使用TPU外护套的光缆
    的头像 发表于 01-10 10:05 ?823次阅读

    ADS1284 MFLG应该怎么处理

    如果模拟端出现一个超量程的信号,MFLAG就会激活,如果这时不Reset ADC,是不是后续ADC的输出都会是0?如果Reset然后重新SYNC,在处理过程中是否就会丢掉一些数据?MFLAG应该怎么处理
    发表于 11-29 06:54

    详解MES系统的生产过程实时监控与异常处理

    万界星空科技的MES系统能实时监控生产过程,检测异常情况并自动纠正,确保生产过程的连续性和稳定性。通过可视化界面,管理人员可以实时查看生产进度和设备状态。预警机制一旦检测到异常情况,会立即触发并通知相关人员。
    的头像 发表于 10-28 15:57 ?796次阅读
    <b class='flag-5'>详解</b>MES系统的生产<b class='flag-5'>过程</b>实时监控与异常<b class='flag-5'>处理</b>

    信号被处理过程包括采集什么什么输出等环节

    信号处理是一个涉及多个步骤的复杂过程,它包括信号的采集、预处理、分析、处理、合成和输出等环节。这个过程在许多领域中都至关重要,如通信、音频
    的头像 发表于 10-15 14:09 ?2297次阅读

    处理器指令的获取过程

    处理器指令的获取是计算机执行程序过程中的关键环节,它决定了微处理器如何对数据和指令进行处理。以下将详细阐述微处理器指令的获取
    的头像 发表于 10-05 15:16 ?1077次阅读

    PLC水处理过滤器运维管理系统解决方案

    ,数之能提供PLC云组态平台的PLC水处理过滤器运维管理系统解决方案。通过接入PLC设备数据,PLC云组态平台可以形成水处理过滤系统的组态界面,实时展示设备状态、工艺参数等信息;管理人员也能远程查看告警信息并进行运维操作,及时
    的头像 发表于 09-23 10:44 ?628次阅读