Question Détectez les frappes dures n'importe où sur iPhone via un accéléromètre


J'essaie de détecter les robinets qui pourraient être n'importe où sur l'iPhone et pas seulement sur l'écran de l'iPhone. Voici une lien ce qui montre que c'est possible.

Fondamentalement, ce que je veux faire, c'est envoyer une alerte si l'utilisateur tape 3 fois sur l'iPhone alors que le téléphone est dans sa poche. Ce que j'ai réalisé, c'est que je peux détecter les 3 taps mais aussi les fausses alertes dans ces cas. 1) si l'utilisateur marche, 2) en agitant son téléphone 3) en cours d'exécution. Je dois juste vérifier si l'utilisateur a frappé son iPhone 3 fois.

Voici mon code.

- (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;
}

Toute aide sera vraiment appréciée.

Merci


19
2017-11-23 00:29


origine


Réponses:


Vous devez appliquer un filtre passe-haut aux données de l'accéléromètre. Cela ne vous donnera que les pics dans le signal - des frappes nettes.

J'ai fait une recherche rapide sur "filtre passe-haut UIAccelerometer" et trouvé plusieurs résultats. Le code le plus simple prend une moyenne mobile de l’accéléromètre, puis soustrait cette moyenne de la lecture instantanée pour trouver des changements soudains. Il existe sans aucun doute des méthodes plus sophistiquées.

Une fois que vous avez du code qui reconnaît les frappes nettes, vous devez créer un code qui détecte 3 frappes nettes à la suite.


11
2017-12-24 01:30



Ceci est, comme suggéré par une autre réponse, tout à voir avec le filtrage des prises du flux de données de l’accéléromètre. Les prises de type impulsionnel auront un spectrogramme caractéristique (combinaison de fréquences) qui peut être détecté lorsque la réponse d'un filtre approprié est supérieure à un seuil.

Ceci est une opération très courante sur iPhone, je vous suggère de regarder la documentation officielle telle que ici

L'exemple de code que j'ai lié vous donne deux choses importantes: un exemple de code officiel pour le filtre passe-haut ET un exemple d'application qui va représenter graphiquement les données de l'accéléromètre. Cela vous permet de visualiser vos robinets, vos pas et vos sauts, afin de mieux comprendre pourquoi votre filtre réagit de manière erronée.

En outre, Internet est une source importante de documentation sur la conception des filtres. Si vous devez créer un filtre de très haute qualité, vous devrez peut-être consulter la documentation. Je pense cependant qu'un filtre de second ordre approprié serait probablement suffisant.

@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

Ce qui est important, c'est que ce filtre est indépendant de la direction, puisque seule l'amplitude de l'accélération est filtrée. Ceci est crucial pour que la réponse semble normale. Sinon, les utilisateurs peuvent avoir l’impression d’avoir à puiser sous différents angles pour trouver un centre commercial.

Sur une autre note, si cette tâche s'avère trop difficile et fastidieuse, je suggère fortement de capturer vos données (dans un fichier WAV par exemple) et d’utiliser un programme d’analyse de signaux commun pour avoir une meilleure idée de ce qui ne va pas. Voir Baudline


3
2017-12-28 14:14



Voici comment je l'ai réalisé.

- (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]];
    }
}

0
2018-06-24 06:54



Essayez ce code:

UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
                                                   initWithTarget:self action:@selector(handleLongPress:)];
        longPress.minimumPressDuration = 2; //seconds
        //longPress.delegate = self;
        [myButton addGestureRecognizer:longPress];

-10
2017-12-27 10:50