文章目录[隐藏]
各种板子引脚定义
其他:
https://arduino.nxez.com/pinout-diagram-of-arduino-versions
3283和328P芯片引脚定义
快速写一个基于Json解析的灵活串口Interface
安装的时候:一定选择ArduinoJson 5版本
任意arduino都可
5版本的使用教程:
深入学习ArduinoJson库 V5版本:
https://blog.csdn.net/dpjcn1990/article/details/92831648
#include <ArduinoJson.h>
unsigned int timecnt;
String ReceivedCache="";
int json_finish_flag = 0;
char ch[200];
StaticJsonBuffer<200> jsonBuffer;
float ki = 10;
float kp = 20;
float kd = 30;
int mode =0;
void setup() {
Serial.begin(115200);
while (!Serial) {
// wait serial port initialization
}
}
void handle_g_bty(){
Serial.println("handle_it!");
}
void loop() {
char readChar=' ';
while(Serial.available()>0){
readChar=(char)Serial.read();
ReceivedCache =ReceivedCache+ (String)readChar;
if(readChar == '}'){
json_finish_flag = 1;
}
}
timecnt = micros();//us
if(!ReceivedCache.startsWith("{")){
ReceivedCache.remove(0);
}
if(json_finish_flag == 1){
json_finish_flag=0;
ReceivedCache.toCharArray(ch, ReceivedCache.length()+1);//TODO
JsonObject& root = jsonBuffer.parseObject(ch);
if (root.success()) {
// In other case, you can do root["time"].as<long>();
const char* sensor = root["sensor"];
long time = root["time"];
double latitude = root["data"][0];
double longitude = root["data"][1];
if(root.containsKey("s_kp")){
kp = root["s_kp"].as<float>();
}
if(root.containsKey("s_ki")){
ki = root["s_ki"].as<float>();
}
if(root.containsKey("s_kd")){
kd = root["s_kd"].as<float>();
}
if(root.containsKey("s_mode")){
mode = root["s_mode"].as<int>();
}
if(root.containsKey("g_bty")){
handle_g_bty();
}
timecnt = micros()-timecnt;
Serial.println(timecnt);
}
ReceivedCache ="";
jsonBuffer.clear();
}
}
解析时间很短:大约几百us,可以跑满115200串口速率。
Arduino 保存int,float,byte到芯片内的FLASH中,再读回来
任意arduino都可
#include<EEPROM.h>
float kp = 88;
float ki = 22;
float kd = 33;
unsigned char mode = 0;
union floatintchar{
uint8_t char_data[4];
float float_data;
int int_data;
};
floatintchar floatintchar_u;
void setup() {
Serial.begin(19200);
loadConfigFromEEPROM();
}
void loop() {
while(1);
}
void saveFloatToEEPROM(int startAddr,float Val){
floatintchar_u.float_data = Val;
for(int i=0;i<4;i++){
EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
}
}
float getFloatFromEEPROM(int startAddr){
for(int i=0;i<4;i++){
floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
}
return floatintchar_u.float_data;
}
int saveIntToEEPROM(int startAddr,int Val){
floatintchar_u.int_data = Val;
for(int i=0;i<4;i++){
EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
}
}
int getIntFromEEPROM(int startAddr){
for(int i=0;i<4;i++){
floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
}
return floatintchar_u.int_data;
}
void saveConfigToEEPROM(){
saveFloatToEEPROM(0,kp);
saveFloatToEEPROM(4,ki);
saveFloatToEEPROM(8,kd);
}
void loadConfigFromEEPROM(){
kp = getFloatFromEEPROM(0);
ki = getFloatFromEEPROM(4);
kd = getFloatFromEEPROM(8);
}
好用又清晰的双电机增量式PID速度控制器+AB霍尔编码器测速+滤波器(注释全)
使用在arduino mega上
/左电机端口定义
#define MotorLpinEN 35 //控制位3
#define MotorLpinDIR 37 //控制位4
#define MotorLpwm 3 //使能调速 ENB
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4
//右电机端口定义
#define MotorRpinEN 31 //控制位1
#define MotorRpinDIR 33 //控制位2
#define MotorRpwm 2 //使能调速 ENA
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2
volatile float motorL=0;//中断变量,左轮子脉冲计数
volatile float motorR=0;//中断变量,右轮子脉冲计数
float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边轮速 单位cm/s
float v1=0; //单位cm/s
float v2=0; //单位cm/s
float Target_V_L=30,Target_V_R=30; //单位cm/s
int Pwm_L=0,Pwm_R=0; //左右轮PWM
//PID变量
float kp=2,ki=0.5,kd=0; //PID参数
/**************************************
* Arduino初始化函数
*
*************************************/
void setup() {
Motor_Init();//电机端口初始化
Serial.begin(19200);//开启串口
}
/*********************************************************
* 函数功能:增量式PI控制器(左轮)
* 入口参数:当前速度(编码器测量值),目标速度
* 返回 值:电机PWM
* 参考资料:
* 增量式离散PID公式:
* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)+Kd*[e(k)-2e(k-1)+e(k-2)]
* e(k):本次偏差
* e(k-1):上一次偏差
* e(k-2):上上次偏差
* Pwm:代表增量输出
* 在速度闭环控制系统里面我们只使用PI控制,因此对PID公式可简化为:
* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)
* e(k):本次偏差
* e(k-1):上一次偏差
* Pwm:代表增量输出
*
* 注意增量式PID先调I,再调P,最后再调D
*********************************************************/
int Incremental_Pi_L(int current_speed,int target_speed){
static float pwm_L,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm_L:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm_L-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//pwm_L 限幅度 Arduino的PWM 最高为255 限制在250
if(pwm_L<-250){
pwm_L=0;
}
if(pwm_L>250){
pwm_L=250;
}
//Serial.println(pwm_L);
return pwm_L; //增量输出
}
//右轮速度增量式PID控制器
int Incremental_Pi_R(float current_speed,float target_speed){
static float pwm_R,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm_R:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm_R=pwm_R-(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//pwm_R 限幅度 Arduino的pwm_R 最高为255限制在250
if(pwm_R<0){
pwm_R=0;
}
if(pwm_R>250){
pwm_R=250;
}
//Serial.println(pwm_R);
return pwm_R; //增量输出
}
/**************************************************************************(测试完成)
函数功能:设置双轮工作模式和运动速度
入口参数:工作模式,左右轮pwm
返回 值:无
**************************************************************************/
void Set_Pwm(int mode,int speed_L,int speed_R){
if(mode==1){
//前进模式
//左电机
digitalWrite(MotorLpinEN,HIGH);
digitalWrite(MotorLpinDIR,HIGH);
analogWrite(MotorLpwm,speed_L);
//右电机
digitalWrite(MotorRpinEN,HIGH);
digitalWrite(MotorRpinDIR,HIGH);
analogWrite(MotorRpwm,speed_R);
}else if(mode==2){
//后退模式
//左电机
digitalWrite(MotorLpinEN,HIGH);
digitalWrite(MotorLpinDIR,LOW);
analogWrite(MotorLpwm,speed_L);
//右电机
digitalWrite(MotorRpinEN,HIGH);
digitalWrite(MotorRpinDIR,LOW);
analogWrite(MotorRpwm,speed_R);
}else if(mode==3){
//左转模式
//左电机
digitalWrite(MotorLpinEN,HIGH);
digitalWrite(MotorLpinDIR,LOW);
analogWrite(MotorLpwm,speed_L);
//右电机
digitalWrite(MotorRpinEN,HIGH);
digitalWrite(MotorRpinDIR,LOW);
analogWrite(MotorRpwm,speed_R);
}else if(mode==4){
//右转模式
//左电机
digitalWrite(MotorLpinEN,LOW);
digitalWrite(MotorLpinDIR,HIGH);
analogWrite(MotorLpwm,speed_L);
//右电机
digitalWrite(MotorRpinEN,LOW);
digitalWrite(MotorRpinDIR,HIGH);
analogWrite(MotorRpwm,speed_R);
}
}
/**************************************************************************(测试完成)
函数功能:电机端口初始化,控制芯片引脚拉低
入口参数:无
返回 值:无
**************************************************************************/
void Motor_Init(){
//左电机
pinMode(MotorLpinEN,OUTPUT); //驱动芯片控制引脚
pinMode(MotorLpinDIR,OUTPUT); //驱动芯片控制引脚
pinMode(MotorLpwm,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(MotorLcountA,INPUT); //左轮编码器A引脚
pinMode(MotorLcountB,INPUT); //左轮编码器B引脚
//右电机
pinMode(MotorRpinEN,OUTPUT); //驱动芯片控制引脚
pinMode(MotorRpinDIR,OUTPUT); //驱动芯片控制引脚
pinMode(MotorRpwm,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(MotorRcountA,INPUT); //右轮编码器A引脚
pinMode(MotorRcountB,INPUT); //右轮编码器B引脚
//驱动芯片控制引脚全部拉低
digitalWrite(MotorLpinEN,LOW); //左电机
digitalWrite(MotorLpinDIR,LOW);
digitalWrite(MotorLpwm,LOW);
digitalWrite(MotorRpinEN,LOW); //右电机
digitalWrite(MotorRpinDIR,LOW);
digitalWrite(MotorRpwm,LOW);
}
/***********************************
* 电机实际速度计算:
* 公式:
* 已知参数:
* 车轮直径65mm,
* 左边轮子一圈:390脉冲(RISING),
* 右边轮子一圈:390脉冲(RISING),
* 单位时间读两个轮子脉冲读取两个轮子脉冲
***********************************/
void Read_Moto_V(){
unsigned long nowtime=0;
motorL=0;
motorR=0;
nowtime=millis()+50;//读50毫秒
attachInterrupt(digitalPinToInterrupt(MotorLcountA),Read_Moto_L,RISING);//左轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(MotorRcountA),Read_Moto_R,RISING);//右轮脉冲开中断计数
while(millis()<nowtime); //达到50毫秒关闭中断
detachInterrupt(digitalPinToInterrupt(MotorLcountA));//左轮脉冲关中断计数
detachInterrupt(digitalPinToInterrupt(MotorRcountA));//右轮脉冲关中断计数
V_L=((motorL/390)*6.5*PI)/0.05; //单位cm/s
V_R=((motorR/390)*6.5*PI)/0.05; //单位cm/s
v1=V_L;
v2=V_R;
}
/***************************
* 中断函数:读左轮脉冲
*
**************************/
void Read_Moto_L(){
motorL++;
}
/**************************
* 中断函数:读右轮脉冲
*
*************************/
void Read_Moto_R(){
motorR++;
}
// 递推平均滤波法(又称滑动平均滤波法)
#define FILTER_N 6
float filter_buf_L[FILTER_N + 1];
float Filter_L(float Val) {
int i;
float filter_sum = 0;
filter_buf_L[FILTER_N] = Val;
for(i = 0; i < FILTER_N; i++) {
filter_buf_L[i] = filter_buf_L[i + 1]; // 所有数据左移,低位仍掉
filter_sum += filter_buf_L[i];
}
return (float)(filter_sum / FILTER_N);
}
float filter_buf_R[FILTER_N + 1];
float Filter_R(float Val) {
int i;
float filter_sum = 0;
filter_buf_R[FILTER_N] = Val;
for(i = 0; i < FILTER_N; i++) {
filter_buf_R[i] = filter_buf_R[i + 1]; // 所有数据左移,低位仍掉
filter_sum += filter_buf_R[i];
}
return (float)(filter_sum / FILTER_N);
}
/***************************************
* Arduino主循环
*
***************************************/
void loop() {
Read_Moto_V();//读取脉冲计算速度
Serial.print(V_L);
Serial.print(" ");
Serial.println(V_R);
V_L = Filter_L(V_L);
V_R = Filter_R(V_R);
Pwm_L=255-Incremental_Pi_L(V_L,Target_V_L);//左轮PI运算
Pwm_R=255-Incremental_Pi_R(V_R,Target_V_R);//右轮PI运算
//Serial.print("Pwm_L:");
//Serial.println(Pwm_L);
Set_Pwm(1,Pwm_L,Pwm_R); //设置左右轮速度
}
在Arduino上使用printf格式化输出到串口(能够使用%f%d这些)
不少朋友尝试在Arduino上使用printf格式化输出,但没有效果。在PC上printf默认输出到屏幕,但arduino不是PC,也没有屏幕,所以不能直接使用。
这里提供一个使用printf输出到串口的方法:
首先在程序中加入这两个函数:
int serial_putc( char c, struct __file * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
void setup() {
Serial.begin(115200);
printf_begin();
//其他代码
}
现在你可以尝试下使用printf输出了:
int cs=12345;
float cf=12.345;
char c[15]="Hello,world!";
printf("***arduino.cn***\n\r");
printf("111:%d\n",cs);
printf("222:%lf\n",cf);
注意!无法格式化%f!这里的为精简的printf
上面几个的综合:底盘控制器(半成品)
#include<EEPROM.h>
#include <ArduinoJson.h>
/*--------------------------Pin Define&Function--------------------------*/
//左电机端口定义
#define MotorLpinEN 35 //控制位3
#define MotorLpinDIR 37 //控制位4
#define MotorLpwm 3 //使能调速 ENB
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4
//右电机端口定义
#define MotorRpinEN 31 //控制位1
#define MotorRpinDIR 33 //控制位2
#define MotorRpwm 2 //使能调速 ENA
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2
volatile float motorL=0;//中断变量,左轮子脉冲计数
volatile float motorR=0;//中断变量,右轮子脉冲计数
float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边轮速 单位cm/s
float v1=0; //单位cm/s
float v2=0; //单位cm/s
float Target_V_L=30,Target_V_R=30; //单位cm/s
int Pwm_L=0,Pwm_R=0; //左右轮PWM
//PID变量
float kp=2,ki=0.5,kd=0; //PID参数
//MODE变量
int mode =0;
//bty_soc变量
int bty_soc = 0;
int debug_mode =0;
/*--------------------------EEPROM Function--------------------------*/
union floatintchar{
uint8_t char_data[4];
float float_data;
int int_data;
};
floatintchar floatintchar_u;
void saveFloatToEEPROM(int startAddr,float Val){
floatintchar_u.float_data = Val;
for(int i=0;i<4;i++){
EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
}
}
float getFloatFromEEPROM(int startAddr){
for(int i=0;i<4;i++){
floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
}
return floatintchar_u.float_data;
}
int saveIntToEEPROM(int startAddr,int Val){
floatintchar_u.int_data = Val;
for(int i=0;i<2;i++){
EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
}
}
int getIntFromEEPROM(int startAddr){
for(int i=0;i<2;i++){
floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
}
return floatintchar_u.int_data;
}
void saveConfigToEEPROM(){
saveFloatToEEPROM(0,kp);
saveFloatToEEPROM(4,ki);
saveFloatToEEPROM(8,kd);
saveIntToEEPROM(12,mode);
}
void loadConfigFromEEPROM(){
kp = getFloatFromEEPROM(0);
ki = getFloatFromEEPROM(4);
kd = getFloatFromEEPROM(8);
mode = getIntFromEEPROM(12);
}
/*--------------------------printf Function--------------------------*/
int serial_putc( char c, struct __file * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
/*--------------------------ArduinoJson Function--------------------------*/
String ReceivedCache="";
int json_finish_flag = 0;
char ch[200];
StaticJsonBuffer<200> jsonBuffer;
void handle_g_bty(){
printf("{\"bty\":%d}",bty_soc);
}
void handle_g_pid_i(){
Serial.print("{");
Serial.print("\"kp\":");
Serial.print(kp);
Serial.print(",");
Serial.print("\"ki\":");
Serial.print(ki);
Serial.print(",");
Serial.print("\"kd\":");
Serial.print(kd);
Serial.print(",");
Serial.print("\"mode\":");
Serial.print(mode);
Serial.print("}");
}
void handle_g_s(){
;
}
void json_loop() {
char readChar=' ';
while(Serial.available()>0){
readChar=(char)Serial.read();
ReceivedCache =ReceivedCache+ (String)readChar;
if(readChar == '}'){
json_finish_flag = 1;
}
}
//timecnt = micros();//us
if(!ReceivedCache.startsWith("{")){
ReceivedCache.remove(0);
}
if(json_finish_flag == 1){
json_finish_flag=0;
ReceivedCache.toCharArray(ch, ReceivedCache.length()+1);//TODO
JsonObject& root = jsonBuffer.parseObject(ch);
if (root.success()) {
// In other case, you can do root["time"].as<long>();
if(root.containsKey("set")){//任何set 都要写一个Set的键,值任意以触发
if(root.containsKey("s_debug")){
debug_mode = root["s_debug"].as<int>();
if(debug_mode ==1){
Serial.print("\r\nset OK!debug_mode_Val=");
Serial.println(debug_mode);
}
}
if(root.containsKey("s_kp")){
kp = root["s_kp"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!kp_Val=");
Serial.println(kp);
}
}
if(root.containsKey("s_ki")){
ki = root["s_ki"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!ki_Val=");
Serial.println(ki);
}
}
if(root.containsKey("s_kd")){
kd = root["s_kd"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!kd_Val=");
Serial.println(kd);
}
}
if(root.containsKey("s_mode")){
mode = root["s_mode"].as<int>();
if(debug_mode ==1){
Serial.print("\r\nset OK!mode_Val=");
Serial.println(mode);
}
}
if(root.containsKey("s_csave")){
if(debug_mode ==1){
saveConfigToEEPROM();
Serial.println("\r\nset OK!config is saved!");
}
}
}
if(root.containsKey("g_s")){
handle_g_s();
}
if(root.containsKey("g_bty")){
handle_g_bty();
}
if(root.containsKey("g_pid_i")){
handle_g_pid_i();
}
//timecnt = micros()-timecnt;
//Serial.println(timecnt);
}
ReceivedCache ="";
jsonBuffer.clear();
}
}
/*--------------------------PID Control Function--------------------------*/
/*********************************************************
* 函数功能:增量式PI控制器(左轮)
* 入口参数:当前速度(编码器测量值),目标速度
* 返回 值:电机PWM
* 参考资料:
* 增量式离散PID公式:
* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)+Kd*[e(k)-2e(k-1)+e(k-2)]
* e(k):本次偏差
* e(k-1):上一次偏差
* e(k-2):上上次偏差
* Pwm:代表增量输出
* 在速度闭环控制系统里面我们只使用PI控制,因此对PID公式可简化为:
* Pwm-=Kp*[e(k)-e(k-1)]+Ki*e(k)
* e(k):本次偏差
* e(k-1):上一次偏差
* Pwm:代表增量输出
*
* 注意增量式PID先调I,再调P,最后再调D
*********************************************************/
int Incremental_Pi_L(int current_speed,int target_speed){
static float pwm_L,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm_L:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm_L-=(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//pwm_L 限幅度 Arduino的PWM 最高为255 限制在250
if(pwm_L<-250){
pwm_L=0;
}
if(pwm_L>250){
pwm_L=250;
}
//Serial.println(pwm_L);
return pwm_L; //增量输出
}
//右轮速度增量式PID控制器
int Incremental_Pi_R(float current_speed,float target_speed){
static float pwm_R,bias,last_bias,prev_bias; //静态变量存在程序全周期:pwm_R:增量输出,bias:本次偏差,last_bias:上次偏差,prev_bais_:上上次偏差
bias=current_speed-target_speed; //计算本次偏差e(k)
pwm_R=pwm_R-(kp*(bias-last_bias)+ki*bias+kd*(bias-2*last_bias+prev_bias)); //增量式PID控制器
prev_bias=last_bias; //保存上上次偏差
last_bias=bias; //保存上一次偏差
//pwm_R 限幅度 Arduino的pwm_R 最高为255限制在250
if(pwm_R<0){
pwm_R=0;
}
if(pwm_R>250){
pwm_R=250;
}
//Serial.println(pwm_R);
return pwm_R; //增量输出
}
/**************************************************************************(测试完成)
函数功能:设置双轮工作模式和运动速度
入口参数:工作模式,左右轮pwm
返回 值:无
**************************************************************************/
void Set_Pwm(int mode,int speed_L,int speed_R){
if(mode==1){
//前进模式
//左电机
digitalWrite(MotorLpinEN,HIGH);
digitalWrite(MotorLpinDIR,HIGH);
analogWrite(MotorLpwm,speed_L);
//右电机
digitalWrite(MotorRpinEN,HIGH);
digitalWrite(MotorRpinDIR,HIGH);
analogWrite(MotorRpwm,speed_R);
}else if(mode==2){
//后退模式
//左电机
digitalWrite(MotorLpinEN,HIGH);
digitalWrite(MotorLpinDIR,LOW);
analogWrite(MotorLpwm,speed_L);
//右电机
digitalWrite(MotorRpinEN,HIGH);
digitalWrite(MotorRpinDIR,LOW);
analogWrite(MotorRpwm,speed_R);
}else if(mode==3){
//左转模式
//左电机
digitalWrite(MotorLpinEN,HIGH);
digitalWrite(MotorLpinDIR,LOW);
analogWrite(MotorLpwm,speed_L);
//右电机
digitalWrite(MotorRpinEN,HIGH);
digitalWrite(MotorRpinDIR,LOW);
analogWrite(MotorRpwm,speed_R);
}else if(mode==4){
//右转模式
//左电机
digitalWrite(MotorLpinEN,LOW);
digitalWrite(MotorLpinDIR,HIGH);
analogWrite(MotorLpwm,speed_L);
//右电机
digitalWrite(MotorRpinEN,LOW);
digitalWrite(MotorRpinDIR,HIGH);
analogWrite(MotorRpwm,speed_R);
}
}
/**************************************************************************(测试完成)
函数功能:电机端口初始化,控制芯片引脚拉低
入口参数:无
返回 值:无
**************************************************************************/
void Motor_Init(){
//左电机
pinMode(MotorLpinEN,OUTPUT); //驱动芯片控制引脚
pinMode(MotorLpinDIR,OUTPUT); //驱动芯片控制引脚
pinMode(MotorLpwm,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(MotorLcountA,INPUT); //左轮编码器A引脚
pinMode(MotorLcountB,INPUT); //左轮编码器B引脚
//右电机
pinMode(MotorRpinEN,OUTPUT); //驱动芯片控制引脚
pinMode(MotorRpinDIR,OUTPUT); //驱动芯片控制引脚
pinMode(MotorRpwm,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(MotorRcountA,INPUT); //右轮编码器A引脚
pinMode(MotorRcountB,INPUT); //右轮编码器B引脚
//驱动芯片控制引脚全部拉低
digitalWrite(MotorLpinEN,LOW); //左电机
digitalWrite(MotorLpinDIR,LOW);
digitalWrite(MotorLpwm,LOW);
digitalWrite(MotorRpinEN,LOW); //右电机
digitalWrite(MotorRpinDIR,LOW);
digitalWrite(MotorRpwm,LOW);
}
/***********************************
* 电机实际速度计算:
* 公式:
* 已知参数:
* 车轮直径65mm,
* 左边轮子一圈:390脉冲(RISING),
* 右边轮子一圈:390脉冲(RISING),
* 单位时间读两个轮子脉冲读取两个轮子脉冲
***********************************/
void Read_Moto_V(){
unsigned long nowtime=0;
motorL=0;
motorR=0;
nowtime=millis()+50;//读50毫秒
attachInterrupt(digitalPinToInterrupt(MotorLcountA),Read_Moto_L,RISING);//左轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(MotorRcountA),Read_Moto_R,RISING);//右轮脉冲开中断计数
while(millis()<nowtime); //达到50毫秒关闭中断
detachInterrupt(digitalPinToInterrupt(MotorLcountA));//左轮脉冲关中断计数
detachInterrupt(digitalPinToInterrupt(MotorRcountA));//右轮脉冲关中断计数
V_L=((motorL/390)*6.5*PI)/0.05; //单位cm/s
V_R=((motorR/390)*6.5*PI)/0.05; //单位cm/s
v1=V_L;
v2=V_R;
}
/***************************
* 中断函数:读左轮脉冲
*
**************************/
void Read_Moto_L(){
motorL++;
}
/**************************
* 中断函数:读右轮脉冲
*
*************************/
void Read_Moto_R(){
motorR++;
}
/*--------------------------Filter Function--------------------------*/
// 递推平均滤波法(又称滑动平均滤波法)
#define FILTER_N 6
float filter_buf_L[FILTER_N + 1];
float Filter_L(float Val) {
int i;
float filter_sum = 0;
filter_buf_L[FILTER_N] = Val;
for(i = 0; i < FILTER_N; i++) {
filter_buf_L[i] = filter_buf_L[i + 1]; // 所有数据左移,低位仍掉
filter_sum += filter_buf_L[i];
}
return (float)(filter_sum / FILTER_N);
}
float filter_buf_R[FILTER_N + 1];
float Filter_R(float Val) {
int i;
float filter_sum = 0;
filter_buf_R[FILTER_N] = Val;
for(i = 0; i < FILTER_N; i++) {
filter_buf_R[i] = filter_buf_R[i + 1]; // 所有数据左移,低位仍掉
filter_sum += filter_buf_R[i];
}
return (float)(filter_sum / FILTER_N);
}
/*--------------------------Main Function--------------------------*/
void setup() {
// put your setup code here, to run once:
loadConfigFromEEPROM();
Serial.begin(115200);
printf_begin();
Motor_Init();//电机端口初始化
}
void loop() {
// put your main code here, to run repeatedly:
Read_Moto_V();//读取脉冲计算速度
V_L = Filter_L(V_L);
V_R = Filter_R(V_R);
Pwm_L=255-Incremental_Pi_L(V_L,Target_V_L);//左轮PI运算
Pwm_R=255-Incremental_Pi_R(V_R,Target_V_R);//右轮PI运算
//Serial.print("Pwm_L:");
//Serial.println(Pwm_L);
Set_Pwm(1,Pwm_L,Pwm_R); //设置左右轮速度
json_loop();
}
新版ABZ编码器读取
#include <FlexiTimer2.h> //定时器库头文件
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2
const double sampTime = 50;
float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边速度 单位cm/s
volatile long timeL;
volatile long timeR;
volatile double countL = 0; // 脉冲数计数器,由A相计数
volatile double countR = 0; // 脉冲数计数器,由A相计数
float V_LFactor = 0;
float V_RFactor = 0;
volatile double distance = 0;
const float D = 0.30; //轮子的直径,单位m
const float pi = 3.141592654;//圆周率
void setup() {
pinMode(MotorLcountA, INPUT_PULLUP);//因为编码器信号为欧姆龙E6B2-CWZ6C,为开漏输出,因此需要上拉电阻,此处采用arduino的内部上拉输入模式,置高
pinMode(MotorLcountB, INPUT_PULLUP);//同上
attachInterrupt(digitalPinToInterrupt(MotorLcountA),EncodeL,FALLING);//左轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(MotorLcountA),EncodeL,RISING);//左轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(MotorRcountA),EncodeR,RISING);//右轮脉冲开
FlexiTimer2::set(sampTime,calcSpeed); //设置计时器参数以及终端子程序
FlexiTimer2::start();
}
// 编码器L脉冲计数中断子程序
void EncodeL()
{
//为了不计入噪音干扰脉冲,
//当2次中断之间的时间大于0.1ms时,计一次有效计数
if ((millis() - timeL) > 0.1)
{
//当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
if ((digitalRead(MotorLcountA) == HIGH) && (digitalRead(MotorLcountB) == HIGH))
{
countL++;
}
else
{
countL--;
}
}
timeL == millis();
}
// 编码器R脉冲计数中断子程序
void EncodeR()
{
//为了不计入噪音干扰脉冲,
//当2次中断之间的时间大于0.1ms时,计一次有效计数
if ((millis() - timeR) > 0.1)
{
//当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
if ((digitalRead(MotorRcountA) == HIGH) && (digitalRead(MotorRcountB) == HIGH))
{
countR--;
}
else
{
countR++;
}
}
timeR == millis();
}
void calcSpeed(){
V_L=countL*V_LFactor; //单位cm/s
V_R=countR*V_RFactor; //单位cm/s
//V_L_Filter = Filter_L(V_L);
//V_R_Filter = Filter_R(V_R);
distance += sampTime*0.001*abs((V_L+V_R))*0.5;
countL = 0;
countR = 0;
//Serial.println(distance);
//Serial.println(V_LFactor);
//Serial.print("The wheel has run : ");Serial.print(distance); Serial.println("m.");
//Serial.print("The w_speed is: ");
//Serial.print(V_L); //Serial.println("r/min.");
//Serial.print(" "); //Serial.println("r/min.");
//Serial.print(V_R); //Serial.println("r/min.");
//Serial.println(" "); //Serial.println("r/min.");
}
新版底盘控制器代码
#define SERIAL_RX_BUFFER_SIZE 2048 //修改串口发送缓冲区大小为2048
#define SERIAL_TX_BUFFER_SIZE 2048 //修改串口接收缓冲区大小为2048
#include <EEPROM.h>
#include <ArduinoJson.h>
#include <FlexiTimer2.h> //定时器库头文件
#include <PID_v1.h>
/*--------------------------Pin Define&Function--------------------------*/
//左电机端口定义
#define MotorLpinEN 31 //控制位3
#define MotorLpinDIR 33 //控制位4
#define MotorLpwm 3 //使能调速 ENB
#define MotorLcountA 18 //编码器A 中断号:5
#define MotorLcountB 19 //编码器B 中断号:4
//右电机端口定义
#define MotorRpinEN 35 //控制位1
#define MotorRpinDIR 37 //控制位2
#define MotorRpwm 2 //使能调速 ENA
#define MotorRcountA 20 //编码器A 中断号:3
#define MotorRcountB 21 //编码器B 中断号:2
const double sampTime = 50;
float V_L=0; //左轮速度 单位cm/s
float V_R=0; //右边速度 单位cm/s
volatile long timeL;
volatile long timeR;
volatile double countL = 0; // 脉冲数计数器,由A相计数
volatile double countR = 0; // 脉冲数计数器,由A相计数
float V_LFactor = 0;
float V_RFactor = 0;
float V_L_Filter =0;
float V_R_Filter =0;
volatile double distance = 0;
const float D = 0.30; //轮子的直径,单位m
const float pi = 3.141592654;//圆周率
float t_targetL = 0, t_targetR = 0;
float v_targetL=0, v_targetR=0; //单位cm/s
int Pwm_L=0,Pwm_R=0; //左右轮PWM
//PID变量
float kp=0,ki=0,kd=0; //PID参数
double SetpointL, InputL, OutputL;
double SetpointR, InputR, OutputR;
PID myPIDL(&InputL, &OutputL, &SetpointL, kp, ki, kd, DIRECT);
PID myPIDR(&InputR, &OutputR, &SetpointR, kp, ki, kd, DIRECT);
//ctl_mode变量
int ctl_mode =0;
//bty_soc变量
int bty_soc = 0;
int debug_mode =0;
int getBtySoc(){
float volt,val;
val = analogRead(A0);
volt = val*5.0/1024.0;
volt = volt/3.3*12.6;
volt = 11.7;
return int(27.746*volt*volt*volt -972.09*volt*volt+11370*volt-44336);
}
/*--------------------------EEPROM Function--------------------------*/
union floatintchar{
uint8_t char_data[4];
float float_data;
int int_data;
};
floatintchar floatintchar_u;
void saveFloatToEEPROM(int startAddr,float Val){
floatintchar_u.float_data = Val;
for(int i=0;i<4;i++){
EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
}
}
float getFloatFromEEPROM(int startAddr){
for(int i=0;i<4;i++){
floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
}
return floatintchar_u.float_data;
}
int saveIntToEEPROM(int startAddr,int Val){
floatintchar_u.int_data = Val;
for(int i=0;i<2;i++){
EEPROM.write(startAddr+i,floatintchar_u.char_data[i]);
}
}
int getIntFromEEPROM(int startAddr){
for(int i=0;i<2;i++){
floatintchar_u.char_data[i] = EEPROM.read(startAddr+i);
}
return floatintchar_u.int_data;
}
void saveConfigToEEPROM(){
saveFloatToEEPROM(0,kp);
saveFloatToEEPROM(4,ki);
saveFloatToEEPROM(8,kd);
saveIntToEEPROM(12,ctl_mode);
saveFloatToEEPROM(14,V_LFactor);
saveFloatToEEPROM(18,V_RFactor);
}
void loadConfigFromEEPROM(){
kp = getFloatFromEEPROM(0);
ki = getFloatFromEEPROM(4);
kd = getFloatFromEEPROM(8);
ctl_mode = getIntFromEEPROM(12);
V_LFactor = getFloatFromEEPROM(14);
V_RFactor = getFloatFromEEPROM(18);
}
/*--------------------------printf Function--------------------------*/
int serial_putc( char c, struct __file * )
{
Serial.write( c );
return c;
}
void printf_begin(void)
{
fdevopen( &serial_putc, 0 );
}
/*--------------------------ArduinoJson Function--------------------------*/
String ReceivedCache="";
int json_finish_flag = 0;
char ch[200];
StaticJsonBuffer<600> jsonBuffer;
void handle_g_bty(){
Serial.print("{");
Serial.print("\"bty\":");
Serial.print(bty_soc);
Serial.println("}");
}
void handle_g_pid_i(){
Serial.print("{");
Serial.print("\"kp\":");
Serial.print(kp);
Serial.print(",");
Serial.print("\"ki\":");
Serial.print(ki);
Serial.print(",");
Serial.print("\"kd\":");
Serial.print(kd);
Serial.print(",");
Serial.print("\"ctl_mode\":");
Serial.print(ctl_mode);
Serial.println("}");
}
void handle_g_sd_i(){
Serial.print("{");
Serial.print("\"sl\":");
Serial.print(V_L);
Serial.print(",");
Serial.print("\"sr\":");
Serial.print(V_R);
Serial.print(",");
Serial.print("\"d\":");
Serial.print(distance);
Serial.println("}");
}
void handle_g_mode(){
;
}
void json_loop() {
char readChar=' ';
while(Serial.available()>0){
readChar=(char)Serial.read();
ReceivedCache =ReceivedCache+ (String)readChar;
//Serial.println(ReceivedCache);
if(readChar == '}'){
json_finish_flag = 1;
break;
}
if(!ReceivedCache.startsWith("{")){
ReceivedCache.remove(0);
}
if(ReceivedCache.length()>50){
//Serial.println("qing");
ReceivedCache="";
}
}
if(!ReceivedCache.startsWith("{")){
ReceivedCache.remove(0);
}
//timecnt = micros();//us
if(json_finish_flag == 1){
json_finish_flag=0;
ReceivedCache.toCharArray(ch, ReceivedCache.length()+1);//TODO
JsonObject& root = jsonBuffer.parseObject(ch);
if (root.success()) {
// In other case, you can do root["time"].as<long>();
if(root.containsKey("unlock")){//任何set 都要写一个Set的键,值任意以触发
if(root.containsKey("s_en")){
int en = root["s_en"].as<int>();
setEnable(en);
if(debug_mode ==1){
Serial.print("\r\nset OK!enable!");
Serial.println(en);
}
}
if(root.containsKey("s_debug")){
debug_mode = root["s_debug"].as<int>();
if(debug_mode ==1){
Serial.print("\r\nset OK!debug_mode_Val=");
Serial.println(debug_mode);
}
}
if(root.containsKey("s_kp")){
kp = root["s_kp"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!kp_Val=");
Serial.println(kp);
}
}
if(root.containsKey("s_ki")){
ki = root["s_ki"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!ki_Val=");
Serial.println(ki);
}
}
if(root.containsKey("s_kd")){
kd = root["s_kd"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!kd_Val=");
Serial.println(kd);
}
}
if(root.containsKey("s_vlf")){
V_LFactor = root["s_vlf"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!vlf_Val=");
Serial.println(V_LFactor);
}
}
if(root.containsKey("s_vrf")){
V_RFactor = root["s_vrf"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!vrf_Val=");
Serial.println(V_RFactor);
}
}
}
if(ctl_mode == 0&&root.containsKey("s_tl")){
t_targetL = root["s_tl"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!tl_Val=");
Serial.println(t_targetL);
}
}
if(ctl_mode == 0&&root.containsKey("s_tr")){
t_targetR = root["s_tr"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!tr_Val=");
Serial.println(t_targetR);
}
}
if(ctl_mode == 1&&root.containsKey("s_sl")){
v_targetL = root["s_sl"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!sl_Val=");
Serial.println(v_targetL);
}
}
if(ctl_mode == 1&&root.containsKey("s_sr")){
v_targetR = root["s_sr"].as<float>();
if(debug_mode ==1){
Serial.print("\r\nset OK!sr_Val=");
Serial.println(v_targetR);
}
}
if(root.containsKey("s_mode")){
ctl_mode = root["s_mode"].as<int>();
if(debug_mode ==1){
Serial.print("\r\nset OK!mode_Val=");
Serial.println(ctl_mode);
}
}
if(root.containsKey("g_sd_i")){
handle_g_sd_i();
}
if(root.containsKey("g_mode")){
handle_g_mode();
}
if(root.containsKey("g_bty")){
handle_g_bty();
}
if(root.containsKey("g_pid_i")){
handle_g_pid_i();
}
if(root.containsKey("s_csave")){
if(debug_mode ==1){
saveConfigToEEPROM();
Serial.println("\r\nset OK!config is saved!");
}
}
//timecnt = micros()-timecnt;
//Serial.println(timecnt);
}
ReceivedCache ="";
jsonBuffer.clear();
}
}
/**************************************************************************(测试完成)
函数功能:设置双轮工作模式和运动速度
入口参数:工作模式,左右轮pwm
返回 值:无
**************************************************************************/
void Set_Pwm(int pwm_L,int pwm_R){
if(pwm_L<0){
digitalWrite(MotorLpinDIR,HIGH);
}else{
digitalWrite(MotorLpinDIR,LOW);
}
if(pwm_R<0){
digitalWrite(MotorRpinDIR,HIGH);
}else{
digitalWrite(MotorRpinDIR,LOW);
}
if(abs(pwm_L)<3){
digitalWrite(MotorLpinEN,LOW);
}else{
digitalWrite(MotorLpinEN,HIGH);
}
if(abs(pwm_R)<3){
digitalWrite(MotorRpinEN,LOW);
}else{
digitalWrite(MotorRpinEN,HIGH);
}
analogWrite(MotorLpwm,255 - abs(pwm_L));
analogWrite(MotorRpwm,255 - abs(pwm_R));
}
/**************************************************************************(测试完成)
函数功能:电机端口初始化,控制芯片引脚拉低
入口参数:无
返回 值:无
**************************************************************************/
void Motor_Init(){
//左电机
pinMode(MotorLpinEN,OUTPUT); //驱动芯片控制引脚
pinMode(MotorLpinDIR,OUTPUT); //驱动芯片控制引脚
pinMode(MotorLpwm,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(MotorLcountA,INPUT); //左轮编码器A引脚
pinMode(MotorLcountB,INPUT); //左轮编码器B引脚
//右电机
pinMode(MotorRpinEN,OUTPUT); //驱动芯片控制引脚
pinMode(MotorRpinDIR,OUTPUT); //驱动芯片控制引脚
pinMode(MotorRpwm,OUTPUT); //驱动芯片控制引脚,PWM调速
pinMode(MotorRcountA,INPUT); //右轮编码器A引脚
pinMode(MotorRcountB,INPUT); //右轮编码器B引脚
//驱动芯片控制引脚全部拉低
digitalWrite(MotorLpinEN,LOW); //左电机
digitalWrite(MotorLpinDIR,LOW);
digitalWrite(MotorLpwm,HIGH);
digitalWrite(MotorRpinEN,LOW); //右电机
digitalWrite(MotorRpinDIR,LOW);
digitalWrite(MotorRpwm,HIGH);
}
void setEnable(int en){
if(en>=1){
digitalWrite(MotorLpinEN,HIGH); //左电机
digitalWrite(MotorRpinEN,HIGH); //右电机
}else{
digitalWrite(MotorLpinEN,LOW); //左电机
digitalWrite(MotorRpinEN,LOW); //右电机
}
}
void calcSpeed(){
V_L=countL*V_LFactor; //单位cm/s
V_R=countR*V_RFactor; //单位cm/s
//V_L_Filter = Filter_L(V_L);
//V_R_Filter = Filter_R(V_R);
distance += sampTime*0.001*abs((V_L+V_R))*0.5;
countL = 0;
countR = 0;
InputL = V_L;
InputR = V_R;
myPIDL.Compute();
myPIDR.Compute();
Set_Pwm(OutputL,OutputR);
bty_soc = (int)Filter_BTY(getBtySoc());
//Serial.println(distance);
//Serial.println(V_LFactor);
//Serial.print("The wheel has run : ");Serial.print(distance); Serial.println("m.");
//Serial.print("The w_speed is: ");
//Serial.print(V_L); //Serial.println("r/min.");
//Serial.print(" "); //Serial.println("r/min.");
//Serial.print(V_R); //Serial.println("r/min.");
//Serial.println(" "); //Serial.println("r/min.");
//Serial.print(OutputL); //Serial.println("r/min.");
//Serial.println(" "); //Serial.println("r/min.");
//Serial.print(OutputR); //Serial.println("r/min.");
//Serial.println(" "); //Serial.println("r/min.");
//Serial.println("");
}
/***********************************
* 电机实际速度计算:
* 公式:
* 已知参数:
* 车轮直径65mm,
* 左边轮子一圈:390脉冲(RISING),
* 右边轮子一圈:390脉冲(RISING),
* 单位时间读两个轮子脉冲读取两个轮子脉冲
***********************************/
// 编码器L脉冲计数中断子程序
void EncodeL()
{
//为了不计入噪音干扰脉冲,
//当2次中断之间的时间大于0.1ms时,计一次有效计数
if ((millis() - timeL) > 0.1)
{
//当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
if ((digitalRead(MotorLcountA) == HIGH) && (digitalRead(MotorLcountB) == HIGH))
{
countL++;
}
else
{
countL--;
}
}
timeL == millis();
}
// 编码器R脉冲计数中断子程序
void EncodeR()
{
//为了不计入噪音干扰脉冲,
//当2次中断之间的时间大于0.1ms时,计一次有效计数
if ((millis() - timeR) > 0.1)
{
//当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
if ((digitalRead(MotorRcountA) == HIGH) && (digitalRead(MotorRcountB) == HIGH))
{
countR--;
}
else
{
countR++;
}
}
timeR == millis();
}
/*--------------------------Filter Function--------------------------*/
// 递推平均滤波法(又称滑动平均滤波法)
#define FILTER_N 2
float filter_buf_L[FILTER_N + 1];
float Filter_L(float Val) {
int i;
float filter_sum = 0;
filter_buf_L[FILTER_N] = Val;
for(i = 0; i < FILTER_N; i++) {
filter_buf_L[i] = filter_buf_L[i + 1]; // 所有数据左移,低位仍掉
filter_sum += filter_buf_L[i];
}
return (float)(filter_sum / FILTER_N);
}
float filter_buf_R[FILTER_N + 1];
float Filter_R(float Val) {
int i;
float filter_sum = 0;
filter_buf_R[FILTER_N] = Val;
for(i = 0; i < FILTER_N; i++) {
filter_buf_R[i] = filter_buf_R[i + 1]; // 所有数据左移,低位仍掉
filter_sum += filter_buf_R[i];
}
return (float)(filter_sum / FILTER_N);
}
#define FILTER_BTY_N 10
float filter_buf_bty[FILTER_BTY_N + 1];
float Filter_BTY(float Val) {
int i;
float filter_sum = 0;
filter_buf_bty[FILTER_BTY_N] = Val;
for(i = 0; i < FILTER_BTY_N; i++) {
filter_buf_bty[i] = filter_buf_bty[i + 1]; // 所有数据左移,低位仍掉
filter_sum += filter_buf_bty[i];
}
return (float)(filter_sum / FILTER_BTY_N);
}
/*--------------------------Main Function--------------------------*/
void setup() {
//saveFloatToEEPROM(0,5);//kp
//saveFloatToEEPROM(4,0);//ki
//saveFloatToEEPROM(8,0);//kd
// put your setup code here, to run once:
loadConfigFromEEPROM();
myPIDL.SetTunings(17*0.618*0.4,40*0.70*0.4,0.01*0.4,1);
myPIDR.SetTunings(17*0.618*0.4,40*0.70*0.4,0.01*0.4,1);
myPIDL.SetOutputLimits(-250,250);
myPIDR.SetOutputLimits(-250,250);
myPIDL.SetSampleTime(50);
myPIDR.SetSampleTime(50);
Serial.begin(115200);
printf_begin();
Motor_Init();//电机端口初始化
//Serial.println(V_LFactor);
//Serial.println(V_RFactor);
attachInterrupt(digitalPinToInterrupt(MotorLcountA),EncodeL,RISING);//左轮脉冲开中断计数
attachInterrupt(digitalPinToInterrupt(MotorRcountA),EncodeR,RISING);//右轮脉冲开中断计数
FlexiTimer2::set(sampTime,calcSpeed); //设置计时器参数以及终端子程序
FlexiTimer2::start();
}
void loop() {
if(ctl_mode == 0){//转矩控制模式
myPIDL.SetMode(MANUAL);
myPIDR.SetMode(MANUAL);
Set_Pwm((int)(t_targetL*2.5),(int)(t_targetR*2.5));
}else if(ctl_mode == 1){//速度控制模式1
myPIDL.SetMode(AUTOMATIC);
myPIDR.SetMode(AUTOMATIC);
}
json_loop();
SetpointL = v_targetL;
SetpointR = v_targetR;
//Set_Pwm(70,70);
//delay(4000);
//Serial.println("uhgvghiujhoijhgbhjiojhbvbjijhb");
//Set_Pwm(-70,-70);
//delay(4000);
//Pwm_L = 255-Incremental_Pi_L(V_L_Filter,-10);//左轮PI运算
//Pwm_R = 255-Incremental_Pi_R(V_R_Filter,-10);//右轮PI运算
//Serial.println(Pwm_R);
//Set_Pwm(1,Pwm_L,Pwm_R); //设置左右轮速度
}
网上另一个ABZ编码器读取,带Z的处理的
来源:https://blog.csdn.net/chenjiayu938/article/details/81349866
#define PinA 2 //外部中断0
#define PinZ 3 //外部中断1
#define PinB 8 //编码器的OUT_B信号连接到数字端口8
//变量初始化
unsigned long time1 = 0; // 时间标记
float time_cw;
float time_ccw;
long count = 0;
const float d = 75.7/1000; //轮子的直径
const float pi = 3.141592654;//圆周率
int num = 0;//圈数
double t;//一圈的运动时间
float velocity;
double time3;//外部中断1产生时的时间,即捕捉到Z相的置零信号时,用于在loop循环内进行一圈时间长短的计算
void setup()
{
pinMode(PinA, INPUT_PULLUP);//因为编码器信号为欧姆龙E6B2-CWZ6C,为开漏输出,因此需要上拉电阻,此处采用arduino的内部上拉输入模式,置高
pinMode(PinB, INPUT_PULLUP);//同上
pinMode(PinZ, INPUT_PULLUP);//同上
attachInterrupt(0, Encode, FALLING);//脉冲中断函数:捕捉A相信号,并判断A、B相先后顺序
attachInterrupt(1, Set_state , FALLING);//用于在捕捉到Z的零信号时,进行状态置零
Serial.begin (9600);
}
void loop()
{
double distance;
//正转
if (count == 2500)
{
// Serial.println("ok");//调试用
num = num + 1;
time_cw = millis();
t = time_cw - time3;
t = t / 1000;
distance = num * d * pi;
velocity = d * pi / t;
Serial.print("The wheel has run ");Serial.print(distance); Serial.println("m.");
Serial.print("The cw_speed is ");Serial.print(velocity); Serial.println("m/s.");
Serial.print("The time is ");Serial.print(t); Serial.println("s.");
}
//反转
if (count == -2500)
{
// Serial.println("ok");//调试用
num = num + 1;
time_ccw = millis();
t = time_ccw - time3;
t = t / 1000;
distance = num * d * pi;
velocity = d * pi / t;
Serial.print("The wheel has run ");Serial.print(distance); Serial.println("m.");
Serial.print("The ccw_speed is ");Serial.print(velocity); Serial.println("m/s.");
Serial.print("The time is ");Serial.print(t); Serial.println("s.");
}
}
// 编码器计数中断子程序
void Encode()
{
//为了不计入噪音干扰脉冲,
//当2次中断之间的时间大于5ms时,计一次有效计数
if ((millis() - time1) > 5)
{
//当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
if ((digitalRead(PinA) == LOW) && (digitalRead(PinB) == HIGH))
{
count--;
}
else
{
count++;
}
}
time1 == millis();
}
void Set_state(){
count = 0;
time3 = millis();//发生中断时的时间
}
arduino 串口自收自发(loop)
String inputString = ""; // 缓存字符串
boolean stringComplete = false; // 是否string已经完成缓存
void setup() {
// 初始化串口:
Serial.begin(9600);
}
void loop() {
// 如果缓存string接收完成:
if (stringComplete) {
Serial.println(inputString);
// 清空String:
inputString = "";
stringComplete = false;
}
}
void serialEvent() {
while (Serial.available()) {
// 获取新的字符:
char inChar = (char)Serial.read();
// 将它加到inputString中:
inputString += inChar;
// 如果收到了换行符,就将一个“旗标”变量设置为true,这样loop函数就知道inputString已经缓存完成了:
if (inChar == '\n') {
stringComplete = true;
}
}
}
0.96 128*64 oled 显示测试
重要参考资料:
Adafruit_SSD1306库学习:
https://blog.csdn.net/qq_41477556/article/details/112311181
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);
static const unsigned char PROGMEM cnWord_shi[] =
{
0x02,0x20,0x12,0x20,0x12,0x20,0x12,0x20,0x12,0x20,0xFF,0xFE,0x12,0x20,0x12,0x20,
0x12,0x20,0x12,0x20,0x13,0xE0,0x10,0x00,0x10,0x00,0x10,0x00,0x1F,0xFC,0x00,0x00,//世
};
static const unsigned char PROGMEM cnWord_jie[] =
{
0x00,0x00,0x1F,0xF0,0x11,0x10,0x11,0x10,0x1F,0xF0,0x11,0x10,0x11,0x10,0x1F,0xF0,
0x02,0x80,0x0C,0x60,0x34,0x58,0xC4,0x46,0x04,0x40,0x08,0x40,0x08,0x40,0x10,0x40,//界
};
static const unsigned char PROGMEM cnWord_jia[] =
{
0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x7C,0xFE,0x44,0x12,0x44,0x12,0x44,0x12,0x44,
0x12,0x44,0x12,0x44,0x12,0x44,0x12,0x44,0x22,0x44,0x22,0x7C,0x4A,0x44,0x84,0x00,//加
};
static const unsigned char PROGMEM bty_0_bitmap[] =
{
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x07,0x07,0xE0,0x07,
0x7F,0x07,0xE0,0x07,0x7F,0x03,0xF0,0x07,0x7F,0x03,0xF8,0x07,0x7F,0x01,0xF8,0x07,
0x7F,0x00,0xFC,0x07,0x7F,0x00,0xFE,0x07,0x7F,0x00,0x7E,0x07,0x7F,0x00,0x3F,0x07,
0x07,0x00,0x3F,0x07,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
};
static const unsigned char PROGMEM bty_1_bitmap[] =
{
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x07,0x00,0x00,0x07,
0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,
0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,0x7F,0x00,0x01,0xF7,
0x07,0x00,0x00,0x07,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
};
static const unsigned char PROGMEM bty_2_bitmap[] =
{
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x07,0x00,0x00,0x07,
0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,
0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,0x7F,0x00,0xF9,0xF7,
0x07,0x00,0x00,0x07,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
};
static const unsigned char PROGMEM bty_3_bitmap[] =
{
0x00,0x00,0x00,0x00,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x07,0x00,0x00,0x07,
0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,
0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,0x7F,0x7C,0xF9,0xF7,
0x07,0x00,0x00,0x07,0x07,0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
};
static const unsigned char PROGMEM welcome_bitmap[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x20,0x61,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x30,0x47,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0xC9,0x01,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0x81,0x01,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x18,0x81,0x01,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x09,0x81,0x01,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x09,0x01,0x01,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0D,0x01,0x01,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x06,0x01,0x01,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x06,0x01,0x08,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x3F,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x70,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x70,0xE0,0x01,0xF8,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x7F,0xC0,0x03,0xF8,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x3F,0xC0,0x03,0xF8,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x03,0xE1,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x03,0xE1,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x03,0xE3,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x07,0xC3,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x07,0xC7,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0x80,0x0F,0x87,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x18,0x00,0x00,
0x0F,0x80,0x0F,0x87,0x80,0x03,0x03,0x06,0xC0,0x0F,0x00,0x00,0x00,0x19,0x80,0x00,
0x0F,0x80,0x0F,0x87,0x80,0x03,0x03,0x06,0xC0,0x18,0x86,0x00,0x00,0x19,0x80,0x00,
0x0F,0x80,0x0F,0x9F,0x00,0x01,0x87,0x0C,0xC0,0x30,0x06,0x00,0x00,0x18,0x00,0x00,
0x0F,0x9F,0x1F,0x1F,0x00,0x01,0x87,0x8C,0xC0,0x30,0x06,0x00,0x00,0x18,0x00,0x00,
0x0F,0x9F,0x1F,0x3F,0xFF,0xE1,0x85,0x8C,0xDE,0x38,0x1F,0xB0,0xC3,0xD9,0x81,0xF0,
0x0F,0x9F,0x3F,0x3F,0xFF,0xE0,0xCC,0x98,0xE7,0x1C,0x06,0x30,0xC6,0x39,0x86,0x18,
0x0F,0x9F,0x3E,0x7F,0xFF,0xC0,0xCC,0x98,0xC3,0x0F,0x06,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFE,0x7F,0xFF,0xC0,0xCC,0x98,0xC3,0x03,0x86,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFC,0x7F,0xFF,0xC0,0x48,0xD0,0xC3,0x01,0xC6,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFC,0x7C,0x0F,0x80,0x78,0xF0,0xC3,0x00,0xC6,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFC,0x7C,0x1F,0x00,0x78,0x70,0xC3,0x00,0xC6,0x30,0xCC,0x19,0x8C,0x0C,
0x0F,0xFF,0xFC,0xF8,0x1F,0x00,0x30,0x60,0xC3,0x21,0x86,0x39,0xC6,0x39,0x86,0x18,
0x0F,0xFF,0xF8,0xF8,0x3F,0x00,0x30,0x60,0xC3,0x1F,0x03,0x9E,0xC3,0xD9,0x83,0xE0,
0x0F,0xFF,0xF9,0xF0,0x3F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xFF,0xF1,0xF0,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xFF,0xF3,0xE0,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xFF,0xF3,0xE0,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xE7,0xF3,0xC0,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xE7,0xE3,0xC0,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xE7,0xE7,0xC0,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0F,0xC7,0xE7,0x80,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
void setup() {
Serial.begin(9600);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
// Clear the buffer 这里如果不清理buffer,默认的显示内容为Adafruit类库的LOGO
display.clearDisplay();
ShowCnText();
}
void loop() {
// put your main code here, to run repeatedly:
}
void ShowCnText()
{
// display.clearDisplay(); // clears the screen and buffer
// display.drawBitmap(10,10,cnWord_shi,16,16, 1);
// display.display();
display.clearDisplay();
//display.drawBitmap(10,10, cnWord_shi, 16,16, 1);//16*16汉字
display.drawBitmap(0,0, welcome_bitmap, 128,64, 1);//welcome logo
display.drawBitmap(90,0, bty_1_bitmap, 32,16, 1);//电池标志
display.display();
delay(1000);
display.drawBitmap(90,0, bty_2_bitmap, 32,16, 1);//电池标志
display.display();
delay(1000);
display.fillRect(90,0,128,16,SSD1306_BLACK);
display.drawBitmap(90,0, bty_1_bitmap, 32,16, 1);//电池标志
display.display();
delay(1000);
display.fillRect(90,0,128,16,SSD1306_BLACK);
display.drawBitmap(90,0, bty_0_bitmap, 32,16, 1);//电池标志
display.display();
delay(1000);
}
版权声明:本文为CSDN博主「whstudio123」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/whstudio123/article/details/119065370
暂无评论