Android:使用加速度计创建一个简单的马拉卡应用

Posted

技术标签:

【中文标题】Android:使用加速度计创建一个简单的马拉卡应用【英文标题】:Android: Using the accelerometer to create a simple maraca app 【发布时间】:2013-09-11 21:50:12 【问题描述】:

我正在尝试通过创建(我认为应该是的)一个简单的应用程序来模仿粗糙的马拉卡,来学习如何使用加速度计。

目的是当手机快速向下轻弹时,它会在轻弹结束时发出马拉卡声音,同样在向上轻弹结束时发出不同的声音。

实现这一点的策略是检测加速度何时超过某个阈值。发生这种情况时,ShakeIsHappening 被设置为 true,并且来自 z 轴的数据被输入到一个数组中。进行比较以查看z数组中的第一个位置是大于还是小于最近的位置,以查看电话是向上还是向下移动。这存储在一个名为 zup 的布尔值中。

一旦加速度低于零,我们假设轻弹运动已经结束并发出声音,根据运动是向上还是向下 (zup) 来选择。

代码如下:

public class MainActivity extends Activity implements SensorEventListener 

    private float mAccelNoGrav;
    private float mAccelWithGrav;
    private float mLastAccelWithGrav;

    ArrayList<Float> z = new ArrayList<Float>();

    public static float finalZ;

    public static boolean shakeIsHappening;
    public static int beatnumber = 0;
    public static float highZ;
    public static float lowZ;
    public static boolean flick;
    public static boolean pull;
    public static SensorManager sensorManager;
    public static Sensor accelerometer;


    private SoundPool soundpool; 
    private HashMap<Integer, Integer> soundsMap;

    private boolean zup;

    private boolean shakeHasHappened;
    public static int shakesound1 = 1;
    public static int shakesound2 = 2;


    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        results = (TextView) findViewById(R.id.results);
        clickresults = (TextView) findViewById(R.id.clickresults);

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        accelerometer = sensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        mAccelNoGrav = 0.00f;
        mAccelWithGrav = SensorManager.GRAVITY_EARTH;
        mLastAccelWithGrav = SensorManager.GRAVITY_EARTH;

        soundpool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
        soundsMap = new HashMap<Integer, Integer>();
        soundsMap.put(shakesound1, soundpool.load(this, R.raw.shake1, 1));
        soundsMap.put(shakesound2, soundpool.load(this, R.raw.shake1, 1));

    

    public void playSound(int sound, float fSpeed) 
        AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
        float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        float volume = streamVolumeCurrent / streamVolumeMax;  
        soundpool.play(soundsMap.get(sound), volume, volume, 1, 0, fSpeed);
       


    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) 

    

    @Override
    public void onSensorChanged(SensorEvent event) 

        float x = event.values[0];
        float y = event.values[1];
        z.add((event.values[2])-SensorManager.GRAVITY_EARTH);
        mLastAccelWithGrav = mAccelWithGrav;
        mAccelWithGrav = android.util.FloatMath.sqrt(x * x + y * y + z.indexOf(z.size()-1) * z.indexOf(z.size()-1));
        float delta = mAccelWithGrav - mLastAccelWithGrav;
        mAccelNoGrav = mAccelNoGrav * 0.9f + delta; // Low-cut filter

        if (mAccelNoGrav > 3) 
            shakeIsHappening = true;
            z.clear();
         
        if (mAccelNoGrav < 0) 
            if (shakeIsHappening) 
            shakeIsHappening = false;
            shakeHasHappened = true;
        
        

        if (shakeIsHappening && z.size() != 0) 
            if (z.get(z.size()-1) > z.get(0)) 
                zup = true;
             else if (z.get(0) > z.get(z.size()-1)) 
                zup  = false;
        
        
        if (shakeHasHappened) 
            Log.d("click", "up is" + zup + "Low Z:" + z.get(0) + " high Z:" + z.get(z.size()-1));
            if (!zup) 
                shakeHasHappened = false;
                playSound(shakesound2, 1.0f);
                z.clear();

             else if (zup) 
                shakeHasHappened = false;
                playSound(shakesound1, 1.0f);
                z.clear();              
            
        
        

我遇到的一些问题是:

    我认为 ShakeHasHappened 在减速开始时开始,当加速度低于零时。也许这应该是当减速停止时,当加速度变为负值并且现在正回到零时。这听起来合理吗?

    检测运动是向上还是向下的方法不起作用 - 这是因为我在查看 z 轴时无法准确读取手机的位置,因为加速度也包括在内在 z 轴数据中,因此没有给我手机的准确位置?

    我得到了很多双击,但我不太明白这是为什么。有时它根本不点击。

如果有人想尝试一下这段代码,看看他们是否能找到一种方法让它更准确、更高效,请继续分享你的发现。如果有人能发现为什么它没有按照我想要的方式工作,请再次分享您的想法。

要将声音链接到此代码,请将 wav 文件放入 res\raw 文件夹并在 R.raw.shake1 位(无扩展名)中引用它们

谢谢

编辑:我做了一些研究,偶然发现了一种叫做动态时间扭曲的东西。我对此一无所知,但会开始研究它。有谁知道 DTW 是否可以成为实现工作马拉卡模拟器应用程序的不同方法?

【问题讨论】:

【参考方案1】:

我可以给你一些建议:

首先,我注意到您为两种结果使用了相同的资源:

soundsMap.put(shakesound1, soundpool.load(this, R.raw.shake1, 1));
soundsMap.put(shakesound2, soundpool.load(this, R.raw.shake1, 1));

shakesound2 的资源应该是R.raw.shake2

其次,以下只处理其中一种动议:

if (mAccelNoGrav > 3)

这应该改为:

if (mAccelNoGrav > 3 || mAccelNoGrav < -3)

目前,您没有拦截向下运动。

第三,加速度值3相当低。如果你想避免/过滤掉正常的手臂运动,这个值应该在 6 或 7 和 -6 或 -7 左右。

第四,您不需要存储 z 值来检查运动是向上还是向下。您可以检查是否:

mAccelnoGrav > 6 ===> implies motion was upwards

mAccelnoGrav < -6 ===> implies motion was downwards

您可以使用此信息相应地设置zup

第五:我只能猜到你在使用if (mAccelNoGrav &lt; 0) 来播放动作结束时的声音。在这种情况下,此检查应更改为:

if (mAccelNoGrav < epsilon || mAccelNoGrav > -epsilon)

其中epsilon 是某个范围,例如 (-1, 1)。

第六,您应该在您的应用程序中包含一个lockout 句点。这将是在满足所有条件并且即将播放声音之后的时期。接下来,比如 1000 毫秒,不要处理传感器值。让运动稳定下来。您需要使用它来避免多次播放。

注意:请在您的代码中包含 cmets。至少,在每个代码块上放置 cmets 以传达您试图用它完成的任务。

【讨论】:

谢谢@user2558882,你给了我很多思考和尝试。很高兴你用细齿梳浏览了我的代码。您关于添加 cmets 的建议已得到适当注意,我会采纳。也许如果我将来这样做,我就不必在赏金上花费积分。非常感谢 @DickyMoore 不客气,伙计。我只想指出一些可以帮助您的事情。不过,我还没有给你一个“代码”解决方案。所以,如果你想暂时不接受我的回答,并等待一个更好的答案,那就继续吧。 @DickyMoore 完全没问题。我注意到人们不太关注已经有公认答案的问题。您应该能够为您花费的代表点数达到最大里程数。 但是,如果我尝试您的更改并且它使应用程序按照我想要的方式运行,那么这是一个确定的答案! 什么是 epsilon?它是一个包含范围的变量类型吗?如何申报?【参考方案2】:

前段时间我尝试自己实现,最终使用了这个解决方案。-

http://jarofgreen.co.uk/2013/02/android-shake-detection-library/

基于您问题中的相同概念。

【讨论】:

以上是关于Android:使用加速度计创建一个简单的马拉卡应用的主要内容,如果未能解决你的问题,请参考以下文章

星际文件系统2021年亚洲黑客马拉松季开启,阿里云万向等助阵

OpenCV2马拉松第5圈——线性滤波

梵文/马拉地语字体

使用加速度计读取 android 手机的 x y z 坐标

信念不熄 热爱当燃|中创算力参加黑客马拉松比赛

Android 加速度计分析