TwinCAT3 - 实现CiA402

news/2024/9/19 21:10:33/文章来源:https://www.cnblogs.com/tossorrow/p/18380990

目录
  • 1,起缘
  • 2,想办法
  • 3,开搞
    • 3.1,CANOpen通信
      • 3.1.1 对象字典
      • 3.1.2 通信建立
    • 3.2,CiA402伺服状态机
    • 3.3,伺服运行
      • 3.3.1 操作模式
      • 3.3.2 轮廓位置模式
      • 3.3.3 轮廓速度模式
      • 3.3.4 其他
  • 4,用起来

1,起缘

在TwinCAT3项目中涉及到轴运动时,通常做法都是在PLC中安装TC1250或者TF5000,搭配支持EtherCAT over CANOpen的驱动器,就可以按照TwinCAT教材中的标准做法实现轴运动控制。

但最近在做一个项目时,起初以为并不需要轴运动,所以PLC安装的是TC1200,也没有购买TF5000,这意味着项目不能添加NC轴。结果见到实际设备后,是有一根实轴的,不过运动控制很简单,只需要距离运动和速度运动就可以了。就想着不买TF5000了,看看有没有别的办法。

2,想办法

翻阅文档后发现,只要从驱动器厂家获取到驱动器的xml设备描述文件,并且在PLC的EtherCAT网络拓扑中扫描出驱动器来,就意味着TwinCAT3已经帮助我们完成了EtherCAT over CANOpen的底层通信,我们要做的就是自己实现NC部分的功能。

TwinCAT的NC部分涉及到两块,一块是CiA402(或曰DS402),定义了运动控制相关的PDO,SDO,驱动器状态机和运行模式等。另一块是PLCOpen,定义了MC_Power,MC_MoveAbsolute,MC_Stop等函数接口以及轴状态的封装。

其中CiA402是必须要实现的,PLCOpen实不实现都无所谓。因此只需要实现CiA402能控制驱动器就行了,并且我也只需要MC_Power,MC_MoveAbsolute,MC_MoveRelative几个基本功能(复杂的我也玩不来)。至于MC函数封装表面上符合PLCOpen,差不多就行了。

3,开搞

手头的驱动器型号是台达ASDA-A2-E,要到网上找对应的CANopen通信手册。其实绝大部分支持CANopen的驱动器通信协议都遵循着CiA402,只是在脉冲换算,支持的操作模式等细节上有差异。最终搜寻到两本靠谱的手册:《SV660C系列伺服应用手册-CANopen通讯篇》和《台達伺服ASDA-A2 CANopen通訊應用手冊》。

3.1,CANOpen通信

3.1.1 对象字典

由于EtherCAT over CANOpen的底层通信已经实现好了,我们只需要知道CANOpen是以对象字典的方式进行通信就够了。简单讲就是每一个16进制数字都代表一个变量。如果你你熟悉C#,那么通信协议可以理解为一个Dictionary<ushort, object>。如果你熟悉MODBUS,那么可以理解16进制数字就是寄存器地址,变量就是寄存器的内容,比如16#6040这个寄存器的内容就是控制字,将控制字填到16#6040这个寄存器时就已经生效了。

3.1.2 通信建立

驱动器上电,按照说明书调好驱动器的参数,用网线连接驱动器和PLC,找驱动器厂家拿到驱动器对应的xml设备描述文件,新建一个TwinCAT3项目,连接到PLC,切换PLC至Config模式,扫描设备,不出意外的话,就会得到驱动器啦。

接下来编辑驱动器的PDO页面,将我们可能用到的对象字典全部添加进来。添加过程略过(可参考TwinCAT3的教程),下面是添加后的结果。

image-20240823101600533

状态字Statusword,操作模式Modes of operation,控制字Controlword,目标位置Target position等对象都是经常用到的,下文都会一一讲到。

3.2,CiA402伺服状态机

手册说必须按照CiA402协议规定的流程引导伺服驱动器,伺服驱动器才可运行于指定的状态。与伺服状态机相关的对象是状态字Statusword和控制字Controlword。状态机转移的详情如下:

状态机转移 控制字(16#6040) 状态字(16#6041)
0 上电→初始化 自然过渡,无需控制指令 0x0000
1 初始化→伺服无故障 自然过渡,无需控制指令 0x0250(0010-0101-0000 伺服无故障)
2 伺服无故障→伺服准备好 0x06(0000-0000-0110 伺服准备) 0x0231(0010-0011-0001 伺服准备好)
3 伺服准备好→等待打开伺服使能 0x07(0000-0000-0111 等待使能) 0x0233(0010-0011-0011 等待伺服使能)
4 等待打开伺服使能→伺服运行 0x0F(0000-0000-1111 伺服运行) 0x0237(0010-0011-0111 伺服运行)
5 伺服运行→等待打开伺服使能 0x07 0x0233
6 等待打开伺服使能→伺服准备好 0x06 0x0231
7 伺服准备好→伺服无故障 0x00(0000-0000-0000 伺服无故障) 0x0250(0010-0101-0000 伺服无故障)
8 伺服运行→伺服准备好 0x06 0x0231
9 伺服运行→伺服无故障 0x00 0x0250
10 等待打开伺服使能→伺服无故障 0x00 0x0250
11 伺服运行→快速停机 0x02(0000-0000-0010 快速停机) 0x0217(0010-0001-0111 快速停机)
12 快速停机→伺服无故障 自然过渡,无需控制指令 0x0250
13 Any→故障停机 发生故障,自动切换 0x021F(0010-0001-1111 故障停机)
14 故障停机→故障 自然过渡,无需控制指令 0x0218(0010-0001-1000 故障)
15 故障→伺服无故障 0x80(0000-1000-0000 故障复位) 0x0250(0010-0101-0000)
16 快速停机→伺服运行 0x0F 0x0237

控制字Controlword和状态字Statusword的详情如下:

控制字的bit 0 1 2 3 4-6 7 8 9-15
名称 伺服开启 主回路上电 快速停机 伺服运行 与操作模式相关 故障复位 暂停 NA
描述 0-否 1-是 0-否 1-是 0-是 1-否 0-否 1-是 上升沿有效 0-否 1-是
操作模式 轮廓位置模式 回零模式 轮廓速度模式 轮廓转矩模式
控制字bit4 使能新位置指令(上升沿触发) 开始回零(上升沿触发,须保持) NA NA
控制字bit5 位置指令更新模式(0-非立刻更新 1-立刻更新) NA NA NA
控制字bit6 位置指令类型(0-绝对位置指令 1-相对位置指令) NA NA NA
状态字的bit 0 1 2 3 4 5 6 7 8 9 10 11 12-15
名称 伺服准备好 伺服可以运行 伺服运行 故障 主回路上电 快速停机 伺服不可运行 警告 厂家自定义 远程控制 目标到达 内部限制 运行模式相关
运行模式 轮廓位置模式 回零模式 轮廓速度模式 轮廓转矩模式
状态字bit12 能够接收位置指令(0-能 1-不能) 原点回零完成(0-否 1-是) 零速信号(0-否 1-是) NA
状态字bit13 位置偏差状态(0-阈值内 1-超出阈值) 原点回零错误(0-无 1-有) NA NA
状态字bit14 NA NA NA NA
状态字bit15 NA NA NA NA

当前的状态机通过状态字Statusword判断(如果进入伺服运行之前出现了表中没有的状态字,很可能是驱动器上电异常或者报错了),而状态机的转移完全由控制字Controlword来操作。一般来讲,驱动器上电之后,我们要根据当前的状态来给出控制字,最终目的是把驱动器转移到伺服运行这个状态机。简单起见,无论当前处于何种状态,我们都往伺服运行这个方向走。

为了实现目的,写如下代码:

TYPE E_DriveStatus :
(Init := 0,  //初始化  //not ready to switch onNoFault := 1,  //伺服无故障  //switch on disabledReady := 2,  //伺服准备好  //ready to switch onWaitOn := 3,  //等待打开伺服使能  //switched onOperation := 4,  //伺服运行  //operation enabledQuickStop := 5,  //快速停机  //quick stop activeFaultStop := 6,  //故障停机  //fault reaction activeFault := 7  //故障  //fault
);
END_TYPE
TYPE AXIS_REF :
STRUCT//0x6040 在OP状态才生效 控制字的每一个bit位单独赋值无意义,必须与其他位共同构成某一控制指令ControlWord AT%Q*: UINT;//0x6041 在OP或Safe-OP下才更新  状态字的每一个bit位单独读取无意义,必须与其他位共同组成当前状态StatusWord AT%I*: UINT;
END_STRUCT
END_TYPE
FUNCTION_BLOCK MC_Run
VAR_IN_OUTAxis: AXIS_REF;
END_VAR
VAR//----------StatusWordBit9: UINT;  //用于判断状态机,状态机只和bit0-bit9有关//----------DriveStatus: E_DriveStatus;  //驱动器状态
END_VAR//状态机转移----------
StatusWordBit9:= Axis.StatusWord AND 2#1111111111;  //用于判断状态机,状态机只和bit0-bit9有关
IF StatusWordBit9 = 16#0 THEN  //0DriveStatus:= E_DriveStatus.Init;
END_IF
IF StatusWordBit9 = 16#0250 THEN  //1  //10  //7  //9  //12  //15DriveStatus:= E_DriveStatus.NoFault;
END_IF
IF StatusWordBit9 = 16#0231 THEN  //2  //6  //8DriveStatus:= E_DriveStatus.Ready;
END_IF
IF StatusWordBit9 = 16#0233 THEN  //3  //5DriveStatus:= E_DriveStatus.WaitOn;
END_IF
IF StatusWordBit9 = 16#0237 THEN  //4  //16DriveStatus:= E_DriveStatus.Operation;
END_IF
IF StatusWordBit9 = 16#0217 THEN  //11DriveStatus:= E_DriveStatus.QuickStop;
END_IF
IF StatusWordBit9 = 16#021F THEN  //13DriveStatus:= E_DriveStatus.FaultStop;
END_IF
IF StatusWordBit9 = 16#0218 THEN  //14DriveStatus:= E_DriveStatus.Fault;
END_IF//用户指令运行----------
CASE DriveStatus OFE_DriveStatus.NoFault:Axis.ControlWord:= 16#06;  //前往E_DriveStatus.Ready,终点是E_DriveStatus.OperationE_DriveStatus.Ready:Axis.ControlWord:= 16#07;  //前往E_DriveStatus.WaitOn,终点是E_DriveStatus.OperationE_DriveStatus.WaitOn:Axis.ControlWord:= 16#0F;  //前往E_DriveStatus.OperationE_DriveStatus.Operation://驱动器的运动,先按下不表E_DriveStatus.QuickStop:Axis.ControlWord:= 16#0F;  //停机完成时会前往E_DriveStatus.Operation(不能让ControlWord保持0x02,会卡在E_DriveStatus.QuickStop)E_DriveStatus.Fault:Axis.ControlWord:= 16#80;  //切换到E_DriveStatus.NoFault
END_CASE

设备上电,程序跑起来,按照程序的逻辑,只要驱动器上电正常不报错,就会进入伺服运行状态机。

3.3,伺服运行

伺服运行状态,就可以控制驱动器开始运动了。

3.3.1 操作模式

大部分驱动器支持8种操作模式:

  1. 周期同步位置模式Cyclic Synchronous Position Mode,即MC_MoveAbsolute和MC_MoveRelative
  2. 周期同步速度模式Cyclic Synchronous Velocity Mode,即MC_MoveVelocity
  3. 周期同步转矩模式Cyclic Synchronous Torque Mode,即MC_TorqueControl
  4. 轮廓位置模式Profile Position Mode,类似MC_MoveAbsolute和MC_MoveRelative
  5. 轮廓速度模式Profile Velocity Mode,类似MC_MoveVelocity
  6. 轮廓转矩模式Profile Torque Mode,类似MC_TorqueControl
  7. 回零模式Homing Mode,类似MC_Home
  8. 插补模式Interpolation Position Mode,类似MC_ExtSetPointGenFeed

8种模式可以分成3类:

  1. 周期同步模式:有3个模式,此模式下PLC内部来做路径规划,在每个与驱动器的通信周期,PLC都会计算出目标位置(速度/扭矩)然后发送给驱动器。这部分功能由PLC供应商来提供,至于倍福,这部分功能就封装在开头提到的TC1250或者TF5000中。
  2. 轮廓模式:也有3个模式,使用起来效果与周期同步模式是类似的,区别在于此模式下PLC仅在指令下达时给予驱动器目标位置、目标速度与加减速度等设定,然后由驱动器自身来做路径规划,CANOpen通信控制的话,主要就是用轮廓模式。
  3. 回零和插补:回零就是找原点嘛,可以用也可自己写。插补就是手动做路径规划,基本用不着。

与操作模式相关的对象是操作模式Modes of operation:(吐槽一下SV660C手册好多地方都写错了,比如下表是操作模式的值,SV660C手册却说是bit)

操作模式的值 0 1 2 3 4 5 6 7
描述 NA 轮廓位置模式 NA 轮廓速度模式 轮廓转矩模式 NA 回零模式 插补模式

3.3.2 轮廓位置模式

与轮廓位置模式相关的对象有操作模式Modes of operation,控制字Controlword,目标位置Target position,轮廓速度Profile velocity,轮廓加速度Profile acceleration,轮廓减速度Profile deceleration。不同的驱动器,目标位置和轮廓速度的换算会不同,下面以台达ASDA-A2为例说明控制流程。

控制流程如下:

  1. 令操作模式Modes of operation=16#1
  2. 给定目标位置Target position,单位是脉冲数
  3. 给定轮廓速度Profile velocity,单位是脉冲数/秒
  4. 给定轮廓加速度Profile acceleration和轮廓减速度Profile deceleration,单位是从0rpm加速到3000rpm所需的毫秒数
  5. 控制字Controlword的bit4上升沿,使能新位置指令。bit5置1,使用立刻更新模式。bit6置0,使用绝对位置模式
  6. 判断状态字StatusWord已经接收到了指令,然后复位控制字Controlword的bit4,方便下次给上升沿
  7. 判断状态字StatusWord到达目标位置,结束流程

其中,脉冲数到实际距离的换算还涉及到驱动器的ScalingFactorDenominator(即电机转一圈多少个脉冲)和减速机的减速比。

综上,写如下代码:

TYPE AXIS_REF :
STRUCT//0x6060 ModeOperation AT%Q*: SINT;//0x6040 在OP状态才生效 控制字的每一个bit位单独赋值无意义,必须与其他位共同构成某一控制指令ControlWord AT%Q*: UINT;//0x607ATargetPosition AT%Q*: DINT;//0x6081ProfileVelocity AT%Q*: DINT;//0x6083ProfileAcc AT%Q*: DINT;//0x6084ProfileDec AT%Q*: DINT;//0x6041 在OP或Safe-OP下才更新  状态字的每一个bit位单独读取无意义,必须与其他位共同组成当前状态StatusWord AT%I*: UINT;
END_STRUCT
END_TYPE
TYPE AXIS_INFO :
STRUCTTargetPosition: REAL;  //目标位置 mmTargetVelocity: REAL;  //目标速度 mm/sProfileVelocity: REAL;  //轮廓速度 mm/sProfileAcc: REAL;  //轮廓加速度 mm/sProfileDec: REAL;  //轮廓减速度 mm/s
END_STRUCT
END_TYPE
TYPE AXIS_PARA :
STRUCTScalingFactorNumerator: REAL;  //分子,填电机转一圈走多少mmScalingFactorDenominator: REAL;  //分母,填电机转一圈多少个脉冲,比如ASDA驱动器就是1280000,SV660C-22bit驱动器就是4194304
END_STRUCT
END_TYPE
FUNCTION_BLOCK MC_Run
VAR_IN_OUTAxis: AXIS_REF;Info: AXIS_INFO;Para: AXIS_PARA;
END_VAR
VAR//----------DriveStatus: E_DriveStatus;  //驱动器状态//----------DriveMoveCommand: INT;  //0:无 1:轮廓位置控制移动 3:轮廓速度控制移动 6:原点模式 20:快速停机 21:故障复位AbsOrRel: INT;  //轮廓位置控制移动方式 0:绝对移动 1:相对移动DriveCommandState: INT;  //指令状态机ScalingFactor: REAL;  //分母/分子 = 脉冲数/mm
END_VAR//Out-----
ScalingFactor:= Para.ScalingFactorDenominator / MAX(Para.ScalingFactorNumerator, 0.001);//用户指令运行----------
CASE DriveStatus OFE_DriveStatus.NoFault:Axis.ControlWord:= 16#06;  //前往E_DriveStatus.Ready,终点是E_DriveStatus.OperationE_DriveStatus.Ready:Axis.ControlWord:= 16#07;  //前往E_DriveStatus.WaitOn,终点是E_DriveStatus.OperationE_DriveStatus.WaitOn:Axis.ControlWord:= 16#0F;  //前往E_DriveStatus.OperationE_DriveStatus.Operation:IF DriveMoveCommand = 1 THEN  //轮廓位置控制移动CASE DriveCommandState OF 0:Axis.ModeOperation:= 1;  //轮廓位置控制模式  //0x6060IF AbsOrRel = 0 THEN  //绝对Axis.TargetPosition:= REAL_TO_DINT(Info.TargetPosition * ScalingFactor);  //0x607A  //单位:脉冲数ELSIF AbsOrRel = 1 THEN  //相对Axis.TargetPosition:= Axis.PositionActualValue + REAL_TO_DINT(Info.TargetPosition * ScalingFactor);END_IFAxis.ProfileVelocity:= REAL_TO_DINT(Info.ProfileVelocity * ScalingFactor);  //0x6081  //单位:脉冲数/秒Axis.ProfileAcc:= LIMIT(1, REAL_TO_DINT(50000.0 * Para.ScalingFactorNumerator / MAX(Info.ProfileAcc, 0.001)), 65500);  //0x6083  //从0rpm到3000rpm的毫秒数  //量程1-65500Axis.ProfileDec:= LIMIT(1, REAL_TO_DINT(50000.0 * Para.ScalingFactorNumerator / MAX(Info.ProfileDec, 0.001)), 65500);  //0x6084Axis.ControlWord.4:= 0;DriveCommandState:= 10;10:IF Axis.ModeOperationDisplay = 1 THEN//控制字0x0F->0x3FAxis.ControlWord.4:= 1;  //上升沿:使能位移指令Axis.ControlWord.5:= 1;  //0:非立刻更新 1:立刻更新Axis.ControlWord.6:= 0;  //位置指令类型 0:表示607A是绝对位置 1:表示607A是相对位置(设1不行啊)DriveCommandState:= 20;END_IF20:IF Axis.StatusWord.12 THEN  //不可接收新位移指令,说明伺服收到了位移指令Axis.ControlWord.4:= 0;  //复位位移指令 0x3F->0x2FDriveCommandState:= 30;END_IF30:IF Axis.StatusWord.10 THEN  //目标到达DriveMoveCommand:= 0;  //完事DriveCommandState:= 0;END_IFEND_CASEEND_IFE_DriveStatus.QuickStop:Axis.ControlWord:= 16#0F;  //停机完成时会前往E_DriveStatus.Operation(不能让ControlWord保持0x02,会卡在E_DriveStatus.QuickStop)E_DriveStatus.Fault:Axis.ControlWord:= 16#80;  //切换到E_DriveStatus.NoFault
END_CASE

按照流程把相应的参数填好,不出意外电机就可以转起来啦。

3.3.3 轮廓速度模式

控制流程和轮廓位置模式类似,具体如下:

  1. 控制字Controlword保持在0x0F
  2. 令操作模式Modes of operation=16#3
  3. 给定目标速度Target velocity,单位是0.1rpm
  4. 给定轮廓加速度Profile acceleration和轮廓减速度Profile deceleration,单位是从0rpm加速到3000rpm所需的毫秒数
  5. 判断状态字StatusWord到达目标速度,结束流程
//用户指令运行----------
CASE DriveStatus OFE_DriveStatus.Operation:IF DriveMoveCommand = 3 THEN  //轮廓速度控制模式CASE DriveCommandState OF 0:Axis.ControlWord:= 16#0F;  //控制字保持在0x0FDriveCommandState:= 10;10:Axis.ModeOperation:= 3;  //轮廓速度控制模式  //0x6060//单位:0.1rpm  //X(mm/s) = 60*X(mm/min) = 60*X/分子(rpm) = 600*X/分子(0.1rpm)Axis.TargetVelocity:= REAL_TO_DINT(600 * Info.TargetVelocity / Para.ScalingFactorNumerator);  //0x60FFAxis.ProfileAcc:= LIMIT(1, REAL_TO_DINT(50000.0 * Para.ScalingFactorNumerator / MAX(Info.ProfileAcc, 0.001)), 65500);  //0x6083  //从0rpm到3000rpm的毫秒数  ms/3000rpm //量程1-65500Axis.ProfileDec:= LIMIT(1, REAL_TO_DINT(50000.0 * Para.ScalingFactorNumerator / MAX(Info.ProfileDec, 0.001)), 65500);  //0x6084  //从0rpm到3000rpm的毫秒数DriveCommandState:= 20;20:IF Axis.ModeOperationDisplay = 3 THENDriveCommandState:= 30;END_IF30:IF Axis.StatusWord.10 THEN  //目标到达DriveMoveCommand:= 0;  //完事DriveCommandState:= 0;END_IFEND_CASEEND_IF
END_CASE      

此处只贴出了关键代码,同样把相应的参数填好,不出意外电机就可以转起来啦。

3.3.4 其他

快速停机和故障复位都比较简单,只需要写控制字Controlword和操作模式Modes of operation就可以实现。

至于MC_SetPosition,暂时没确定倍福是怎么做的,不过用操作模式Modes of operation的回零模式,并结合回零方式Homing Method可以实现相同的效果。

4,用起来

后面忘了截图了,贴一张前期刚刚用轮廓速度模式把电机转起来的图。

DS402

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

地理:美国各州首府系列

America1.botson文化重镇波士顿 2.annapolis 3.弗吉尼亚州首府里士满 4.罗得岛州的普罗维登斯 5.加利福尼亚州的萨克拉门托 6.纽约州奥尔巴尼市 7.阿拉巴马州蒙哥马利市 8.alascap 9.arizona 10.little阿肯色州首府小石城 11.康涅狄格州哈特福德市 12.特拉华州多佛市 13.印第安…

利用kafka和kafka connect插件debezium实现oracle表同步

1.kafka安装 1.1.java安装 openjdk下载,建议使用17,至少应该高于版本11 # 进入家目录,解压下载的java包,配置环境变量 tar vxf openjdk-20.0.1_linux-x64_bin.tar.gz -C /usr/local/ vi .bash_profile # 注意要把JAVA的目录放到$PATH之前 export JAVA_HOME=/usr/local/jdk-…

WPF中如何根据数据类型使用不同的数据模板

我们在将一个数据集合绑定到列表控件时,有时候想根据不同的数据类型,显示为不同的效果。 例如将一个文件夹集合绑定到ListBox时,系统文件夹显示为不同的效果,就可以使用模板选择器功能。 WPF提供了一个模板选择器类型DataTemplateSelector,它可以根据数据对象和数据绑定元…

Typecho Joe 导航菜单目录以及搜索关键字回显主题优化版

Joe 是 Typecho 博客中一款开源免费且非常精美的主题,但是这款主题很早就停止维护了,有些功能作者并没有开发,并且在 Typecho 更新到 1.2.1 版本后还出现了一个小 BUG Joe 主题的知名度很高,所以在原作者停止维护后很多大佬发布过自己魔改的版本,不可否认魔改后的主题 BUG…

gdb学习记录

目录如何查看地址值查看当前函数参数多线程调试只暂停指定线程,其他线程不影响总结如何查看地址值 查看下一个地址:x/x 0x12345679 以八进制显示:x/o 0x12345678 以十进制显示:x/d 0x12345678 显示更多的地址和值:x/8xw 0x12345678(显示从该地址开始的8个字(word),每个…

UE4 CharacterMovementComponent

之前在学习网络通信时,经常需要对客户端上的行为进行预测,来降低延迟带来的比较差的体验。而UE4为角色移动提供了CharacterMovementComponent这个组件,其在移动时可以发现已经实现了客户端预测。 现在可以通过对CharacterMovementComponent进行扩展,实现我们自定义的运动模…

抖音coze接入网站实现智能在线客服

在coze.cn上创建好机器人并且可以发布为API,方便接入自己的系统 一定要先创建新令牌,再点发布,并且必须勾选,发布到API 在客服系统gofly.v1kf.com中配置 【菜单】【团队设置】【大模型设置】接口地址部分,请填写扣子的机器人ID 接口密钥部分,请填写扣子的API令牌然后…

centos9(linux): 安装clamav

一,官方网站 https://www.clamav.net/ 如图:二,安装 1,开启epel仓库 [root@blog ~]# yum install -y epel-release 2,安装clamav [root@blog ~]# yum install clamav 说明:病毒库数据较大,200多M,安装时需要等待较长时间 安装完成后查看clamav的版本: [root@blog ~]# clam…

pygame手搓五子棋

代码:#coding=utf-8import os,sys,re,time import pygame import random from win32api import GetSystemMetricspygame.init() pygame.display.set_caption("五子棋")percent = 0.6 screen_width = GetSystemMetrics(0) screen_height = GetSystemMetrics(1) windo…

网站提示404错误:页面未找到怎么办

当网站提示 404 Error 或 “页面未找到” 时,这意味着客户端尝试访问的资源在服务器上不存在或无法找到。这种情况很常见,可以通过以下几个步骤来诊断和解决问题: 常见原因URL 输入错误:这是最常见的原因之一。由于人为疏忽或输入错误,导致请求的 URL 与服务器上实际存在的…

OpenCV开发笔记(七十九):基于Stitcher类实现全景图片拼接

前言一个摄像头视野不大的时候,我们希望进行两个视野合并,这样让正视的视野增大,从而可以看到更广阔的标准视野。拼接的方法分为两条路,第一条路是stitcher类,第二条思路是特征点匹配。  本篇使用stitcher匹配,进行两张图来视野合并拼接。 Demo 两张图拼接过程步骤一:…

WPF 路由事件

一、什么是路由事件? 根据MSDN定义:功能定义:路由事件是一种可以针对元素树中的多个侦听器(而不是仅针对引发该事件的对象)调用处理程序的事件。 实现定义:路由事件是由 类的实例支持的 CLR 事件, RoutedEvent 由事件 Windows Presentation Foundation (WPF) 系统处理。…