极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 20913|回复: 12

平衡車電源供應與pid平衡與卡爾曼率波思維調整 與製作平衡小車分享 依情況更新

[复制链接]
发表于 2016-2-25 19:08:38 | 显示全部楼层 |阅读模式
本帖最后由 lieak59 于 2016-3-2 19:41 编辑

如題 小弟最近遇到了兩個奇怪的問題


一個是 atmega328p+直流馬達後用橋式連接器 可以正常供電跟自己做平衡

但換一般9v直流電源或6*1.5v的直流電源無法運作(有上穩壓器7805) 這是為何?

加了兩個104電容後 不但沒動而且還讓接電腦後車子無法自己做平衡....

第2個是pid與卡爾曼濾波器思維

每次調整程式都要調整pid的比例系數,積分系數,微分系數

多半都調整Kp居多為啥? 我調查到說調整ki與kd會很傷元件

以下是我之前發文的程式碼.....



  1. #include "Wire.h"
  2. #include <Servo.h>
  3. #include <Kalman.h>

  4. #include "I2Cdev.h"
  5. #include "MPU6050.h"
  6. float fRad2Deg = 57.295779513f;       //将弧度转为角度的乘数
  7. const int nCalibTimes = 1000;         //校准时读数的次数
  8. int calibData[6];                     //校准数据
  9. MPU6050 accelgyro;

  10. int16_t ax, ay, az;
  11. int16_t gx, gy, gz;
  12. bool blinkState = false;
  13. char flag = false;                    //打印开关

  14. unsigned long nLastTime = 0;          //上一次读数的时间

  15. Kalman kalmanRoll;                    //Roll角滤波器
  16. Kalman kalmanPitch;                   //Pitch角滤波器

  17. /*********** PID控制器参数 *********/
  18. float ITerm, lastInput;              // 积分项、前次输入
  19. float Output = 0.0;                  // PID输出值

  20. /***********电机参数**************/
  21. Servo ServoL;
  22. Servo ServoR;

  23. /***********四元数参数***********/
  24. #define KP 0.025f//10.0f
  25. #define KI 0.0f//0.008f
  26. #define halfT 0.001f
  27. float q0 = 1, q1 = 0, q2 = 0, q3 = 0;
  28. float exInt = 0, eyInt = 0, ezInt = 0;
  29. float Angle_roll, Angle_pitch, Angle_yaw;
  30. byte ENA = 11;byte IN1 = 9; byte IN2 = 10;
  31. byte ENB = 6;byte IN3 = 7;byte IN4 = 8;


  32. void setup() {
  33.     // join I2C bus (I2Cdev library doesn't do this automatically)
  34.     Wire.begin();

  35.     // initialize serial communication
  36.     // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but
  37.     // it's really up to you depending on your project)
  38.     Serial.begin(38400);

  39.     // initialize device
  40.     Serial.println("Initializing I2C devices...");
  41.     accelgyro.initialize();

  42.     // verify connection
  43.     Serial.println("Testing device connections...");
  44.     Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
  45. for( byte i = 0; i <= 12 ; i++){
  46. pinMode(i,OUTPUT);
  47. }
  48.     // configure Arduino LED for

  49. }

  50. void loop() {
  51.     int16_t readouts[6];
  52.   ReadAccGyr(readouts); //读出测量值

  53.   float realVals[6];
  54.   Rectify(readouts, realVals); //根据校准的偏移量进行纠正

  55.   //四元数解析出欧拉角
  56. // AngleUpdate(realVals);

  57.   //计算加速度向量的模长,均以g为单位
  58.   float fRoll = GetRoll(realVals); //计算Roll角
  59.   float fPitch = GetPitch(realVals); //计算Pitch角

  60.   //计算两次测量的时间间隔dt,以秒为单位
  61.   unsigned long nCurTime = micros();
  62.   float dt = (double)(nCurTime - nLastTime) / 1000000.0;
  63.   //对Roll角和Pitch角进行卡尔曼滤波
  64.   float fNewRoll = kalmanRoll.getAngle(fRoll, realVals[4], dt);
  65.   float fNewPitch = kalmanPitch.getAngle(fPitch, realVals[5], dt);
  66.   //更新本次测的时间
  67.   nLastTime = nCurTime;

  68.   //PID控制
  69.   nCurTime = millis();                                    // 当前时间(ms)
  70.   float TimeCh = (nCurTime - nLastTime) / 1000.0;         // 采样时间(s)
  71.   float Kp = 5.0, Ki = 0.0, Kd = 0.0;                    // 比例系数、积分系数、微分系数
  72.   float SampleTime = 0.1;                                 // 采样时间(s)
  73.   float Setpoint = 0;                                     // 设定目标值(degree)
  74.   float outMin = -255, outMax = +255;                     // 输出上限、输出下限
  75.   if (TimeCh >= SampleTime) {                             // 到达预定采样时间时
  76.     float Input = fNewPitch;                            // 输入赋值
  77.     float error = Setpoint - Input;                     // 偏差值
  78.     ITerm += (Ki * error * TimeCh);                     // 计算积分项
  79.     ITerm = constrain(ITerm, outMin, outMax);           // 限定值域
  80.     float DTerm = Kd * (Input - lastInput) / TimeCh;    // 计算微分项
  81.     Output = Kp * error + ITerm - DTerm;                // 计算输出值
  82.     Output = constrain(Output, outMin, outMax);         // 限定值域
  83.     //servoL.write(Output + servoL_offset);             // 控制左驱
  84.     //servoR.write(Output + servoR_offset);             // 控制右驱
  85.     lastInput = Input;                                  // 记录输入值
  86.     nLastTime = nCurTime;                               // 记录本次时间
  87.   }
  88.   if (Serial.available() > 0) {
  89.     char rece = Serial.read();
  90.     delay(10);
  91.     if (rece == (char)'#')
  92.     {
  93.       Serial.println("Nano is Activity...");
  94.     }
  95.     flag = rece;
  96.   }
  97.   if (flag == (char)'R')
  98.   {
  99.     Serial.print("Roll:(");
  100.     Serial.print(fNewRoll);
  101.     Serial.print(")\t");
  102.     Serial.print("Pitch:(");
  103.     Serial.print(fNewPitch);
  104.     Serial.print(")\r\n");
  105.   }
  106.   
  107.   if(Output > 0){
  108.   analogWrite(ENA,Output);
  109.   digitalWrite(IN1,HIGH);digitalWrite(IN2,LOW);
  110.    analogWrite(ENB,Output);
  111.   digitalWrite(IN3,HIGH);digitalWrite(IN4,LOW);
  112.   }
  113.   else{analogWrite(ENA,Output);
  114.   digitalWrite(IN1,LOW);digitalWrite(IN2,HIGH);
  115.    analogWrite(ENB,Output);
  116.   digitalWrite(IN3,LOW);digitalWrite(IN4,HIGH);
  117.   
  118.   
  119.   }
  120. }
  121. void ReadAccGyr(int16_t *pVals)
  122. {
  123.   accelgyro.getMotion6(&pVals[0], &pVals[1], &pVals[2], &pVals[3], &pVals[4], &pVals[5]);
  124. }

  125. //对大量读数进行统计,校准平均偏移量
  126. void Calibration()
  127. {
  128.   int16_t valSums[6] = {0};
  129.   //先求和
  130.   for (int i = 0; i < nCalibTimes; ++i)
  131.   {
  132.     ReadAccGyr(valSums);
  133.   }
  134.   //再求平均
  135.   for (int i = 0; i < 6; ++i)
  136.   {
  137.     calibData[i] = int(valSums[i] / nCalibTimes);
  138.   }
  139.   calibData[2] += 16384; //设芯片Z轴竖直向下,设定静态工作点。
  140. }

  141. //对读数进行纠正,消除偏移,并转换为物理量。公式见文档。
  142. void Rectify(int *pReadout, float *pRealVals)
  143. {
  144.   for (int i = 0; i < 3; ++i)
  145.   {
  146.     pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 16384.0f;
  147.   }

  148.   for (int i = 3; i < 6; ++i)
  149.   {
  150.     pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 131.0f;
  151.   }
  152. }

  153. //算得Roll角。算法见文档。
  154. float GetRoll(float *pRealVals)
  155. {
  156.   float fNorm = sqrt(pRealVals[0] * pRealVals[0] + pRealVals[1] * pRealVals[1] + pRealVals[2] * pRealVals[2]);
  157.   float fNormXZ = sqrt(pRealVals[0] * pRealVals[0] + pRealVals[2] * pRealVals[2]);
  158.   float fCos = fNormXZ / fNorm;
  159.   if (pRealVals[1] > 0)
  160.   {
  161.     return -acos(fCos) * fRad2Deg;
  162.   }
  163.   else
  164.     return acos(fCos) * fRad2Deg;
  165. }

  166. //算得Pitch角。算法见文档。
  167. float GetPitch(float *pRealVals)
  168. {
  169.   float fNorm = sqrt(pRealVals[0] * pRealVals[0] + pRealVals[1] * pRealVals[1] + pRealVals[2] * pRealVals[2]);
  170.   float fNormYZ = sqrt(pRealVals[1] * pRealVals[1] + pRealVals[2] * pRealVals[2]);
  171.   float fCos = fNormYZ / fNorm;
  172.   if (pRealVals[0] < 0)
  173.   {
  174.     return -acos(fCos) * fRad2Deg;
  175.   }
  176.   else
  177.     return acos(fCos) * fRad2Deg;
  178. }


  179. void AngleUpdate(float *pRealVals)
  180. {
  181.   float norm;
  182.   float vx, vy, vz;
  183.   float ex, ey, ez;

  184.   if (pRealVals[0]*pRealVals[1]*pRealVals[2] == 0)
  185.     return;

  186.   norm = sqrt(pRealVals[0] * pRealVals[0] + pRealVals[1] * pRealVals[1] + pRealVals[2] * pRealVals[2]);

  187.   vx = 2 * (q1 * q3 - q0 * q2);
  188.   vy = 2 * (q0 * q1 - q3 * q2);
  189.   vz = q0 * q0 - q1 * q1 - q2 * q2 + q3 * q3;

  190.   ex = pRealVals[1] / norm * vz - pRealVals[2] / norm * vy;
  191.   ey = pRealVals[2] / norm * vx - pRealVals[0] / norm * vz;
  192.   ez = pRealVals[0] / norm * vy - pRealVals[1] / norm * vx;

  193.   exInt = exInt + ex * KI;
  194.   eyInt = eyInt + ey * KI;
  195.   ezInt = ezInt + ez * KI;

  196.   q0 = q0 + (-q1 * pRealVals[3] + KP * ex + exInt - q2 * pRealVals[4] + KP * ey + eyInt - q3 * pRealVals[5] + KP * ez + ezInt) * halfT;
  197.   q1 = q1 + (q0 * pRealVals[3] + KP * ex + exInt + q2 * pRealVals[5] + KP * ez + ezInt - q3 * pRealVals[4] + KP * ey + eyInt) * halfT;
  198.   q2 = q2 + (q0 * pRealVals[4] + KP * ey + eyInt - q1 * pRealVals[5] + KP * ez + ezInt + q3 * pRealVals[3] + KP * ex + exInt) * halfT;
  199.   q3 = q3 + (q0 * pRealVals[5] + KP * ez + ezInt + q1 * pRealVals[4] + KP * ey + eyInt - q2 * pRealVals[3] + KP * ex + exInt) * halfT;

  200.   norm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
  201.   q0 = q0 / norm;
  202.   q1 = q1 / norm;
  203.   q2 = q2 / norm;
  204.   q3 = q3 / norm;

  205.   Angle_roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2 * q2 + 1) * fRad2Deg;
  206.   Angle_pitch = asin(-2 * q1 * q3 + 2 * q0 * q2) * fRad2Deg;
  207.   Angle_yaw = atan2(2 * q1 * q2 + 2 * q0 * q3, -2 * q2 * q2 - 2 * q3 * q3 + 1) * fRad2Deg;
  208. }
复制代码


以上的程式碼是用卡爾曼濾波+pid系統做出來的程式碼

看完之後我覺得很新奇 因為這個程式碼沒有用到網路上面大家都在用的pid_v1文件庫

檢測完以我的車子是要用pitch來檢測 趨近於0做平衡


目前成果是 車子接電腦會自己做平衡

但接一般直流電壓就會變第一個問題

看板上車子立起來的思維是 輪子做正反轉 搖晃 最後慢慢立起來

我的車子是 兩個輪子往一個地方衝.....

車子俯視圖


藍芽孔有架喔 但怕會有干擾就暫時把藍芽拔掉

rx tx與橋式連接器共用

pcb圖





這個小弟失敗了很多次最後做出來....不過也算很開心了 因為就算失敗了也能做一般四輪遙控玩具車(小時候玩到大的玩具)

這些圖都是用平板拍後在弄到電腦做切圖好不容易才能上傳....

現在要重弄關於用電腦的電壓 立起來的問題....

請給個建議 每有一些進展 更新一次 願互相學習

------------------------------------------------------------------------------------------------------------------------------------------------------------
程式庫:





關於卡爾曼率波若有錯要報  我這邊光卡爾曼率波就有3個程式庫

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2016-2-26 08:13:07 | 显示全部楼层
小车做的很精致,如果是简体中文字就更好啦。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-2-26 16:48:23 | 显示全部楼层
先說說 用了啥元件吧

Atmega328p*1
(主晶片)

22p電容*2

104電容*2

16MHZ*1
(我不會用Atmega328p內建的8MHZ)



CP212102 USB-TTL BOARD 橋式連接器*1
(自製PCB版子溝通電腦)

SJ 藍芽模組(找不到排字 有找到標籤) 可接5.0V 3.3V 藍芽*1
(若要自己做 請注意電壓 最好是5V 不然會壞)

MPU6050 GY-521*1
(這種的比較多 我也是努力拿常見的做)

L298N*1
(接香蕉電機)

香蕉電機*2

7805穩壓器

25V 10uF *2(接穩壓器的)

1.5v*6電池盒(电源)
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-2-27 15:58:58 | 显示全部楼层
目前發現要手碰才會讓 才會讓一邊的輪子動

懷疑是電流不夠

可是接電腦 電流照理說因該不會不夠阿

馬達也拆開來看過都還好好的...通電也會動阿

傾另一邊 另一邊馬達不會做修正阿

看來要慢慢搞了...

回复 支持 反对

使用道具 举报

发表于 2016-2-27 20:13:45 | 显示全部楼层
你的电机都没看到码盘,怎么采样转速信号的?
没有转速采样如何做负反馈,PID控制呢?
还有,这个TT电机玩玩可以,如果做控制,赶紧换个好点的带编码器的减速电机。
回复 支持 反对

使用道具 举报

发表于 2016-2-29 18:45:22 | 显示全部楼层
干电池,即时是南孚电池,在面对电机运动的时候,电压降低特别明显。我的PID调整老不成功,不知道换航模锂电池会不会好点。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-3-1 15:43:04 | 显示全部楼层
艰苦奋斗 发表于 2016-2-27 20:13
你的电机都没看到码盘,怎么采样转速信号的?
没有转速采样如何做负反馈,PID控制呢?
还有,这个TT电机玩 ...

恩 大致上理解 小弟會研究一下如何在這馬達上放測速碼盤

畢竟當初拿到並沒有測速馬達這東西 只有香蕉電機....

只是沒想到要做測速與pid控制要用到測速模組
回复 支持 反对

使用道具 举报

发表于 2016-3-2 09:42:27 | 显示全部楼层
146行,void Calibration()函数没看到在那儿用阿?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-3-2 16:31:02 | 显示全部楼层
本帖最后由 lieak59 于 2016-3-2 16:46 编辑

如果是以Arduino控制馬達轉速的話 是不是還要讓馬達分個接頭到a0-a3的角位?

arduino上面也是有pwm 不知道可不可以共用 但本來就有馬達模組L298N了 會不會多此一舉?

或是使用ARDUINO的PWM接L298N的ENA跟ENB抓轉速?

小弟最近入手了 減速馬達(沒編碼盤) 到時候可能要再入手個編碼盤 或測速模組?



回复 支持 反对

使用道具 举报

发表于 2016-3-2 17:36:45 | 显示全部楼层
Kalman.h库哪位大侠有啊,官网下不下来
回复 支持 反对

使用道具 举报

发表于 2016-3-5 16:52:38 | 显示全部楼层
85行:nLastTime = nCurTime; 与105行nLastTime = nCurTime; 是否冲突?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-3-8 15:58:39 | 显示全部楼层

RE: 平衡車電源供應與pid平衡與卡爾曼率波思維調整 與製作平衡小車分享 依情況更新

本帖最后由 lieak59 于 2016-3-8 16:00 编辑

新增更新 之前發過的4到3輪的小車控制程式碼

  1. #include <SoftwareSerial.h>    // 引用「軟體序列埠」程式庫
  2. SoftwareSerial BT(3, 2);    // 設定軟體序列埠(接收腳, 傳送腳)

  3. char command;          // 設定啟動或停止馬達的參數
  4. // 一開始先設定「停止」
  5. boolean run = false;

  6. // 左馬達控制設定
  7. const byte LEFT1 = 10;
  8. const byte LEFT2 = 9;
  9. const byte LEFT_PWM = 5;

  10. // 右馬達控制設定
  11. const byte RIGHT1 = 8;
  12. const byte RIGHT2 = 7;
  13. const byte RIGHT_PWM = 6;

  14. // 設定PWM輸出值(註:FA-130馬達供電不要超過3v)
  15. byte motorSpeed = 100;

  16. void forward() {        // 馬達轉向:前進
  17. digitalWrite(LEFT1, HIGH);
  18. digitalWrite(LEFT2, LOW);
  19. digitalWrite(RIGHT1, HIGH);
  20. digitalWrite(RIGHT2, LOW);
  21. }

  22. void backward() {        // 馬達轉向:後退
  23. digitalWrite(LEFT1, LOW);
  24. digitalWrite(LEFT2, HIGH);
  25. digitalWrite(RIGHT1, LOW);
  26. digitalWrite(RIGHT2, HIGH);
  27. }

  28. void turnLeft() {        // 馬達轉向:左轉
  29. digitalWrite(LEFT1, LOW);
  30. digitalWrite(LEFT2, HIGH);
  31. digitalWrite(RIGHT1, HIGH);
  32. digitalWrite(RIGHT2, LOW);
  33. }

  34. void turnRight() {        // 馬達轉向:右轉
  35. digitalWrite(LEFT1, HIGH);
  36. digitalWrite(LEFT2, LOW);
  37. digitalWrite(RIGHT1, LOW);
  38. digitalWrite(RIGHT2, HIGH);
  39. }

  40. void setup() {
  41.   BT.begin(9600);
  42.   
  43.   pinMode(LEFT1, OUTPUT);
  44.   pinMode(LEFT2, OUTPUT);
  45.   pinMode(LEFT_PWM, OUTPUT);
  46.   pinMode(RIGHT1, OUTPUT);
  47.   pinMode(RIGHT2, OUTPUT);
  48.   pinMode(RIGHT_PWM, OUTPUT);
  49. }

  50. void loop() {
  51.   if (BT.available() > 0) {
  52.     command = BT.read();
  53.    
  54.     switch (command) {
  55.       case 'w':                // 接收到'w',前進。
  56.         forward();
  57.         run = true;        // 啟動馬達
  58.         break;
  59.       case 'x':                // 接收到'x',後退。
  60.         backward();
  61.         run = true;        // 啟動馬達
  62.         break;
  63.       case 'a':                // 接收到'a',左轉。
  64.         turnLeft();
  65.         run = true;        // 啟動馬達
  66.         break;
  67.       case 'd':                // 接收到'd',右轉。
  68.         turnRight();
  69.         run = true;        // 啟動馬達
  70.         break;
  71.       case 's':
  72.         run = false;        // 停止馬達
  73.         break;
  74.     }
  75.   }
  76.   
  77.   if (run) {       
  78.     // 如果要啟動馬達…
  79.     // 向馬達輸出指定的類比電壓值
  80.     analogWrite(LEFT_PWM, motorSpeed);
  81.     analogWrite(RIGHT_PWM, motorSpeed);
  82.   } else {
  83.     // 否則…
  84.     // 將馬達的電壓值設定成0
  85.     analogWrite(LEFT_PWM, 0);
  86.     analogWrite(RIGHT_PWM, 0);
  87.   }
  88. }
复制代码


若是要製作功能性小車可以以這程式碼為底 這程式碼只有控制2個電機 剩下輪子可以用一般小輪子替代 記得要會轉的...

關於安卓控制可以用網路上的控制藍芽程式 弄完後小車可以拿手機控制了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-3-14 21:28:20 | 显示全部楼层
碼盤已經買到 要照著接抓轉速吧?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-25 02:00 , Processed in 0.049952 second(s), 22 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表