Controlling DC servo motor with mpu6050 and M-Duino 21+

Hi guys, i am doing this kind of project that using the mpu6050 to obtain the pitch and raw value, make the motor rotate in degree which will be controlled by PWM pin in M-duino 21+. When i am running my code, the two dc servo motors start shaking and also rotating so i guess that i need to do the calibration and eliminate the drifting for mpu 6050 cause before that i checked with sample sweep code, the motors are working. But i did the calibration for that, it is also not working and i am not sure the calibration is correct one. i will upload my code and M-duino 21+ datasheet (i am sure the connection is correct). i really need some help appreciate guys! Sorry i cant upload code file because i am new user.


#include <Wire.h>

#include <MPU6050.h>

#include <Servo.h>   

Servo servo1;    
//Servo servo2;       

int servo_pin1 = 6;
int servo_pin2 = 4;

MPU6050 sensor ;

int16_t ax, ay, az ;

int16_t gx, gy, gz ;

void setup ( )

{ 

servo1.attach ( servo_pin1 );
//servo2.attach ( servo_pin2 );

Wire.begin ( );

Serial.begin  (9600); 

Serial.println  ( "Initializing the sensor" ); 

sensor.initialize ( ); 

Serial.println (sensor.testConnection ( ) ? "Successfully Connected" : "Connection failed"); 

delay (10); 

Serial.println ( "Taking Values from the sensor" );

//sensor.setXAccelOffset(-3804);
//sensor.setYAccelOffset(3107);
//sensor.setZAccelOffset(1386);
//sensor.setXGyroOffset(194);
//sensor.setYGyroOffset(-57);
//sensor.setZGyroOffset(2);



delay (10);

}




void loop ( ) 

{ 

sensor.getMotion6 (&ax, &ay, &az, &gx, &gy, &gz);

ax = map (ax, -255, 255, 0, 180) ;

Serial.println (ax);

servo1.write (ax); 

gx = map (gx, -255, 255, 0, 180) ;

Serial.println (gx);

servo1.write (gx); 

delay (15);

}

(https://www.mouser.cn/datasheet/2/999/Industrial-Shields-1832086.pdf)(page 26 for pwm)

(http://wired.chillibasket.com/2015/01/calibrating-mpu6050/), i used that for calibration.

What 'ax' and 'gx' values are being displayed? Are they almost always the same? If not, that would explain why switching the servo back and forth between the two values would cause the servo to shake. The servo can't be in two positions.

Try removing servo1.write (ax); to see if the servo can track the 'gx' values.

'ax' is Accelerometer X axis g force, 'gx' is Gyroscope X axis degree per second, i try to comment it, and run it is not working still shaking. I am thinking that calibration is not done, it get two steps once you showed that:

MPU6050 Calibration Sketch

Your MPU6050 should be placed in horizontal position, with package letters facing up. 
Don't touch it until you see a finish message.

MPU6050 connection successful

Reading sensors for first time...

Calculating offsets...
...
...
...
...

FINISHED!

Sensor readings with offsets:	-3	3	16377	0	0	-1
Your offsets:	-4301	3103	1279	193	-55	1

Data is printed as: acelX acelY acelZ giroX giroY giroZ
Check that your sensor readings are close to 0 0 16384 0 0 0
If calibration was succesful write down your offsets so you can set them in your projects using something similar to mpu.setXAccelOffset(youroffset)

and u can run another code to write ur offset, should show yaw, raw, patch all close to 0.

i2cSetup
MPU6050Connect
MPU connection Try #1
DMP Initialization failed (code initial memory load failed)
Enabling DMP...
Enabling interrupt detection (Arduino external interrupt pin 2 on the Uno)...
mpu.getInterruptDrive=  0
Setup complete

i did connect the INT from Gyroscope to interrupt pin 2 and i don't know why is not working.

I just added a sketch to get the offsets for calibration of the mpu6050

The library you will want to use: GitHub - ZHomeSlice/Simple_MPU6050: Going Live

/* ============================================
  I2Cdev device library code is placed under the MIT license
  Copyright (c) 2021 Homer Creutz

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ===============================================
*/

/*
    Use with any MPU: MPU6050, MPU6500, MPU9150, MPU9155, MPU9250
    Attach the MPU to the I2C buss
    Power MPU According to specs of the breakout board. Generic Breakout Version Powers with 5V and has a onboard Voltage regulator.
    run the Sketch
*/

#include "Simple_MPU6050.h"
#define MPU6050_DEFAULT_ADDRESS     0x68 // address pin low (GND), default for InvenSense evaluation board

Simple_MPU6050 mpu;

//***************************************************************************************
//******************                Setup and Loop                 **********************
//***************************************************************************************

void setup() {
  uint8_t val;
  // join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#ifdef __AVR__
  Wire.setWireTimeout(3000, true); //timeout value in uSec
#endif
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
#endif
  // initialize serial communication
  Serial.begin(115200);
  while (!Serial); // wait for Leonardo enumeration, others continue immediately
  Serial.println(F("Start:"));

  // Setup the MPU
  mpu.SetAddress(MPU6050_DEFAULT_ADDRESS); //Sets the address of the MPU.
  Serial.println(F("\n\n The MPU6050 comes with no calibration values set.\n"
                   " This is what the default calibratin values are:\n"
                   " "));
  mpu.PrintActiveOffsets();
   
  Serial.println(F("\n\n We are going to calibrate this specific MPU6050,\n"
                   " Start by having the MPU6050 placed stationary on a flat surface to get a proper accellerometer calibration\n"
                   " \t\t\t[Press Any Key]"));
  while (Serial.available() && Serial.read()); // empty buffer
  while (!Serial.available());                 // wait for data
  while (Serial.available() && Serial.read()); // empty buffer again
  mpu.CalibrateMPU();  // Calibrates the Gyro.
  delay(1000);
  Serial.println();
  for (int i = 5; i<=30;i+=5){
    Serial.println("Stabalization Delay");
    delay (100);
    mpu.CalibrateAccel(i); // Calibrates the Accellerometer.
    mpu.CalibrateGyro(i);  // Calibrates the Gyro.
  }
  mpu.PrintActiveOffsets();
  // Setup is complete!

}

void loop() {
  delay(100);
  Serial.println(F("\n\nThe above values are your calibration offsets.\n"
                   " You can recheck the calibration using the last calibration as a starting poing for this specific MPU6050,\n"
                   " This can further inprove the accuracy and may refine the calibration values\n"
                   " Placed the MPU6050 stationary on a flat surface to get a proper accellerometer calibration\n"
                   " \t\t\t[Press Any Key]"));
  while (Serial.available() && Serial.read()); // empty buffer
  while (!Serial.available());                 // wait for data
  while (Serial.available() && Serial.read()); // empty buffer again
  mpu.CalibrateMPU();  // Calibrates the MPU.
  mpu.PrintActiveOffsets();
}

This should help a lot.

thanks, the calibration should be fine, how i can map the Gyroscope move 1 degree while motor move 1 degree:


sensor.getMotion6 (&ax, &ay, &az, &gx, &gy, &gz);

gx = map (gx, -180, 180, 0, 180) ;

Serial.println (gx);

servo1.write (gx);

i think that one is not correct.

have you used the MPU6050 DMP output
Example:
using my https://github.com/ZHomeSlice/Simple_MPU6050 library

here's the example that returns degrees from the mpu6050
I'm able to help you with this library as I know it quite well. will this work for you?

/* ============================================
  I2Cdev device library code is placed under the MIT license
  Copyright (c) 2021 Homer Creutz

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
  ===============================================
*/

/*
 *  Use with any MPU: MPU6050, MPU6500, MPU9150, MPU9155, MPU9250 
 *  Attach the MPU to the I2C buss
 *  Power MPU According to specs of the breakout board. Generic Breakout Version Powers with 5V and has a onboard Voltage regulator.
 *  run the Sketch
 */

#include "Simple_MPU6050.h"
#define MPU6050_DEFAULT_ADDRESS     0x68 // address pin low (GND), default for InvenSense evaluation board

Simple_MPU6050 mpu;

//***************************************************************************************
//******************                Print Funcitons                **********************
//***************************************************************************************
//Gyro, Accel and Quaternion
int PrintAllValues(int16_t *gyro, int16_t *accel, int32_t *quat, uint16_t SpamDelay = 100) {
  Quaternion q;
  VectorFloat gravity;
  float ypr[3] = { 0, 0, 0 };
  float xyz[3] = { 0, 0, 0 };
  mpu.GetQuaternion(&q, quat);
  mpu.GetGravity(&gravity, &q);
  mpu.GetYawPitchRoll(ypr, &q, &gravity);
  mpu.ConvertToDegrees(ypr, xyz);
  Serial.print(F("Yaw "));   Serial.print(xyz[0]);   Serial.print(F(",   "));
  Serial.print(F("Pitch ")); Serial.print(xyz[1]);   Serial.print(F(",   "));
  Serial.print(F("Roll "));  Serial.print(xyz[2]);   Serial.print(F(",   "));
  Serial.print(F("ax "));    Serial.print(accel[0]); Serial.print(F(",   "));
  Serial.print(F("ay "));    Serial.print(accel[1]); Serial.print(F(",   "));
  Serial.print(F("az "));    Serial.print(accel[2]); Serial.print(F(",   "));
  Serial.print(F("gx "));    Serial.print(gyro[0]);  Serial.print(F(",   "));
  Serial.print(F("gy "));    Serial.print(gyro[1]);  Serial.print(F(",   "));
  Serial.print(F("gz "));    Serial.print(gyro[2]);  Serial.print(F("\n"));
  Serial.println();
}

//***************************************************************************************
//******************              Callback Funciton                **********************
//***************************************************************************************

// See mpu.on_FIFO(print_Values); in the Setup Loop
void print_Values (int16_t *gyro, int16_t *accel, int32_t *quat, uint32_t *timestamp) {
  uint8_t Spam_Delay = 100; // Built in Blink without delay timer preventing Serial.print SPAM
  PrintAllValues(gyro, accel, quat, Spam_Delay);
}

//***************************************************************************************
//******************                Setup and Loop                 **********************
//***************************************************************************************

void setup() {
  uint8_t val;
  // join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  Wire.setClock(400000); // 400kHz I2C clock. Comment this line if having compilation difficulties
#ifdef __AVR__  
  Wire.setWireTimeout(3000, true); //timeout value in uSec
#endif
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
#endif
  // initialize serial communication
  Serial.begin(115200);
  while (!Serial); // wait for Leonardo enumeration, others continue immediately
  Serial.println(F("Start:"));

  // Setup the MPU
    mpu.Set_DMP_Output_Rate_Hz(10);           // Set the DMP output rate from 200Hz to 5 Minutes.
  //mpu.Set_DMP_Output_Rate_Seconds(10);   // Set the DMP output rate in Seconds
  //mpu.Set_DMP_Output_Rate_Minutes(5);    // Set the DMP output rate in Minute
  mpu.SetAddress(MPU6050_DEFAULT_ADDRESS); //Sets the address of the MPU.
  mpu.CalibrateMPU();                      // Calibrates the MPU.
  mpu.load_DMP_Image();                    // Loads the DMP image into the MPU and finish configuration.
  mpu.on_FIFO(print_Values);               // Set callback function that is triggered when FIFO Data is retrieved
  // Setup is complete!
  
}

void loop() {
  static unsigned long FIFO_DelayTimer;
  if ((millis() - FIFO_DelayTimer) >= (99)) { // 99ms insted of 100ms to start polling the MPU 1ms prior to data arriving.
    if( mpu.dmp_read_fifo(false)) FIFO_DelayTimer= millis() ; // false = no interrupt pin attachment required and When data arrives in the FIFO Buffer reset the timer
  }
  // dmp_read_fifo(false) does the following
  // Tests for Data in the FIFO Buffer
  // when it finds data it runs the mpu.on_FIFO(print_Values)
  // the print_Values functin which we set run the PrintAllValues Function
  // When data is caputred dmp_read_fifo will return true.
  // The print_Values function MUST have the following variables available to attach data
  // void print_Values (int16_t *gyro, int16_t *accel, int32_t *quat, uint32_t *timestamp)
  // Variables:
  // int16_t *gyro for the gyro values to be passed to it (The * tells the function it will be a pointer to the value)
  // int16_t *accel for the accel values to be passed to it
  // int32_t *quat for the quaternion values to be passed to it
  // uint32_t *timestamp which will be the micros()value at the time we retrieved the Newest value from the FIFO Buffer.
}

Z

thanks zhomeslice! i will try it tomorrow and let u know that.

1 Like

Sorry it is not working i did use another way but motor is not working but can read value( by the way i use the M-durino 21+.

#include <Wire.h>
#include <Servo.h>

Servo servoRightLeft;
Servo servoUpDown;
int Yvalue,Xvalue,ServoRightLeftValue,ServoUpDownValue;

long   acceLX ,acceLY ,acceLZ ;
float  gFroceX,gFroceY,gFroceZ;

long   gyroX,gyroY  ,gyroZ;
float  rotX ,rotY   ,rotZ ;

void setup() {
  // put your setup code here, to run once:
   Serial.begin(9600);
   Wire.begin();
   servoRightLeft.attach(6);
   //servoUpDown.attach(4);
   SetupMPU();
}

void loop() {
  // put your main code here, to run repeatedly:
  RecordAccelRegisters();
  RecordGyroRegisters();
  PrintData();
  

}
void SetupMPU()
{
  Wire.beginTransmission(0b1101000); //This is the I2C address of the MPU (b1101000/b1101001 for AC0 low/high datasheet sec. 9.2)
  Wire.write(0x6B); //Accessing the register 6B - Power Management (Sec. 4.28)
  Wire.write(0b00000000); //Setting SLEEP register to 0. (Required; see Note on p. 9)
  Wire.endTransmission();  
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x1B); //Accessing the register 1B - Gyroscope Configuration (Sec. 4.4) 
  Wire.write(0x00000000); //Setting the gyro to full scale +/- 250deg./s 
  Wire.endTransmission(); 
  Wire.beginTransmission(0b1101000); //I2C address of the MPU
  Wire.write(0x1C); //Accessing the register 1C - Acccelerometer Configuration (Sec. 4.5) 
  Wire.write(0b00000000); //Setting the accel to +/- 2g
  Wire.endTransmission();
}
void RecordAccelRegisters()
{
  Wire.beginTransmission(0b1101000);
  Wire.write(0x3B); //Starting register for Accel Readings
  Wire.endTransmission();
  Wire.requestFrom(0b1101000,6); //Request Accel Registers (3B - 40)
 while(Wire.available() < 6);
  acceLX = Wire.read()<<8|Wire.read(); //Store first two bytes into accelX
  acceLY = Wire.read()<<8|Wire.read(); //Store middle two bytes into accelY
  acceLZ = Wire.read()<<8|Wire.read(); //Store last two bytes into accelZ
  ProccessAccelData();
  
}
void ProccessAccelData()
{
   gFroceX=acceLX/16384.0;
   gFroceY=acceLY/16384.0;
   gFroceZ=acceLZ/16384.0;
}
void RecordGyroRegisters()
{
  Wire.beginTransmission(0b1101000);
  Wire.write(0x43);
  Wire.endTransmission();
  Wire.requestFrom(0b1101000,6);
  while(Wire.available()<6);
  
  gyroX = Wire.read()<<8 | Wire.read() ;
  gyroY = Wire.read()<<8 | Wire.read() ;
  gyroZ = Wire.read()<<8 | Wire.read() ;
  ProccessGyroData();
}
void ProccessGyroData()
{
  rotX=gyroX/131.0 ;
  rotY= gyroY/131.0 ;  
  rotZ= gyroZ/131.0 ;
}
void PrintData()
{
  Serial.print("Gyro (deg) ");
  Serial.print(" X=");
  Serial.print(rotX);
  Serial.print(" Y=");
  Serial.print(rotY);
  Serial.print(" Z=");
  Serial.println(rotZ);
  Serial.println();
  Serial.println();
  /////////////////////////////////////
  Serial.print("Accel (deg) ");
  Serial.print(" X=");
  Serial.print(gFroceX);
  Serial.print(" Y=");
  Serial.print(gFroceY);
  Serial.print(" Z=");
  Serial.println(gFroceZ);
  ///////////////////////////////////////
   Yvalue=map(acceLX,-16834,16834,-90,90);// I got this number  16834 from the datasheet of MPU for sensitivity it is scale range
   Xvalue=map(acceLY,-16834,16834,-90,90);//-90,90 it is the scale range of the Servo motor  it is 180  degree
   
  ServoRightLeftValue=map(Xvalue,-90,90,0,179);
  ServoUpDownValue=map(Yvalue,-90,90,0,179);
  
  servoRightLeft.write(ServoRightLeftValue);

  servoUpDown.write(ServoUpDownValue);  
}

Could you send us a link to the "M-durino 21+"
Google failed me in searching for it.
z

Google must like me better. :slight_smile:

https://www.industrialshields.com/shop/product/m-duino-plc-arduino-ethernet-21-i-o-s-analog-digital-plus-3#attr=1354,1642,2546,1613,5,681,145,807,729,1356,149,1377,563,303,583,1126,1405,509,41,643

(https://www.mouser.cn/datasheet/2/999/Industrial-Shields-1832086.pdf)

i get detailed one

It's in a plastic case and not a breakout board!!!! cool :slight_smile:
They dressed up an ATmega2560
I have an Arduino mega somewhere I can test with... let's see if I can find it.
Z

My sketch compiled for the mega....

the code:

  Yvalue=map(acceLX,-16834,16834,-90,90);// I got this number  16834 from the datasheet of MPU for sensitivity it is scale range
  Xvalue=map(acceLY,-16834,16834,-90,90);//-90,90 it is the scale range of the Servo motor  it is 180  degree

Mapping
acceLX and acceLY is a representation from +-2g and not a representation of +-90°

Additional math is required to get this to be correct.

I believe this is what it takes to get angles from accelerometers:

double xAngle = atan( acceLX/ (sqrt(square(acceLY) + square(acceLZ))));
double yAngle = atan( acceLY/ (sqrt(square(acceLX) + square(acceLZ))));
double zAngle = atan( sqrt(square(acceLX) + square(acceLY)) / acceLZ);

xAngle *= 180.00; 
yAngle *= 180.00;
zAngle *= 180.00;
xAngle /= 3.141592; 
yAngle /= 3.141592; 
zAngle /= 3.141592;

Z

Thanks zhomeslice, Is that means i just need to directly use write function put its angle from accelerometers to motor position? i am not sure.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.