BALA2.ino 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /*
  2. Description: BALA2 stand and run in balance.
  3. Note: click the device power button and Long press button B will setup
  4. calibration mode. at calibration mode Press the A/C key to increase or
  5. decrease the correction value. When it is adjusted to a satisfactory value,
  6. press the B key to save the parameter.
  7. */
  8. #define M5STACK_MPU6886
  9. #include <M5Stack.h>
  10. #include "MadgwickAHRS.h"
  11. #include "bala.h"
  12. #include "calibration.h"
  13. #include "freertos/FreeRTOS.h"
  14. #include "imu_filter.h"
  15. #include "pid.h"
  16. extern uint8_t bala_img[41056];
  17. static void PIDTask(void *arg);
  18. static void draw_waveform();
  19. static float angle_point = -1.5;
  20. float kp = 24.0f, ki = 0.0f, kd = 90.0f;
  21. float s_kp = 15.0f, s_ki = 0.075f, s_kd = 0.0f;
  22. bool calibration_mode = false;
  23. Bala bala;
  24. PID pid(angle_point, kp, ki, kd);
  25. PID speed_pid(0, s_kp, s_ki, s_kd);
  26. // the setup routine runs once when M5Stack starts up
  27. void setup() {
  28. // Initialize the M5Stack object
  29. M5.begin(true, false, false, false);
  30. Serial.begin(115200);
  31. M5.IMU.Init();
  32. int16_t x_offset, y_offset, z_offset;
  33. float angle_center;
  34. calibrationInit();
  35. if (M5.BtnB.isPressed()) {
  36. calibrationGryo();
  37. calibration_mode = true;
  38. }
  39. calibrationGet(&x_offset, &y_offset, &z_offset, &angle_center);
  40. Serial.printf("x: %d, y: %d, z:%d, angle: %.2f", x_offset, y_offset,
  41. z_offset, angle_center);
  42. angle_point = angle_center;
  43. pid.SetPoint(angle_point);
  44. SemaphoreHandle_t i2c_mutex;
  45. ;
  46. i2c_mutex = xSemaphoreCreateMutex();
  47. bala.SetMutex(&i2c_mutex);
  48. ImuTaskStart(x_offset, y_offset, z_offset, &i2c_mutex);
  49. xTaskCreatePinnedToCore(PIDTask, "pid_task", 4 * 1024, NULL, 4, NULL, 1);
  50. M5.Lcd.drawJpg(bala_img, 41056);
  51. if (calibration_mode) {
  52. M5.Lcd.setCursor(0, 0);
  53. M5.Lcd.printf("calibration mode");
  54. }
  55. }
  56. // the loop routine runs over and over again forever
  57. void loop() {
  58. static uint32_t next_show_time = 0;
  59. vTaskDelay(pdMS_TO_TICKS(5));
  60. if (millis() > next_show_time) {
  61. draw_waveform();
  62. next_show_time = millis() + 10;
  63. }
  64. M5.update();
  65. if (M5.BtnA.wasPressed()) {
  66. angle_point += 0.25;
  67. pid.SetPoint(angle_point);
  68. }
  69. if (M5.BtnB.wasPressed()) {
  70. if (calibration_mode) {
  71. calibrationSaveCenterAngle(angle_point);
  72. }
  73. }
  74. if (M5.BtnC.wasPressed()) {
  75. angle_point -= 0.25;
  76. pid.SetPoint(angle_point);
  77. }
  78. }
  79. static void PIDTask(void *arg) {
  80. float bala_angle;
  81. float motor_speed = 0;
  82. int16_t pwm_speed;
  83. int16_t pwm_output;
  84. int16_t pwm_angle;
  85. int32_t encoder = 0;
  86. int32_t last_encoder = 0;
  87. uint32_t last_ticks = 0;
  88. pid.SetOutputLimits(1023, -1023);
  89. pid.SetDirection(-1);
  90. speed_pid.SetIntegralLimits(40, -40);
  91. speed_pid.SetOutputLimits(1023, -1023);
  92. speed_pid.SetDirection(1);
  93. for (;;) {
  94. vTaskDelayUntil(&last_ticks, pdMS_TO_TICKS(5));
  95. // in imu task update, update freq is 200HZ
  96. bala_angle = getAngle();
  97. // Get motor encoder value
  98. bala.UpdateEncoder();
  99. encoder = bala.wheel_left_encoder + bala.wheel_right_encoder;
  100. // motor_speed filter
  101. motor_speed = 0.8 * motor_speed + 0.2 * (encoder - last_encoder);
  102. last_encoder = encoder;
  103. if (fabs(bala_angle) < 70) {
  104. pwm_angle = (int16_t)pid.Update(bala_angle);
  105. pwm_speed = (int16_t)speed_pid.Update(motor_speed);
  106. pwm_output = pwm_speed + pwm_angle;
  107. if (pwm_output > 1023) {
  108. pwm_output = 1023;
  109. }
  110. if (pwm_output < -1023) {
  111. pwm_output = -1023;
  112. }
  113. bala.SetSpeed(pwm_output, pwm_output);
  114. } else {
  115. pwm_angle = 0;
  116. bala.SetSpeed(0, 0);
  117. bala.SetEncoder(0, 0);
  118. speed_pid.SetIntegral(0);
  119. }
  120. }
  121. }
  122. static void draw_waveform() {
  123. #define MAX_LEN 120
  124. #define X_OFFSET 100
  125. #define Y_OFFSET 95
  126. #define X_SCALE 3
  127. static int16_t val_buf[MAX_LEN] = {0};
  128. static int16_t pt = MAX_LEN - 1;
  129. val_buf[pt] = constrain((int16_t)(getAngle() * X_SCALE), -50, 50);
  130. if (--pt < 0) {
  131. pt = MAX_LEN - 1;
  132. }
  133. for (int i = 1; i < (MAX_LEN); i++) {
  134. uint16_t now_pt = (pt + i) % (MAX_LEN);
  135. M5.Lcd.drawLine(i + X_OFFSET,
  136. val_buf[(now_pt + 1) % MAX_LEN] + Y_OFFSET,
  137. i + 1 + X_OFFSET,
  138. val_buf[(now_pt + 2) % MAX_LEN] + Y_OFFSET, TFT_BLACK);
  139. if (i < MAX_LEN - 1) {
  140. M5.Lcd.drawLine(
  141. i + X_OFFSET, val_buf[now_pt] + Y_OFFSET, i + 1 + X_OFFSET,
  142. val_buf[(now_pt + 1) % MAX_LEN] + Y_OFFSET, TFT_GREEN);
  143. }
  144. }
  145. }