Обнаружение жестких нажатий в любом месте на iPhone с помощью акселерометра

Я пытаюсь обнаружить нажатия, которые могут быть где угодно на iPhone, а не только на экране iPhone. Вот ссылка, которая показывает, что это возможно.

В основном то, что я хочу сделать, это отправить предупреждение, если пользователь 3 раза нажимает на iPhone, когда телефон находится в его кармане. Я достиг того, что я могу обнаружить 3 нажатия, но я также получаю ложные предупреждения в этих случаях. 1) если пользователь ходит, 2) машет телефоном 3) бежит. Мне нужно просто проверить, ударил ли пользователь свой iPhone 3 раза.

Вот мой код.

- (void)accelerometer:(UIAccelerometer *)accelerometer
        didAccelerate:(UIAcceleration *)acceleration
{
    if (handModeOn == NO)
    {
        if(pocketFlag == NO)
            return;
    }

    float accelZ = 0.0;
    float accelX = 0.0;
    float accelY = 0.0;

    accelX = (acceleration.x * kFilteringFactor) + (accelX * (1.0 - kFilteringFactor));
    accelY = (acceleration.y * kFilteringFactor) + (accelY * (1.0 - kFilteringFactor));
    accelZ = (acceleration.z * kFilteringFactor) + (accelZ * (1.0 - kFilteringFactor));

        self.z.text = [NSString stringWithFormat:@"%0.1f", -accelZ];

        if((-accelZ >= [senstivity floatValue] && timerFlag) || (-accelZ <= -[senstivity floatValue] && timerFlag)|| (-accelX >= [senstivity floatValue] && timerFlag) || (-accelX <= -[senstivity floatValue] && timerFlag) || (-accelY >= [senstivity floatValue] && timerFlag) || (-accelY <= -[senstivity floatValue] && timerFlag))
        {
            timerFlag = false;
            addValueFlag = true;
            timer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];
        }

        if(addValueFlag)
        {
            if (self.xSwitch.on)
            {
                NSLog(@"X sWitch is on");
                [self.accArray addObject:[NSNumber numberWithFloat:-accelX]];
            }
            if (self.ySwitch.on)
            {
                NSLog(@"Y Switch is on");
                [self.accArray addObject:[NSNumber numberWithFloat:-accelY]];
            }
            if (self.zSwitch.on)
            {
                NSLog(@"Z Switch is on");
                [self.accArray addObject:[NSNumber numberWithFloat:-accelZ]];
            }

        }
    //}
}

- (void)timerTick:(NSTimer *)timer1
{
    [timer1 invalidate];
    addValueFlag = false;
    int count = 0;

    for(int i = 0; i < self.accArray.count; i++)
    {
        if(([[self.accArray objectAtIndex:i] floatValue] >= [senstivity floatValue]) || ([[self.accArray objectAtIndex:i] floatValue] <= -[senstivity floatValue]))
        {
            count++;
            [self playAlarm:@"beep-1" FileType:@"mp3"];
        }

        if(count >= 3)
        {
            [self playAlarm:@"06_Alarm___Auto___Rapid_Beeping_1" FileType:@"caf"];
            [self showAlert];
            timerFlag = true;
            [self.accArray removeAllObjects];
            return;
        }
    }
    [self.accArray removeAllObjects];
    timerFlag = true;
}

Любая помощь будет действительно оценена.

Спасибо


person sajjoo    schedule 23.11.2013    source источник
comment
Это именно то, что я тоже ищу! Вы когда-нибудь находили решение?   -  person Jonovono    schedule 22.12.2013
comment
@sajoo ты когда-нибудь находил решение этой проблемы! Хотелось бы поговорить об этом. Моя электронная почта должна быть в моем профиле. Удачи!   -  person Jonovono    schedule 22.12.2013
comment
@Jonovono Да, я этого добился. Я попытаюсь опубликовать свое решение, когда вернусь в свою систему. или если вы хотите поговорить со мной, то вот мой адрес электронной почты. [email protected].   -  person sajjoo    schedule 03.02.2014


Ответы (4)


Вы должны применить фильтр верхних частот к данным акселерометра. Это даст вам только всплески сигнала - резкие постукивания.

Я сделал быстрый поиск по «фильтру высоких частот UIAccelerometer» и нашел несколько совпадений. Самый простой код использует скользящее среднее значение входных данных акселерометра, а затем вычитает это среднее значение из мгновенных показаний, чтобы найти внезапные изменения. Несомненно, существуют и более изощренные методы.

Когда у вас есть код, который распознает резкие нажатия, вам нужно создать код, который обнаруживает 3 резких нажатия подряд.

person Duncan C    schedule 24.12.2013

Это, как предполагает другой ответ, связано с фильтрацией нажатий из потока данных акселерометра. Импульсные отводы будут иметь характерную спектрограмму (комбинацию частот), которую можно обнаружить, когда отклик соответствующего фильтра выше порогового значения.

Это очень распространенная операция на iPhone, я бы посоветовал вам ознакомиться с официальной документацией, такой как здесь

Пример кода, на который я дал ссылку, дает вам две важные вещи: официальный пример кода для фильтра верхних частот И пример приложения, которое отображает данные акселерометра в виде графика. Это вы можете использовать для визуализации ваших нажатий, шагов и прыжков, чтобы лучше понять, почему ваш фильтр ложно реагирует.

Кроме того, Интернет является огромным источником литературы по проектированию фильтров — если вам нужно сделать фильтр очень высокого качества, вам может понадобиться обратиться к литературе. Однако я думаю, что подходящего фильтра второго порядка, вероятно, будет достаточно.

@implementation HighpassFilter

- (id)initWithSampleRate:(double)rate cutoffFrequency:(double)freq
{
    self = [super init];

    if (self != nil)
    {
        double dt = 1.0 / rate;
        double RC = 1.0 / freq;
        filterConstant = RC / (dt + RC);
    }

    return self;    
}

- (void)addAcceleration:(UIAcceleration *)accel
{
    double alpha = filterConstant;   

    if (adaptive)
    {
        double d = Clamp(fabs(Norm(x, y, z) - Norm(accel.x, accel.y, accel.z)) / kAccelerometerMinStep - 1.0, 0.0, 1.0);
        alpha = d * filterConstant / kAccelerometerNoiseAttenuation + (1.0 - d) * filterConstant;
    }

    x = alpha * (x + accel.x - lastX);
    y = alpha * (y + accel.y - lastY);
    z = alpha * (z + accel.z - lastZ);

    lastX = accel.x;
    lastY = accel.y;
    lastZ = accel.z;
}

- (NSString *)name
{
    return adaptive ? @"Adaptive Highpass Filter" : @"Highpass Filter";
}

@end

Важно отметить, что этот фильтр не зависит от направления, поскольку фильтруется только величина ускорения. Это очень важно, чтобы реакция казалась нормальной. В противном случае пользователям может показаться, что нужно нажимать под разными углами, чтобы найти золотую середину.

С другой стороны, если эта задача окажется слишком сложной и неудобной, я настоятельно рекомендую записать ваши данные (например, в файл WAV) и использовать одну из любых распространенных программ анализа сигналов, чтобы лучше понять, где что-то идет не так. См. Baudline.

person user3125280    schedule 28.12.2013

Вот как я этого добился.

- (void)accelerometer:(UIAccelerometer *)accelerometer
        didAccelerate:(UIAcceleration *)acceleration
{
    if (pause)
    {
        return;
    }
    if (handModeOn == NO)
    {
        if(pocketFlag == NO)
            return;
    }

//  float accelZ = 0.0;
//  float accelX = 0.0;
//  float accelY = 0.0;

    rollingX = (acceleration.x * kFilteringFactor) + (rollingX * (1.0 - kFilteringFactor));
    rollingY = (acceleration.y * kFilteringFactor) + (rollingY * (1.0 - kFilteringFactor));
    rollingZ = (acceleration.z * kFilteringFactor) + (rollingZ * (1.0 - kFilteringFactor));

    float accelX = acceleration.x - rollingX;
    float accelY = acceleration.y - rollingY;
    float accelZ = acceleration.z - rollingZ;

    if((-accelZ >= [senstivity floatValue] && timerFlag) || (-accelZ <= -[senstivity floatValue] && timerFlag)|| (-accelX >= [senstivity floatValue] && timerFlag) || (-accelX <= -[senstivity floatValue] && timerFlag) || (-accelY >= [senstivity floatValue] && timerFlag) || (-accelY <= -[senstivity floatValue] && timerFlag))
    {
        timerFlag = false;
        addValueFlag = true;
        timer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];
    }

    if(addValueFlag)
    {
        [self.accArray addObject:[NSNumber numberWithFloat:-accelX]];
        [self.accArray addObject:[NSNumber numberWithFloat:-accelY]];
        [self.accArray addObject:[NSNumber numberWithFloat:-accelZ]];
    }
}
person sajjoo    schedule 24.06.2014
comment
Описание этого Кодекса приведено ниже. Я добавил настройки чувствительности в свои настройки, где пользователь может установить чувствительность акселерометра. Поэтому, если я получу значение, равное или превышающее эту чувствительность. я запускаю таймер на 1,5 секунды, а затем проверяю другие значения между этими 1,5 секундами. Как только мой таймер закончился, у меня теперь есть значения акселерометра в массиве, которые равны или превышают чувствительность. а затем в методе timerTick я проверяю из массива, есть ли в массиве от 3 до 4 значений, которые мне нужны. Я запускаю метод оповещения об обнаружении жесткого касания. - person sajjoo; 13.03.2015

Несколько хороших ответов. Вот некоторый рабочий код. Я реализовал это как подкласс UIGestureRecognizer, чтобы вы могли просто вставить его и прикрепить к UIView или UIButton. После срабатывания для него будет установлено «давление» с плавающей запятой от 0,0f до 2,0f. Вы можете дополнительно установить минимальное и максимальное давление, необходимое для распознавания. Наслаждаться.

#import <UIKit/UIKit.h>

#define CPBPressureNone         0.0f
#define CPBPressureLight        0.1f
#define CPBPressureMedium       0.3f
#define CPBPressureHard         0.8f
#define CPBPressureInfinite     2.0f

@interface CPBPressureTouchGestureRecognizer : UIGestureRecognizer <UIAccelerometerDelegate> {
@public
float pressure;
float minimumPressureRequired;
float maximumPressureRequired;

@private
float pressureValues[30];
uint currentPressureValueIndex;
uint setNextPressureValue;
}

@property (readonly, assign) float pressure;
@property (readwrite, assign) float minimumPressureRequired;
@property (readwrite, assign) float maximumPressureRequired;

@end


//
//  CPBPressureTouchGestureRecognizer.h
//  PressureSensitiveButton
//
//  Created by Anthony Picciano on 3/21/11.
//  Copyright 2011 Anthony Picciano. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions
//  are met:
//  1. Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//  
//  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
//  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
//  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
//  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
//  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

#import <UIKit/UIGestureRecognizerSubclass.h>
#import "CPBPressureTouchGestureRecognizer.h"

#define kUpdateFrequency            60.0f
#define KNumberOfPressureSamples    3

@interface CPBPressureTouchGestureRecognizer (private)
- (void)setup;
@end

@implementation CPBPressureTouchGestureRecognizer
@synthesize pressure, minimumPressureRequired, maximumPressureRequired;

 - (id)initWithTarget:(id)target action:(SEL)action {
self = [super initWithTarget:target action:action];
if (self != nil) {
   [self setup]; 
}
return self;
}

 - (id)init {
self = [super init];
if (self != nil) {
    [self setup];
}
return self;
}

- (void)setup {
minimumPressureRequired = CPBPressureNone;
maximumPressureRequired = CPBPressureInfinite;
pressure = CPBPressureNone;

[[UIAccelerometer sharedAccelerometer] setUpdateInterval:1.0f / kUpdateFrequency];
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
}

#pragma -
#pragma UIAccelerometerDelegate methods

-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
int sz = (sizeof pressureValues) / (sizeof pressureValues[0]);

// set current pressure value
pressureValues[currentPressureValueIndex%sz] = acceleration.z;

if (setNextPressureValue > 0) {

    // calculate average pressure
    float total = 0.0f;
    for (int loop=0; loop<sz; loop++) total += pressureValues[loop]; 
    float average = total / sz;

    // start with most recent past pressure sample
    if (setNextPressureValue == KNumberOfPressureSamples) {
        float mostRecent = pressureValues[(currentPressureValueIndex-1)%sz];
        pressure = fabsf(average - mostRecent);
    }

    // caluculate pressure as difference between average and current acceleration
    float diff = fabsf(average - acceleration.z);
    if (pressure < diff) pressure = diff;
    setNextPressureValue--;

    if (setNextPressureValue == 0) {
        if (pressure >= minimumPressureRequired && pressure <= maximumPressureRequired)
            self.state = UIGestureRecognizerStateRecognized;
        else
            self.state = UIGestureRecognizerStateFailed;
    }
}

currentPressureValueIndex++;
 }

 #pragma -
 #pragma UIGestureRecognizer subclass methods

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
setNextPressureValue = KNumberOfPressureSamples;
self.state = UIGestureRecognizerStatePossible;
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
self.state = UIGestureRecognizerStateFailed;
}

- (void)reset {
pressure = CPBPressureNone;
setNextPressureValue = 0;
currentPressureValueIndex = 0;
}

 @end
person JohnWick    schedule 27.12.2013
comment
вы вообще не читали исходный вопрос и проблему...? как это может быть возможным решением для этого? - person holex; 27.12.2013
comment
@holex Извиняюсь, даже сейчас мне интересно, кто опубликовал это из моего аккаунта. обновить ответ - person JohnWick; 30.08.2019