Minimu-9 - get heading

Hi,
I’m trying to get heading from my minimu-9. There is my code:


vector m_max = {464, 324, 408};
vector m_min = {-616, -634, -730};

void readMag(vector * mag)
{
	// odczyt wartości z magnetometru
	i2c_start();
	i2c_write_byte(0x3C); // write mag
	i2c_write_byte(0x03); // OUTXH_M, MSB set to enable auto-increment
	i2c_start();		  // repeated start
	i2c_write_byte(0x3D); // read mag
	unsigned char mxh = i2c_read_byte();
	unsigned char mxl = i2c_read_byte();
	unsigned char myh = i2c_read_byte();
	unsigned char myl = i2c_read_byte();
	unsigned char mzh = i2c_read_byte();
	unsigned char mzl = i2c_read_last_byte();
	i2c_stop();
	
	mag->x = (int16_t)(mxh << 8 | mxl);
	mag->y = (int16_t)(myh << 8 | myl);
	mag->z = (int16_t)(mzh << 8 | mzl);

}

void readAcc(vector *acc)
{
	
	// odczyt wartości z akcelerometru
	i2c_start();
	i2c_write_byte(0x30); // write acc
	i2c_write_byte(0xa8); // OUT_X_L_A, MSB set to enable auto-increment
	i2c_start();		  // repeated start
	i2c_write_byte(0x31); // read acc
	unsigned char axl = i2c_read_byte();
	unsigned char axh = i2c_read_byte();
	unsigned char ayl = i2c_read_byte();
	unsigned char ayh = i2c_read_byte();
	unsigned char azl = i2c_read_byte();
	unsigned char azh = i2c_read_last_byte();
	i2c_stop();

	acc->x = ((int16_t)(axh << 8 | axl)) >> 4;
	acc->y = ((int16_t)(ayh << 8 | ayh)) >> 4;
	acc->z = ((int16_t)(azh << 8 | azl)) >> 4;

}

void vector_cross(const vector *a, const vector *b, vector *out)
{
	out->x = a->y * b->z - a->z * b->y;
	out->y = a->z * b->x - a->x * b->z;
	out->z = a->x * b->y - a->y * b->x;
}

float vector_dot(const vector *a, const vector *b)
{
	return a->x * b->x + a->y * b->y + a->z * b->z;
}

void vector_normalize(vector *a)
{
	float mag = sqrt(vector_dot(a, a));
	a->x /= mag;
	a->y /= mag;
	a->z /= mag;
}
int heading(vector from)
{
	vector a;
	vector m;
	readAcc(&a);
	readMag(&m);
	
	// shift and scale
	m.x = (m.x - m_min.x) / (m_max.x - m_min.x) * 2 - 1.0;
	m.y = (m.y - m_min.y) / (m_max.y - m_min.y) * 2 - 1.0;
	m.z = (m.z - m_min.z) / (m_max.z - m_min.z) * 2 - 1.0;

	vector temp_a = a;
	// normalize
	vector_normalize(&temp_a);
	//vector_normalize(&m);

	// compute E and N
	vector E;
	vector N;
	vector_cross(&m, &temp_a, &E);
	vector_normalize(&E);
	vector_cross(&temp_a, &E, &N);
	
	// compute heading
	int heading = round(atan2(vector_dot(&E, &from), vector_dot(&N, &from)) * 180 / M_PI);
	if (heading < 0) heading += 360;
	
	return heading;
}

I’m sure my values vector m_max = {464, 324, 408} vector m_min = {-616, -634, -730}; are correct(I made a calibration).
What’s problem with my program? When did I turn around my minimu, the angles wasn’t between 0…359, but there were values 0…40…0 and 330…360…330.
What’s wrong with my code?
Thanks for reply
Best regards

Hello,

Are you using the IMU with an Orangutan robot controller? Do you know which version of the MinIMU-9 you have? We have made boards that use three different compass ICs: MinIMU-9 with LSM303DLH, MinIMU-9 with LSM303DLM, and MinIMU-9 v2 with LSM303DLHC. The code you posted will only work with the LSM303DLH; it would need to be changed slightly for the other chips.

- Kevin

I’m sure it’s not MinIMU-9 v2 with LSM303DLHC. But i can’t recognize, is it DLM or DLH. Could you tell me, what should I change in my code, to work with both devices?

The magnetometer Y and Z output registers are reversed on the DLM compared to the DLH. You can look at the datasheet for more details or refer to our LSM303DLM Orangutan example project to see how to change your code, if necessary.

If you can read the markings on the top of the LSM303, you should be able to tell which version you have. The DLH is printed with “HM55”, while the DLM is printed with “303DH”.

- Kevin