dIMU Accelerometer

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
Post Reply
shaoxiaojun
Posts: 13
Joined: 30 May 2012, 06:53

dIMU Accelerometer

Post by shaoxiaojun »

Hi All,
I got this powerful sensor days ago, I prefer start from the accelerometer, after downloaded a sample code, I always got a error in NXT screen 'File error -1', I saw the aurthor of this code is the best friend mattallen here, so please help to comment.
I use the latest testing Fireware(build at July), sensor connected to S1 and the blue light is already on.

Code: Select all

/*
* Example of calibrating and reading the dIMU Accelerometer.
*
* Matthew Richardson
* http://mattallen37.wordpress.com/
* October 8 2011
*/

#define dIMU_Accel_Addr 0x3A

void dIMU_Accel_Init(byte port, byte G_Mode = 2){
  byte I2Csnd[3];
  I2Csnd[0] = dIMU_Accel_Addr;  // I2C Address of Accelerometer.

  I2Csnd[1] = 0x16;                   // Register address of Mode Control
  I2Csnd[2] = 0x05;                   // 0x05 For 2G
  if(G_Mode == 0x01) I2Csnd[2] = 0x09; // 0x09 For 4G
  if(G_Mode == 0x02) I2Csnd[2] = 0x01; // 0x01 For 8G

  byte I2Crec[1];           // We are looking for a single byte returned.
  byte cnt = 1;             // cnt must be a variable, not a constant
  I2CBytes(port, I2Csnd, cnt, I2Crec);
}

void dIMU_Accel_Read(byte port, float & Accel_X, float & Accel_Y, float & Accel_Z){

  byte I2Csnd[2];              // Sending buffer
  I2Csnd[0] = dIMU_Accel_Addr; // I2C Address of accl.
  I2Csnd[1] = 0x00;            // First Register of the data we're requesting.
  byte cnt = 6;                // Number of bytes to request
  byte I2Crec[6];              // Receiving buffer

  I2CBytes(port, I2Csnd, cnt, I2Crec);    //Send the address and the register, and receive the 6 bytes of data.

  Accel_X = I2Crec[1] * 256 + I2Crec[0];  // Convert the returned array into floating point G values
  Accel_Y = I2Crec[3] * 256 + I2Crec[2];  //                ''
  Accel_Z = I2Crec[5] * 256 + I2Crec[4];  //                ''

  if(Accel_X > 512)Accel_X -= 1023;       // Convert into a signed value
  if(Accel_Y > 512)Accel_Y -= 1023;       //                ''
  if(Accel_Z > 512)Accel_Z -= 1023;       //                ''

  Accel_X /= 64;
  Accel_Y /= 64;
  Accel_Z /= 64;

  return;
}

void dIMU_Accel_Write_Calibration(byte port, int x_offset, int y_offset, int z_offset){

  byte I2Csnd[8];
  I2Csnd[0] = dIMU_Accel_Addr; // I2C Address of accel.
  I2Csnd[1] = 0x10;            // Register of the data we're writing.
  I2Csnd[2] = x_offset;         // The drift value to write for calibration.
  I2Csnd[3] = x_offset>>8;      //                 ''
  I2Csnd[4] = y_offset;         //                 ''
  I2Csnd[5] = y_offset>>8;      //                 ''
  I2Csnd[6] = z_offset;         //                 ''
  I2Csnd[7] = z_offset>>8;      //                 ''

  byte I2Crec[1];              // We are looking for a single byte returned.
  byte cnt = 1;                // cnt must be a variable, not a constant
  I2CBytes(port, I2Csnd, cnt, I2Crec);   //Send the address, register, and 6 bytes of calibration data.
}

void dIMU_Accel_Calibrate(byte port, int gravity_direction = 3){

  dIMU_Accel_Write_Calibration(port, 0, 0, 0);    // Clear all of the calibration registers.

  int x_offset;
  int y_offset;
  int z_offset;

  byte I2Csnd[2];              // Sending buffer
  I2Csnd[0] = dIMU_Accel_Addr; // I2C Address of accel.
  I2Csnd[1] = 0x00;            // First Register of the data we're requesting.
  byte cnt = 6;                // Number of bytes to request
  byte I2Crec[6];              // Receiving buffer

  I2CBytes(port, I2Csnd, cnt, I2Crec);     //Send the address and the register, and receive the 6 bytes of data.

  x_offset = I2Crec[1] * 256 + I2Crec[0];  // Convert the returned array into floating point G values
  y_offset = I2Crec[3] * 256 + I2Crec[2];  //                ''
  z_offset = I2Crec[5] * 256 + I2Crec[4];  //                ''
  if(x_offset > 512)x_offset -= 1023;      // Convert into a signed value
  if(y_offset > 512)y_offset -= 1023;      //                ''
  if(z_offset > 512)z_offset -= 1023;      //                ''

  // We will calibrate depending on the axis that's inline with the gravity field.
  // Positive 1G is 64 decimal, or 0x40 Hex, so we need to subtract that from the proper axis.
  switch(gravity_direction){

   case 1: x_offset -= 0x40;   // Subtract out the 1-g that the x-axis is experiencing.
    break;

   case 2: y_offset -= 0x40;   // Subtract out the 1-g that the y-axis is experiencing.
    break;

   case 3: z_offset -= 0x40;   // Subtract out the 1-g that the z-axis is experiencing.
    break;
  }

  x_offset *= (-2);    // Treated normally here and just zero it out.
  y_offset *= (-2);    // Treated normally here and just zero it out.
  z_offset *= (-2);    // Treated normally here and just zero it out.

  dIMU_Accel_Write_Calibration(port, x_offset, y_offset, z_offset);
}

float Accel_X, Accel_Y, Accel_Z;

task main(){
  SetSensorLowspeed(S1);

  // Initialize the accelerometer. Only needs to be done once per power cycle.
  dIMU_Accel_Init(S1);              // Here we define the G-Mode we're going to use. 0x00 = +/- 2g, 0x01 = +/- 4g, 0x02 = +/- 8g.
  
  // Calibrate the values.
  dIMU_Accel_Calibrate(S1);         // Calibrated z-axis. 1 x-axis, 2 y-axis, 3 z-axis.  Default is z-axis.

  while (true){
      dIMU_Accel_Read(S1, Accel_X, Accel_Y, Accel_Z);      // Get accel data.

      ClearScreen();
      NumOut(0, LCD_LINE1, Accel_X);
      NumOut(0, LCD_LINE2, Accel_Y);
      NumOut(0, LCD_LINE3, Accel_Z);
      
      Wait(50);
  }
}

still have some further question here.
1. Calibration
what's this purpose here? for me, when the sensor lost the precision, like hardware defects or big enviroment change, then we need to do that. why and when we need to put a base value to calibrate the sensor, what that drift value mean?
2. drive the sensor
the code is straight forward, use the API I2CBytes(p1,p2,p3,p4) to communicate with the sensor with a complicated process, so how the process come? is there any patter or we have to read the hardware spec every time when a new sensor come?

thanks.
this is shawn.
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: dIMU Accelerometer

Post by afanofosc »

I would recommend using the functions built into recent test releases of BricxCC/NBC/NXC which include support for the Dexter Industries dIMU device. This device is both a Gyro and an Accelerometer in a single package. The NXC API functions for this device are documented here:

http://bricxcc.sourceforge.net/nbc/nxcd ... a_p_i.html

Here is an example of how to use the device:

http://bricxcc.sourceforge.net/nbc/nxcd ... ample.html

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: dIMU Accelerometer

Post by mattallen37 »

I wrote that code before there was official support for the dIMU in NXC. I recommend you go with John's suggestion, and use the official API.

To answer your question about why you would calibrate it, it's because it doesn't seem to be factory calibrated, and some of the axes suffer from a significant offset. Calibrating it adjusts the offset, so that the readings are more accurate.

"drift" is not the right word; it should be "offset". This is a left-over comment from some other code.

In regard to question 2: "drive the sensor", I'm not sure what you're asking.

Your other post seems to be a duplicate, so I won't bother replying there as well.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
shaoxiaojun
Posts: 13
Joined: 30 May 2012, 06:53

Re: dIMU Accelerometer

Post by shaoxiaojun »

thanks guys, but could you comment little bit on calibrate function:

Code: Select all

void CalibrateDIAccl(const byte port, int iter)
{
  TextOut(0, LCD_LINE1, "Calibrating...");
  Wait(SEC_1);
  SetSensorDIAcclDrift(port, 0, 0, 0);
  int x = 0, y = 0, z = 0;
  VectorType raw;
  repeat(iter)
  {
    ReadSensorDIAcclRaw(S1, raw);
    x += raw.X;
    y += raw.Y;
    z += raw.Z;
    Wait(MS_10);
  }
  x = (0-(x/iter))*2;
  y = (0-(y/iter))*2;
  z = (60-(z/iter))*2;
  NumOut(0, LCD_LINE2, x);
  NumOut(0, LCD_LINE3, y);
  NumOut(0, LCD_LINE4, z);
  Wait(SEC_1);
  SetSensorDIAcclDrift(port, x, y, z);
  TextOut(0, LCD_LINE5, "Completed!");
  Wait(SEC_1);
  ClearScreen();
}

I'm confused on this, for me, if we want to calibrate a sensor, we need to know the right value(from an calibrated item) and the reading value, the difference between them is the drift, am I correct?
this is shawn.
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: dIMU Accelerometer

Post by mattallen37 »

Yes, that is correct (except it's called "offset" not "drift").

The calibrate function does the following:
  • Clears any existing offsets
  • Takes a bunch of readings and averages them
  • Formats the offsets to how the accel expects them
  • Writes them to the accel
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
shaoxiaojun
Posts: 13
Joined: 30 May 2012, 06:53

Re: dIMU Accelerometer

Post by shaoxiaojun »

thanks for the reply, so is there any specific angle should I lay down the sensor on a flat surface when doing the calibration? as I know, the gravity is a static acceleration value and can be mapping to 3 axis, so even in a idle state, we still can read 3 value from x,y,z. does this disturb the calibration? and thus we never know how to calibrate an lost-precision sensor from itself?

and not quite understand this transfer:

Code: Select all

x = (0-(x/iter))*2;
y = (0-(y/iter))*2;
z = (60-(z/iter))*2;

please help to get clarify.
thanks.
this is shawn.
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: dIMU Accelerometer

Post by mattallen37 »

You should position the sensor upright, perfectly level, and stationary. X and Y axes should be experiencing 0 G forces, and axis Z should be experiencing 1 G force (assuming you do the calibration on the earth). Any difference from the expected result will be fed into the calibration registers, to offset the values.

x/iter is dividing x by the number of times it was added to originally, thus creating an average. 0 - inverts the number, and * 2 is a requirement of the accel chip. Z using 60 - is also inverting the offset, but is compensating for the fact that the sensor is experiencing 1G of force positively.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests