|
本帖最后由 SproutME 于 2014-12-20 22:52 编辑
2014年12月13日
本帖不定时更新
--------------------------------------------------------------------------------------
自己做的自平衡小车的材料:
MCU:Arduino mega 2560 R3
MPU:MPU-6050
电机驱动:LN298N
电源:18650 X2(串联供电)
电机:直流电机带编码器
电源管理:升压模块(8V升压到16V,直接供给电机)
--------------------------------------------------------------------------------------
注意:
有很多人都会忽略电源供电问题(开了很多帖子,好像没有看见人说电机供电问题),但是我却偏偏遇到了。
-----
一开始我用的是五号电池那种型号的锂电四节串联,容量比较小;一开始硬件搭建好后开始调节PID参数(刚开始没有用编码器,后来发现车在斜坡上不能平衡才改了用编码器的,这里建议大家在搭建自己的模型的时候还是买那种带编码器的电机,或者直接上步进电机),晚上调的时候测试还是蛮好的,但是第二天再去测试的时候就不稳定了。
--------------------------------------------------------------------------------------
2014年12月13日
上个之前拍的视频
贴个自己的代码
主程序
这个是主程序FilterCompare.ino所用到的库,网址 https://github.com/SproutOrc/Arduino/tree/master/libraries
软件部分
第一步:MPU数据处理(数据读取的话参考论坛代码,如果这一块有问题的话底下留言)
读取MPU6050数据,我没有使用6050内部自带的DMP- I2Cdev::readBytes(ADR,ACCEL,6,buffer);
- ax = (((int16_t)buffer[0]) << 8) | buffer[1];
- ay = (((int16_t)buffer[2]) << 8) | buffer[3];
- az = (((int16_t)buffer[4]) << 8) | buffer[5];
- I2Cdev::readBytes(ADR,GYRO,6,buffer);
- gx = (((int16_t)buffer[0]) << 8) | buffer[1];
- gy = (((int16_t)buffer[2]) << 8) | buffer[2];
- gz = (((int16_t)buffer[4]) << 8) | buffer[5];
- // 计算与x轴夹角
- angleAx = -(atan2(ax, -az) * 180.0 / PI - 1.5);
- // 计算角速度
- gyroGy = -gy / 131.00;
复制代码
参数说明:
angleAx -> 小车的倾斜角度,范围-90 ~ 0 ~ +90
gyroGy -> Y轴的角速度
这里的角度值变化要和角速度值得正负号一致,比如:角度值在变小,那么角速度就应该是负的。方向千万不能出错,不然后面滤波就不对了。
下面提供一个自己写的processing程序
软件使用说明:
软件是通过串口进行通信的;
单片机上利用串口输出字符串,格式 a = 0.0, b = 0.0, c = 0.0 以换行符结束
下面是我的串口调试数据 - Serial2.print("setPoint = ");
- Serial2.print(0.0);
- Serial2.print(',');
- Serial2.print("angleAx = ");
- Serial2.print(angleAx);
- Serial2.print(',');
- Serial2.print("kfAngle = ");
- Serial2.print(kf.getAngle());
- Serial2.print(',');
- Serial2.print("GYRO = ");
- Serial2.print(kf.getRate());
- Serial2.print(',');
- Serial2.print("rightSpeed = ");
- Serial2.print(float(sendRSpeed));
- Serial2.print(',');
- Serial2.print("leftSpeed = ");
- Serial2.println(float(sendLSpeed));
复制代码
单片机能输出串口数据后,打开processing软件,选择SelfBlanceRobot.pde;
代码简单的说明[pre lang="processing" line="1"]// 显示需要显示的波形
// 创建一个波形对象,就是一个变量名
//
Waveform angle;
Waveform angleAx;
Waveform gyro;
Waveform leftSpeed;
Waveform rightSpeed;
///////////////////////////////////
ConnectProtocol connect;
void setup() {
size(600, 600);
// 显示所有连接的串口
println(Serial.list());
// 配置单片机所使用的串口号
// COM4 -> 打开COM4口
// 115200 -> 波特率115200
connect = new ConnectProtocol("COM4", 115200);
// 配置波形显示的位置和范围
//
// 窗口示意图
// ——————————————————————>x+
// |
// |
// |
// |
// |
// |
// |
// |
// |
// y+
// Waveform(0, 0, width, 100)
// 在x = 0, y = 0为起点,
// width个像素作为波形显示宽度
// 100个像素作为波形显示高度
angle = new Waveform(0, 0, width, 100);
// 波形显示的范围,
// 串口如果发的数据大于等于40,则波形在再高的位置
angle.setRange(40, -40);
angleAx = new Waveform(0, 100, width, 100);
angleAx.setRange(40, -40);
gyro = new Waveform(0, 200, width, 100);
gyro.setRange(200, -200);
leftSpeed = new Waveform(0, 300, width, 100);
leftSpeed.setRange(400, -400);
rightSpeed = new Waveform(0, 400, width, 100);
rightSpeed.setRange(400, -400);
}
void draw() {
if (connect.available() > 0) {
background(0);
// 浮点型字典
// 串口发送过来的数据会保存在a中,保存形式
// 如
// a 0.0
// b 0.0
// c 0.0
FloatDict a = connect.getInFloatDict();
// 向波形添加数据
// 获取字符串为kfAngle的值的大小
// 这个kfAngle和单片机里面发送的值是一一对应的
angle.add(a.get("kfAngle"));
// 设置波形的x轴
angle.setZero(a.get("Setpoint"));
angleAx.add(a.get("angleAx"));
angleAx.setZero(a.get("setPoint"));
gyro.add(a.get("GYRO"));
gyro.setZero(0.0);
leftSpeed.add(a.get("leftSpeed"));
leftSpeed.setZero(0.0);
rightSpeed.add(a.get("rightSpeed"));
rightSpeed.setZero(0.0);
angle.showByLine();
angleAx.showByLine();
gyro.showByLine();
leftSpeed.showByLine();
rightSpeed.showByLine();
// println("leftSpeed = " + a.get("leftSpeed"));
// println("rightSpeed = " + a.get("rightSpeed"));
}
}[/code]
--------------------------------------------------------------------------------------
2014年12月15日
第二步:角度和角速度进行滤波处理。
滤波算法我用的是kalman滤波器(在上面提供的github地址里面有kalman滤波的库,这个库来自 http://aguegu.net/?p=1232, aGuegu / 官微宏),滤波器比较参照论坛里面 http://www.geek-workshop.com/thread-10172-1-1.html 感谢275891381提供源码和测试程序,对我有很大帮助。
为什么选用kalman滤波?我也不知道。。。。。。就是想试试看看效果怎么样,后来就懒得改了。
kalman滤波器有两个输出,最优的角度和最优的角速度
第三步:小车直立核心角度环控制 - /**
- * 角度环控制
- */
- // 角度偏移值
- #define ANGLE_OFFSET 0
- // 角速度偏移值
- #define GYRO_OFFSET 0
- // 角度环比例系数(P)大小
- #define ANGLE_P 55.60
- // 角度环积分系数(D)大小
- #define ANGLE_D 0.52
- /**
- * 角度环控制主函数
- * 输出
- * angleControl
- * (&意思是取引用,如果你对C++不了解的话)
- * 输入
- * angle -> 角度值
- * guro -> 角速度
- */
- void AngleSabilityControl(
- float &angleControl,
- const float &angle,
- const float &gyro
- ) {
- /**
- * 这里这个变量别省,涉及到参数的完整性问题
- * 详细看 [url]http://www.geek-workshop.com/thread-11336-1-1.html[/url]
- */
-
- float value;
- value = (ANGLE_OFFSET - angle) * ANGLE_P +
- (GYRO_OFFSET - gyro) * ANGLE_D;
- angleControl = value;
- }
复制代码
角度环参数调节和一般的PD调节一样先调节P然后调节D,单纯一个P是可以让小车立起来的,这个我已经做过测试了。
--------------------------------------------------------------------------------------
2014年12月20日
最近比较黑,快学期末了嘛。考试啊,突击花钱啊,英语六级(英语太烂了),找工作(大四了快失业了)
--------------------------------------------------------------------------------------
话不多说接着小车;
PID调节一直都比较麻烦,看到别人有用电位器来调节参数的。这样有好处有弊端:好处是调节方便,坏处是对电位器电路设计要求比较高。(当然还有模糊PID调节啦,那个不会)。这里还是建议大家用软件调节 - // 接收的总的字符长度
- #define RECEIVE_LENGTH 20
- // 接收到的数据通过这个结构体类型输出
- typedef struct {
- String key;
- float value;
- }DictFloat;
- // 字符转换成浮点数字典类型
- bool stringToFloat(
- // 输出字典类型
- DictFloat &dict,
- // 输入字符串
- // 结构 ax = 0.0 是字符串当然要加上\0
- const char *const buff
- ) {
- // 临时存储用的
- char charAtIndex;
- // 索引
- unsigned char i;
- // 输入字符串索引
- unsigned char buffIndex = 0;
- // 输出字符串索引
- unsigned char stringIndex = 0;
- // 存储输出的字符串
- char string[RECEIVE_LENGTH];
-
- for (i = 0; i < RECEIVE_LENGTH; ++i) {
- // 提取索引位置的字符
- charAtIndex = *(buff + buffIndex);
- buffIndex ++;
- // 如果到字符串末尾直接结束
- if (charAtIndex == '\0') {
- *(string + stringIndex) = '\0';
- break;
- } else if (charAtIndex == '=') {
- *(string + stringIndex) = '\0';
- break;
- } else if (charAtIndex== ' ') {
- continue;
- } else {
- *(string + stringIndex) = charAtIndex;
- stringIndex++;
- }
- }
-
- if (i == RECEIVE_LENGTH) {
- return false;
- }
-
- dict.key = string;
- // 将字符串数据转换成浮点数
- // buff是字符串指针
- // 所以buff+buffIndex就是等于号后面的数据的字符串
- dict.value = atof(buff + buffIndex);
-
- return true;
- }
复制代码
上面有个字符串转浮点数的代码,大家可以试试。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
|