Reading accelerometers to change servo output

ok i have a simple program that will loop a servo to move from point x to point x over and over…

[code]#include <Servo.h>

Servo myservo; // create servo object to control a servo

int pos = 0; // variable to store the servo position

void setup()
{
myservo.attach(3); // attaches the servo on pin 9 to the servo object
myservo.write(90); // set servo to mid-point
}

void loop()
{
for(pos = 0; pos < 90; pos += 1) // goes from 0 degrees to +90 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable ‘pos’
delay(20); // waits for the servo to reach the position
}
for(pos = 90; pos>=1; pos-=1) // goes from +90 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable ‘pos’
delay(10); // waits for the servo to reach the position
}
}[/code]

This works great from its intention but i now wish to Read the built in Accelerometer data (mpu6050 Arduino) to write out to the servo to change its speed or even the loop pattern?

The ideal features would be to have the servo looping over and over as normal when board is level.
once the board starts to tilt in x direction the servo may speed up

any examples on how to access this would be great.

J

Build in accelerometer in an Arduino? Hadn’t heard of that. Actually in the Atmel uP, or just on the board? If on the board (as I suspect), you probably access it with an analog channel. although it could be by I2C. Which one?

There are “wire” libraries for I2C, and probably an analog LIB well. What do you have? We can help you figure it out.

Link to board?

Alan KM6VV

Sorry yes mpu6050 Arduino sounds confusing.
Its a ATmega328 MWC-S V1.5
witespyquad.gostorego.com/flip-m … oller.html
But project is not anything to do with flying. Lol
Im just using this board for another type of project.

OK, MPU6050 (MPU6000). If I’m not mistaken, that chip is used on the APM2 flight controller. So There is probably a library command in the AP libraries to support it. I2C or SPI can be used to interface it. I2C library using wiring. I just interfaced SRF08’s to my 'bot using it. APM code should handle it.

Alan KM6VV

yes thanks Alan.

Unfortunately i still am unable to find the info i need.
The servo is already at the speed i require just by using increments of ±1 but im guessing its a simple reading of the Accelerometer and then add that number to the servo increments making the servo travel faster due to the reading from the Acc?

The servos rotational angle is to remain the same (from 0 degrees to +90 degrees)

such a noob at programming. :frowning:

playground.arduino.cc//Main/MPU-6050

Probably a little more complicated then just adding servo counts (degrees?).

I’d think you’d want to calculate a 3D vector for the 3-axis accelerometer, and the current position of the 'bot body. Then algebraically subtract the resultant error vector. From the three axis error components, you can run three PID loops to return the 'bot body to neutral. OK, the PID loop could be simpler. Is that what you want to do?

Or do you want to “drive” the body with accelerometer inputs? (like a teach pendant)? Similar software, just the math gets changed from “correcting” an error to a “following” 'bot. You “scale” the accelerometer output to the range of the servo (per axis). That could be simpler.

Alan KM6VV

No. My project only has One servo. It doesnt sound like a very exciting project does it. lol
i already have the servo moving exactly as i need it to 'regarding angles and speed, but i would like to use the accelerometer tilting motion to effect the speed that the servo moves.

so if sitting neutral then the servo will just continue its normal speed but if as the acc tilts, id like the degrees of tilt “In all directions” to speed the servo up. so 1º = pos += 1)

Can you read the accelerometers yet? Do you know their range?
Get the range of the accelerometers, scale, and add to the speed.

Alan KM6VV

im onto something…

iv included the “I2Cdev.h” and “MPU6050.h” libraries in order to access the functions.
i read raw accel/gyro measurements from device with

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

Then display accel/gyro x/y/z values

Serial.print("a/g:\t"); Serial.print(ax); Serial.print("\t"); Serial.print(ay); Serial.print("\t"); Serial.print(az); Serial.print("\t"); Serial.print(gx); Serial.print("\t"); Serial.print(gy); Serial.print("\t"); Serial.println(gz);

But ill have some time to go over these and hopefully get some developments on this soon.

ok so i can make the servo change its Position relative to the sensor eg.

myservo.write(ax);

but im not sure how to scale the data into a readable number that can be added to effect the servo Speed not position…

any ideas?

It all depends on how much you want the accelerometer input to affect the servo position.

If you position one accelerometer axis (Z) vertical, you will read 1G. Tilt in any way and the acceleration read will go down, and another axis’ value will go up (X). Going from horizontal to vertical would probably match what you want to do. Get the values (~ = 0) at horizontal, and ~= 1G at vertical. This will be read as so many mv for each. Into an A/D, you might get counts of 50 and 200 (wild guess). Whatever you get, you have a low (50) value and a range (150). You can subtract off the low value, and then you have a range of 0 to 150.

What servo position values can you send to the servo? 0 to 180 (degrees?) Then you can send your range from the accelerometer to the servo!

Your values will probably vary! But it’s a start until we get real values.

Just remember, take an axis (probably X) go from horizontal to vertical, you’ll get a range of output that you can scale to what you need for the servo. You can add a multiplier to get more out of your range,

1.2 * 150 = 180.

If that’s what you want.

Alan KM6VV

thanks for taking the time to write that alan. its all good info,

But Alan. “Not Position or angle of the servo”
The servo must have its constant trajectory of “0 degrees to +90 degrees”.
Im looking to convert the Acc/Gyr data into analogue, then use that data to set the servos “Speed” depending on Acc/Gyr data not position.

getting the servo to gyrate around the same angle of the board is easy, but im not looking to do that.

Thats why im struggling. :wink:

Not sure what changing the speed of a cycling (?) servo would do, and I don’t see a command to do it. All you can do is control the servo by a series of steps, and change the rate that the steps (position) is changed. Change the rate of change by the accl value?

Or change the step size (increment) according to the accl.

Alan KM6VV

Yep, thats exactly how im expecting it will be. Ill have some time today to look at this again. Even if there was a way to enter the value as part of the delay () would be nice but then again irs a variable not a constant value.

Or you could use my hacked up version of the servo library (ServoEx), which added timed moves… I have not played with this for awhile, but I know I uploaded it in a few different threads, like: viewtopic.php?t=8038&p=80023#p80023 The version on my machine may have a few changes (at least the date is different), so could upload again if wanted.

Kurt

hi Kurt,
Well i have it working now. well i say working i have managed to get the servo to get fast the more you incline the sensor. :smiley: but its quite temperamental and unstable as the sensors have multiple home address’s.

im using the Varspeedservo.h library

allowing me to control angle and speed quite nicely.

int servoSpeeds = 50; // sweep speed, 1 is slowest, 255 fastest) int servoMinPosition = 180; // the minimum servo angle int servoMaxPosition = 90; // the maximum servo angle

the reason for this is that the Delay function was messing up the readings from the sensors.

and then with the sensor readings i it looks like this…

[code] {
// sweep the servos

if( MyServo.read() == servoMinPosition)
{
MyServo.slowmove(servoMaxPosition,servoSpeeds/accel_t_gyro.value.x_accel) ;
}
else if( MyServo.read() == servoMaxPosition)
{
MyServo.slowmove(servoMinPosition,servoSpeeds/accel_t_gyro.value.x_accel) ;
}

}[/code]

i just need to play with the numbers.

one thing i am stuck on is how to convert the sensor readings into usable numbers. i need to set a Speed Limit also as it does get fast on a larger incline. really i only want a small increase in speed. not to fast but noticeable.

There are many ways of doing it. Not sure what your range of values that come from x_accel, but you could always do scaling of it your self, or you could for example, figure out what is the range of values you wish to give to slowmove, and then simply use the map function to map the range of values that come from the accel into the range of values you wish for the motion.

Kurt

my work here is done.

The device is working as intended and so im am going to stop there. i could go round and clean the code up more but as i said, my gole has been reached. and yes there may be other ways of doing for sure. :wink:
Thanks for all the input guys.

If you want to look at the complete code i can attach it but here a quick overview of the “main” structure and functions:
i have only used one of the DOF “x_accel” but i have left the others in the code anyway.

[code]#include <VarSpeedServo.h>

typedef union accel_t_gyro_union
{
struct
{
uint8_t x_accel_h;
uint8_t x_accel_l;
uint8_t y_accel_h;
uint8_t y_accel_l;
uint8_t z_accel_h;
uint8_t z_accel_l;
uint8_t t_h;
uint8_t t_l;
uint8_t x_gyro_h;
uint8_t x_gyro_l;
uint8_t y_gyro_h;
uint8_t y_gyro_l;
uint8_t z_gyro_h;
uint8_t z_gyro_l;
} reg;
struct
{
int x_accel;
int y_accel;
int z_accel;
int x_gyro;
int y_gyro;
int z_gyro;
} value;
};
//Servo myservo; // create servo object to control a servo
VarSpeedServo MyServo; // servo objects
int pos = 0; // variable to store the servo position

int servoMinPosition = 180; // the minumum servo angle
int servoMaxPosition = 90; // the maximum servo angle
//myServo.attach (mainPin, ServoMin, ServoMax);
//…
//myServo.slowmove (newpos, speed);
//…
void setup()
{
int error;
uint8_t c;

Serial.begin(9600);
Serial.println(F(“BabyBreaths MPU-6050”));

MyServo.attach(3); // attaches the servo on pin 3 to the servo object
// myservo.write(90); // set servo to mid-point
MyServo.slowmove(servoMinPosition,10) ; // start sweeping from min position
// Initialize the ‘Wire’ class for the I2C-bus.
Wire.begin();

// default at power-up:
// Gyro at 250 degrees second
// Acceleration at 2g
// Clock source at internal 8MHz
// The device is in sleep mode.
//

error = MPU6050_read (MPU6050_WHO_AM_I, &c, 1);
Serial.print(F(“BabyBreaths MPU-6050”));
Serial.print(c,HEX);
Serial.print(F(",[email protected]"));
Serial.println(error,DEC);

// Clear the ‘sleep’ bit to start the sensor.
MPU6050_write_reg (MPU6050_PWR_MGMT_1, 0);
}

void loop()
{
int error;
double dT;
accel_t_gyro_union accel_t_gyro;

Serial.println(F(""));
Serial.println(F(“MPU-6050”));

// Read the raw values.
// Read 14 bytes at once,
// containing acceleration, and gyro.
// With the default settings of the MPU-6050,
// there is no filter enabled, and the values
// are not very stable.
error = MPU6050_read (MPU6050_ACCEL_XOUT_H, (uint8_t *) &accel_t_gyro, sizeof(accel_t_gyro));
Serial.print(F("Read accel, gyro, = "));
Serial.println(error,DEC);

// Swap all high and low bytes.
// After this, the registers values are swapped,
// so the structure name like x_accel_l does no
// longer contain the lower byte.
uint8_t swap;
#define SWAP(x,y) swap = x; x = y; y = swap

SWAP (accel_t_gyro.reg.x_accel_h, accel_t_gyro.reg.x_accel_l);
SWAP (accel_t_gyro.reg.y_accel_h, accel_t_gyro.reg.y_accel_l);
SWAP (accel_t_gyro.reg.z_accel_h, accel_t_gyro.reg.z_accel_l);
SWAP (accel_t_gyro.reg.t_h, accel_t_gyro.reg.t_l);
SWAP (accel_t_gyro.reg.x_gyro_h, accel_t_gyro.reg.x_gyro_l);
SWAP (accel_t_gyro.reg.y_gyro_h, accel_t_gyro.reg.y_gyro_l);
SWAP (accel_t_gyro.reg.z_gyro_h, accel_t_gyro.reg.z_gyro_l);

// Print the raw acceleration values

Serial.print(F(“accel x,y,z: “));
Serial.print(accel_t_gyro.value.x_accel, DEC);
Serial.print(F(”, “));
Serial.print(accel_t_gyro.value.y_accel, DEC);
Serial.print(F(”, “));
Serial.print(accel_t_gyro.value.z_accel, DEC);
Serial.println(F(””));

// Print the raw gyro values.

Serial.print(F(“gyro x,y,z : “));
Serial.print(accel_t_gyro.value.x_gyro, DEC);
Serial.print(F(”, “));
Serial.print(accel_t_gyro.value.y_gyro, DEC);
Serial.print(F(”, “));
Serial.print(accel_t_gyro.value.z_gyro, DEC);
Serial.print(F(”, “));
Serial.println(F(””));

int val = analogRead(accel_t_gyro.value.x_accel);
val = map(val, 0, 1023, 0, 255);

{
// sweep the servos

if( MyServo.read() == servoMinPosition)
{
MyServo.slowmove(servoMaxPosition, val/3) ; // sweep speed, 1 is slowest, 255 fastest)
}
else if( MyServo.read() == servoMaxPosition) // sweep speed, 1 is slowest, 255 fastest)
{
MyServo.slowmove(servoMinPosition,val/2.5) ;
}

}

}[/code]

[code]// --------------------------------------------------------
// MPU6050_read
//
// This is a common function to read multiple bytes
// from an I2C device.
//
// It uses the boolean parameter for Wire.endTransMission()
// to be able to hold or release the I2C-bus.
// This is implemented in Arduino 1.0.1.
//
// Only this function is used to read.
// There is no function for a single byte.
//
int MPU6050_read(int start, uint8_t *buffer, int size)
{
int i, n, error;

Wire.beginTransmission(MPU6050_I2C_ADDRESS);
n = Wire.write(start);
if (n != 1)
return (-10);

n = Wire.endTransmission(false); // hold the I2C-bus
if (n != 0)
return (n);

// Third parameter is true: relase I2C-bus after data is read.
Wire.requestFrom(MPU6050_I2C_ADDRESS, size, true);
i = 0;
while(Wire.available() && i<size)
{
buffer*=Wire.read();
}
if ( i != size)
return (-11);

return (0); // return : no error
}[/code]

[code]// --------------------------------------------------------
// MPU6050_write
//
// This is a common function to write multiple bytes to an I2C device.
//
// If only a single register is written,
// use the function MPU_6050_write_reg().
//
// Parameters:
// start : Start address, use a define for the register
// pData : A pointer to the data to write.
// size : The number of bytes to write.
//
// If only a single register is written, a pointer
// to the data has to be used, and the size is
// a single byte:
// int data = 0; // the data to write
// MPU6050_write (MPU6050_PWR_MGMT_1, &c, 1);
//
int MPU6050_write(int start, const uint8_t *pData, int size)
{
int n, error;

Wire.beginTransmission(MPU6050_I2C_ADDRESS);
n = Wire.write(start); // write the start address
if (n != 1)
return (-20);

n = Wire.write(pData, size); // write data bytes
if (n != size)
return (-21);

error = Wire.endTransmission(true); // release the I2C-bus
if (error != 0)
return (error);

return (0); // return : no error
}[/code]

[code]// --------------------------------------------------------
// MPU6050_write_reg
//
// An extra function to write a single register.
// It is just a wrapper around the MPU_6050_write()
// function, and it is only a convenient function
// to make it easier to write a single register.
//
int MPU6050_write_reg(int reg, uint8_t data)
{
int error;

error = MPU6050_write(reg, &data, 1);

return (error);
}[/code]*

Pfff…
Where is the proof that it work…???