找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 10743|回復(fù): 9
收起左側(cè)

Arduino智能小車(chē)程序+資料 pid算法

  [復(fù)制鏈接]
ID:559809 發(fā)表于 2019-7-11 15:06 | 顯示全部樓層 |閱讀模式
  1. #include "pid.h"
  2. #ifdef ARDUINO_DEBUG
  3. int debugLeftSpeed;
  4. int debugRightSpeed;
  5. uint8_t debugIrs = 0;
  6. #endif
  7. const float motorSpeed = 140; //
  8. const int IR_PIN[] = {A0, A1, A2, A3, A4}; //
  9. const int IN_A1 = 13;   //
  10. const int IN_A2 = 12;   //
  11. const int IN_B1 = 11;  //
  12. const int IN_B2 = 10;  //
  13. const int _pwmLeftPin = 5;
  14. const int _pwmRightPin = 6;
  15. pid_t pid;
  16. float pidValue = 0;
  17. bool turnFlag = false;
  18. void setup(void)
  19. {
  20.   int i;
  21.   /********************1.初始化電機(jī)模塊引腳13,12,11,10為輸出模式******************/
  22.   /*
  23.    *   編寫(xiě)代碼
  24.   */
  25.     /********************2.設(shè)置小車(chē)啟動(dòng)速度為140---set_speed()******************/
  26.   /*
  27.    *   編寫(xiě)代碼
  28.   */
  29.   
  30.   /********************3.初始化循跡模塊引腳A0, A1, A2, A3, A4為輸入模式******************/
  31.    /*
  32.    *   編寫(xiě)代碼
  33.   */
  34.   //3.設(shè)置PID參數(shù)
  35.   pid.sampleTime = SAMPLE_TIME;
  36.   pid.Kp = KP_VALUE;
  37.   pid.Ki = KI_VALUE;
  38.   pid.Kd = KD_VALUE;
  39.   pid.error = 0;
  40.   pid.previous_error = 0;
  41.   Serial.begin(115200);
  42.   delay(5000);
  43.   goForward();
  44.   return;
  45. }

  46. //get ir data and middle filter
  47. uint8_t getIrData(void)  //
  48. {
  49.   int i, j;
  50.   uint8_t level;
  51.   uint8_t temp;
  52.   uint8_t irs[9] = {0};//采集9次傳感器的傳感器的數(shù)據(jù)
  53.         //減少取平均值,濾波。
  54.   for (j = 0; j < 9; j ++) {
  55.    
  56.       for (i = 0; i < 5; i++) {
  57.         level = digitalRead(IR_PIN[i]);
  58.         if (level) {
  59.           bitSet(irs[j], i); // 設(shè)置irs[j]的第jbit 為1
  60.         } else {
  61.           bitClear(irs[j], i); //清除irs[j]的第jbit為0
  62.         }
  63.       }
  64.   }
  65.   //對(duì)irs中的數(shù)據(jù),進(jìn)行9次排序,取最中間的值。
  66.   for (i = 0; i < 9 - 1; i ++) {
  67.     for (j = 0; j < 9 - i - 1; j ++) {
  68.       if (irs[j] > irs[j + 1]) {
  69.         temp = irs[j];
  70.         irs[j] = irs[j + 1];
  71.         irs[j + 1] = temp;
  72.       }
  73.     }
  74.   }
  75.   
  76. #ifdef ARDUINO_DEBUG
  77.   debugIrs = irs[4];
  78. #endif
  79.   return irs[4];
  80. }
  81. int calcErrorByIrsValue(uint8_t irs)
  82. {
  83.   //右邊壓線(xiàn)為負(fù),左邊壓線(xiàn)為正。
  84.   //要左轉(zhuǎn)為負(fù),要右轉(zhuǎn)為正
  85.   int curError = pid.error; //獲得上一次的pid誤差值
  86.   //壓線(xiàn)輸出低電平0,沒(méi)有壓線(xiàn)輸出高電平1
  87.   switch (irs) { //h'h
  88.     case B11110: curError = -8; break; //最右邊壓線(xiàn)
  89.    
  90.     case B10000:
  91.     case B11000: curError = -7; break; //右四個(gè)或者右3個(gè)沿線(xiàn)
  92.    
  93.     case B11100: curError = -6; break; //右兩個(gè)沿線(xiàn)
  94.     case B11101: curError = -4; break; //右倒數(shù)第二個(gè)沿線(xiàn)
  95.     case B11001: curError = -2; break; //
  96.    
  97.     case B00000:       //全部壓線(xiàn)和中間壓線(xiàn),對(duì)應(yīng)"十字"彎道,直接郭即可。
  98.     case B11011: curError = 0;  break;
  99.    
  100.     case B10011: curError = 2;  break;
  101.     case B10111: curError = 4;  break;
  102.     case B00111: curError = 6;  break;
  103.    
  104.     case B00011:
  105.     case B00001: curError = 7;  break;
  106.    
  107.     case B01111: curError = 8;  break;
  108. //都沒(méi)有沿線(xiàn),按照上次的PID值來(lái)計(jì)算
  109.     case B11111: curError = pid.error > 0 ? 9 : - 9; break;
  110.   }
  111.   return curError;
  112. }

  113. void _sortData(int *p, int n)
  114. {
  115.   int temp;
  116.   int i, j;
  117.   
  118.   /**編寫(xiě)冒泡排序,對(duì)采集到的錯(cuò)誤碼,進(jìn)行十次從小到大排序 **/
  119.   /*
  120.    *   編寫(xiě)代碼
  121.   */
  122.   return;
  123. }

  124. //計(jì)算當(dāng)前的誤差值,得到當(dāng)前的偏移值
  125. void calcCurrentError(void)
  126. {
  127.   int i;
  128.   uint8_t irs;
  129.   float sum = 0;
  130.   int errorData[10];
  131.   //1.采集10次循跡模塊獲得的錯(cuò)誤碼
  132.   for (i = 0; i < 10; i ++) {
  133.     irs =  getIrData();  //得到這次傳感器采集的數(shù)據(jù)(5路數(shù)據(jù))
  134.     errorData[i] =  calcErrorByIrsValue(irs);
  135.   }
  136.   //2.要求對(duì)errorData中的數(shù)據(jù)進(jìn)行排序(從小到大排序)
  137.     /*
  138.    *   編寫(xiě)代碼
  139.   */
  140.   //3.十次中去處一個(gè)最高數(shù),去除一個(gè)最低數(shù),求中間8次錯(cuò)誤碼數(shù)據(jù)的和。
  141.   /*
  142.    *   編寫(xiě)代碼
  143.   */
  144. //4.求8次錯(cuò)誤碼數(shù)據(jù)中的偏差。
  145.   pid.error = sum / 8;
  146.   return;
  147. }
  148. void turnRight(void)
  149. {
  150.     digitalWrite(IN_B1, HIGH); //左正
  151.     digitalWrite(IN_B2, LOW);
  152.     digitalWrite(IN_A1, LOW); //右反
  153.     digitalWrite(IN_A2, HIGH);
  154.    
  155. }
  156. void turnLeft(void)
  157. {
  158.     digitalWrite(IN_B1, LOW);// 左反
  159.     digitalWrite(IN_B2, HIGH);
  160.     digitalWrite(IN_A1, HIGH); //右正
  161.     digitalWrite(IN_A2, LOW);
  162.    
  163. }
  164. void goForward(void)
  165. {
  166.   digitalWrite(IN_B1, HIGH); //左正傳
  167.   digitalWrite(IN_B2, LOW);
  168.   digitalWrite(IN_A1, HIGH); //右正轉(zhuǎn)
  169.   digitalWrite(IN_A2, LOW);
  170. }
  171. void motorControl(float pidValue, bool turnFlag)
  172. {
  173.   int leftMotorSpeed = 0;
  174.   int rightMotorSpeed = 0;
  175.   
  176.   leftMotorSpeed  = constrain((motorSpeed + pidValue), -255, 255);
  177.   rightMotorSpeed = constrain((motorSpeed - pidValue), -255, 255);
  178.   if (turnFlag) {
  179.     if (abs(leftMotorSpeed) > abs(rightMotorSpeed)) {
  180.       leftMotorSpeed  = abs(leftMotorSpeed);
  181.       rightMotorSpeed = leftMotorSpeed;
  182.     } else {
  183.       rightMotorSpeed =  abs(rightMotorSpeed);
  184.       leftMotorSpeed  = rightMotorSpeed;
  185.     }
  186.   } else {
  187.     leftMotorSpeed  = leftMotorSpeed  > 0 ? leftMotorSpeed  : -leftMotorSpeed;
  188.     rightMotorSpeed = rightMotorSpeed > 0 ? rightMotorSpeed : -rightMotorSpeed;
  189.   }
  190.   analogWrite(_pwmLeftPin,  leftMotorSpeed );
  191.   analogWrite(_pwmRightPin, rightMotorSpeed);
  192. #ifdef ARDUINO_DEBUG
  193.   debugLeftSpeed  = leftMotorSpeed ;
  194.   debugRightSpeed = rightMotorSpeed;
  195. #endif
  196.   return;
  197. }
  198. bool calculatePid(float *pValue)
  199. {
  200.   float P = 0;
  201.   static float I = 0 ;
  202.   float D = 0 ;
  203.   static unsigned long lastTime = 0;
  204.   //獲得機(jī)器從啟動(dòng)到現(xiàn)在所運(yùn)行的時(shí)間
  205.   unsigned long now = millis();
  206.   int timeChange = now - lastTime;//得到這次的時(shí)間
  207.   //若是我們的這次變化的時(shí)間,小于pid的
  208.   //采樣時(shí)間,就需要等待一下。
  209.   if (timeChange < pid.sampleTime) {
  210.     return false;
  211.   }
  212.   P = pid.error; //本次小車(chē)傳感器的偏移大小
  213.   I = I + pid.error;//歷史的偏移
  214.   D = pid.error - pid.previous_error; //最近兩次的差值
  215.   *pValue = (pid.Kp * P) + (pid.Ki * I) + (pid.Kd * D) + 1;
  216.   //constrain()函數(shù)的功能:若是*pvalue > motorSpeed,取 motorSpeed,
  217.   //                       若是*pvalue < -motorSpeed,取 -motorSpeed,
  218.   //        若是-pValue<=*pvalue <= motorSpeed,取*pvalue
  219.   *pValue = constrain(*pValue, -motorSpeed,motorSpeed);
  220.   pid.previous_error = pid.error; //記錄本次的pid.error,作為下一次的歷史差值。
  221.   lastTime = now; // 記錄本次的運(yùn)行時(shí)間,作為下一次時(shí)間的歷史時(shí)間參考。
  222.   return true;
  223. }

  224. void calcDirection(void) //判斷小車(chē)的轉(zhuǎn)向
  225. {
  226.   //pid.error > 0 說(shuō)明小車(chē)左邊壓線(xiàn)多,要
  227.   if (pid.error >= 7 && pid.error <= 8) {
  228.     turnRight();
  229.     turnFlag = true;
  230.   } else if (pid.error >= -8 && pid.error <= -7) {
  231.     turnLeft();
  232.     turnFlag = true;
  233.   } else if(pid.error == 9 || pid.error == -9){
  234.     turnRight();
  235.     turnFlag = true;
  236.     }else {
  237.     goForward();
  238.     turnFlag = false;
  239.   }
  240.   return;
  241. }
  242. void loop(void)
  243. {
  244.   bool ok;
  245.   float  pidValue;
  246.   calcCurrentError();
  247.   ok = calculatePid(&pidValue); //計(jì)算當(dāng)前pid的差值。,若是就緒了
  248.   if (ok) {
  249.     calcDirection();
  250.     motorControl(pidValue, turnFlag);
  251.   }
  252.   return;
  253. }
復(fù)制代碼

pid_student.zip

3.1 KB, 下載次數(shù): 148, 下載積分: 黑幣 -5

Arduino 智能小車(chē) - 尋跡.pdf

322.06 KB, 下載次數(shù): 149, 下載積分: 黑幣 -5

回復(fù)

使用道具 舉報(bào)

ID:560595 發(fā)表于 2019-7-16 13:46 | 顯示全部樓層
這個(gè)要頂
回復(fù)

使用道具 舉報(bào)

ID:505027 發(fā)表于 2019-7-16 21:26 | 顯示全部樓層
這個(gè)好,記錄一下
回復(fù)

使用道具 舉報(bào)

ID:594846 發(fā)表于 2019-8-4 22:31 | 顯示全部樓層
真好,贊一個(gè)!
回復(fù)

使用道具 舉報(bào)

ID:609660 發(fā)表于 2019-9-9 19:22 來(lái)自手機(jī) | 顯示全部樓層
這個(gè)資源確實(shí)好
回復(fù)

使用道具 舉報(bào)

ID:149774 發(fā)表于 2019-9-21 21:44 | 顯示全部樓層
做個(gè)記號(hào),回頭學(xué)習(xí)一下
回復(fù)

使用道具 舉報(bào)

ID:614623 發(fā)表于 2019-9-22 00:16 | 顯示全部樓層
這個(gè)可以參考
回復(fù)

使用道具 舉報(bào)

ID:652804 發(fā)表于 2019-12-15 15:42 | 顯示全部樓層
謝謝,學(xué)習(xí)學(xué)習(xí)。
回復(fù)

使用道具 舉報(bào)

ID:28942 發(fā)表于 2020-9-15 19:24 | 顯示全部樓層
編輯不過(guò)是什么原因?
回復(fù)

使用道具 舉報(bào)

ID:1011808 發(fā)表于 2022-4-5 13:47 | 顯示全部樓層
arduino如何設(shè)置小車(chē)啟動(dòng)速度
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表