通过电机编码器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 Errors float right_LastError=0.0; // Error[-1] float right_SumError=0.0; // Sums of Errors int 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 Const leftP=(Left_Speed[Left_Speed_Hold]- leftSpeed); rightP=(Right_Speed[Right_Speed_Hold] - rightSpeed); //积分常数 Integral Const left_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:"); //发送帧头大写S Serial.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; //发生一次中断则加1 Direction_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 fire if(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 Data int 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){ //Forward isMatch=1; MOTOR_GO_FORWARD; }else if(data[2]==0x01 && data[3]==0x02){ //Back isMatch=1; MOTOR_GO_BACK; }else if(data[2]==0x01 && data[3]==0x03){ //Turn Left isMatch=1; MOTOR_GO_LEFT; }else if(data[2]==0x01 && data[3]==0x04){ //Turn Right isMatch=1; MOTOR_GO_RIGHT; }else if(data[2]==0x01 && data[3]==0x05){ //Stop isMatch=1; MOTOR_GO_STOP; Serial.println("Stop"); }else if(data[2]==0x02 && data[3]==0x01){ //Left Speed isMatch=1; byte ena=data[4]; if(ena>=0 && ena<=9){ Left_Speed_Hold=ena; } }else if(data[2]==0x02 && data[3]==0x02){ //Right Speed isMatch=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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述
2010-01-20 .net2.0中的正则表达式的RegexOptions.Compiled选项