EtherCAT 伺服控制功能块实现

        EtherCAT 是运动控制领域主要的通信协议,开源EtherCAT 主站协议栈 IgH 和SOEM 两个项目,IgH 相对更普及一些,但是它是基于Linux 内核的方式,比SOEM更复杂一些。使用IgH 协议栈编写一个应用程序,控制EtherCAT 伺服电机驱动器是比较简单的。但是要实现一个通用的EtherCAT 组件库(例如IEC61131-3 ,或者IEC61499功能块)就复杂一些了,例如动态地加入一个从站驱动器,通过组件控制某一个从站。

本博文研究基于组件的EtherCAT 程序架构及其实现方法。

背景技术

CiA402 运动控制的CANopen 驱动器规范

        EtherCAT 的运动控制器是基于CANopen 的CiA402规范。这套配置文件规范标准化了伺服驱动器、变频器和步进电机控制器的功能行为。它定义了状态机,控制字,状态字,参数值,它们映射到过程数据对象(PDO)配置文件已在 IEC 61800-7 系列中部分实现国际标准化。

COE 协议

CANopen Over EtherCAT 协议被称为COE,它的架构为:

正是由于如此,基于EtherCAT 的运动控制器的控制方式,PDO 定义,控制方式都是类似的。

主要的一些数据对象

 

PLCopen 运动控制库

 最著名的运动控制的标准应当数PLCopen 运动控制库,它是PLC 上的一个功能块集。PLC 的应用程序通过这些功能块能够方便地实现运动控制。但是这些功能块如何实现,如何与硬件驱动结合。内部实现应该是比较复杂的。笔者看来,应该有两种方式:

  •    PLC 内嵌运动控制模型
  •    通过Ethercat 总线外接运动控制模块

两种结构的实现方法应该各不相同。是否有支持etherCAT 的PLCopen 功能块库?

EtherCAT 主站程序

        EtherCAT 协议是倍福公司提出的,从站通常使用专用ASIC 芯片,FPGA 实现,而主站使用通用Ethernet接口和软件实现。EtherCAT 主站协议有专业公司开发的商业化产品,也有开源代码,下面是两个比较流行的EtherCAT Master

  • IgH
  • SOEM

感觉IgH  更普及一点,于是我们选择IgH 协议栈。

EtherCAT 组件设计

IgH 主要实现Ethercat 协议数据帧的映射,以及通过Ethernet 发送和接收。如果设计成为组件库,许多参数需要可编程,比如:

  •     多少从站
  •    每个从站的位置
  •    每个从站的操作模型,操作算法
  •    每个从机的状态

        本项目的基本思路是构建一个从站类,每个物理从站对应一个虚拟从站,应用程序通过虚拟从站控制从站,将虚拟从站的参数映射到物理从站参数,通过Ethercat 网络发送和接收。

从站类(SevoController Class)与主站类(Master Class)

        为了实现动态的建立和控制从站,采用虚拟从站类。为每个物理的从站创建一个从站类(SevoController). 该类型中包含了物理伺服驱动控制器的参数和状态。应用程序可以通过修改SevoController 的参数,实现对物理伺服的驱动。

        为了相对于,我们同时设立一个Master 类(Master Class)。存放主站的参数。

系统架构

        从上图可见,使用Slaver 类作为应用程序和EtherCAT 底层的接口。EtherCAT 底层程序读取Slave 的参数,对EtherCAT 初始化,并且建立一个EtherCAT 线程,周期扫描各个从站。

从站类(slave class)

#ifndef _SEVOCONTROLLER_H
#define _SEVOCONTROLLER_H#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
#include "ecrt.h"
#define PROFILE_POSITION 1
#define VEOLOCITY 2
#define PROFILE_VELOCITY 3
#define PROFILE_TORQUE 4
#define HOMING 6
#define CYCLICE_SYNC_POSITION 8
using namespace std;
struct pdo_offset
{unsigned int ctrl_word;unsigned int operation_mode;unsigned int target_velocity;unsigned int target_position;unsigned int profile_velocity;unsigned int status_word;unsigned int mode_display;unsigned int current_velocity;
};
class SevoController
{
public:pdo_offset offset;uint16_t position;uint32_t vendor_id;uint32_t product_code;uint32_t position_actual;uint32_t velocity_actual;uint32_t operation_modes;uint32_t target_velocity;uint32_t target_position;uint32_t profile_velocity;ec_slave_config_t *slave_config;void eventAction(string EventName);SevoController(uint32_t Position, uint32_t Vendor_id, uint32_t Product_cdode, uint32_t Modes_operation);
};
#endif

控制代码

#include "ecrt.h"
#include "stdio.h"
#include <errno.h>
#include <sys/resource.h>
#include <list>
#include "SevoController.hpp"
#include <pthread.h>
void check_domain_state(void);
void check_slave_config_states(void);
pthread_t cycle_thread;
int cycles;
int Run = 1;
ec_master_t *master = NULL;
static ec_master_state_t master_state = {};static ec_domain_t *domainServo = NULL;
static ec_domain_state_t domainServo_state = {};
static uint8_t *domain_pd = NULL;
std::list<SevoController *> SevoList;
ec_pdo_entry_reg_t *domainServo_regs;
static ec_pdo_entry_info_t pdo_entries[] = {/*RxPdo 0x1600*/{0x6040, 0x00, 16},{0x6060, 0x00, 8 }, {0x60FF, 0x00, 32},{0x607A, 0x00, 32},{0x6081, 0x00, 32},/*TxPdo 0x1A00*/{0x6041, 0x00, 16},{0x6061, 0x00, 8},{0x606C, 0x00, 32}
};static ec_pdo_info_t Slave_pdos[] = {// RxPdo{0x1600, 5, pdo_entries + 0},// TxPdo{0x1A00, 3, pdo_entries + 5}};static ec_sync_info_t Slave_syncs[] = {{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},{1, EC_DIR_INPUT, 0, NULL, EC_WD_DISABLE},{2, EC_DIR_OUTPUT, 1, Slave_pdos + 0, EC_WD_DISABLE},{3, EC_DIR_INPUT, 1, Slave_pdos + 1, EC_WD_DISABLE},{0xFF}};
int ConfigPDO()
{domainServo = ecrt_master_create_domain(master);if (!domainServo){return -1;}//domainServo_regs = new ec_pdo_entry_reg_t[9];std::list<SevoController *>::iterator it;int index = 0;for (it = SevoList.begin(); it != SevoList.end(); it++){domainServo_regs[index++] = {0, (**it).position, (**it).vendor_id, (**it).product_code, 0x6040, 0x00, &((**it).offset.ctrl_word)};domainServo_regs[index++] = {0, (**it).position, (**it).vendor_id, (**it).product_code, 0x6060, 0x00, &((**it).offset.operation_mode)};domainServo_regs[index++] = {0, (**it).position, (**it).vendor_id, (**it).product_code, 0x60FF, 0x00, &((**it).offset.target_velocity)};domainServo_regs[index++] = {0, (**it).position, (**it).vendor_id, (**it).product_code, 0x607A, 0x00, &((**it).offset.target_position)};domainServo_regs[index++] = {0, (**it).position, (**it).vendor_id, (**it).product_code, 0x6081, 0x00, &((**it).offset.profile_velocity)};domainServo_regs[index++] = {0, (**it).position, (**it).vendor_id, (**it).product_code, 0x6041, 0x00, &((**it).offset.status_word)};domainServo_regs[index++] = {0, (**it).position, (**it).vendor_id, (**it).product_code, 0x6061, 0x00, &((**it).offset.mode_display)};domainServo_regs[index++] = {0, (**it).position, (**it).vendor_id, (**it).product_code, 0x606C, 0x00, &((**it).offset.current_velocity)};printf("product_code:%x\n", (**it).product_code);}domainServo_regs[index++] = {}; ////for (it = SevoList.begin(); it != SevoList.end(); it++){(**it).slave_config = ecrt_master_slave_config(master, 0, (**it).position, (**it).vendor_id, (**it).product_code);ecrt_slave_config_pdos((**it).slave_config, EC_END, Slave_syncs);}//if (ecrt_domain_reg_pdo_entry_list(domainServo, domainServo_regs)){printf("PDO entry registration failed!\n");return -1;}return 0;
}
void check_master_state(void)
{ec_master_state_t ms;ecrt_master_state(master, &ms);if (ms.slaves_responding != master_state.slaves_responding){printf("%u slave(s).\n", ms.slaves_responding);}if (ms.al_states != master_state.al_states){printf("AL states: 0x%02X.\n", ms.al_states);}if (ms.link_up != master_state.link_up){printf("Link is %s.\n", ms.link_up ? "up" : "down");}master_state = ms;
}
void *cyclic_task(void *arg)
{uint16_t status;//  int8_t      opmode;static uint16_t command = 0x004F;printf("Cycles Task Start\n");while (Run){ecrt_master_receive(master);ecrt_domain_process(domainServo);check_domain_state();check_master_state();check_slave_config_states();std::list<SevoController *>::iterator it;for (it = SevoList.begin(); it != SevoList.end(); it++){status = EC_READ_U16(domain_pd + (**it).offset.status_word);if ((status & command) == 0x0040){printf("Switch On disabled\n");EC_WRITE_U16(domain_pd + (**it).offset.ctrl_word, 0x0006);EC_WRITE_S8(domain_pd + (**it).offset.operation_mode, (**it).operation_modes);command = 0x006F;}/*Ready to switch On*/else if ((status & command) == 0x0021){EC_WRITE_U16(domain_pd + (**it).offset.ctrl_word, 0x0007);command = 0x006F;}/* Switched On*/else if ((status & command) == 0x0023){printf("Switched On\n");EC_WRITE_U16(domain_pd + (**it).offset.ctrl_word, 0x000f);if ((**it).operation_modes == PROFILE_VELOCITY){EC_WRITE_S32(domain_pd + (**it).offset.target_velocity, (**it).target_velocity);}else{EC_WRITE_S32(domain_pd + (**it).offset.target_position, (**it).target_position);EC_WRITE_S32(domain_pd + (**it).offset.profile_velocity, (**it).profile_velocity);}command = 0x006F;}// operation enabledelse if ((status & command) == 0x0027){printf("operation enabled:%d\n", cycles);if (cycles == 0)EC_WRITE_U16(domain_pd + (**it).offset.ctrl_word, 0x001f);if ((status & 0x400) == 0x400){printf("target reachedd\n");Run = 0;EC_WRITE_U16(domain_pd + (**it).offset.ctrl_word, 0x0180); // halt}cycles = cycles + 1;}}ecrt_domain_queue(domainServo);ecrt_master_send(master);usleep(10000);}return ((void *)0);
}
void ethercat_initialize()
{master = ecrt_request_master(0);ConfigPDO();if (ecrt_master_activate(master)){printf("Activating master...failed\n");return;}if (!(domain_pd = ecrt_domain_data(domainServo))){fprintf(stderr, "Failed to get domain data pointer.\n");return;}// 启动master Cycles Threadpthread_create(&cycle_thread, NULL, cyclic_task, NULL);
}
void check_domain_state(void)
{ec_domain_state_t ds = {};// ec_domain_state_t ds1 = {};// domainServoInputecrt_domain_state(domainServo, &ds);if (ds.working_counter != domainServo_state.working_counter){printf("domainServoInput: WC %u.\n", ds.working_counter);}if (ds.wc_state != domainServo_state.wc_state){printf("domainServoInput: State %u.\n", ds.wc_state);}domainServo_state = ds;
}
void check_slave_config_states(void)
{ec_master_state_t ms;ecrt_master_state(master, &ms);if (ms.slaves_responding != master_state.slaves_responding){printf("%u slave(s).\n", ms.slaves_responding);}if (ms.al_states != master_state.al_states){printf("AL states: 0x%02X.\n", ms.al_states);}if (ms.link_up != master_state.link_up){printf("Link is %s.\n", ms.link_up ? "up" : "down");}master_state = ms;
}

主程序

/*****************************************************************************
sudo /etc/init.d/ethercat start
gcc testbyesm.c -Wall -I /opt/etherlab/include -l ethercat -L /opt/etherlab/lib -o testbyesm****************************************************************************/
#include  "time.h"
#include  "SevoController.hpp"
#include "ethercat.hpp"
#define Panasonic           0x0000066F,0x60380004
#define TASK_FREQUENCY          100 /*Hz*/
#define TIMOUT_CLEAR_ERROR  (1*TASK_FREQUENCY)  /*clearing error timeout*/
#define TARGET_VELOCITY         8388608 /*target velocity*/
#define PROFILE_VELOCITY            3   /*Operation mode for 0x6060:0*/
#define PROFILE_POSITION            1  
int main(){printf("EtherCAT Component Test\n");SevoController *Sevo1=new SevoController(0,Panasonic,PROFILE_POSITION);Sevo1->profile_velocity=TARGET_VELOCITY*100;Sevo1->target_velocity=TARGET_VELOCITY*10;Sevo1->target_position=TARGET_VELOCITY/2;SevoList.push_back(Sevo1);ethercat_initialize();while(1){sleep(10);}
}

小结

上面的程序基于松下A6 EtherCAT 伺服电机。

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

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

相关文章

酷柚易汛ERP - 权限设置操作指南

1、产品介绍 对系统的同事管理、角色管理、数据授权进行设置 1.1 同事管理 对当前系统添加同事账号&#xff0c;超级管理员不允许删除 1.2 角色管理 对当前系统添加角色&#xff0c;系统中可以设置多种角色&#xff0c;不同角色设置不同权限&#xff0c;方便添加同事时进行…

代码随想录算法训练营第五十八天丨 动态规划part18

739. 每日温度 思路 首先想到的当然是暴力解法&#xff0c;两层for循环&#xff0c;把至少需要等待的天数就搜出来了。时间复杂度是O(n^2) 那么接下来在来看看使用单调栈的解法。 什么时候用单调栈呢&#xff1f; 通常是一维数组&#xff0c;要寻找任一个元素的右边或者左边…

机器视觉选型-什么时候用远心镜头

物体厚 当被检测物体厚度较大&#xff0c;需要检测不止一个平面时&#xff0c;典型应用如食品盒&#xff0c;饮料瓶等。 物体位置变化 当被测物体的摆放位置不确定&#xff0c;可能跟镜头成一定角度时。 物体上下跳动 当被测物体在被检测过程中上下跳动&#xff0c;如生产线上下…

人工智能基础_机器学习040_Sigmoid函数详解_单位阶跃函数与对数几率函数_伯努利分布---人工智能工作笔记0080

然后我们再来详细说一下Sigmoid函数,上面的函数的公式 我们要知道这里的,Sigmoid函数的意义,这逻辑斯蒂回归的意义就是,在多元线性回归的基础上,把 多元线性回归的结果,缩放到0到1之间对吧,根据中间的0.5为分类,小于0.5的一类,大于的一类, 这里的h theta(x) 就是概率函数 然…

婴儿沐浴座椅上架亚马逊美国站安全标准要求ASTM F1967-19测试,CPC认证

亚马逊婴儿沐浴座椅产品认证 在亚马逊上架的婴儿沐浴座椅产品&#xff0c;亚马逊会要求店家上传相关的产品测试报告&#xff0c;若被抽查到没有相关认证的产品将面临产品被下架或罚款等处罚&#xff01; 婴儿沐浴座椅产品示例&#xff1a; 婴儿沐浴座椅是一种用于浴缸、盥洗盆…

微软宣布计划在 Windows 10 版本 22H2 中引入 AI 助手 Copilot

根据之前的传言&#xff0c;微软宣布计划在 Windows 10 版本 22H2 中引入 AI 助手 Copilot。Copilot 将包含在 Windows 10 家庭版和专业版中。该更新的发布日期尚未公布&#xff0c;但预计将在不久的将来发布。 在一份新闻稿中&#xff0c;微软表示在向 Windows 11 用户提供 Co…

什么是模糊测试?

背景&#xff1a;近年来&#xff0c;随着信息技术的发展&#xff0c;各种新型自动化测试技术如雨后春笋般出现。其中&#xff0c;模糊测试&#xff08;fuzz testing&#xff09;技术开始受到行业关注&#xff0c;它尤其适用于发现未知的、隐蔽性较强的底层缺陷。这里&#xff0…

C语言--给定一行字符串,获取其中最长单词【图文详解】

一.问题描述 给定一行字符串,获取其中最长单词。 比如&#xff1a;给定一行字符串&#xff1a; hello wo shi xiao xiao su 输出&#xff1a;hello 二.题目分析 “打擂台算法”&#xff0c;具体内容小伙伴们可以参考前面的内容。 三.代码实现 char* MaxWord(const char* str)…

【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷F

1、常见的加密算法可以分为对称加密算法和非对称加密算法&#xff0c;以下关于它们的描述正确的是 A、AES是一种常见的非对称加密算法 B、凯撒密码是一种非对称加密 C、非对称加密算法的解密使用的秘钥与加密不同 D、对称加密算法无法被暴力破解 答案&#xff1a;C 2、12根…

Apache DolphinScheduler在通信行业的多集群统一建设与管理实践

背景介绍 为什么我们考虑构建统一的调度平台&#xff1f; 主要原因是&#xff1a;我们公司的大数据中心目前拥有七个大数据集群&#xff0c;这些集群分布在不同的机房&#xff0c;例如内蒙、南京、苏州和广州。而且&#xff0c;这些机房之间的网络并不互通。如果每个集群都独立…

Pytorch多GPU并行训练: DistributedDataParallel

1 模型并行化训练 1.1 为什么要并行训练 在训练大型数据集或者很大的模型时一块GPU很难放下&#xff0c;例如最初的AlexNet就是在两块GPU上计算的。并行计算一般采取两个策略&#xff1a;一个是模型并行&#xff0c;一个是数据并行。左图中是将模型的不同部分放在不同GPU上进…

[开源]基于 AI 大语言模型 API 实现的 AI 助手全套开源解决方案

原文&#xff1a;[开源]基于 AI 大语言模型 API 实现的 AI 助手全套开源解决方案 一飞开源&#xff0c;介绍创意、新奇、有趣、实用的开源应用、系统、软件、硬件及技术&#xff0c;一个探索、发现、分享、使用与互动交流的开源技术社区平台。致力于打造活力开源社区&#xff0…

pytorch文本分类(一):文本预处理

pytorch文本分类&#xff08;一&#xff09;&#xff1a;文本预处理 本文为自己在鲸训练营答题总结&#xff0c;作业练习都在和鲸社区数据分析协作平台 ModelWhale 上。 &#x1f6a9;学习任务原链接在这里 相关数据链接&#xff1a;https://pan.baidu.com/s/1iwE3LdRv3uAkGGI…

基础课6——开放领域对话系统架构

开放领域对话系统是指针对非特定领域或行业的对话系统&#xff0c;它可以与用户进行自由的对话&#xff0c;不受特定领域或行业的知识和规则的限制。开放领域对话系统需要具备更广泛的语言理解和生成能力&#xff0c;以便与用户进行自然、流畅的对话。 与垂直领域对话系统相比…

2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-C卷

2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-C卷 2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-C卷A模块基础设施设置/安全加固&#xff08;200分&#xff09;A-1&#xff1a;登录安全加固&#xff08;Windows, Linux&#xff09;A-2&#…

Flutter笔记:桌面端应用多窗口管理方案

Flutter笔记 桌面端应用多窗口管理方案 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/134468587 【简介…

Windows11怎样投屏到电视上?

电视屏幕通常比电脑显示器更大&#xff0c;能够提供更逼真的图像和更震撼的音效&#xff0c;因此不少人也喜欢将电脑屏幕投屏到电视上&#xff0c;缓解一下低头看电脑屏幕的烦恼。 Windows11如何将屏幕投射到安卓电视&#xff1f; 你需要在电脑和电视分贝安装AirDroid Cast的电…

【MySQL】数据类型

数据类型 前言正式开始数值类型整数类型bit类型浮点数类型floatdecimal 字符串类型charvarcharchar和varchar比较 日期和时间类型enum和setenum和set类型的查找 前言 我在前一篇讲表的操作的时候碰到了一些数据类型&#xff0c;但是没有正式讲这些类型&#xff0c;本篇就重点讲…

根据店铺ID/店铺链接/店铺昵称获取京东店铺所有商品数据接口|京东店铺所有商品数据接口|京东API接口

要获取京东店铺的所有商品数据&#xff0c;您需要使用京东开放平台提供的API接口。以下是一些可能有用的API接口&#xff1a; 商品SKU列表接口&#xff1a;该接口可以获取指定店铺下的所有商品SKU列表&#xff0c;包括商品ID、名称、价格等信息。您可以使用该接口来获取店铺中…

一文看分布式锁

为什么会存在分布式锁&#xff1f; 经典场景-扣库存&#xff0c;多人去同时购买一件商品&#xff0c;首先会查询判断是否有剩余&#xff0c;如果有进行购买并扣减库存&#xff0c;没有提示库存不足。假如现在仅存有一件商品&#xff0c;3人同时购买&#xff0c;三个线程同时执…