B8组 (康夏涛,符友,罗积昌) 两轮平衡小车

做车嘛,电机是主要的吧,所以第一部开调电机的程序了,我用的电机是n20减速电机,查了一下资料,要用到驱动,刚好之前买了个L298N步进电机的驱动模板,试了一下,驱动模板一边接电机,另一边接单片机两个io口,赋一个高电平一个低电平,电机和单片机没有共地,电机可以工作,发现转速有一点慢,应该接更高伏的电。一开始我是想用PWM信号的方法进行调速,调好程序后确实有效果,不过后来我想用三个轮子做一个车就好了,后面装一个万向轮,就可以省了用步进电机转弯,我的想法是靠前面减速电机的速度差进行转弯,最后决定就用两个速度,一个非常慢,一个是最快速度。首先用到四个独立按键进行控制小车前后左右的效果,因为还没有搭架子,直接接两个电机看效果,调好程序后,简直完美。

今天上午调了一上午的平衡程序,发现好多问题,电机这块的程序就好多bug,然后就越调越乱,最后完全没了思路,所以准备把车全部搭出来再程序写程序。
把各种模块都拿出来,需要的电阻,电容,烙铁,跳线,摆一排,感觉我们三是要大干一场了。
(搭第二层中)
这里打孔又是最麻烦的,因为不在实验室,没有钻孔机了,我们用锤子螺丝慢慢打出来的
(红外线循迹)
(资料是在网上找的)
(原理图)
红外发射二极管跟普通的LED二极管一样,只是LED发射的是肉眼看得到的可见光,而红外发射二极管发射的是人眼看不见的红外线,红外发射二极管的正向压降大约1.2V,发射的功率以及发射的距离跟流经的电流成正比,嘴大平均工作电流不要大于100mA,这里选用的是220欧姆的限流电阻,流经红外发射二极管电流为(5V-1.2V)/220欧姆=17.2mA。
停止
红外接收二极管跟光敏电阻类似,可以根据这一点在其电路上加一个10K欧姆的电阻,当有红外线照射时电阻很小,则在上图中与红外接收二极管负极相连的P2.0/P2.1/P2.2输出口输出为“0”,当没有红外线照射时,输出为“+5V”。
可以根据这一点进行红外巡线,巡线原理如下:当红外发光二极管照射到白色地板时,反射红外线,被与之对应的红外接收二极管接收,输出为逻辑“0”。反之当红外发光二极管照射到黑线时,红外线被黑色物体吸收,则与之对应的红外接受二极管无红外线照射,输出为逻辑“1”。
我的想法是将其分为“左”“中”“右”3路,那么分别会有8中情况,情况如下
程序以后供上
左路
中路
右路
电机动作
0
0
0
停止
0
0
1
右转
0
1
0
前进
0
1
1
右转
1
0
0
左转
1
0
1
停止
1
1
0
左转
1
1
1
(电源)
从图中可以看出我接了两个电源接口,一个5V usb接口,一个接9V 电池,因为电池不怎么好,所以调程序时准备用移动电源。
(整体)
一下午搭的,是不是帅到想哭
(顺便分享一点经验,就是用排针引出VCC或GND时多接几个排针,像我们只接了四个排针,回来发现少了时再引出来特别麻烦的。)
然后简单的写了个lcd的程序,哈哈,感觉特别特别特别帅
(附上所用单片机IO口)
接下来的日子就只要是把一个一个的模块程序写出来,然后慢慢调吧

不好意思几天没有更新了,这几天事情多,进展比较慢
首先说一下小车控制方面,小车一开始是打算用蓝牙控制的,因为运气太差,淘宝到一个坏的蓝牙模板,害我们调半天搞不懂,后来就准备用无线,我们首先是用315无线发射和接收模块,没有资料,百度说要自己编码,找几个例程和天书一样,当时就蒙了,然后试了各种方法,还是放弃了,于是我发现了一种带遥控器的无线模板
固定码,接收到信号就输出高电平,一下就简单了。
(信号问题)
这个无线模板主要问题是信号弱,我们首先想到的就是加天线,一开始我们是自己用绝缘线做的,根本没起到什么作用,百度一查,原来天线中间是空心的,然后我们果断拆了路由器的一根天线装上,效果一下就来了,距离可以到一两米了。

今天我们把电机的程序程序重新写了一遍,用到PWM信号调速,方便平衡小车的自平衡
程序如下:
#include <reg51.h> //IO口 sbit dian1=P2^0; sbit dian2=P2^1; sbit dian3=P2^2; sbit dian4=P2^3; sbit k1=P2^6; sbit k2=P2^7; sbit k3=P3^4; sbit k4=P3^5; //变量 unsigned char i,n,nk,nd,fx; //n,nk,nd 速度标识 //fx(1正 2反 3左 4右) 方向标识 //声明函数 void zz(); //电机正方向 void ff(); //电机反方向 void fz(); //电机左转 void zf(); //电机右转 void sudu(); //根据速度标识选择速度值 void dian(); //检测按键 void delay(unsigned int z); //延时函数 //主函数 void main() { while(1) { dian(); sudu(); if(fx==1) //根据方向标识选择方向 zz(); if(fx==2) ff(); if(fx==3) fz(); if(fx==4) zf(); } } //函数 void dian() //检测按键,用无线模块控制,接收到信号时输出高电平 { if(k1==1) //前进 { n++; if(n==5) n=4; fx=1; } if(k2==1) //左转 { n++; if(n==5) n=4; fx=3; } if(k3==1) //减速、后退 { n++; if(n==5) n=4; fx=2; } if(k4==1) //右转 { n++; if(n==5) n=4; fx=4; } if((k1!=1)&&(k2!=1)&&(k3!=1)&&(k4!=1))//没有无线信号时电机停止转动 { n=0; } } void zz() //正方向 { for(i=0;i<nd;i++) { dian1=1; dian2=1; dian3=1; dian4=1; delay(5); } for(i=0;i<nk;i++) { dian1=0; dian2=1; dian3=0; dian4=1; delay(5); } } void ff() //反方向 { for(i=0;i<nd;i++) { dian1=1; dian2=1; dian3=1; dian4=1; delay(5); } for(i=0;i<nk;i++) { dian1=1; dian2=0; dian3=1; dian4=0; delay(5); } } void fz() //一正一反,左转 { for(i=0;i<nd;i++) { dian1=0; dian2=0; dian3=1; dian4=1; delay(5); } for(i=0;i<nk;i++) { dian1=1; dian2=0; dian3=0; dian4=1; delay(5); } } void zf() //一反一正,右转 { for(i=0;i<nd;i++) { dian1=1; dian2=1; dian3=0; dian4=0; delay(5); } for(i=0;i<nk;i++) { dian1=0; dian2=1; dian3=1; dian4=0; delay(5); } } void sudu()//根据n的值赋nk,nd来调节IO口输出的PWM信号 { switch(n) { case 0: nk=0; nd=4; break; case 1: nk=1; nd=3; break; case 2: nk=2; nd=2; break; case 3: nk=3; nd=1; break; case 4: nk=4; nd=0; break; } } void delay(unsigned int z) //延时函数 (1ms) { unsigned int x; for(;z>0;z--) for(x=110;x>0;x--); }

陀螺仪方面已经完成了,是根据陀螺仪资料里的例程改写的,在1602上显示x,y,z轴加速度和角速度
因为是模块化编程,就只上一部分程序了
(陀螺仪头文件)
#ifndef __TLY_H__ #define __TLY_H__ #include <reg51.h> #include "kxt.h" sbit SCL=P3^7; //IIC时钟引脚定义 sbit SDA=P3^6; //IIC数据引脚定义 #define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz) #define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz) #define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s) #define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz) #define ACCEL_XOUT_H 0x3B #define ACCEL_XOUT_L 0x3C #define ACCEL_YOUT_H 0x3D #define ACCEL_YOUT_L 0x3E #define ACCEL_ZOUT_H 0x3F #define ACCEL_ZOUT_L 0x40 #define TEMP_OUT_H 0x41 #define TEMP_OUT_L 0x42 #define GYRO_XOUT_H 0x43 #define GYRO_XOUT_L 0x44 #define GYRO_YOUT_H 0x45 #define GYRO_YOUT_L 0x46 #define GYRO_ZOUT_H 0x47 #define GYRO_ZOUT_L 0x48 #define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用) #define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读) #define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取 void InitMPU6050(); //初始化MPU6050 void I2C_Start(); void I2C_Stop(); void I2C_SendACK(bit ack); bit I2C_RecvACK(); void I2C_SendByte(unsigned char dat); unsigned char I2C_RecvByte(); void I2C_ReadPage(); void I2C_WritePage(); unsigned char Single_ReadI2C(unsigned char REG_Address);/读取I2C数据 void Single_WriteI2C(unsigned char REG_Address,unsigned char REG_data);//向I2C写入数据 #endif
(陀螺仪I2C通信)
#include "tly.h" void Delay10us() { unsigned char a,b; for(b=1;b>0;b--) for(a=2;a>0;a--); } void I2C_Start() { SDA = 1; //拉高数据线 SCL = 1; //拉高时钟线 Delay10us(); //延时 SDA = 0; //产生下降沿 Delay10us(); //延时 SCL = 0; //拉低时钟线 } void I2C_Stop() { SDA = 0; //拉低数据线 SCL = 1; //拉高时钟线 Delay10us(); //延时 SDA = 1; //产生上升沿 Delay10us(); //延时 } //I2C发送应答信号 void I2C_SendACK(bit ack) { SDA = ack; //写应答信号 SCL = 1; //拉高时钟线 Delay10us(); //延时 SCL = 0; //拉低时钟线 Delay10us(); //延时 } //I2C接收应答信号 bit I2C_RecvACK() { SCL = 1; //拉高时钟线 Delay10us(); //延时 CY = SDA; //读应答信号 SCL = 0; //拉低时钟线 Delay10us(); //延时 return CY; } //向I2C总线发送一个字节数据 void I2C_SendByte(uc dat) { uc i; for (i=0; i<8; i++) //8位计数器 { dat <<= 1; //移出数据的最高位 SDA = CY; //送数据口 SCL = 1; //拉高时钟线 Delay10us(); //延时 SCL = 0; //拉低时钟线 Delay10us(); //延时 } I2C_RecvACK(); } //从I2C总线接收一个字节数据 unsigned char I2C_RecvByte() { uc i; uc dat = 0; SDA = 1; //使能内部上拉,准备读取数据, for (i=0; i<8; i++) //8位计数器 { dat <<= 1; SCL = 1; //拉高时钟线 Delay10us(); //延时 dat |= SDA; //读数据 SCL = 0; //拉低时钟线 Delay10us(); //延时 } return dat; } //向I2C设备写入一个字节数据 void Single_WriteI2C(uc REG_Address,uc REG_data) { I2C_Start(); //起始信号 I2C_SendByte(SlaveAddress); //发送设备地址+写信号 I2C_SendByte(REG_Address); //内部寄存器地址, I2C_SendByte(REG_data); //内部寄存器数据, I2C_Stop(); //发送停止信号 } //初始化MPU6050 void InitMPU6050() { Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠状态 Single_WriteI2C(SMPLRT_DIV, 0x07); Single_WriteI2C(CONFIG, 0x06); Single_WriteI2C(GYRO_CONFIG, 0x18); Single_WriteI2C(ACCEL_CONFIG, 0x01); }
(主函数部分)
void main() { delay(500); //上电延时 LCDI(); //液晶初始化 InitMPU6050(); //初始化MPU6050 delay(150); while(1) { Display10BitData(GetData(ACCEL_XOUT_H),2,0); //显示X轴加速度 Display10BitData(GetData(ACCEL_YOUT_H),7,0); //显示Y轴加速度 Display10BitData(GetData(ACCEL_ZOUT_H),12,0); //显示Z轴加速度 Display10BitData(GetData(GYRO_XOUT_H),2,1); //显示X轴角速度 Display10BitData(GetData(GYRO_YOUT_H),7,1); //显示Y轴角速度 Display10BitData(GetData(GYRO_ZOUT_H),12,1); //显示Z轴角速度 delay(500); } }
回复
打赏帖 | |
---|---|
【Cortex-M】Systick Timer使用被打赏10分 | |
分享汽车防盗系统的组成与分类(一)被打赏5分 | |
VOFA+波形显示+JYD-31蓝牙发送和解析不定长数据被打赏10分 | |
【换取手持数字示波器】-STM32F4PWM控制LED灯管亮度被打赏22分 | |
【换取手持数字示波器】STM32F4驱动RPR-0521RS照度、接近一体型传感器被打赏23分 | |
宏定义和const关键字定义被打赏5分 | |
【分享开发笔记,赚取电动螺丝刀】几个单片机I2S外设的BLCK时钟对比被打赏20分 | |
【功率监测与控制系统DIY活动成果贴】DIY功率计与LabVIEW数据采集被打赏100分 | |
【分享开发笔记,赚取电动螺丝刀】使用ESP32S3调试I2S音频模块MAX98357被打赏22分 | |
【Freertos】任务管理被打赏10分 |