原创:XXXXXXXXXXXXXXXX/Zv8wg(CSDN)
使用最简单的arduino框架对于MPU6050解码控制输出PWM值从而控制多个舵机
video_220809_194340.mp4 点击下载
video_220809_120951.mp4 点击下载
主要代码来自于CSDN,取自@GuanFuCSDN的帖子“如何自制自平衡云台基于mpu6050,arduino输出三维倾斜角度的方法”,对其进行了更改,使其可以输出围绕X,Y,Z轴旋转的不同PWM值,最多可以控制八个舵机实现陀螺仪三个轴运动方向的检测。
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
/*舵机*/
#include <Servo.h>
Servo myservo; //创建一个舵机控制对象,第一个舵机
Servo myservo2; //创建一个舵机控制对象,第二个舵机
Servo myservo3; //创建一个舵机控制对象,第三个舵机 // 使用Servo类最多可以控制8个舵机
MPU6050 accelgyro;
unsigned long now, lastTime = 0;
float dt; //微分时间
int16_t ax, ay, az, gx, gy, gz; //加速度计和陀螺仪的原始数据
float aax=0, aay=0,aaz=0, agx=0, agy=0, agz=0; //角度变量
long axo = 0, ayo = 0, azo = 0; //加速度计偏移量
long gxo = 0, gyo = 0, gzo = 0; //陀螺仪偏移量
float pi = 3.1415926;
float AcceRatio = 16384.0; //加速度计比例系数
float GyroRatio = 131.0; //陀螺仪比例系数
uint8_t n_sample = 8; //加速度计滤波算法采样个数
float aaxs[8] = {0}, aays[8] = {0}, aazs[8] = {0}; //x,y轴采样队列
long aax_sum, aay_sum,aaz_sum; //x,y轴采样和
float a_x[10]={0}, a_y[10]={0},a_z[10]={0} ,g_x[10]={0} ,g_y[10]={0},g_z[10]={0};
float Px=1, Rx, Kx, Sx, Vx, Qx; //x轴卡尔曼变量
float Py=1, Ry, Ky, Sy, Vy, Qy; //y轴卡尔曼变量
float Pz=1, Rz, Kz, Sz, Vz, Qz; //z轴卡尔曼变量
void setup()
{ myservo.attach(9); // 该舵机由arduino第九脚控制
myservo2.attach(8); // 该舵机由arduino第八脚控制
myservo3.attach(7); // 该舵机由arduino第七脚控制 myservo4.attach(6); // 该舵机由arduino第六脚控制
Wire.begin();
Serial.begin(115200);
accelgyro.initialize(); //初始化
unsigned short times = 200; //采样次数
for(int i=0;i<times;i++)
{
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //读取六轴原始数值
axo += ax; ayo += ay; azo += az;
gxo += gx; gyo += gy; gzo += gz;
}
axo /= times; ayo /= times; azo /= times; //计算加速度计偏移
gxo /= times; gyo /= times; gzo /= times; //计算陀螺仪偏移
}
void loop()
{
unsigned long now = millis(); //当前时间
dt = (now - lastTime) / 1000.0; //微分时间
lastTime = now; //上一次采样时间
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); //读取六轴原始数值
float accx = ax / AcceRatio; //x轴加速度
float accy = ay / AcceRatio; //y轴加速度
float accz = az / AcceRatio; //z轴加速度
aax = atan(accy / accz) * (-180) / pi; //y轴对于z轴的夹角
aay = atan(accx / accz) * 180 / pi; //x轴对于z轴的夹角
aaz = atan(accz / accy) * 180 / pi; //z轴对于y轴的夹角
aax_sum = 0; // 对于加速度计原始数据的滑动加权滤波算法
aay_sum = 0;
aaz_sum = 0;
for(int i=1;i<n_sample;i++)
{
aaxs[i-1] = aaxs[i];
aax_sum += aaxs[i] * i;
aays[i-1] = aays[i];
aay_sum += aays[i] * i;
aazs[i-1] = aazs[i];
aaz_sum += aazs[i] * i;
}
aaxs[n_sample-1] = aax;
aax_sum += aax * n_sample;
aax = (aax_sum / (11*n_sample/2.0)) * 9 / 7.0; //角度调幅至0-90°
aays[n_sample-1] = aay; //此处应用实验法取得合适的系数
aay_sum += aay * n_sample; //本例系数为9/7
aay = (aay_sum / (11*n_sample/2.0)) * 9 / 7.0;
aazs[n_sample-1] = aaz;
aaz_sum += aaz * n_sample;
aaz = (aaz_sum / (11*n_sample/2.0)) * 9 / 7.0;
float gyrox = - (gx-gxo) / GyroRatio * dt; //x轴角速度
float gyroy = - (gy-gyo) / GyroRatio * dt; //y轴角速度
float gyroz = - (gz-gzo) / GyroRatio * dt; //z轴角速度
agx += gyrox; //x轴角速度积分
agy += gyroy; //x轴角速度积分
agz += gyroz;
/* kalman start */
Sx = 0; Rx = 0;
Sy = 0; Ry = 0;
Sz = 0; Rz = 0;
for(int i=1;i<10;i++)
{ //测量值平均值运算
a_x[i-1] = a_x[i]; //即加速度平均值
Sx += a_x[i];
a_y[i-1] = a_y[i];
Sy += a_y[i];
a_z[i-1] = a_z[i];
Sz += a_z[i];
}
a_x[9] = aax;
Sx += aax;
Sx /= 10; //x轴加速度平均值
a_y[9] = aay;
Sy += aay;
Sy /= 10; //y轴加速度平均值
a_z[9] = aaz;
Sz += aaz;
Sz /= 10;
for(int i=0;i<10;i++)
{
Rx += sq(a_x[i] - Sx);
Ry += sq(a_y[i] - Sy);
Rz += sq(a_z[i] - Sz);
}
Rx = Rx / 9; //得到方差
Ry = Ry / 9;
Rz = Rz / 9;
Px = Px + 0.0025; // 0.0025在下面有说明...
Kx = Px / (Px + Rx); //计算卡尔曼增益
agx = agx + Kx * (aax - agx); //陀螺仪角度与加速度计速度叠加
Px = (1 - Kx) * Px; //更新p值
Py = Py + 0.0025;
Ky = Py / (Py + Ry);
agy = agy + Ky * (aay - agy);
Py = (1 - Ky) * Py;
Pz = Pz + 0.0025;
Kz = Pz / (Pz + Rz);
agz = agz + Kz * (aaz - agz);
Pz = (1 - Kz) * Pz;
Serial.print(agx);Serial.print(",");
Serial.print(agy);Serial.print(",");
Serial.print(agz);Serial.println();
myservo.write(agx); // 指定舵机转向的角度
delay(15); // 等待15ms让舵机到达指定位置
myservo2.write(agy); // 指定舵机转向的角度
delay(15); // 等待15ms让舵机到达指定位置
}
接线方式:
VCC >>>> 5V
GND>>>> 地线
SCL >>>>>MPU6050作为从机时IIC时钟线
SDA>>>>>MPU6050作为主机时IIC数据线jie'm
arduino的数字接口>>>>>>PWM值接舵机信号线,取决于代码设置
但是arduino的能力有限,可以适当更改之后使用其他单片机控制伺服舵机或者控制四轴无人飞行器,从而起到更好的控制效果。
另外,想问一下机械开伞有大佬有兴趣在研究吗?想了很长时间没有好的想法。
思路还可以,但是在实际应用中还得考虑控制算法,包括调参方式,如何让火箭不引起震荡以及达到理想控制。还有很重要的是模拟舵机是用20ms的信号来控制,也就是说除了20ms的信号传递还要加上自身旋转控制的时间和程序的时延,总体时间就会较大,在火箭的那种加速度下或许控制会不到位,要想方设法优化一下这样的问题,如换数字舵机之类的
陀螺仪和Arduino连线按照相应接口,其他舵机信号线分别接7、8、9口输出
用6050的三轴角位置得用好点的姿态估计,要不一小会儿就漂到不知道哪去了
陀螺仪每次开机都会自己矫正吗?scl 和sda但你的代码上面接的是安卓开发板哪个引脚?
陀螺仪这么竖着放不影响吧
lz,你好对于机械开伞这一提问,我现在有一个比较创新简单的思路——侧面开伞
首先弄一个伞舱(这是必不可少的),再从伞舱的侧面开一个灵活转动的舱门(大小刚好可以使降落伞自由出入),弄一个皮筋挂住舱门(使舱门平时都是弹开状态),最后弄一个线挂载住舱门(使舱门暂时关闭),之后通过一系列感应处理(可以通过mpu6050和气压计实现),点燃一个3.7v就可以加热驱动的电热点火头(将挂载绳烧断从而打开舱门,带出降落伞)
(该想法处于设计阶段,没有经过实际实验,但纯属本人原创想法,有意见可以自由反驳)
还有一个问题想请教lz,用arduino Nano当主板是否可行?
MPU6050输出的数据会有浮动,建议lz加上卡尔曼等滤波算法解算MPU6050的原始数据后再使用,避免炸鸡
这个程序是控制三个舵机,但是视频是四个舵机,如果要控制四个舵机那着程序咋改
如果真要做飞控的话不建议用arduino,我去年用Nano做了个比你这个复杂一点的,用的也是6050,多了个位移积分,非常卡,舵机基本上都是零点几秒动一下。而且很飘,几分钟能飘一百多米。可以用维特智能的JY61p(精度高,模块化,自带卡尔曼),MCU建议用ST或者树莓派的RP2040(我在用的),加个定位模块,rtt写代码(挺好用的),再弄个无线电回传,再也不怕找不到尸体了
如果真要做飞控的话不建议用arduino,我去年用Nano做了个比你这个复杂一点的,用的也是6050...
我现在的主控用的是RP2040,然后传感器用的JY901B,还算比较有性价比的选择,而且也自带了滤波,稳的一批
然后对于PID控制这块我个人去掉了积分部分,感觉PD控制已经够用了,回传目前用的zigbee,但是后续肯定得换
6050的精度个人觉得也就能拿来做个开伞,之前我尝试过对它做滤波,结果差强人意,如果比较在意预算的话可以采用这种方案
200字以内,仅用于支线交流,主线讨论请采用回复功能。