Servo ansteuern MPU6050

Hallo Forum :slight_smile:

Ich habe jetzt endlich mein MPU6050 (GY-521) und ein Steckbrett.
Jetzt habe ich aber folgendes Problem.
Ich habe jetzt ein Sketch, was mir die Y-Werte ausliest (Kalman).
Dazu habe ich einen Servo mit in das Sketch gepackt, das der sich bewegt.
Das klappt auch soweit ganz gut, solange ich feste Werte vergebe.
Wie kann ich es machen, das der Servo eine kontinuierliche Bewegung macht?
Also das er sich gleichmäßig mit dem Sensor bewegt.

Ich hoffe ihr versteht mich :slight_smile:

LG Matze

/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Kristian Lauszus, TKJ Electronics
Web : http://www.tkjelectronics.com
e-mail : kristianl@tkjelectronics.com
*/

#include <Wire.h>
#include "Kalman.h" // Source: https://github.com/TKJElectronics/KalmanFilter
#include <Servo.h> 

Kalman kalmanX;
Kalman kalmanY;
Servo myservo;

const uint8_t IMUAddress = 0x68;

/* IMU Data */
int16_t accX;
int16_t accY;
int16_t accZ;
int16_t tempRaw;
int16_t gyroX;
int16_t gyroY;
int16_t gyroZ;

double accXangle; // Angle calculate using the accelerometer
double accYangle;
double temp;
double gyroXangle = 180; // Angle calculate using the gyro
double gyroYangle = 180;
double compAngleX = 180; // Calculate the angle using a Kalman filter
double compAngleY = 180;
double kalAngleX; // Calculate the angle using a Kalman filter
double kalAngleY;

uint32_t timer;

void setup() {
  Serial.begin(115200);
  myservo.attach(9);
  Wire.begin();
  i2cWrite(0x6B,0x00); // Disable sleep mode
  if(i2cRead(0x75,1)[0] != 0x68) { // Read "WHO_AM_I" register
    Serial.print(F("MPU-6050 with address 0x"));
    Serial.print(IMUAddress,HEX);
    Serial.println(F(" is not connected"));
    while(1);
  }
  kalmanX.setAngle(180); // Set starting angle
  kalmanY.setAngle(180);
  timer = micros();
}

void loop() {

if (kalAngleY < 250)
{
  myservo.write(45);
}

else if (kalAngleY > 280)

{
  myservo.write(135);
}
else
{
  myservo.write(90);
}


  
  /* Update all the values */
  uint8_t* data = i2cRead(0x3B,14);
  accX = ((data[0] << 8) | data[1]);
  accY = ((data[2] << 8) | data[3]);
  accZ = ((data[4] << 8) | data[5]);
  tempRaw = ((data[6] << 8) | data[7]);
  gyroX = ((data[8] << 8) | data[9]);
  gyroY = ((data[10] << 8) | data[11]);
  gyroZ = ((data[12] << 8) | data[13]);
  
  /* Calculate the angls based on the different sensors and algorithm */
  accYangle = (atan2(accX,accZ)+PI)*RAD_TO_DEG;
  accXangle = (atan2(accY,accZ)+PI)*RAD_TO_DEG;
  
  double gyroXrate = (double)gyroX/131.0;
  double gyroYrate = -((double)gyroY/131.0);
  gyroXangle += gyroXrate*((double)(micros()-timer)/1000000); // Calculate gyro angle without any filter
  gyroYangle += gyroYrate*((double)(micros()-timer)/1000000);
  //gyroXangle += kalmanX.getRate()*((double)(micros()-timer)/1000000); // Calculate gyro angle using the unbiased rate
  //gyroYangle += kalmanY.getRate()*((double)(micros()-timer)/1000000);
  
  compAngleX = (0.93*(compAngleX+(gyroXrate*(double)(micros()-timer)/1000000)))+(0.07*accXangle); // Calculate the angle using a Complimentary filter
  compAngleY = (0.93*(compAngleY+(gyroYrate*(double)(micros()-timer)/1000000)))+(0.07*accYangle);
  
  kalAngleX = kalmanX.getAngle(accXangle, gyroXrate, (double)(micros()-timer)/1000000); // Calculate the angle using a Kalman filter
  kalAngleY = kalmanY.getAngle(accYangle, gyroYrate, (double)(micros()-timer)/1000000);
  timer = micros();
  
  temp = ((double)tempRaw + 12412.0) / 340.0;
  
  /* Print Data */
  /*
Serial.print(accX);Serial.print("\t");
Serial.print(accY);Serial.print("\t");
Serial.print(accZ);Serial.print("\t");
Serial.print(gyroX);Serial.print("\t");
Serial.print(gyroY); Serial.print("\t");
Serial.print(gyroZ);Serial.print("\t");
*/

  Serial.print(kalAngleY);Serial.print("\t");
  
  //Serial.print(temp);Serial.print("\t");
   
  Serial.print("\n");
  
  delay(1); // The accelerometer's maximum samples rate is 1kHz
}
void i2cWrite(uint8_t registerAddress, uint8_t data){
  Wire.beginTransmission(IMUAddress);
  Wire.write(registerAddress);
  Wire.write(data);
  Wire.endTransmission(); // Send stop
}
uint8_t* i2cRead(uint8_t registerAddress, uint8_t nbytes) {
  uint8_t data[nbytes];
  Wire.beginTransmission(IMUAddress);
  Wire.write(registerAddress);
  Wire.endTransmission(false); // Don't release the bus
  Wire.requestFrom(IMUAddress, nbytes); // Send a repeated start and then release the bus after reading
  for(uint8_t i = 0; i < nbytes; i++)
    data[i] = Wire.read();
  return data;
}

Entschuldigung für den Doppelpost, aber Edit ging nicht?!

Ist es denn nicht möglich, den Schritt von 90° zu 45° wenn kalAngleY < 250 ist,
in "einzelschritten" zu machen? Also so, das der Servo da nicht gleich hinspringt sondern sanft in 1grad schritten?
Das selbe sollte dann natürlich auch in die andere Richtung funktionieren.

LG Matze

bluebravo:
Wie kann ich es machen, das der Servo eine kontinuierliche Bewegung macht?
Also das er sich gleichmäßig mit dem Sensor bewegt.

Ich hoffe ihr versteht mich :slight_smile:

So ganz verstehe ich das nicht, aber wahrscheinlich meinst Du die unstetige Ansteuerung des Servos in Deiner loop-Funktion:

if (kalAngleY < 250)
{
  myservo.write(45);
}

else if (kalAngleY > 280)

{
  myservo.write(135);
}
else
{
  myservo.write(90);
}

D.h. egal wie langsam und stetig sich der Eingangswert kalAngleY ändert, kennt der Servo-Stellwert nur drei Antworten darauf:

  • Vollausschlag links
  • Vollausschlag rechts
  • Mittelstellung
    Also der Servo steht bei sich ändernder Eingangsgröße, dann bewegt er sich sprunghaft auf einen anderen Stellwert und steht wieder.

An den programmierten Umschaltpunkten (250 und 280) bekommt der Servo bei minimaler Änderung plötzlich das Laufen, während er ansonsten stillsteht.

Und Du möchtest wahrscheinlich eine stetige Funktion, so dass geringe Änderungen von kalAngleY auch zu kleinen Servoausschlägen führen?

In dem Fall müßtest Du natürlich nicht zwei feste Umschaltpunkte zwischen drei Servostellungen programmieren, sondern eine stetige Funktion. Wenn Du Dir mal in einem XY-Diagramm den Eingangswert kalAngleY als x aufträgst und die Servostellung als y, wäre die einfachste denkbare Funktion zwischen kalAngleY und Servostellung eine Gerade. Wenn Du zwei Punkte einer Geraden hast, kannst Du Dir daraus alle Punkte auf der Geraden ermitteln.

D.h. wenn Du weißt, welcher Wert von kalAngleY zur Servostellung 45 und welcher Wert von kalAngleY zur Servostellung 135 gehören soll, kannst Du jeden Wert dazwischen ausrechnen. Stichwort "lineare Interpolation".

Wenn die Neutralstellung 90° des Servos dann nicht auf der gewünschten Geraden liegt, sondern gesondert vorgegeben werden soll, könnte man es mit einer "geknickten Geraden" machen, d.h. von Servostellung 45 bis 90 gilt die eine Gerade/Interpolationsformel und für eine Servostellung 90 bis 135 gilt eine andere Gerade/Interpolationsformel.

Im Endeffekt müßtest Du also erstmal eine grobe Wertetabelle haben, welche Werte von kalAngleY auf welche Servostellungen abgebildet werden sollen. Und dann siehst Du, ob Du als Regelkurve eine Gerade, eine Gerade mit Knick an der Neutralstellung, eine Parabel, eine logarithmische Kurve oder irgendwas anderes als Stellfunktion programmieren mußt. Aber irgendwas mußt Du vorgeben.

Hall jurs, danke für deine Antwort.

D.h. egal wie langsam und stetig sich der Eingangswert kalAngleY ändert, kennt der Servo-Stellwert nur drei Antworten darauf:

  • Vollausschlag links
  • Vollausschlag rechts
  • Mittelstellung
    Also der Servo steht bei sich ändernder Eingangsgröße, dann bewegt er sich sprunghaft auf einen anderen Stellwert und steht wieder.

An den programmierten Umschaltpunkten (250 und 280) bekommt der Servo bei minimaler Änderung plötzlich das Laufen, während er ansonsten stillsteht.

Und Du möchtest wahrscheinlich eine stetige Funktion, so dass geringe Änderungen von kalAngleY auch zu kleinen Servoausschlägen führen?

Genau so ist es momentan. So würde ich es gerne haben wie in deinem letzten Satz.

D.h. wenn Du weißt, welcher Wert von kalAngleY zur Servostellung 45 und welcher Wert von kalAngleY zur Servostellung 135 gehören soll, kannst Du jeden Wert dazwischen ausrechnen. Stichwort "lineare Interpolation".

Also muß ich jedem Wert den ich haben will, einen Servo Winkel zuweisen?
Dann werden das aber ziemlich viele werte.

Darum dachte ich, das es da eine Möglichkeit gibt,
das wenn der sensor sagt "ich bin schräg"
der Servo sagt "ich steuer mal gegen so weit ich kann"

Sowas in etwa soll es werden.
Nur halt nicht so "sprunghaft"

lg Matze