利用手机传感器测量高度

最近简单了解了一下android系统的传感器。其中较常用的是加速计Accelerometer,可以用来实现“摇一摇”。

一开始,从加速计这个名字本身来理解,以为是测量加速度的,也就是手机如果静止,则加速计应该返回0.

后来发现其实是表示手机外部的支撑物对于手机施加的力产生的加速度,其方向相对于手机的本地坐标系。简单说如果手机静止不动,加速计的值为-g。这样的好处是这个值可以反映手机在静止时的方向。


手机本地坐标系

也就是说,手机在跌落的过程中,Accelerometer的值为0(根据不同的硬件,会有不同范围的误差)。嗯,有点意思。

我们完全可以利用这个,通过让手机跌落,记录跌落的时间,然后通过简单的物理公式,算出跌落的距离大笑

在NDK环境下简单实现了一下。在检测到碰撞后顺便估算一下冲击强度。

enum State
{
	Ready = 0,
	Dropping,
	Colliding,
	Idle,
};

int state			= Ready;
int64_t timeStamp	= 0;
float dropHeight	= 0.f;

float averageImpact = 0.f;
int nImpact = 0;


// use accelerometer track the dropping process
// measuring time and estimate dropping height
void TrackDropping(const ASensorEvent& event){

	Vector3f v(event.acceleration.x, event.acceleration.y, event.acceleration.z);
	float lenth = v.Length();

	if (state == Ready) {
		// detect weightlessness
		if (lenth < 1.0f) {
			timeStamp = event.timestamp;
			state = Dropping;
		}
	}

	if (state == Dropping) {
		// detect collide
		if (lenth > ASENSOR_STANDARD_GRAVITY) {
			// in milliseconds, timestamp is in nanoseconds
			int64_t during = (event.timestamp - timeStamp) / 1000000;
			float sec = during / 1000.0f;

			dropHeight = 0.5f * ASENSOR_STANDARD_GRAVITY * sec * sec;

			state = Colliding;
			return;
		}
	}

	if (state == Colliding) {
		averageImpact += lenth;
		nImpact++;

		if (lenth < ASENSOR_STANDARD_GRAVITY) {
			averageImpact /= nImpact;
			GLog.LogInfo("Drop height %.2f impact %.2f", dropHeight, averageImpact);

			averageImpact = 0.f;
			nImpact = 0;
			timeStamp = event.timestamp;
			state = Idle;
		}
		return;
	}

	if (state == Idle) {
		if ((event.timestamp - timeStamp) / 1000000 > 3000) {
			state = Ready;
		}
	}
}

while ((ident = ALooper_pollAll(engine.animating ? 0 : -1, 
			NULL, &events,  (void**)&source)) >= 0) {

            // Process this event.
            if (source != NULL) {
                source->process(state, source);
            }

            // If a sensor has data, process it now.
			if (ident == LOOPER_ID_USER) {
				if (engine.accelerometerSensor != NULL) {
					ASensorEvent event;
					while (ASensorEventQueue_getEvents(engine.sensorEventQueue, &event, 1) > 0) {

						TrackDropping(event);						
					}
				}
			}

            // Check if we are exiting.
            if (state->destroyRequested != 0) {
                engine_term_display(&engine);
                return;
            }
        }

其实用Java实现就可以,但不知道在采样率和延迟方面会不会有所区别。

在我的测试机coolpad大神f1上测试后发现,从开始跌落到通过传感器检测到跌落有一段明显的延迟(大于0.1s),远大于采样间隔(10ms)。同理检测碰撞时也有明显延迟。最重要的是这两个间隔还不一样大,严重影响的测量结果的准确性。

后来想了一个办法,用一次标准跌落(比如1米高)算出一个延迟时间的修正值。使用了延迟修正以后,测量准确度明显提高了(10%以内)。


温馨提示:

小伙伴们自己也可以尝试一下,不过最好先备份手机中的重要数据,以避免不必要的损失。偷笑

展开阅读全文

没有更多推荐了,返回首页