通过电机编码器AB相输出确定电机转向
AB相输出相差90度,即当A相"正跳变"时如果B相是高电平那么是"正转",反之是"反转"
图片:
正转
反转
#include <TimerOne.h> #define D_Left_PIN 7 #define D_Right_PIN 8 #define IN1 22 #define IN2 23 #define IN3 24 #define IN4 25 #define ENA 5 #define ENB 13 #define FIREPIN 9 #define Kp 0.5 #define Ki 0.5 #define Kd 0.0#define MOTOR_GO_FORWARD {STOP=0; digitalWrite(IN1,LOW);digitalWrite(IN2,HIGH);digitalWrite(IN3,LOW);digitalWrite(IN4,HIGH);} #define MOTOR_GO_BACK {STOP=0; digitalWrite(IN1,HIGH);digitalWrite(IN2,LOW);digitalWrite(IN3,HIGH);digitalWrite(IN4,LOW);} #define MOTOR_GO_RIGHT {STOP=0; digitalWrite(IN1,HIGH);digitalWrite(IN2,LOW);digitalWrite(IN3,LOW);digitalWrite(IN4,HIGH);} #define MOTOR_GO_LEFT {STOP=0; digitalWrite(IN1,LOW);digitalWrite(IN2,HIGH);digitalWrite(IN3,HIGH);digitalWrite(IN4,LOW);} #define MOTOR_GO_STOP {STOP=1; digitalWrite(IN1,LOW);digitalWrite(IN2,LOW);digitalWrite(IN3,LOW);digitalWrite(IN4,LOW);} int Left_Speed[10]={20,30,35,40,45,50,55,60,65,70};//左侧速度档位 int Right_Speed[10]={20,30,35,40,45,50,55,60,65,70};//右侧速度档位int Left_Speed_Hold=5;//定义左侧速度变量 int Right_Speed_Hold=5;//定义右侧速度变量byte Fireing=0; long FireStopTime=0; unsigned long lastSendTime=0; unsigned long lastReceiveTime=0; byte RecCache[512]; volatile int CacheIndex=0;byte STOP=1; //=============================PID Args=========================== float left_LastError=0.0; // Error[-1] float left_SumError=0.0; // Sums of Errorsfloat right_LastError=0.0; // Error[-1] float right_SumError=0.0; // Sums of Errorsint flag=0; //定义定时器中断标志,用于判断是否发生中断long counter_val_right[2] = {0,0}; //定义数组,用于存放外部中断计数值 byte CurCnt_right = 0; //定义当前计数器标志,用于判断当前正在计数的数组 byte Direction_right=2; int rightSpeed=0; float rightPWM=0.0;long counter_val_left[2] = {0,0}; //定义数组,用于存放外部中断计数值 byte CurCnt_left = 0; //定义当前计数器标志,用于判断当前正在计数的数组 byte Direction_left=2; int leftSpeed = 0; float leftPWM=0.0; unsigned long lastPrintTime=0; //========================End PID=========================void setup() {Serial.begin(38400);//初始化波特率为115200 initWifi();initIO();setCache(0,512);attachInterrupt(0,counter_left,RISING);attachInterrupt(1,counter_right, RISING);//设置中断方式为上升沿 Timer1.initialize(100000);// 设置定时器中断时间,单位微秒,此处为1秒Timer1.attachInterrupt( timerIsr ); // 打开定时器中断interrupts(); //打开外部中断 } void initIO(){pinMode(D_Left_PIN,INPUT);pinMode(D_Right_PIN,INPUT);pinMode(IN1,OUTPUT);pinMode(IN2,OUTPUT);pinMode(IN3,OUTPUT);pinMode(IN4,OUTPUT);pinMode(ENA,OUTPUT);pinMode(ENB,OUTPUT);pinMode(FIREPIN,OUTPUT);digitalWrite(IN1,LOW);digitalWrite(IN2,LOW);digitalWrite(IN3,LOW);digitalWrite(IN4,LOW);digitalWrite(FIREPIN,LOW);} void loop() {handleTXR();checkStopFire();speedControl();}void setSpeed(){float leftP=0.0,rightP=0.0,leftD=0.0,rightD=0.0;// 比例常数 Proportional ConstleftP=(Left_Speed[Left_Speed_Hold]- leftSpeed);rightP=(Right_Speed[Right_Speed_Hold] - rightSpeed);//积分常数 Integral Constleft_SumError +=leftP;right_SumError+=rightP;//微分常数 Derivative Const // leftD=leftP-left_LastError; // rightD=rightP-right_LastError; // left_LastError=leftD; // right_LastError=rightD; // leftPWM=(leftP * Kp) + (left_SumError * Ki) +(leftD * Kd); // rightPWM=((rightP) * Kp) + (right_SumError * Ki) +(rightD * Kd) ; leftPWM=(leftP * Kp) + (left_SumError * Ki) ;rightPWM=((rightP) * Kp) + (right_SumError * Ki) ; if(leftPWM>255)leftPWM=255;if(leftPWM<0)leftPWM=0;if(rightPWM>255)rightPWM=255;if(rightPWM<0)rightPWM=0;analogWrite(ENA,rightPWM);analogWrite(ENB,leftPWM); }void speedControl(){if(flag==1) //判断是否发生定时器中断,即定时时间是否到达 {flag=0; //清除定时器中断标志位if((CurCnt_left&0x01) == 0) //当前使用的是偶数计数器,则上次频率值存放在第二个元素中 {leftSpeed =counter_val_left[1]; //读取数组第二个元素中的数值counter_val_left[1]=0; //读完清除原来的数值,以便下次使用 }else //当前使用的是奇数计数器,则上次频率值存放在第一个元素中 {leftSpeed =counter_val_left[0]; //读取数组第二个元素中的数值counter_val_left[0]=0; //读完清除原来的数值,以便下次使用 }if((CurCnt_right&0x01) == 0) //当前使用的是偶数计数器,则上次频率值存放在第二个元素中 {rightSpeed =counter_val_right[1]; //读取数组第二个元素中的数值counter_val_right[1]=0; //读完清除原来的数值,以便下次使用 }else //当前使用的是奇数计数器,则上次频率值存放在第一个元素中 {rightSpeed =counter_val_right[0]; //读取数组第二个元素中的数值counter_val_right[0]=0; //读完清除原来的数值,以便下次使用 }if(!STOP) setSpeed();if((millis()-lastPrintTime)>500){Serial.print("L:"); //发送帧头大写SSerial.print( leftSpeed); //发送频率数据,并回车换行Serial.print(",");Serial.print(Direction_left);Serial.print(",R:");Serial.print(rightSpeed);Serial.print(",");Serial.print(Direction_right);Serial.println("");Serial.println(leftPWM);Serial.println(rightPWM);lastPrintTime=millis();}} } //外部中断处理函数void counter_left(){counter_val_left[CurCnt_left & 0x01] +=1;Direction_left=digitalRead(D_Left_PIN); }void counter_right() {//通过当前计数器来实现对外部中断计数值存储的切换counter_val_right[CurCnt_right& 0x01] += 1; //发生一次中断则加1Direction_right=digitalRead(D_Right_PIN); }//定时器中断处理函数 void timerIsr() {flag=1; //置位定时器中断标志位CurCnt_right++; //当前计数器的值加1,实现另一个计数值切换CurCnt_left++; }//===================End PID =============== void initWifi(){Serial2.begin(115200);delayAndRead(100);Serial2.println("AT+CIOBAUD=38400");delayAndRead(100);Serial2.println("AT+RST");delayAndRead(3000);Serial2.begin(38400);Serial2.println("AT+CIPMUX=1");delayAndRead(500);Serial2.println("AT+CIPSERVER=1");delayAndRead(200);Serial2.println("AT+CIPSTO=60");delayAndRead(300); }void fire(long fireDelay){if(Fireing==1)return;digitalWrite(FIREPIN,HIGH);Fireing=1;FireStopTime=millis() + fireDelay;Serial.println(FIREPIN);Serial.println("fireDelay" + (String)fireDelay);Serial.println(FireStopTime); } void checkStopFire(){//check stop fireif(Fireing==1 && (FireStopTime <=millis())){Fireing=0;digitalWrite(FIREPIN,LOW);} }void reply(bool isOk,String cmd,String msg){String rStr="";if(isOk){rStr="$FOK-" + cmd +":" +msg +"$E";Serial2.println("AT+CIPSEND=0," + (String)rStr.length()); delay(10);Serial2.println(rStr);}else{rStr="$FEE-" + cmd +":" +msg +"$E";Serial2.println("AT+CIPSEND=0," + (String)rStr.length()); delay(10);Serial2.println(rStr);} }void replyBytes(bool isOk,String cmd,byte msg[],int msgLen){String rStr="";int sendLen=0;if(isOk){rStr="$FOK-" + cmd +":" ;sendLen= rStr.length() + msgLen +2; //2 is "$E"Serial2.println("AT+CIPSEND=0," + (String)sendLen); delay(10);Serial2.print(rStr);for(int i=0;i<msgLen;i++){Serial2.write(msg[i]);}Serial2.println("$E");}else{rStr="$FEE-" + cmd +":" ;sendLen= rStr.length() + msgLen +2; //2 is "$E"Serial2.println("AT+CIPSEND=0," + (String)sendLen); delay(10);Serial2.print(rStr);for(int i=0;i<msgLen;i++){Serial2.write(msg[i]);}Serial2.println("$E");} }void cls(){while(Serial2.available()){Serial2.read();} }void delayAndRead(int waitTime){delay(waitTime);while(Serial2.available()){Serial.write(Serial2.read()); } }void handleTXR(){while(Serial2.available()){byte c=(byte) Serial2.read();RecCache[CacheIndex++]=c;}if(CacheIndex>512){CacheIndex=0;setCache(0,512); Serial.println("Cut");}int bIndex=bIndexOf("$F",0);if(bIndex>=0){int eIndex=bIndexOf("$E",bIndex);if(eIndex>bIndex){//Extra Dataint dataLen= eIndex - (bIndex +2);byte data[dataLen];for(int i=0;i<dataLen;i++){data[i]= RecCache[bIndex+2 +i] ;}for(int w=(eIndex +2);w<CacheIndex && w<512;w++){RecCache[w-(eIndex + 2)]=RecCache[w]; }CacheIndex = CacheIndex - (eIndex +2);setCache(CacheIndex,512);lastReceiveTime=millis();handleCmd(data,dataLen);}}if(CacheIndex>512){CacheIndex=0;setCache(0,512); }}void setCache(int start,int endIndex){for(int i=start;i<endIndex;i++){RecCache[i]=0;} } boolean bStartsWith(byte data[],int len, String fStr){int fLen=fStr.length() ;byte fBytes[fLen + 1];fStr.getBytes(fBytes,fLen+1);if(len<=0)return false;if(len<fLen)return false;byte flag=1;for(int j=0;j<fLen;j++){if(fBytes[j]!=data[j]){flag=0;break; }}if(flag) return true; return false; }int bIndexOf(String fStr,int start){int fLen=fStr.length() ;byte fBytes[fLen + 1];fStr.getBytes(fBytes,fLen+1);for(int i=start;i<(CacheIndex + 1 -fLen);i++){if(RecCache[i]==fBytes[0]){byte flag=1;for(int j=1;j<fLen;j++){if(fBytes[j]!=RecCache[i+j]){flag=0;break; }}if(flag)return i;}}return -1; }void handleCmd(byte data[], int len){if(bStartsWith(data,len,"HB:")){hbCmd();}else if(bStartsWith(data,len,"F:")){fireCmd(data,len);}else if(bStartsWith(data,len,"C:")){controlCmd(data,len);} }void hbCmd(){byte bs[4];unsigned long mlis=millis();long2byte(mlis,bs);Serial.println(mlis);replyBytes(true,"HB",bs,4); }void fireCmd(byte data[], int len){byte duration=data[2];if(duration>15) duration=15;if(duration<=0)duration=1;long longDuration= duration * 1000;fire(longDuration);reply(true,"F",""); }void controlCmd(byte data[], int len){byte bs[2]={0,0};bs[0]=data[2];bs[1]=data[3];byte isMatch=0;if(data[2]==0x01 && data[3]==0x01){//ForwardisMatch=1;MOTOR_GO_FORWARD;}else if(data[2]==0x01 && data[3]==0x02){//BackisMatch=1;MOTOR_GO_BACK;}else if(data[2]==0x01 && data[3]==0x03){//Turn LeftisMatch=1;MOTOR_GO_LEFT;}else if(data[2]==0x01 && data[3]==0x04){//Turn RightisMatch=1;MOTOR_GO_RIGHT;}else if(data[2]==0x01 && data[3]==0x05){//StopisMatch=1;MOTOR_GO_STOP;Serial.println("Stop");}else if(data[2]==0x02 && data[3]==0x01){//Left SpeedisMatch=1;byte ena=data[4];if(ena>=0 && ena<=9){Left_Speed_Hold=ena; }}else if(data[2]==0x02 && data[3]==0x02){//Right SpeedisMatch=1;byte enb=data[4];if(enb>=0 && enb<=9){Right_Speed_Hold=enb;}}if(isMatch==1)replyBytes(true,"C",bs,2); }void long2byte(unsigned long res,byte targets[] ) { targets[0] = (byte) (res & 0xff);// 鏈�浣庝綅 targets[1] = (byte) ((res >> 8) & 0xff);// 娆′綆浣� targets[2] = (byte) ((res >> 16) & 0xff);// 娆¢珮浣� targets[3] = (byte) (res >> 24);// 鏈�楂樹綅,鏃犵鍙峰彸绉汇�� } unsigned long bytes2long(byte buf[]) { unsigned long firstByte = 0; unsigned long secondByte = 0; unsigned long thirdByte = 0; unsigned long fourthByte = 0; int index = 0; firstByte = (0x000000FFU & ( buf[index+3])); secondByte = (0x000000FFU & ( buf[index + 2])); thirdByte = (0x000000FFU & ( buf[index + 1])); fourthByte = (0x000000FFU & ( buf[index ])); index = index + 4; return ((unsigned long) (firstByte << 24 | secondByte << 16 | thirdByte << 8 | fourthByte)) & 0xFFFFFFFFUL; }
View Code
转载于:https://www.cnblogs.com/wdfrog/p/5144008.html
通过电机编码器AB相输出确定电机转向相关推荐
- 基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新)
基于STM32F103C6T6的AB相霍尔编码电机的PID转速调节(CubeMx-HAL库)(未完成-持续更新) 主要是记录一下,以后忘了再来看看,也记录记录自己做过的东西 首先是硬件电路图,一下是驱 ...
- 电机编码器调零步骤_BLDC电机换向方法解析
无刷直流电机 无刷直流电机(或简称 BLDC电机)是一种采用直流电源并通过外部电机控制器控制实现电子换向的电机.不同于有刷电机,BLDC 电机依靠外部控制器来实现换向.简言之,换向就是切换电机各相中的 ...
- 07 TIM编码器和AB相电机测速
AB相编码器 编码器分为光学和磁极. 编码器的性能指标单位:分辨率(转一圈输出多少个信号值) 精度390线的编码器:A相390个方波,B相390个方波.相位相差90°,实现4倍频,所以实现390*4= ...
- 电机编码器的使用方法
电机编码器位置的校正原理(PMSM Encoder Calibration) 绝对编码器 不需要在运行开始前进行参考点的定位(增量编码器需要先找到Z信号),即便是在掉电期间产生的转动也不妨碍后续上电后 ...
- Arduino UNO控制带AB相磁通量式编码器电动推杆(测试阻尼)实录(L289N电机驱动)
前段时间为了测试实验器材的阻尼,需要去开发一套装置来测试.提出用Arduino单片机来控制电动推杆(Linear Actuator)来制造相应速度的运动,搭配上测力计,从而根据来测得阻尼,在这里简单记 ...
- 玩转电机驱动——电机编码器
玩转电机驱动--电机编码器 文章目录 玩转电机驱动--电机编码器 前言 一.旋转编码器 1. 光学编码器 2. 光学旋转编码器与Arduino连接 3. 程序 二.Arduino Encoder.h库 ...
- 电机编码器调零步骤_各种编码器的调零方法
各种编码器的调零方法 增量式编码器的相位对齐方式 增量式编码器的输出信号为方波信号, 又可以分为带换相信号的增量式编码器和普通的增量 式编码器,普通的增量式编码器具备两相正交方波脉冲输出信号 A 和 ...
- 电机编码器调零步骤_伺服电机编码器调零对位方法
伺服电机编码器调零对位方法 伺服电机在拆开检查的时候,有时因为不小心将电机尾部固定的编码器也拆下来了,那要怎么办呢?因为伺服电机编码器动过位置了,编码器原点漂移了,所以需要重新校正.具体如下: 应急调 ...
- 电机编码器调零步骤_什么是无刷直流电机换向的最有效方法?
私信"干货"二字,即可领取138G伺服与机器人专属及电控资料! 无刷直流电机(或简称 BLDC电机)是一种采用直流电源并通过外部电机控制器控制实现电子换向的电机.不同于有刷电机,B ...
最新文章
- [转载]Oracle 11g新特征之形式料理(2)
- excel单元格斜线_如何用EXCEL做一套田字格模板?在家给小孩练习写字
- Spring Boot 小技巧
- 32位汇编语言helloworld_梦开始的地方——Hello World!
- 复习1 - String,StringBuilder,StringBuffer的执行效率区别
- Golang 入门 : 数组
- 小程序onload_微信小程序开发入门之共享账本(十四)
- corosync + pacemaker + drbd 实现mysql存储的高可用(一)
- 全国首个!北京手机一卡通开卡费今起取消
- docker 容器启动后立马退出的解决方法
- Win10与Ubuntu18.04之smb相互共享
- java简单的思维逻辑训练_简单的代码,适合初学者,锻炼逻辑思维能力,可以练习练习!...
- sass-------sass的基本介绍、node.js的sass工具
- Firebird学习(02):数据库的中文参考资料
- 关于Windows 7操作系统进行磁盘碎片整理时提示“已使用其他程序计划了磁盘碎片整理程序”的解决办法
- 如何让c语言编的游戏运行,如何用C语言编写游戏一.doc
- 【调剂】985东南大学2020年苏州联合研究生院相关专业调剂信息
- android自定义侧滑菜单代码,原生Android 侧滑菜单实践(部分)
- 代码是如何控制硬件的?
- 安全合规/GDPR--21--我们是如何开展PTA、PIA、DPIA风险评估的
热门文章
- 百度搜索自动提示搜索相关内容----模拟实现
- MySQL- SHOW TABLE STATUS命令
- leetcode算法题--除数博弈★
- leetcode算法题--划分为k个相等的子集★
- python 做个创越火线挂_一日一技:用Python做个能挂墙上的大钟表
- 快捷启动_start for mac(快捷启动应用)
- PHP 8 确认支持 JIT
- CSS 从入门到放弃系列:CSS的引入方式
- 1067. [SCOI2007]降雨量【线段树】
- iOS 获取app进程被杀死事件applicationWillTerminate