[Arduino]ADXL345,HMC5883L,ITG3200读数的物理意义

Photobucket

作为飞控系统中最常用的传感器芯片,ADXL345、HMC5883、ITG3200 是应用最为广泛的。本文就配合视频,给大家提供一个感性认识,也作为自己的一个学习笔记。

  • ADXL345,为加速度感应芯片,常用于测量设备倾角。
  • HMC5883L,为电子罗盘,俗称电子指南针。
  • ITG3200/ITG3205,为角速度传感器/电子陀螺仪。

三种传感器,都与物体的空间位置和角度有关,在理解其读数意义前,需要首先建立直空间三维坐标系,由右手法则确定。

ADXL345,是加速度感应芯片,可以测量正交三轴的加速度,好像有点玄乎。其实,换一个角度来看,因为 F=ma,(牛顿第一定律),其中质量m为定值,所以测量了加速度a,也就得出了当前物体所受的力。其物理模型,可以理解为在无重力环境下的空心立方体中悬浮的小球。在经典物理学静止的状态下,这个小球无疑受到重力的作用,所以,这个小球是悬浮不起来的。所以它就落在“地上”。而“地面”感应到了来自于小球的压力,进而测量出小球的重力。而测出了重力,就测得了当前的重力加速度。再扩展一下,这个加速度可以来自于重力,当然也可以来自于其它力,比方说引擎的推力,刹车的摩擦力,等等。由于在经典环境下重力加速度始终存在,所以加速度传感器时刻感应到的是重力,以及外界合力,共同作用下所得到的加速度。

换句话说,加速度传感器始终能测量到重力加速度,所以在静止状态下,根据重力加速度在x、y、z三轴的分量,可以测得传感器当前的倾斜角度。所以ADXL345常被用作倾斜传感器来使用。

以右手定则,建立直角坐标系以后,注需要加速度的方向坐标系是“反向”的关系。也就说,在静止平放的状态下。Z轴正方向朝上,重力角速度方向朝下,而此时传感器的Z轴读数却是“正数”。或者说,虽然是传感器内部的小球受的加速度影响,但是我们是通过小球对其周边各个作用面的影响,来获取读数的。根据《牛顿第三运动定律》,我们实际测得是这个”方向相反、大小相同”的力。至于为什么是这样,其实这和传感器内部的原理有关,可以参考《A Guide To using IMU (Accelerometer and Gyroscope Devices) in Embedded Applications》。

因为这些传感器,都是基于MEMS(微机电系统)制造,所以把“自身重力”,作为地球对它的引力,当所受外力之一,这样应该能更容易理解一些。

HMC5883L 是电子罗盘,也就是指南针。类似于 ADXL345,将垂直向下的重力(加速度),变为了指向北方的磁力。结合视频,可以看到,当坐标轴正方向,指向北方的时候,读数为正数;当坐标轴正方向指向南方的时候,读数为负数。视频中这个部分讲得比较晕,越看越糊涂的朋友,不妨静音观看。视频中,有几次把这个模块称为“3886”,为口误,特此更正。

ITG3200/3205,为电子陀螺仪,为角速度感应芯片,测量的是X、Y、Z正交三轴的角速度。角速度的方向,同样通过“右手法则”确定。比方说,平放在桌面上,Z轴正方向朝上,根据右手法则,将模块逆时针旋转的时候,Z轴角速度读数为正数,若顺时针旋转,读数为负数。视频介绍中,也讲反了一回,特此更正,让各位观众笑话了。在实际应用过程中,通过对角速度进行积分,简单来说,就是把角速度与扫描时间间隔的乘积进行累加,可以算出“角位移”,也就是模块沿X, Y, Z三轴旋转的角度。

细心的朋友或许就会注意到,这个角度,不正是先前 ADXL345 的数据差不多是同一个概念么?确实是这样,在“静止”的情况下。因为只有在“静止”的情况下,345只受重力作用的情况下,ADXL345 才能作为倾斜角度传感器使用。

同样,HMC3885,在外部磁场环境不发生改变的情况下,也能计算出角位移。

而在对 ADXL345 测得的三轴加速度进行二次积分,第一次积分获得线速度,第二次获得位移,就能获得空间的位移。没错,传说中的“惯性制导”的原理大概就是这个样子,再高速运算的基础上,配合另外2个传感器参数,不断对计算进行校正,就能获得更加精确的数据。这三种传感器的数据,并不是相互孤立,而是在某种程度上,可以相互进行验证的。这些内容,请参见《大学物理》。

也就是说这三个传感器,看似是在测量不同的数据,其实在某种程度上可以相互验证以及辅助,从而达到制导的目的。可是估计到这样比较费劲,计算比较复杂,累计误差比较严重。后来想想还是发射24颗卫星,整个GPS更靠谱吧~

Photobucket

背面接线图,非常的简单,因为买到的模块,ADXL345+HMC5883这边,总线接口在右边,而ITG3200的接口在左边,所以配合电压转换板,4条L型并列的线就能完成了。
Photobucket

介绍一下我的测试平台,这三个模块,都可以通过I2C总线进行连接。总线,英文原词就是“Bus”,公交车那个词。因为是公交,所以可以搭乘多名旅客,也就是说,可以同时连接多个模块,并分别进行对话,获取数据。所以除了电源的部分,数据线只有两条,SDA和SLK。而借助 Arduino 官方的 Wire 库,可以非常简单地实现通讯。

显示部分,是以 ST7920 为驱动芯片的 12864 液晶图形模式显示,软件部分自己写的类库驱动(是不是看到了在M2407上面使用的字库呀?)I2C代码的部分,将三种模块的驱动代码,使用统一的格式整合在一起,至于内部的对驱动模块的配置,还是仔细去研究手册,代码中不作特殊的宏定义,以方便与 datasheet 中实际的代码进行对应。测试代码中,未对偏差进行任何补偿,因为对于程序来说来说,这些都是恐怖的“神秘数”,在搞清楚为什么之前,还是直接显示原始的数据为好。

这三个模块,最主要的读数,都是以int型(16位、有符号),所以显示的代码也能很方便的整合在一起。我在12864上显示的时候,直接以十六进制方式打印,若第一位<8,则为正数,否则为负数(左起第一位为符号位)。这样依然不够直观,所以我将其取绝对值后,根据符号正负,打印在中线的两侧,长度与其绝对值成正比。如果在视频中看得不太清楚,可以对着照片先看一下。 示例代码如下,并不完整,只包含I2C通讯的部分。显示部分,大家可以根据自身实际灵活替换。 Photobucket

///////////////////////////////////////////////////
// Test code for ADXL345, HMC5883L, ITG3200 with Wire library
// author: aGuegu / Weihong Guan
// Date: April 22nd, 2012
///////////////////////////////////////////////////
#include "PilotTest.h"
#include "Lcd12864.h"
#include "Lcd12864Image.h"

#include "Wire.h"
Lcd12864 lcd(8, 9, 10, 11, 12, 13);
Lcd12864Image img(&lcd);

void writeToWire(int device, byte address, byte val);
void readfromWire(int device, byte address, int count, byte * p);

void setup()
{
	img.init();
	img.clear(0x00);

	Wire.begin();

	writeToWire(0x53, 0x2d, 0x08); // 345
	writeToWire(0x53, 0x2c, 0x09); // 345

	writeToWire(0x1e, 0x00, 0x70); // 5883L
	writeToWire(0x1e, 0x01, 0x20); // 5883L
	writeToWire(0x1e, 0x02, 0x00); // 5883L

	writeToWire(0x68, 0x3e, 0x00); //3200
	writeToWire(0x68, 0x15, 19); //3200
	writeToWire(0x68, 0x16, 0x1e); //3200
	writeToWire(0x68, 0x17, 0x00); //3200
}

void writeToWire(int device, byte address, byte val)
{
	Wire.beginTransmission(device);
	Wire.write(address);
	Wire.write(val);
	Wire.endTransmission();
}

void readfromWire(int device, byte address, int count, byte * p)
{
	Wire.beginTransmission(device);
	Wire.write(address);
	Wire.endTransmission();

	Wire.requestFrom(device, count);

	while (Wire.available())
	{
		*(p++) = Wire.read();
	}
}

void printString(uint16_t c, uint16_t r, const char *s);
void showdata(int *data, char *chr, byte length, byte col, byte shift_right);

void showAdxl345(byte col)
{
	byte data[6];
	readfromWire(0x53, 0x32, 6, data);
	char chr[3] = { 'X', 'Y', 'Z' };
	int acc[3];

	for (byte i = 0; i < 3; i++)
	{
		acc[i] = makeWord(data[i + i + 1], data[i + i]);
	}

	showdata(acc, chr, 3, col, 4);
}

void showHmc5883(byte col)
{
	byte data[6];
	readfromWire(0x1e, 0x03, 6, data);
	char chr[3] = { 'X', 'Y', 'Z'};
	int mag[3];

	for (byte i = 0; i < 3; i++)
	{
		mag[i] = makeWord(data[i + i], data[i + i + 1]);
	}

	int temp = mag[1]; mag[1] = mag[2]; mag[2] = temp; // the original order is X, Z, Y

	showdata(mag, chr, 3, col, 5);
}

void showItg3200(byte col)
{
	byte data[6];
	readfromWire(0x68, 0x1d, 6, data);
	char chr[3] = { 'X', 'Y', 'Z' };
	int angular_rate[3];

	for (byte i = 0; i < 3; i++)
	{
		angular_rate[i] = makeWord(data[i + i], data[i + i + 1]);
	}

	showdata(angular_rate, chr, 3, col, 8);
}

void loop()
{
	img.clear(0x00);

	img.putNumberV(0, img.HEIGHT_MAX - (46 - 14 - 4), '-');
	img.putNumberV(0, img.HEIGHT_MAX - (46 + 14), '+');

	printString(8, img.HEIGHT_MAX, "ADXL345:");
	showAdxl345(16);
	printString(44, img.HEIGHT_MAX, "HMC5883:");
	showHmc5883(52);
	printString(80, img.HEIGHT_MAX, "ITG3200:");
	showItg3200(88);

	img.print();
}

在 Arduino 平台上,连接这样的I2C总线的模块,非常的简单。这也正是 Arduino 平台强大之所在。:)

这像这三种芯片,正广泛地被应用到移动领域,iPhone、Android 有大量的娱乐应用,都用到这些传感器,没准型号还和本文提到的这几款一模一样。用Arduino 可以非常轻松地与这些模块实现交互,快来试试吧~

视频说明:(全屏播放能看得更清楚哦,720p录制)

特别感谢:@Zogna圣源电子
关于这几个模块的单独驱动、以及接线说明,大家也可以参考 极客工坊的 Aduino 学习笔记

PS. 做这样的实验电路,硬件连接一定要靠谱哦,如果是用 面包板 + 面包线 的话,还请三思而后行……

关于aGuegu

阿古 真名:官微宏,技术Geek,玩Arduino,玩Linux,爱Google,爱开源;现居福州
此条目发表在模块分类目录,贴了, , , , , , , , , 标签。将固定链接加入收藏夹。
  • zcb

    讲解很用心,基本明白几个传感器模块的用处。谢谢

  • 很详细的讲解,很直观的展示,很用心的制作,顶!!

    • 嘿嘿,有头像的评论,还是蛮不容易哦~

  • xiaoha_abc

    把背景音乐去掉多好啊。。。

    • 没音乐的话,一个人自说自话,气氛很诡异哦~

  • MG

    受益非浅!!很感谢楼主详细的讲解,使我明白了不少这三个模块的物理概念和应用。

  • Pingback: [arduino|module] Kalman Filter on ADXL345, ITG3200(视频) | Agu's Mill 古作坊()

  • Woody

    感觉博主的ADXL345和ITG3200参数弄反了

  • 李韬

    楼主果然犀利。我对这方面很感兴趣,也研究了一些技术。看了你的简介,很想和你做个朋友交流啊。不知道你是否方便哦。

  • 李韬

    楼主你好啊。我对这个也正在研究,不知是否可以交个朋友,想向你学点东西!~