unity如何控制android陀螺仪

2015年01月14日 11:40 1 点赞 0 评论 更新于 2025-11-21 14:21

一、背景

1. 需求

陀螺仪硬件未直接连接到Android CPU,因此不存在陀螺仪驱动。陀螺仪数据是通过用户空间的一个C程序传输过来的。

2. 思路

修改陀螺仪HAL(Hardware Abstraction Layer,硬件抽象层)层,在HAL层构建socket客户端,同时在数据源的C程序上构建socket服务端。当有数据产生时,C程序通过socket将数据发送到陀螺仪HAL层并进行上报。

二、步骤

1. 拷贝目录

device/samsung/crespo/libsensors目录拷贝到hardware/libhardware/modules目录下。

2. 修改传感器数组定义

打开sensors.cpp文件,修改其中的传感器数组定义。由于我们仅使用陀螺仪传感器,所以将其他传感器的定义删除,仅保留陀螺仪传感器的定义,代码如下:

static const struct sensor_t sSensorList[] = {
{ "K3G Gyroscope sensor",
"STMicroelectronics",
1, SENSORS_GYROSCOPE_HANDLE,
SENSOR_TYPE_GYROSCOPE, RANGE_GYRO, CONVERT_GYRO, 6.1f, 1190, { } },
};

3. 修改sensors_poll_context_t结构体

sensors.cpp中修改sensors_poll_context_t结构体,删除其他传感器的定义,代码如下:

struct sensors_poll_context_t {
struct sensors_poll_device_t device; // must be first

sensors_poll_context_t();
~sensors_poll_context_t();
int activate(int handle, int enabled);
int setDelay(int handle, int64_t ns);
int pollEvents(sensors_event_t* data, int count);

private:
enum {
gyro = 0,
numSensorDrivers,
numFds,
};

static const size_t wake = numFds - 1; //wake = 1
static const char WAKE_MESSAGE = 'W';
struct pollfd mPollFds[numFds];//2
int mWritePipeFd;
SensorBase* mSensors[numSensorDrivers];// 2

int handleToDriver(int handle) const {
switch (handle) {
case ID_GY:
return gyro;
}
return -EINVAL;
}
};

同时,修改构造函数,删除与其他传感器相关的代码,只保留陀螺仪传感器的初始化代码:

sensors_poll_context_t::sensors_poll_context_t()
{
mSensors[gyro] = new GyroSensor();
mPollFds[gyro].fd = mSensors[gyro]->getFd();
mPollFds[gyro].events = POLLIN;
mPollFds[gyro].revents = 0;

int wakeFds[2];
int result = pipe(wakeFds);
LOGE_IF(result<0, "error creating wake pipe (%s)", strerror(errno));
fcntl(wakeFds[0], F_SETFL, O_NONBLOCK);
fcntl(wakeFds[1], F_SETFL, O_NONBLOCK);
mWritePipeFd = wakeFds[1];

mPollFds[wake].fd = wakeFds[0];//store the reading fd of the pipe
mPollFds[wake].events = POLLIN;
mPollFds[wake].revents = 0;
}

4. 修改GyroSensor.cppgetFd()函数

GyroSensor.cppgetFd()函数中原来返回陀螺仪设备驱动的文件描述符,改为返回socket的文件描述符,代码如下:

int GyroSensor::getFd()
{
if(mSocketFd == -1){
socket_connect();
}
return mSocketFd;
}

其中,mSocketFd是在GyroSensor.h中新定义的socket文件描述符,socket_connect()函数用于实现连接socket服务端。

sensors.cpp文件的sensors_poll_context_t构造函数中,初始化gyro类后,通过getFd()获取socket文件描述符,然后就可以使用poll函数对文件描述符进行数据读取。poll的读取操作在sensors_poll_context_t::pollEvents函数中,代码无需改变,如下所示:

int sensors_poll_context_t::pollEvents(sensors_event_t* data, int count)
{
int nbEvents = 0;
int n = 0;

do {
// see if we have some leftover from the last poll()
for (int i=0 ; count && i<numSensorDrivers ; i++) {
SensorBase* const sensor(mSensors[i]);
if ((mPollFds[i].revents & POLLIN) || (sensor->hasPendingEvents())) {
int nb = sensor->readEvents(data, count);
if (nb < count) {
// no more data for this sensor
mPollFds[i].revents = 0;
}
count -= nb;
nbEvents += nb;
data += nb;
}
}

if (count) {
// we still have some room, so try to see if we can get
// some events immediately or just wait if we don't have
// anything to return
n = poll(mPollFds, numFds, nbEvents ? 0 : -1);
if (n<0) {
LOGE("poll() failed (%s)", strerror(errno));
return -errno;
}
//read data from pipe
if (mPollFds[wake].revents & POLLIN) {
char msg;
int result = read(mPollFds[wake].fd, &msg, 1);
LOGE_IF(result<0, "error reading from wake pipe (%s)", strerror(errno));
LOGE_IF(msg != WAKE_MESSAGE, "unknown message on wake queue (0x%02x)", int(msg));
mPollFds[wake].revents = 0;
}
}
// if we have events and space, go read them
} while (n && count);

return nbEvents;
}

5. 修改gyro类的构造函数

删除gyro类构造函数中与设备驱动相关的代码,代码如下:

GyroSensor::GyroSensor()
: SensorBase(NULL, "gyro"),
mEnabled(0),
mInputReader(4),
mHasPendingEvent(false),
mEnabledTime(0),
mSocketFd(-1)
{
mPendingEvent.version = sizeof(sensors_event_t);
mPendingEvent.sensor = ID_GY;
mPendingEvent.type = SENSOR_TYPE_GYROSCOPE;
memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data));
}

6. 修改gyro类的其他成员函数

删除gyro类的setInitialStateenablesetDelay等函数中的代码,使其返回0,代码如下:

int GyroSensor::setInitialState() {
return 0;
}

int GyroSensor::enable(int32_t, int en) {
return 0;
}

int GyroSensor::setDelay(int32_t handle, int64_t delay_ns) {
return 0;
}

7. 修改gyro类的readEvents函数

该函数是最重要的函数,用于从socket中读取陀螺仪数据,代码如下:

float value = 0;
char buf[2];
int nread = 0;

nread = read(mSocketFd, buf, 2);
if(nread <= 0){
LOGE("GyroSensor::readEvents read error.");
return 0;
}

value = (float)((buf[0] << 8) + buf[1]);
value *= CONVERT_GYRO_X;

mPendingEvent.data[0] = value;
mPendingEvent.data[1] = value;
mPendingEvent.data[2] = value;

*data = *mPendingEvent;
return 1;

socket服务端每次发送2个字节的数据,由于是单轴陀螺仪,只有1个数据。我们将mPendingEventdata数组的3个元素都填充为同一数据,然后赋值给*data,最后返回1,表示读到1个数据。

8. 修改Android.mk文件

打开libsensors下的Android.mk文件,修改如下:

LOCAL_MODULE := sensors.default

9. 编译与测试

编译后,会生成sensors.default.so文件,将其放置到lib/hw目录下。删除hw目录下其他包含sensors文字的.so文件,仅保留sensors.default.so文件。然后就可以在模拟器或设备上进行测试,以下是App层的Java测试程序:

package com.hase.ng102.sensors;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class SensorDemoActivity extends Activity {
//设置LOG标签
private static final String TAG = "sensors";
private  SensorManager sm;
private TextView tv1;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

tv1 = (TextView)this.findViewById(R.id.tv1);
sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Log.d(String.format("getSystemService %s", sm == null ? "sucess":"fail"), TAG);
int sensorType = Sensor.TYPE_GYROSCOPE;
sm.registerListener(myAccelerometerListener,sm.getDefaultSensor(sensorType),SensorManager.SENSOR_DELAY_NORMAL);
}

/*
* SensorEventListener接口的实现,需要实现两个方法
* 方法1 onSensorChanged 当数据变化的时候被触发调用
* 方法2 onAccuracyChanged 当获得数据的精度发生变化的时候被调用,比如突然无法获得数据时
* */
final SensorEventListener myAccelerometerListener = new SensorEventListener(){
//复写onSensorChanged方法
public void onSensorChanged(SensorEvent sensorEvent){
if(sensorEvent.sensor.getType() == Sensor.TYPE_GYROSCOPE){
Log.i(TAG,"onSensorChanged");

//图解中已经解释三个值的含义
float x = sensorEvent.values[0];
float y = sensorEvent.values[1];
float z = sensorEvent.values[2];

String msg = String.format("x=%f\n y=%f\n z=%f\n",x,y,z);
tv1.setText(msg);
Log.i(TAG, msg);
}
else {
String msg = "other sensors data changed";
tv1.setText(msg);
Log.i(TAG, msg);
}
}
//复写onAccuracyChanged方法
public void onAccuracyChanged(Sensor sensor , int accuracy){
Log.i(TAG, "onAccuracyChanged");
}
};

public void onPause(){
/*
* 很关键的部分:注意,说明文档中提到,即使activity不可见的时候,感应器依然会继续的工作,测试的时候可以发现,没有正常的刷新频率
* 也会非常高,所以一定要在onPause方法中关闭触发器,否则将耗费用户大量电量,很不负责。
* */
sm.unregisterListener(myAccelerometerListener);
super.onPause();
}
}