通过NativeActivity NDK访问(更快的轮询)加速度计

14

我在Stack Overflow上搜索了如何使用 NDK 更快地轮询加速度计的教程和答案,但是仍未找到解决方案。只发现了Android开发者文档(这里)

我需要按照每秒100个样本(100Hz)的频率轮询加速度,但默认情况下我的设备(三星Galaxy SL i9003,搭载Gingerbread 2.3.5)只能获得约每秒60个样本(60Hz)。 因此,我尝试通过使用基于sensor.h和looper.h的文件生成器来访问NDK的传感器:

#include <jni.h>
#include <string.h>

#include <android/sensor.h>
#include <android/log.h>
#include <android/looper.h>

#define TAG "accelerondk"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)

#define LOOPER_ID 1
#define SAMP_PER_SEC 100 //i've changed to 120, even 10, but nothing happen

void Java_azka_web_ndk_AcceleroNDKActivity_startMonitoring(JNIEnv* env, jclass clazz) {
    ASensorManager* sensorManager = ASensorManager_getInstance();

    ALooper* looper = ALooper_forThread();
    if(looper == NULL)
        looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);

    ASensorRef accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager,ASENSOR_TYPE_ACCELEROMETER);
    LOGI("accelerometerSensor: %s, vendor: %s", ASensor_getName(accelerometerSensor), ASensor_getVendor(accelerometerSensor));

    ASensorEventQueue* queue = ASensorManager_createEventQueue(sensorManager, looper, LOOPER_ID, NULL, NULL);

    ASensorEventQueue_enableSensor(queue, accelerometerSensor);
    ASensorEventQueue_setEventRate(queue, accelerometerSensor, (1000L/SAMP_PER_SEC)*1000); 

    int ident;//identifier 
    int events;
    while (1) {
        while ((ident=ALooper_pollAll(-1, NULL, &events, NULL) >= 0)) {
            // If a sensor has data, process it now.
            if (ident == LOOPER_ID) {
                ASensorEvent event;
                while (ASensorEventQueue_getEvents(queue, &event, 1) > 0) {
                    LOGI("aaaaaaa accelerometer X = %f y = %f z=%f ", event.acceleration.x, event.acceleration.y, event.acceleration.z);
                }
            }
        }
    }

}

到目前为止,我已经使用NativeActivity访问了加速度计,但是采集的样本数没有改变。即使我将ASensorEventQueue_setEventRate更改为足够大或足够小,记录的加速度仍然每秒约60个样本(每15毫秒1个样本)。

我的代码中有任何错误吗?或者我遗漏了什么吗?

提前致谢。


你解决了这个问题吗? - Jeegar Patel
4个回答

10
我也尝试了一些与传感器的采样率相关的内容。我使用的是Galaxy Nexus手机。如果我只使用加速度传感器,则频率非常低(约为40Hz),但是如果我使用加速度传感器、磁力传感器和陀螺仪传感器,每个传感器的采样率约为100Hz。我不知道为什么会出现这种情况。另一个观察结果是,传递给ASensorEventQueue_setEventRate的值没有任何效果。采样率始终相同。SDK代码的行为完全相同。
下面是我用于基准测试的代码:
#include <string.h>
#include <jni.h>
#include <android/sensor.h>
#include <android/looper.h>
#include <android/log.h>
#include <time.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "TestJNIActivity", __VA_ARGS__))
#define LOOPER_ID 1
#define SAMP_PER_SEC 100

ASensorEventQueue* sensorEventQueue;

int accCounter = 0;
int64_t lastAccTime = 0;

int gyroCounter = 0;
int64_t lastGyroTime = 0;

int magCounter = 0;
int64_t lastMagTime = 0;

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
 */

static int get_sensor_events(int fd, int events, void* data);

struct tm* start;
struct tm* finish;


jstring
Java_de_tum_ndktest_TestJNIActivity_stringFromJNI( JNIEnv* env, jobject thiz )
{
    LOGI("stringFromJNI");
    return (*env)->NewStringUTF(env,"Hello from JNI !");
}

void
Java_de_tum_ndktest_TestJNIActivity_sensorValue( JNIEnv* env, jobject thiz ) {

    ASensorEvent event;
    int events, ident;
    ASensorManager* sensorManager;
    const ASensor* accSensor;
    const ASensor* gyroSensor;
    const ASensor* magSensor;
    void* sensor_data = malloc(1000);

    LOGI("sensorValue() - ALooper_forThread()");

    ALooper* looper = ALooper_forThread();

    if(looper == NULL)
    {
        looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
    }

    sensorManager = ASensorManager_getInstance();

    accSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER);
    gyroSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_GYROSCOPE);
    magSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_MAGNETIC_FIELD);



    sensorEventQueue = ASensorManager_createEventQueue(sensorManager, looper, 3, get_sensor_events, sensor_data);

    ASensorEventQueue_enableSensor(sensorEventQueue, accSensor);
    ASensorEventQueue_enableSensor(sensorEventQueue, gyroSensor);
    ASensorEventQueue_enableSensor(sensorEventQueue, magSensor);

    //Sampling rate: 100Hz
    int a = ASensor_getMinDelay(accSensor);
    int b = ASensor_getMinDelay(gyroSensor);
    int c = ASensor_getMinDelay(magSensor);
    LOGI("min-delay: %d, %d, %d",a,b,c);
    ASensorEventQueue_setEventRate(sensorEventQueue, accSensor, 100000);
    ASensorEventQueue_setEventRate(sensorEventQueue, gyroSensor, 100000);
    ASensorEventQueue_setEventRate(sensorEventQueue, magSensor, 100000);

    LOGI("sensorValue() - START");
}



static int get_sensor_events(int fd, int events, void* data) {
  ASensorEvent event;
  //ASensorEventQueue* sensorEventQueue;
  while (ASensorEventQueue_getEvents(sensorEventQueue, &event, 1) > 0) {
        if(event.type == ASENSOR_TYPE_ACCELEROMETER) {
                //LOGI("accl(x,y,z,t): %f %f %f %lld", event.acceleration.x, event.acceleration.y, event.acceleration.z, event.timestamp);
                if(accCounter == 0 || accCounter == 1000)
                    {
                     LOGI("Acc-Time: %lld (%f)", event.timestamp,((double)(event.timestamp-lastAccTime))/1000000000.0);
                     lastAccTime = event.timestamp;
                     accCounter = 0;
                    }

                accCounter++;
        }
        else if(event.type == ASENSOR_TYPE_GYROSCOPE) {
                //LOGI("accl(x,y,z,t): %f %f %f %lld", event.acceleration.x, event.acceleration.y, event.acceleration.z, event.timestamp);
                if(gyroCounter == 0 || gyroCounter == 1000)
                    {

                     LOGI("Gyro-Time: %lld (%f)", event.timestamp,((double)(event.timestamp-lastGyroTime))/1000000000.0);
                     lastGyroTime = event.timestamp;
                     gyroCounter = 0;
                    }

                gyroCounter++;
        }
        else if(event.type == ASENSOR_TYPE_MAGNETIC_FIELD) {
                //LOGI("accl(x,y,z,t): %f %f %f %lld", event.acceleration.x, event.acceleration.y, event.acceleration.z, event.timestamp);
                if(magCounter == 0 || magCounter == 1000)
                    {
                     LOGI("Mag-Time: %lld (%f)", event.timestamp,((double)(event.timestamp-lastMagTime))/1000000000.0);
                     lastMagTime = event.timestamp;
                     magCounter = 0;
                    }

                magCounter++;
        }

  }
  //should return 1 to continue receiving callbacks, or 0 to unregister
  return 1;
}

你需要创建一个Activity来运行这个程序吗?如果是的话,能否请您发布那段代码? - Richard
抱歉,我已经三年没做这个了。据我所记,我使用了一个Java GUI,从中调用了JNI函数。不幸的是,我没有代码,但我认为它只是一个函数调用。最复杂的部分是在我的电脑上设置NDK(Eclipse)。 - steckl
我已经想通了,谢谢。我可能会在你的答案上加上一些指针。 - Richard
它能够工作,但我们观察到加速度计一直保持唤醒锁定状态!这有意义吗?我认为只有“重要的移动传感器”才会保持唤醒锁定状态。 - ransh

3

这个问题有点老了,但也许对于其他遇到这个问题的人来说,这两篇文章会有所帮助,让他们明白为什么要费心,或如何优化NDK中的示例。

这两篇简短的文章阐述了问题和潜在解决方案(但没有完整的源代码解决方案)。

Java接口传感器性能

本地采样改进


3
链接已失效。请尝试将相关材料复制到您的回答中,以便信息得以保留。 - Richard

2
这是一个旧问题,但由于缺乏文档和文章,我想分享一下我的经验。我在 Nexus 5X 上进行了所有测试。您的设备可能不同。
原始代码看起来没问题。从文档中不明显的是,您只能在启用传感器后才能设置事件速率,并且... 如果重新启用传感器(例如,在 onPause() 和 onResume() 之后),则需要再次设置事件速率。 如果您在 init() 中执行了 enable()/setEventRate() 的“双重启用”,但在 onResume() 中仅执行了 enable(),则会获得默认轮询速率。

0

你可能受到设备加速度计硬件速度的限制。不过,你可以使用插值来获取一些额外的数据点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接