Trying to move two servos with mpu6050 imu

Hello, I am working on a tvc rocket mount prototype and have been stuck on this problem for some time. I am new to using arduino ide, as well as creating circuits in general. The problem that I am experiencing is mainly with how the servos interact. When one servo reaches near its maximum angle, it tends to stray off in the opposite direction (causes the other servo to move), or both of them begin to spasm. I am using a 6V battery via 4 AAs, which is connected to a pca9685 board that is used to control the two mg90 servos. Everything is wired together on a breadboard for prototyping purposes, connected to a raspberry pi pico 2. If there is an issue with my code, I have linked it down below.

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <IMU_Fusion_SYC.h>
#include <iostream>
#include <vector>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
IMU imu(Wire);

#define SERVOMIN 150
#define SERVOMAX 455

std::vector<int> servos = {0, 1};

void set_default() {
  for (int i : servos) {
    pwm.setPWM(i, 0, map(90, 0, 180, SERVOMIN, SERVOMAX));
  }
}

void setup() {
  // Serial initiation
  Serial.begin(9600);

  // Wire initiation
  Wire.begin();

  // MPU6050 initiation
  imu.begin();
  imu.MPU6050_CalcGyroOffsets();

  // Analog initiation
  // analogReadResolution(12);

  // Servo initiation
  pwm.begin();
  pwm.setPWMFreq(60);
  set_default();
  delay(1000);

  // Buzzer initiation
  pinMode(0, OUTPUT);

  tone(0, 622.25, 500);  //e
  delay(333);
  tone(0, 932.33, 500);  //b

  // LED initiation
  pinMode(3, OUTPUT);

  // Switch initiation
  pinMode(2, INPUT);

  delay(1000);
}

// Global functions
void blink() {
  digitalWrite(1, HIGH);
  tone(0, 500, 500);
  delay(1000);
  digitalWrite(1, LOW);
  delay(1000);
}

// Global variables
uint32_t switch_state;
uint32_t touch_state;
uint32_t previous_x;
uint32_t previous_y;

void loop() {
  // LED Check
  switch_state = digitalRead(2);

  if (switch_state == HIGH) {
    digitalWrite(3, LOW);

    // Local Variables
    uint32_t current_x = ceil(imu.getAngleX() + 90);
    uint32_t current_y = ceil(imu.getAngleY() + 90);

    // MPU6050 Check
    imu.Calculate();

    if (ceil(imu.getAngleX() + 90) >= 0 && ceil(imu.getAngleX() + 90) <= 180 && current_x != previous_x) {
      Serial.print("X-Angle: ");
      Serial.println(imu.getAngleX() + 90);
      pwm.setPWM(0, 0, map(current_x, 0, 180, SERVOMIN, SERVOMAX));
      previous_x = current_x;
    }

    if (ceil(imu.getAngleY() + 90) >= 0 && ceil(imu.getAngleY() + 90) <= 180 && current_y != previous_y) {
      Serial.print("Y-Angle: ");
      Serial.println(imu.getAngleY() + 90);
      pwm.setPWM(1, 0, map(current_y, 0, 180, SERVOMIN, SERVOMAX));
      previous_y = current_y;
    }
    
    delay(50);
  } else {
    digitalWrite(3, HIGH);
  }
}

void setup1() {
  //LED initiation
  pinMode(1, OUTPUT);

  delay(2000);
}

void loop1() {
  if (switch_state == HIGH) {
    blink();
  } else {
    digitalWrite(1, LOW);
  }
}

Generally, do not use Arduino Uno/Nano pins 0 or 1. They are usually reserved for Serial communications.

This sounds like a power issue. Not enough power. Torque/stress on the servos. Are the servos 9v or is a buck converter used? Try running each servo alone to see if they perform their task as expected.

Breadboards might not be good for high-current applications. Maybe keep the servo power wiring point-to-point and skip the breadboard?

From Adafruit:

Use caution when adjusting SERVOMIN and SERVOMAX. Hitting the physical limits of travel can strip the gears and permanently damage your servo.

Please post schematics. Your words are not clear enough.

I await hearing what board you're using that has enough capacity to handle vector and iostream. I'm betting it's not going to be a 328P based one.

And I have no idea what that means. 4AAs would be 6V.

A schematic is the lingua franca of electronics. Words do not suffice; see immediately above for an example of why.

I added a diagram of the circuit. My bad for saying 9V, I meant to say 6V.

I have now added a diagram, which will hopefully be sufficient.

Your diagram (which is not a schematic, sorry to say) shows you powering your servos from the 5V line. And that green box labelled "pca9685 breakout" matches no pinout I'm familiar with for a PCA9685 breakout. Furthermore, it has no power going to it.

And are we to assume that you're using some sort of Pico for your board? Which one? That kind of basic information is the kind of thing you should be saying up front.

It's a wokwi "custom chip"

Unfortunately, so does Adafruit...

i see the Pi's 3.3V line going to Vcc on that PCA9685 breakout, not V+. V+ is being provided from an external 5V supply. Which is a different situation than... oh heck, that Fritzing style diagram the OP provided doesn't have anything powering the 5V line, does it? I thought it was hooked up to the 5V line on the Pico, but it's not. It's not hooked up to anything that's providing power. Well that's no help then, is it? :frowning:

The internet is full of similar drawings... all with 5v powering the PCA9685. Perhaps their meaning is for the PCA9685 to get 5v, but every servo is to be supplied by its/their own power supply (4.8vdc to 6vdc).

I updated the diagram to hopefully resolve any confusion with the pca9685 board.

Hi, @drdreadful0
Welcome to the forum.

Please do not update old posts.
If changes are made, do it in a new post.
Updating old posts makes the threat of your topic hard to follow.

Do you have a DMM? Digital MultiMeter?

Thanks.. Tom.... :smiley: :+1: :coffee: :australia:

Here is my test code. LED on servo0 pins and a servo on servo1 pins. Works with 5v and 3.3v processors


#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

#define servo_min 220
#define servo_max 520

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
uint8_t servonum = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("16 channel Servo test!");
  pwm.begin();
  pwm.setPWMFreq(50);  // Analog servos run at ~50 Hz updates
}

void loop() {

  servonum = 0;
  
  Serial.println(servonum);

  for (uint16_t pulselen = 0; pulselen < 4095; pulselen++) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);

  for (uint16_t pulselen = 4095; pulselen > 0; pulselen--) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);

  servonum = 1;

  Serial.println(servonum);  
  for (uint16_t pulselen = servo_min; pulselen < servo_max; pulselen++) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);

  for (uint16_t pulselen = servo_max; pulselen > servo_min; pulselen--) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);

//  servonum++;
//  if (servonum > 15) servonum = 0;
}

Thank you for your response, but this unfortunately did not fix my problem.

If you are moving only two servos, why not send the PWM directly from the microcontroller, just like you are directly controlling the two LEDs?

Here is a basic Arduino simulation with an MPU6050 (Adafruit library) controlling two servos (Margolis library).

#include <Adafruit_MPU6050.h>  // https://github.com/adafruit/Adafruit_MPU6050
#include <Adafruit_Sensor.h>   // https://github.com/adafruit/Adafruit_Sensor
Adafruit_MPU6050 mpu;          // create MPU object

#include <Servo.h>  // https://github.com/arduino-libraries/Servo
Servo xservo;  // create Servo object
Servo yservo;
byte xservopin = 5;  // servo PWM pin
byte yservopin = 2;

void setup(void) {
  xservo.attach(xservopin);  // attach servo object to pin
  yservo.attach(yservopin);

  mpu.begin();
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);  // 2, 4, 8, 16
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);       // 250, 500, 1000, 2000
  mpu.setFilterBandwidth(MPU6050_BAND_5_HZ);     // 260, 184, 94, 44, 21, 10, 5
}

void loop() {
  sensors_event_t a, g, temp;   // declare variables
  mpu.getEvent(&a, &g, &temp);  // retreive data from MPU

  float x = a.acceleration.x;
  float y = a.acceleration.y;

  xservo.write(map(x, -20, 20, 0, 180));  // map acceleration range to servo range
  yservo.write(map(y, -20, 20, 0, 180));

  delay(100);
}
diagram.json for wokwi
{
  "version": 1,
  "author": "xfpd",
  "editor": "wokwi",
  "parts": [
    {
      "type": "wokwi-arduino-nano",
      "id": "nano",
      "top": -93,
      "left": 20.7,
      "rotate": 90,
      "attrs": {}
    },
    {
      "type": "wokwi-mpu6050",
      "id": "imu1",
      "top": -208.82,
      "left": 21.28,
      "rotate": 180,
      "attrs": {}
    },
    {
      "type": "wokwi-servo",
      "id": "servo1",
      "top": -221.8,
      "left": 97.8,
      "rotate": 270,
      "attrs": {}
    },
    { "type": "wokwi-servo", "id": "servo2", "top": -98, "left": 192, "attrs": {} },
    { "type": "wokwi-vcc", "id": "vcc1", "top": -124.04, "left": 144, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd1", "top": -9.6, "left": 162.6, "attrs": {} },
    {
      "type": "wokwi-text",
      "id": "text1",
      "top": -86.4,
      "left": 211.2,
      "attrs": { "text": "Accel Y" }
    },
    {
      "type": "wokwi-text",
      "id": "text2",
      "top": -153.6,
      "left": 211.2,
      "attrs": { "text": "Accel X" }
    }
  ],
  "connections": [
    [ "vcc1:VCC", "servo1:V+", "red", [ "v28.8", "h28.7" ] ],
    [ "vcc1:VCC", "servo2:V+", "red", [ "v0" ] ],
    [ "servo1:PWM", "nano:5", "green", [ "v0" ] ],
    [ "servo2:PWM", "nano:2", "green", [ "h0" ] ],
    [ "nano:GND.2", "gnd1:GND", "black", [ "h0" ] ],
    [ "servo1:GND", "gnd1:GND", "black", [ "v0" ] ],
    [ "servo2:GND", "gnd1:GND", "black", [ "h0" ] ],
    [ "imu1:VCC", "nano:5V", "red", [ "v0" ] ],
    [ "imu1:GND", "nano:GND.1", "black", [ "v0" ] ],
    [ "imu1:SCL", "nano:A5", "gold", [ "v0" ] ],
    [ "imu1:SDA", "nano:A4", "blue", [ "v0" ] ]
  ],
  "dependencies": {}
}

You will need to do data filtering and averaging to make it steady.

You need to use PID control for that.
https://www.digikey.com/en/maker/projects/introduction-to-pid-controllers/763a6dca352b4f2ba00adde46445ddeb

Did you run the i2c_scanner sketch to see if the PCA9685 is responding? Mine shows at 0x40.

#include <Wire.h>

void setup() {
  Wire.begin();

  Serial.begin(9600);
  Serial.println("\nI2C Scanner");
}

void loop() {
  int nDevices = 0;

  Serial.println("Scanning...");

  for (byte address = 1; address < 127; ++address) {
    // The i2c_scanner uses the return value of
    // the Wire.endTransmission to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    byte error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.print(address, HEX);
      Serial.println("  !");

      ++nDevices;
    } else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  } else {
    Serial.println("done\n");
  }
  delay(5000); // Wait 5 seconds for next scan
}

The pca9685 is running, hence the servos being able to move. However, when both servos reach towards their maximum angle, the other begins to stray off.

Thank you, I will see if this hopefully helps.

If one begins to "stray off", then I presume you are exceeding the minimum or maximum pulse width.

I use a 50Hz pulse repetition rate and keep the pulse between 1ms and 2ms for servos. I use other channels to control lighting brightness.

I've used up to 8 servos on my PCA9685 with no problem.