136浏览
查看: 136|回复: 2

[项目] 【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人

[复制链接]

感觉我最近只做自平衡机器人,但我又做了一次。 。 。 (^_^;)
作为之前的改进,对车架进行了审查,增加了伺服系统,并将步进电机改为通用的 NEMA 14。
这款自平衡机器人基于 BROBOT EVO 打造,由 jjrobots 开源,在 B-Robot 论坛上架,但目前链接已死。

用 3D 打印机制作的框架
框架采用PLA灯丝,由于伺服安装和步进电机规格变化,与之前略有不同,但尺寸几乎相同。 这是开发图和装配的图像。

通用板使用 70mm × 50mm。
ESP32 和 DRV8825 母头的排针和步进电机和伺服的排针如图所示,短排针母头放在 ESP32 下方进行MPU6050,5V稳压器很热,所以安装了散热器,它的右侧用于电源和开关。它是一个块连接器。

MPU6050使用云台控制器自带的,商用MPU6050上的引脚数量不同,需要注意安装方向。
图片的正面是正向方向,在这个MPU6050的情况下,在这个方向上,在商业MPU6050的情况下,它看起来像下图。

从机架的开发图可以看出,底座用2mm的自攻螺钉安装在板上的板上,步进电机用8mm×3mm的螺钉固定。
切割板板和侧框的 3 毫米丝锥,并用尼龙螺钉固定(如果没有丝锥,3 毫米丝锥也可以)。
车轮安装有O型圈,切一个3mm的丝锥并用imo螺钉固定在轴上,伺服从背面用2mm的丝锥螺钉固定为SG90,舵机从正面插入MG90S并用丝锥螺钉固定,开关推入。
如图所示,电池用魔术贴固定在车架上,顺便说一句,连接器是从左侧开始的,按照伺服、左电机和右电机的顺序(从车身背面看)。
框架盖是一块0.5mm的PVC板,切成83mm×160mm,插入框架的凹槽中,将插图印刷粘贴在印章的印刷纸上。

B-Robot 论坛上发布的文章中有 Github 的链接,所以下载 zip 文件,问题的解决方法也在论坛中讨论,请大家读到最后。
我用Arduino IDE写作,所以是给用过的人准备的,网上有很多方法使用,这里就省略了。
您将需要 ESP32 库。
另外,Arduino IDE版本是Ver1.8.7,编译时出错,所以我使用了Ver1.9.0的测试版。
首先,解压从 Github 下载的 zip 文件,并将生成文件夹中的“BRobotEvo2ESP32”文件夹移动到要与 Arduino 一起使用的文件夹(通常是文档中的 Aruduino 文件夹),尽管该文件夹中有 14 个文件。删除“BRobotEvo2ESP32.ino”,因为它只包含消息,并将“ESP32SelfbalancingBot.CPP”文件重命名为“ESP32SelfbalancingBot.ino”。

另外,论坛上写的缺陷有修复,但我不记得我修复了哪个文件,所以我把这个单元3修改过的文件放在一起,做成一个Zip文件,所以我会上传它们。
B-Robot_ESP32_ZIP_file
在写草图之前,请拆下伺服连接器和电池,以防止不必要的作。

步进电机驱动器电流值调整
写入成功完成后,调整步进电机驱动器的电流值,首先在舵机和左右步进电机连接器断开的情况下测量电流值。就这个机器人而言,它的电流约为150mA
断电后,连接一个步进电机,接通电源,步进电机运行时的电流值通过驱动模块上的音量调节到250mA左右,另一个也以同样的方式调节。
这个现值对我来说刚刚好,但可能存在个体差异,所以还是实际作调整一下比较好,调整完成后,连接好所有连接器,躺着打开电源,过一会儿车轮会微微移动,当运动停止时,慢慢抬起车身,它就会站起来。

在智能手机上进行控制
jjrobots 发布了一个适用于 Android 和 iOS 的应用程序,我使用的是 Android 智能手机,所以我从 Google Play 安装了该应用程序。
当您打开 B-Robot 时,智能手机的 WiFi 设置列表中会显示“Bbot”,选择它,输入密码“12345678”,然后连接。
启动应用程序并选择“B-ROBOT EVO2”进行控制。
伺服系统可以通过右侧的按钮进行作,PID 可以通过智能手机的设置屏幕进行调整。

*笔记
如果在机器人站立时连接智能手机,作会停止片刻,机器人会摔倒,所以在机器人倒下时将智能手机连接到WiFi,然后让机器人站起来,然后在能够用智能手机控制它后让机器人站起来。


【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图1

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图2

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图3

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图4

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图5

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图7

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图6

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图8

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图9

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图10


【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图11

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图12

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人图13

驴友花雕  中级技神
 楼主|

发表于 昨天 20:14

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人

项目代码

  1. #include <Arduino.h>
  2. #include <WiFi.h>
  3. #include <WiFiClient.h>
  4. #include <ArduinoOTA.h>
  5. #include <Wire.h>
  6. #include "MPU6050.h"
  7. #include <stdio.h>
  8. #include "esp_types.h"
  9. #include "soc/timer_group_struct.h"
  10. #include "driver/periph_ctrl.h"
  11. #include "driver/timer.h"
  12. #include "driver/ledc.h"
  13. #include "defines.h"
  14. #include "globals.h"
  15. #include "Motors.h"
  16. #include "Control.h"
  17. #include <WiFiUdp.h>
  18. #include "OSC.h"
  19. #include "esp32-hal-ledc.h"
  20. void initTimers();
  21. void initWifiAP() {
  22.         //Serial.println("Setting up WiFi AP...");
  23.         if (WiFi.softAP("bbot", "12345678")) {
  24.                 Serial.println("Wifi AP set up successfully");
  25.         }
  26.         WiFi.softAPConfig(IPAddress(192, 168, 4, 1), IPAddress(192, 168, 4, 1),
  27.                         IPAddress(255, 255, 255, 0));
  28. }
  29. void initMPU6050() {
  30.         MPU6050_setup();
  31.         delay(500);
  32.         MPU6050_calibrate();
  33. }
  34. void setup() {
  35.         pinMode(PIN_ENABLE_MOTORS, OUTPUT);
  36.         digitalWrite(PIN_ENABLE_MOTORS, HIGH);
  37.         pinMode(PIN_MOTOR1_DIR, OUTPUT);
  38.         pinMode(PIN_MOTOR1_STEP, OUTPUT);
  39.         pinMode(PIN_MOTOR2_DIR, OUTPUT);
  40.         pinMode(PIN_MOTOR2_STEP, OUTPUT);
  41.         pinMode(PIN_SERVO, OUTPUT);
  42.         ledcSetup(6, 50, 16); // channel 6, 50 Hz, 16-bit width
  43.         ledcAttachPin(PIN_SERVO, 6);   // GPIO 22 assigned to channel 1
  44.         delay(50);
  45.         ledcWrite(6, SERVO_AUX_NEUTRO);
  46.         Serial.begin(115200);
  47.         Wire.begin();
  48.         initWifiAP();
  49.         initMPU6050();
  50.         initTimers();
  51.         OSC_init();
  52.         digitalWrite(PIN_ENABLE_MOTORS, LOW);
  53.         for (uint8_t k = 0; k < 5; k++) {
  54.                 setMotorSpeedM1(5);
  55.                 setMotorSpeedM2(5);
  56.                 ledcWrite(6, SERVO_AUX_NEUTRO + 250);
  57.                 delay(200);
  58.                 setMotorSpeedM1(-5);
  59.                 setMotorSpeedM2(-5);
  60.                 ledcWrite(6, SERVO_AUX_NEUTRO - 250);
  61.                 delay(200);
  62.         }
  63.         ledcWrite(6, SERVO_AUX_NEUTRO);
  64.         digitalWrite(PIN_ENABLE_MOTORS, HIGH);
  65. }
  66. void processOSCMsg() {
  67.         if (OSCpage == 1) {
  68.                 if (modifing_control_parameters)  // We came from the settings screen
  69.                 {
  70.                         OSCfader[0] = 0.5; // default neutral values
  71.                         OSCfader[1] = 0.5;
  72.                         OSCtoggle[0] = 0;  // Normal mode
  73.                         mode = 0;
  74.                         modifing_control_parameters = false;
  75.                 }
  76.                 if (OSCmove_mode) {
  77.                         //Serial.print("M ");
  78.                         //Serial.print(OSCmove_speed);
  79.                         //Serial.print(" ");
  80.                         //Serial.print(OSCmove_steps1);
  81.                         //Serial.print(",");
  82.                         //Serial.println(OSCmove_steps2);
  83.                         positionControlMode = true;
  84.                         OSCmove_mode = false;
  85.                         target_steps1 = steps1 + OSCmove_steps1;
  86.                         target_steps2 = steps2 + OSCmove_steps2;
  87.                 } else {
  88.                         positionControlMode = false;
  89.                         throttle = (OSCfader[0] - 0.5) * max_throttle;
  90.                         // We add some exponential on steering to smooth the center band
  91.                         steering = OSCfader[1] - 0.5;
  92.                         if (steering > 0)
  93.                                 steering = (steering * steering + 0.5 * steering)
  94.                                                 * max_steering;
  95.                         else
  96.                                 steering = (-steering * steering + 0.5 * steering)
  97.                                                 * max_steering;
  98.                 }
  99.                 if ((mode == 0) && (OSCtoggle[0])) {
  100.                         // Change to PRO mode
  101.                         max_throttle = MAX_THROTTLE_PRO;
  102.                         max_steering = MAX_STEERING_PRO;
  103.                         max_target_angle = MAX_TARGET_ANGLE_PRO;
  104.                         mode = 1;
  105.                 }
  106.                 if ((mode == 1) && (OSCtoggle[0] == 0)) {
  107.                         // Change to NORMAL mode
  108.                         max_throttle = MAX_THROTTLE;
  109.                         max_steering = MAX_STEERING;
  110.                         max_target_angle = MAX_TARGET_ANGLE;
  111.                         mode = 0;
  112.                 }
  113.         } else if (OSCpage == 2) { // OSC page 2
  114.                 if (!modifing_control_parameters) {
  115.                         for (uint8_t i = 0; i < 4; i++)
  116.                                 OSCfader[i] = 0.5;
  117.                         OSCtoggle[0] = 0;
  118.                         modifing_control_parameters = true;
  119.                         OSC_MsgSend("$P2", 4);
  120.                 }
  121.                 // User could adjust KP, KD, KP_THROTTLE and KI_THROTTLE (fadder1,2,3,4)
  122.                 // Now we need to adjust all the parameters all the times because we dont know what parameter has been moved
  123.                 Kp_user = KP * 2 * OSCfader[0];
  124.                 Kd_user = KD * 2 * OSCfader[1];
  125.                 Kp_thr_user = KP_THROTTLE * 2 * OSCfader[2];
  126.                 Ki_thr_user = KI_THROTTLE * 2 * OSCfader[3];
  127.                 // Send a special telemetry message with the new parameters
  128.                 char auxS[50];
  129.                 sprintf(auxS, "$tP,%d,%d,%d,%d", int(Kp_user * 1000),
  130.                                 int(Kd_user * 1000), int(Kp_thr_user * 1000),
  131.                                 int(Ki_thr_user * 1000));
  132.                 OSC_MsgSend(auxS, 50);
  133. #if DEBUG>0
  134.                 Serial.print("Par: ");
  135.                 Serial.print(Kp_user);
  136.                 Serial.print(" ");
  137.                 Serial.print(Kd_user);
  138.                 Serial.print(" ");
  139.                 Serial.print(Kp_thr_user);
  140.                 Serial.print(" ");
  141.                 Serial.println(Ki_thr_user);
  142. #endif
  143.                 // Calibration mode??
  144.                 if (OSCpush[2] == 1) {
  145.                         Serial.print("Calibration MODE ");
  146.                         angle_offset = angle_adjusted_filtered;
  147.                         Serial.println(angle_offset);
  148.                 }
  149.                 // Kill robot => Sleep
  150.                 while (OSCtoggle[0] == 1) {
  151.                         //Reset external parameters
  152.                         PID_errorSum = 0;
  153.                         timer_old = millis();
  154.                         setMotorSpeedM1(0);
  155.                         setMotorSpeedM2(0);
  156.                         digitalWrite(PIN_ENABLE_MOTORS, HIGH);  // Disable motors
  157.                         OSC_MsgRead();
  158.                 }
  159.         }
  160. }
  161. void loop() {
  162.         OSC_MsgRead();
  163.         if (OSCnewMessage) {
  164.                 OSCnewMessage = 0;
  165.                 processOSCMsg();
  166.         }
  167.         timer_value = micros();
  168.         if (MPU6050_newData()) {
  169.                 MPU6050_read_3axis();
  170.                 loop_counter++;
  171.                 slow_loop_counter++;
  172.                 dt = (timer_value - timer_old) * 0.000001; // dt in seconds
  173.                 timer_old = timer_value;
  174.                 angle_adjusted_Old = angle_adjusted;
  175.                 // Get new orientation angle from IMU (MPU6050)
  176.                 float MPU_sensor_angle = MPU6050_getAngle(dt);
  177.                 angle_adjusted = MPU_sensor_angle + angle_offset;
  178.                 if ((MPU_sensor_angle > -15) && (MPU_sensor_angle < 15))
  179.                         angle_adjusted_filtered = angle_adjusted_filtered * 0.99
  180.                                         + MPU_sensor_angle * 0.01;
  181. #if DEBUG==1
  182.                 Serial.print(dt);
  183.                 Serial.print(" ");
  184.                 Serial.print(angle_offset);
  185.                 Serial.print(" ");
  186.                 Serial.print(angle_adjusted);
  187.                 Serial.print(",");
  188.                 Serial.println(angle_adjusted_filtered);
  189. #endif
  190.                 //Serial.print("\t");
  191.                 // We calculate the estimated robot speed:
  192.                 // Estimated_Speed = angular_velocity_of_stepper_motors(combined) - angular_velocity_of_robot(angle measured by IMU)
  193.                 actual_robot_speed = (speed_M1 + speed_M2) / 2; // Positive: forward
  194.                 int16_t angular_velocity = (angle_adjusted - angle_adjusted_Old) * 25.0; // 25 is an empirical extracted factor to adjust for real units
  195.                 int16_t estimated_speed = -actual_robot_speed + angular_velocity;
  196.                 estimated_speed_filtered = estimated_speed_filtered * 0.9
  197.                                 + (float) estimated_speed * 0.1; // low pass filter on estimated speed
  198. #if DEBUG==2
  199.                                 Serial.print(angle_adjusted);
  200.                                 Serial.print(" ");
  201.                                 Serial.println(estimated_speed_filtered);
  202. #endif
  203.                 if (positionControlMode) {
  204.                         // POSITION CONTROL. INPUT: Target steps for each motor. Output: motors speed
  205.                         motor1_control = positionPDControl(steps1, target_steps1,
  206.                                         Kp_position, Kd_position, speed_M1);
  207.                         motor2_control = positionPDControl(steps2, target_steps2,
  208.                                         Kp_position, Kd_position, speed_M2);
  209.                         // Convert from motor position control to throttle / steering commands
  210.                         throttle = (motor1_control + motor2_control) / 2;
  211.                         throttle = constrain(throttle, -190, 190);
  212.                         steering = motor2_control - motor1_control;
  213.                         steering = constrain(steering, -50, 50);
  214.                 }
  215.                 // ROBOT SPEED CONTROL: This is a PI controller.
  216.                 //    input:user throttle(robot speed), variable: estimated robot speed, output: target robot angle to get the desired speed
  217.                 target_angle = speedPIControl(dt, estimated_speed_filtered, throttle,
  218.                                 Kp_thr, Ki_thr);
  219.                 target_angle = constrain(target_angle, -max_target_angle,
  220.                                 max_target_angle); // limited output
  221. #if DEBUG==3
  222.                 Serial.print(angle_adjusted);
  223.                 Serial.print(" ");
  224.                 Serial.print(estimated_speed_filtered);
  225.                 Serial.print(" ");
  226.                 Serial.println(target_angle);
  227. #endif
  228.                 // Stability control (100Hz loop): This is a PD controller.
  229.                 //    input: robot target angle(from SPEED CONTROL), variable: robot angle, output: Motor speed
  230.                 //    We integrate the output (sumatory), so the output is really the motor acceleration, not motor speed.
  231.                 control_output += stabilityPDControl(dt, angle_adjusted, target_angle,
  232.                                 Kp, Kd);
  233.                 control_output = constrain(control_output, -MAX_CONTROL_OUTPUT,
  234.                                 MAX_CONTROL_OUTPUT); // Limit max output from control
  235.                 // The steering part from the user is injected directly to the output
  236.                 motor1 = control_output + steering;
  237.                 motor2 = control_output - steering;
  238.                 // Limit max speed (control output)
  239.                 motor1 = constrain(motor1, -MAX_CONTROL_OUTPUT, MAX_CONTROL_OUTPUT);
  240.                 motor2 = constrain(motor2, -MAX_CONTROL_OUTPUT, MAX_CONTROL_OUTPUT);
  241.                 int angle_ready;
  242.                 if (OSCpush[0])     // If we press the SERVO button we start to move
  243.                         angle_ready = 82;
  244.                 else
  245.                         angle_ready = 74;  // Default angle
  246.                 if ((angle_adjusted < angle_ready) && (angle_adjusted > -angle_ready)) // Is robot ready (upright?)
  247.                                 {
  248.                         // NORMAL MODE
  249.                         digitalWrite(PIN_ENABLE_MOTORS, LOW);  // Motors enable
  250.                         // NOW we send the commands to the motors
  251.                         setMotorSpeedM1(motor1);
  252.                         setMotorSpeedM2(motor2);
  253.                 } else   // Robot not ready (flat), angle > angle_ready => ROBOT OFF
  254.                 {
  255.                         digitalWrite(PIN_ENABLE_MOTORS, HIGH);  // Disable motors
  256.                         setMotorSpeedM1(0);
  257.                         setMotorSpeedM2(0);
  258.                         PID_errorSum = 0;  // Reset PID I term
  259.                         Kp = KP_RAISEUP;   // CONTROL GAINS FOR RAISE UP
  260.                         Kd = KD_RAISEUP;
  261.                         Kp_thr = KP_THROTTLE_RAISEUP;
  262.                         Ki_thr = KI_THROTTLE_RAISEUP;
  263.                         // RESET steps
  264.                         steps1 = 0;
  265.                         steps2 = 0;
  266.                         positionControlMode = false;
  267.                         OSCmove_mode = false;
  268.                         throttle = 0;
  269.                         steering = 0;
  270.                 }
  271.                 // Push1 Move servo arm
  272.                 if (OSCpush[0])  // Move arm
  273.                 {
  274.                         if (angle_adjusted > -40)
  275.                                 ledcWrite(6, SERVO_MIN_PULSEWIDTH);
  276.                         else
  277.                                 ledcWrite(6, SERVO_MAX_PULSEWIDTH);
  278.                 } else
  279.                         ledcWrite(6, SERVO_AUX_NEUTRO);
  280.                 // Servo2
  281.                 //ledcWrite(6, SERVO2_NEUTRO + (OSCfader[2] - 0.5) * SERVO2_RANGE);
  282.                 // Normal condition?
  283.                 if ((angle_adjusted < 56) && (angle_adjusted > -56)) {
  284.                         Kp = Kp_user;            // Default user control gains
  285.                         Kd = Kd_user;
  286.                         Kp_thr = Kp_thr_user;
  287.                         Ki_thr = Ki_thr_user;
  288.                 } else // We are in the raise up procedure => we use special control parameters
  289.                 {
  290.                         Kp = KP_RAISEUP;         // CONTROL GAINS FOR RAISE UP
  291.                         Kd = KD_RAISEUP;
  292.                         Kp_thr = KP_THROTTLE_RAISEUP;
  293.                         Ki_thr = KI_THROTTLE_RAISEUP;
  294.                 }
  295.         } // End of new IMU data
  296.         // Medium loop 7.5Hz
  297.         if (loop_counter >= 15) {
  298.                 loop_counter = 0;
  299.                 // Telemetry here?
  300. #if TELEMETRY_ANGLE==1
  301.                 char auxS[25];
  302.                 int ang_out = constrain(int(angle_adjusted * 10), -900, 900);
  303.                 sprintf(auxS, "$tA,%+04d", ang_out);
  304.                 OSC_MsgSend(auxS, 25);
  305. #endif
  306. #if TELEMETRY_DEBUG==1
  307.                 char auxS[50];
  308.                 sprintf(auxS, "$tD,%d,%d,%ld", int(angle_adjusted * 10), int(estimated_speed_filtered), steps1);
  309.                 OSC_MsgSend(auxS, 50);
  310. #endif
  311.         } // End of medium loop
  312.         else if (slow_loop_counter >= 100) // 1Hz
  313.                         {
  314.                 slow_loop_counter = 0;
  315.                 // Read  status
  316. #if TELEMETRY_BATTERY==1
  317.                 BatteryValue = (BatteryValue + BROBOT_readBattery(false)) / 2;
  318.                 sendBattery_counter++;
  319.                 if (sendBattery_counter >= 3) { //Every 3 seconds we send a message
  320.                         sendBattery_counter = 0;
  321.                         Serial.print("B");
  322.                         Serial.println(BatteryValue);
  323.                         char auxS[25];
  324.                         sprintf(auxS, "$tB,%04d", BatteryValue);
  325.                         OSC_MsgSend(auxS, 25);
  326.                 }
  327. #endif
  328.         }  // End of slow loop
  329. }
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 昨天 20:17

【Arduino 动手做】B-Robot ESP32 版本 Unit 3 自平衡机器人

回复

使用道具 举报

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

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail