项目概况
该项目涉及一个微控制器、一个电机驱动器、两个电机、一个 OLED 显示器、一个 MPU-9250/6500 传感器和一个 3.7V 锂离子电池。以下是其工作原理和所用组件的简要说明。
组件:
微控制器:项目的大脑,控制所有其他组件。
电机驱动器 (DRV8833):管理提供给电机的电源,允许它们向前或向后运行。
电机:为项目提供运动,例如机器人的轮子。
OLED 显示屏:显示传感器读数或状态消息等信息。
MPU-9250/6500 传感器:测量方向和运动,可用于导航或稳定。
3.7V锂离子电池:为整个电路供电。
怎么运行的:
电源:3.7V 锂离子电池为微控制器和其他组件供电。
控制信号:微控制器向电机驱动器发送控制信号,然后电机驱动器调整电机的功率,控制电机的速度和方向。
传感器数据:MPU-9250/6500 传感器收集有关方向和运动的数据,并将其发送到微控制器。
显示信息:单片机处理传感器数据并在OLED屏幕上显示相关信息。
此设置非常适合小型机器人或自动化系统等需要控制电机和显示信息的项目。









项目代码
#include <Wire.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include "DataParser.h"
#include "Cdrv8833.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "Compass.h"
#include "HeadingController.h"
#include "MPU9250.h"
MPU9250 mpu;
HeadingController headingController;
// OLED display size
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // Reset pin not used, can be set to -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Initialize Compass
Compass compass(&display);
#define WIFI_SSID "ground"
#define WIFI_PASSWORD "12345678"
#define UDP_PORT 12345
// Motor Pins
#define IN1_PIN 4
#define IN2_PIN 3
#define RCHANNEL 0
#define IN3_PIN 0
#define IN4_PIN 2
#define LCHANNEL 1
DataParser dataParser; // Create an instance of DataParser
int Speed = 20;
int Right_speed = 0;
int Left_speed = 0;
String movement;
WiFiUDP udp;
Cdrv8833 LMotor;
Cdrv8833 RMotor;
float initialHeading = 0; // Initial heading when heading keeping is enabled
bool headingKeepingEnabled = false;
float globalYaw = 1.0;
float globalPitch = 1.0;
float globalRoll = 1.0;
int display_data = 0;
void setup() {
Serial.begin(115200);
// Initialize OLED display
Wire.begin(6, 7); // SDA = 6, SCL = 7
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
//while (true); // Stop execution if OLED init fails
}
if (!mpu.setup(0x68)) { // change to your own address
// while (1) {
Serial.println("MPU connection failed. Please check your connection with `connection_check` example.");
delay(5000);
// }
}
delay(100);
// Initialize Compass
mpu.update();
//delay(100);
compass.init((mpu.getYaw()* (PI / 180.0))); // Set the initial yaw as "front"
// Display startup message
display.clearDisplay();
display.setTextSize(1); // Normal 1x scale
display.setTextColor(SSD1306_WHITE);
display.println("Starting...");
display.println("WiFi Connecting...");
display.display();
// Initialize Motors
RMotor.init(IN1_PIN, IN2_PIN, LCHANNEL, false);
LMotor.init(IN3_PIN, IN4_PIN, RCHANNEL, false);
// Initialize HeadingController with default PID values
headingController.setPID(1, 0, 0.1);
// Default PID values, can be overridden by user
// Connect to WiFi
connectToWiFi();
// Start UDP
udp.begin(UDP_PORT);
Serial.println("UDP Listening on port ");
delay(5000);
calibrate_imu();
}
void loop() {
if (mpu.update()) {
mpu.update();
globalYaw = mpu.getYaw(); // Current yaw angle
globalRoll = mpu.getPitch();
globalPitch = mpu.getRoll();
// Display based on user input
if (display_data == 1) {
compass.drawCompass(-mpu.getYaw() * (PI / 180.0));
} else if (display_data == 2) {
compass.drawCube(mpu.getPitch() * (PI / 180.0),
mpu.getYaw() * (PI / 180.0),
mpu.getRoll() * (PI / 180.0));
} else {
displayIMUValues();
}
}
// Maintain heading while moving or stationary
if (movement == "f" || movement == "b") {
// If moving, maintain heading within ±2 degrees
keepHeading(globalYaw, true); // `true` indicates moving
} else if (movement == "s") {
// If stopped, maintain heading
keepHeading(globalYaw, false); // `false` indicates stationary
} else {
headingController.reset(); // Reset PID when turning or idle
headingController.setTargetHeading(globalYaw); // Update heading
}
udpReceiveTask();
}
void connectToWiFi() {
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
WiFi.setTxPower(WIFI_POWER_8_5dBm); // Needed for ESP32-C3
Serial.print("Connecting to WiFi");
// Update OLED during WiFi connection attempts
display.clearDisplay();
display.setCursor(0, 0);
display.println("WiFi Connecting...");
display.display();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
display.print(".");
display.display();
}
// Update OLED upon successful connection
Serial.println("\nConnected to WiFi!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
display.clearDisplay();
display.setCursor(0, 0);
display.println("WiFi Connected!");
display.println("SSID: " + String(WIFI_SSID));
display.print("IP: ");
display.println(WiFi.localIP());
display.print("Port: ");
display.println(UDP_PORT);
display.display();
}
void udpReceiveTask() {
char packetBuffer[255];
//Serial.println("udp");
int packetSize = udp.parsePacket();
if (packetSize) {
int len = udp.read(packetBuffer, 255);
if (len > 0) {
packetBuffer[len] = 0; // Null-terminate the string
Serial.println("Received UDP packet:");
Serial.println(packetBuffer);
String IncomingData = packetBuffer;
// Parse data using DataParser
dataParser.parseData(IncomingData, ','); // Pass data and delimiter
Speed = (dataParser.getField(1)).toInt();
// Update PID gains from user input
float kp = (dataParser.getField(2)).toFloat();
float ki = (dataParser.getField(3)).toFloat();
float kd = (dataParser.getField(4)).toFloat();
display_data = (dataParser.getField(5)).toInt();
headingController.setPID(kp, ki, kd);
Left_speed = Speed;
Right_speed = Speed;
movement = (dataParser.getField(0));
// Execute movement based on the parsed data
if (movement == "f") {
forward();
} else if (movement == "b") {
backward();
} else if (movement == "l") {
left();
} else if (movement == "r") {
right();
} else if (movement == "s") {
Stop();
}
}
}
delay(10); // Small delay to avoid CPU overload
}
void motor_speed(int Right_Speed, int Left_Speed) {
Left_speed = Left_Speed;
Right_speed = Right_Speed;
}
void forward() {
//Serial.print("fwd");
LMotor.move(Left_speed);
RMotor.move(Right_speed);
}
void backward() {
LMotor.move(-Left_speed);
RMotor.move(-Right_speed);
}
void left() {
LMotor.move(Left_speed);
RMotor.move(-Right_speed);
}
void right() {
LMotor.move(-Left_speed);
RMotor.move(Right_speed);
}
void Stop() {
LMotor.move(0);
RMotor.move(0);
}
void keepHeading(float currentHeading, bool isMoving) {
// Compute PID correction
float pidOutput = headingController.computePID(currentHeading);
if (isMoving) {
// Add PID correction to motor speeds to maintain straight path
int leftSpeed = constrain(Speed - pidOutput, -100, 100);
int rightSpeed = constrain(Speed + pidOutput, -100, 100);
LMotor.move(leftSpeed);
RMotor.move(rightSpeed);
} else {
// Adjust motors to maintain heading while stationary
int leftSpeed = constrain(-pidOutput, -100, 100);
int rightSpeed = constrain(pidOutput, -100, 100);
LMotor.move(leftSpeed);
RMotor.move(rightSpeed);
}
}
void displayIMUValues() {
// Get IMU values
mpu.update();
float pitch =mpu.getPitch();
float yaw = mpu.getYaw();
float roll = mpu.getRoll();
// Update OLED with IMU values
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1); // Use 1x scale
display.println("IMU Values:");
display.print("Pitch: ");
display.println(pitch, 1); // Display with 1 decimal place
display.print("Yaw: ");
display.println(yaw, 1);
display.print("Roll: ");
display.println(roll, 1);
display.display();
}
void calibrate_imu()
{
// Update OLED with IMU values
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1); // Use 1x scale
// calibrate anytime you want to
Serial.println("Accel Gyro calibration will start in 5sec.Please leave the device still on the flat plane.");
display.println("Accel Gyro calibration will start in 5sec.Place on the flat plane.");
mpu.verbose(true);
display.display();
delay(5000);
mpu.calibrateAccelGyro();
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1); // Use 1x scale
Serial.println("Mag calibration will start in 5sec.Please Wave device in a figure eight until done.");
display.println("Mag calibration will start in 5sec.Please Wave device in a figure eight until done.");
display.display();
delay(5000);
mpu.calibrateMag();
print_calibration();
mpu.verbose(false);
}
void print_calibration() {
Serial.println("< calibration parameters >");
Serial.println("accel bias [g]: ");
Serial.print(mpu.getAccBiasX() * 1000.f / (float)MPU9250::CALIB_ACCEL_SENSITIVITY);
Serial.print(", ");
Serial.print(mpu.getAccBiasY() * 1000.f / (float)MPU9250::CALIB_ACCEL_SENSITIVITY);
Serial.print(", ");
Serial.print(mpu.getAccBiasZ() * 1000.f / (float)MPU9250::CALIB_ACCEL_SENSITIVITY);
Serial.println();
Serial.println("gyro bias [deg/s]: ");
Serial.print(mpu.getGyroBiasX() / (float)MPU9250::CALIB_GYRO_SENSITIVITY);
Serial.print(", ");
Serial.print(mpu.getGyroBiasY() / (float)MPU9250::CALIB_GYRO_SENSITIVITY);
Serial.print(", ");
Serial.print(mpu.getGyroBiasZ() / (float)MPU9250::CALIB_GYRO_SENSITIVITY);
Serial.println();
Serial.println("mag bias [mG]: ");
Serial.print(mpu.getMagBiasX());
Serial.print(", ");
Serial.print(mpu.getMagBiasY());
Serial.print(", ");
Serial.print(mpu.getMagBiasZ());
Serial.println();
Serial.println("mag scale []: ");
Serial.print(mpu.getMagScaleX());
Serial.print(", ");
Serial.print(mpu.getMagScaleY());
Serial.print(", ");
Serial.print(mpu.getMagScaleZ());
Serial.println();
}
【Arduino 动手做】实现 PID 和传感器融合以进行航向控制的BIT esp32 机器人
项目链接:https://github.com/sastejugaad/Bit
项目作者:舒巴姆·巴特
项目视频:https://www.youtube.com/watch?v=q9708Vejnlg
项目代码:https://github.com/sastejugaad/Bit/blob/main/Tiny_bit_oled_mpu_v3/Tiny_bit_oled_mpu_v3.ino
3D 文件:https://grabcad.com/library/bit-esp32-robot-with-imu-1


评论