我们知道平面的坐标系直角坐标系(也称笛卡尔坐标系)可以推广到空间,并且空间坐标系更符合实际情况。我们通常采用右手系的空间坐标,其X-Y-Z轴之间符合右手螺旋法则,摈弃向量运算的描述方法,直观的说,就如图所示。与右手系相对,左手系在X-Y轴相同的情况下,Z轴的正方向与右手系相反。一般默认情况下,我们只使用右手系。
根据中学物理可以知道,在如下的场景中,质量为m的物体与底座用弹性系数为k的弹簧相连,物体与底座间忽略摩擦,相对地面的观察者,底座以正向向右加速度a运动时,弹簧将会被拉长x,其中符合关系kx=ma。这给出了一种测量加速度的基本方法。
我们知道加速度是矢量(vector),它的运算符合平行四边形法则或者首尾相接法则。我们选择空间直角坐标系的正交基底、、分解空间中任意方向大小的加速度矢量,就可以得到
因此,我们只要分别构造在X、Y和Z三个方向的加速度计,即可获得三个加速度分量、、的大小。我们知道所有静止在地球表面的物体都有重力加速度,所以在Z轴的方向上有
的加速度在起作用。下图是在Z轴朝正上方时,三个方向加速度计内部的情况。
X、Y方向 Z方向
当我们把三轴加速度计固定在一个物体上,然后随意倾斜一个姿态,这时候,原本在地面的坐标系X-Y-Z上的重力加速度(0, 0, -g)在新的三轴加速度计的坐标系x-y-z中就会产生不同的分量(,,)。
其中也就是重力加速度的铅垂方向,其中、、分别表示、、与方向的夹角。且根据三角学的基本知识,可以知道,
这意味着,使用三轴加速度计,就可以获取物体的姿态,这对于许多类型机器人来说非常重要。无论是飞行器、云台、机械臂还是遥控手柄,都有这方面的需求。所不同的是,今天众多的传感器都是基于半导体技术而非机械结构式的,所以芯片里其实没有弹簧和一个固定质量的物体。自2007年苹果的iPhone手机发布以来,在各种消费电子产品中加入各种传感器已经不是什么新鲜事,三轴加速度计是当今智能手机中必备的传感器,否则你就无法通过双手横向握持手机来模拟方向盘玩赛车游戏了。其实这些更新更好的用户体验都是以手机中拥有此类传感器为前提条件的。
下面,我们以市面上较为常见的一款加速度计传感器芯片ADXL345为例简要说明如何使用Arduino连接它。ADXL345是美国AD公司的产品,关于该芯片的详细技术细节,可以查看ADXL345 datasheet。
如图所示,是一个常见的ADXL345模块,此模块把ADXL345周边所需的常用元器件都集成到一块电路板上,从而减少了连线的麻烦。在电路板丝印层上还有标明每个引脚的作用,从ADXL345的DataSheet中得知,ADXL的供电范围是VS=1.8~3.6V,很显然模块上还有降压模块,才能直连Arduino的5V供电电压。不同于ADXL335(此款三轴加速度计通过模拟量直接输出信息,但是目前普及率已经没有ADXL345高),此芯片采用了I2C总线结构与控制器通信,所以只需要连接VCC、GND、SCL、SDA即可工作,其它高级功能引脚初学者暂时无须理会。关于I2C总线的详细内容,请参考附录或维基百科。
I2C总线不同于串口,它的主从设备分别共用时钟线SDA(Serial Data)和数据线SCL(Serial Clock),而且主设备(Master)可以通过I2C总线同时连接几个从设备(Slave),但每个时刻只能和其中一个设备通信。这样在不多占用输入输出(I/O)的前提下,我们就可以连接多个传感器或者其它电子模块。
新版的Arduino UNO R3的I2C总线丝印隐藏在背后,如图所示,很多不细心的使用者刚开始可能没有发现。
图所示,我们以SparkFun的ADXL345模块为例,给出连线图,其它模块大同小异。唯一需要注意的是,如果模块上没有降压模块,一般模块上的Vcc要和Arduino的3.3V引脚相连,而不是5V。
GitHub社区上面许多Arduino爱好者为我们提供了大量简单实用的ADXL345三轴加速度计范例程序,我们往往只需拿来简单修改后即可满足自己的使用需求。
下面以其中一个为范例来简要说明,关于代码需要的Arduino库文件,请访问GitHub网站
https://github.com/jarzebski/Arduino-ADXL345获取。
/*
ADXL345三轴加速度计范例程序
源代码出处信息:
http://www.jarzebski.pl/arduino/czujniki-i-sensory/3-osiowy-akcelerometr-adxl345.html
(c) 2014作者Korneliusz Jarzebski
*/
//Wire.h库文件用于加载I2C通信协议
#include <Wire.h>
#include <ADXL345.h>
ADXL345 accelerometer;
void showRange(void)
{
Serial.print(“Selected measurement range: “);
switch(accelerometer.getRange())
{
case ADXL345_RANGE_16G: Serial.println(“+/- 16 g”); break;
case ADXL345_RANGE_8G: Serial.println(“+/- 8 g”); break;
case ADXL345_RANGE_4G: Serial.println(“+/- 4 g”); break;
case ADXL345_RANGE_2G: Serial.println(“+/- 2 g”); break;
default: Serial.println(“Bad range”); break;
}
}
void showDataRate(void)
{
Serial.print(“Selected data rate: “);
switch(accelerometer.getDataRate())
{
case ADXL345_DATARATE_3200HZ: Serial.println(“3200 Hz”); break;
case ADXL345_DATARATE_1600HZ: Serial.println(“1600 Hz”); break;
case ADXL345_DATARATE_800HZ: Serial.println(“800 Hz”); break;
case ADXL345_DATARATE_400HZ: Serial.println(“400 Hz”); break;
case ADXL345_DATARATE_200HZ: Serial.println(“200 Hz”); break;
case ADXL345_DATARATE_100HZ: Serial.println(“100 Hz”); break;
case ADXL345_DATARATE_50HZ: Serial.println(“50 Hz”); break;
case ADXL345_DATARATE_25HZ: Serial.println(“25 Hz”); break;
case ADXL345_DATARATE_12_5HZ: Serial.println(“12.5 Hz”); break;
case ADXL345_DATARATE_6_25HZ: Serial.println(“6.25 Hz”); break;
case ADXL345_DATARATE_3_13HZ: Serial.println(“3.13 Hz”); break;
case ADXL345_DATARATE_1_56HZ: Serial.println(“1.56 Hz”); break;
case ADXL345_DATARATE_0_78HZ: Serial.println(“0.78 Hz”); break;
case ADXL345_DATARATE_0_39HZ: Serial.println(“0.39 Hz”); break;
case ADXL345_DATARATE_0_20HZ: Serial.println(“0.20 Hz”); break;
case ADXL345_DATARATE_0_10HZ: Serial.println(“0.10 Hz”); break;
default: Serial.println(“Bad data rate”); break;
}
}
void setup(void)
{
Serial.begin(9600);
//初始化ADXL345
Serial.println(“Initialize ADXL345”);
if (!accelerometer.begin())
{
Serial.println(“Could not find a valid ADXL345 sensor, check wiring!”);
delay(500);
}
// 设置加速度测量范围
accelerometer.setRange(ADXL345_RANGE_16G);
// 显示目前设置参数
showRange();
showDataRate();
}
void loop(void)
{
//读取规范化数值
Vector raw = accelerometer.readRaw();
Vector norm = accelerometer.readNormalize();
//输出行
Serial.print(” Xraw = “);
Serial.print(raw.XAxis);
Serial.print(” Yraw = “);
Serial.print(raw.YAxis);
Serial.print(” Zraw: “);
Serial.print(raw.ZAxis);
//输出规范化加速度值,单位是m/s^2
Serial.print(” Xnorm = “);
Serial.print(norm.XAxis);
Serial.print(” Ynorm = “);
Serial.print(norm.YAxis);
Serial.print(” Znorm = “);
Serial.print(norm.ZAxis);
Serial.println();
delay(100);
}
此范例程序在运行后只会在串口简单输出三个轴向的加速度值,这样对于物体姿态的判断仍然不够直观,在此作者提供的代码库中还有一个Processing的范例,配合Processing的上位机程序,显示物体姿态的效果非常直观,如图所示。关于如何安装和运行Processing程序,请参考附录或者到本书配套网站查询。