BLE Sense IMU and Servo

Hello, hoping for your help thanks!
What I’m trying to achieve is nothing new - it’s a project to move a servo to reflect the angle movements of the sense’s IMU. I can make that happen for 0 to 90 degrees because the inbuilt LSM9DS1 IMU accelerometer x axis output is +0.00 to +1.00 so multiplying by 90 gives a degree to send to the servo.

That’s not a very accurate method though I am aware and I don’t know how to calculate and send accurate degrees using the gyro and accelerometer (ant2 calculation?). The historically in the pre-BLE Sense days when one would have connected the same IMU to an Arduino you would have utilised the Adafruit LSM9D1 library which has the correct code to throw out degrees but from what I can see (novice) that library needs amending to reflect the BLE Sense setup...

Has anybody got a solution?
Appreciate your help.

update: I have achieved 180 degree rotation but it feels extremely makeshift. I set the servo’s variable position (‘pos’) to initiate at 90 then in the loop ‘pos’ is written to the servo at ‘pos + Xaxis * 90’. Printout on monitor to see what’s happening. Code below.

#include <Arduino_LSM9DS1.h>
#include <Servo.h>

Servo myservo;
int pos = 90; // variable to store the servo position

void setup() {
Serial.begin(9600);
(IMU.begin());
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}

void loop()
{
float x, y, z;

if (IMU.accelerationAvailable()) {
IMU.readAcceleration(x, y, z);
}
myservo.write(pos + (x * 90));
Serial.print(pos + (x * 90));
Serial.print(’\n’);
delay(10);

}

I guess you should use the gyroscope for this.

The accellerometer gives information about position change.
The gyroscope about orientation change.
The magnetometer about compass direction.

Also I assume that you will have to add the rotations to find the value for the servo input.

Thanks,

Update: the code below is working fine using the accelerometer but as you point out the gyro will be better. I suppose the gyro readings need to be calculated for change over time? The servo struggles to get all the way to 90 degrees upright - consequence of cheap materials maybe?

#include <Arduino_LSM9DS1.h>
#include <Servo.h>

Servo myservo;
int pos = 90;    // variable to store the servo position

void setup() {
  Serial.begin(9600);
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }
}

void loop() {
  float pitch, roll;
  float x, y, z;

  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(x, y, z);

  pitch = atan2(x, sqrt(y * y) + (z * z));
  pitch *= 180.0 / M_PI;
  
  Serial.print(pitch, 0);
  Serial.print('\n');
}

delay(10);
myservo.write(pos + pitch);

}

I see what you are trying to do now and the method should work fine.
Poor quality of the servo could be a explanation. The other is that you are feeding the servo a measured signal with noise.
To investigate you could feed it a signal without noise, something like pitch = 90*sin (float(millis())/1000);
If that runs smoothly try to limit the number of new positions send to the servo. e.g. an SG90 accepts a PWM width of 50 Hz. So the delay should at least be 20. You could try to build in some threshold, so only send a new position if the last send position deviates more than a certain delta from the measured position.

In the call to the atan2 function the two arguments should swap places. That way you don't need to add the 90 deg offset. See link

so
pitch = atan2(sqrt(y * y) + (z * z),x)*180/PI;
and
myservo.write(pitch);
should do.

Thanks for your help. I will do that later today and update.

The subexpression sqrt(y * y) + (z * z) is wrong.
Instead of the component perpendicular to x it equals: y +z2
It should have an extra set of brackets sqrt((y * y) + (z * z))

For smoothness I would put the
delay(10);
myservo.write(pos + pitch);
inside the statement if (IMU.accelerationAvailable()) {…}

I just found out how to use the serial plotter ( ctrl-shift-L )
This is what I made of your program. It runs nicely with an Tower Pro SG90, but it does not fully reach the -90 deg position either. That is unboubtedly due to the cheap quality

#include <Arduino_LSM9DS1.h>
#include <Servo.h>
Servo myservo;

void setup() {
  Serial.begin(9600);
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
  if (!IMU.begin()) {
     Serial.println("Failed to initialize IMU!");
     while (1);  }
  while(!Serial);   //wait for a serial connection
  Serial.println("pitch\tX\tYZ"); //shows in the legend of the serial plotter.
}
void loop() {
  float pitch, roll;
  float x, y, z;
  if (IMU.accelerationAvailable()) {
      IMU.readAcceleration(x, y, z);
      float yz= sqrt((y * y) + (z * z));
      pitch = atan2(yz,x)*180/PI;
      myservo.write(pitch);
      Serial.print(pitch, 0);
      Serial.print('\t');  
  Serial.print(90*x);
  Serial.print('\t');  
  Serial.println(90* yz);
  delay(20);
  }
}

note: it waits for a serial connection in order to catch the first printed line.