I'm trying to detect left, right, up and down shakes. I researched and found this code.
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float[] values = event.values;
// Movement
float x = values[0];
float y = values[1];
float z = values[2];
long actualTime = System.currentTimeMillis();
if ((actualTime - lastUpdate) > 100)
{
long diffTime = (actualTime - lastUpdate);
lastUpdate = actualTime;
if(Round(x,4)>8.0000){
Log.d("sensor", "=====LEFT====");
}
else if(Round(x,4)<-8.0000){
Log.d("sensor", "=====RIGHT====");
}
else if(Round(z,4) < -0.0){
Log.d("sensor", "=====UP====");
}
else if(Round(y,4) < 1.0){
Log.d("sensor", "=====DOWN====");
}
float speed = Math.abs(x+y+z - last_x - last_y - last_z) / diffTime * 10000;
if (speed > SHAKE_THRESHOLD) {
//Log.d("sensor", "shake detected w/ speed: " + speed);
//Toast.makeText(this, "shake detected w/ speed: " + speed, Toast.LENGTH_SHORT).show();
}
last_x = x;
last_y = y;
last_z = z;
}
}
}
I tried to study it but I have some questions.
1. Where can I set the delay every after shake detection? I want it to detect a shake after 1 second of the last detected shake.
2. Why is that if I place it to a flat surface, it always detects down.
3. I can't detect a right movement shake
Any help and suggestion will be appreciated.
First off, this code appears to detect movement rather than a shake (where I understand shake to be a motion which goes one way, then back the other), which might be causing problems in how you understand it's working.
Where can I set the delay every after shake detection? I want it to detect a shake after 1 second of the last detected shake.
if ((actualTime - lastUpdate) > 100) is where the interval between detections occurs - the 100 here tells the program to only see what the change is after 100 ms, so if you wanted 1 second, change this to 1000
Why is that if I place it to a flat surface, it always detects down.
I'm not entirely sure of the context and direction here as it's not clear, but don't forget that 'down' as you think of it might not be down in your space, but in the phone's, where down is only down when the phone is upright. If the phone is on it's back, left and right are the same, but down (i.e. towards the ground) is actually back for the sensor, as it's towards the back of the phone.
I can't detect a right movement shake
TBH I've no idea why, the code seems fine here. May be a phone thing, may be an implementation problem (as I said, the above code is movement overall, not a returning 'shake', so it could be how you're shaking it right to test this isn't triggering it, but your shake left is triggering it
Related
I am making an android project to detect when mobile fall down, can anyone tell me which sensor should I use in my app, I know accelerometer will use for this kind of purpose, but accelerometer can also detect when I shake the phone in my hand and I want to get the toast only when the mobile falls down.
here is my code:
int count = 1;
private boolean init;
private Sensor mySensor;
private SensorManager SM;
private float x1, x2, x3;
private static final float ERROR = (float) 7.0;
private static final float SHAKE_THRESHOLD = 15.00f; // m/S**2
private static final int MIN_TIME_BETWEEN_SHAKES_MILLISECS = 1000;
private long mLastShakeTime;
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
if ((curTime - mLastShakeTime) > MIN_TIME_BETWEEN_SHAKES_MILLISECS) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
double acceleration = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)
+ Math.pow(z, 2))
- SensorManager.GRAVITY_EARTH;
Log.d("mySensor", "Acceleration is " + acceleration + "m/s^2");
if (acceleration > SHAKE_THRESHOLD) {
mLastShakeTime = curTime;
Toast.makeText(getApplicationContext(), "FALL DETECTED",
Toast.LENGTH_LONG).show();
} } }}
There is no "Fall Sensor", your guess in using the accelerometer is right. Record and measure the accelerometer data when it falls and deduct a model from there.
You have to use accelerometer. But it will detect very small movement.
The only way to do is , get the difference between two readings on change of state.
If its very very high, the mobile has traveled a longer distances say fell down from hand, or change an abnormal position say a shock.
For this we have to use little bit of Physics, any object falling under the influence of gravity has an acceleration of 9.8 m/s/s, downward (on Earth). SO with this knowledge we can get the downward direction speed
1)based on the axis(Y-axis) using accelerometer check it is downward and
2) check if it is equal to this 9.8 m/s constant and also check the
if the fall speed is approx near 9.8 m/s show the toast
Hi all i done this by myself a little change was get me rid of this , that is just taking two variable set those values negative and call them as threshold value of acceleration .
thanks everyone
I am developing a simple game in which a character fly when you tap/click the screen. keep tapping the character will fly (some what similar to flappy bird and jet pack). However the movement is not smooth at all, as of jet pack.
Here is sample of my code.
Varaible Initilization
maxSpeedLimit = spriteHeight/10;
speed = maxSpeedLimit/2; //half of the max speed
touch event
public void onTapOrClick(int action) {
if (action == UP) {
sprite.up= true;
}
else {
sprite.up = false;
}
}
Sprite update called from game loop
public void update() {
if (up) {
y -= speed; //fly up
}
else {
y += speed;// fly down
}
if (speed < maxSpeedLimit) {
speed++; // May be cheep way to add **velocity/acceleration**
}
}
I think speed++ is not a smooth way to increase the speed, I am not sure but may be adding some time related variable to increment may improve it, moreover adding a gravity will make it more realistic but i have no idea how to do it, I have read few blogs, first thing I am not able to search with the correct keyword, and second thing is they are so hard understand because they contains platform related codes. Please help.
I am making this game in android, but code in any language is accepted (HTML5, javascript, Android, Flash or any).
Q: How to add acceleration and gravity to an object (sprite) which fly when user tap or click and fall on release?
Something similar to jet pack joyride (only up and down movement)
UPDATED
After #scottt advise I have implemented the dY += gravity + flapp I can feel now gravity, however there are 2 issues.
My screen height is 480, and sprite immediately touch upper(y=0) and lower(y=480) boundary, becuase i think it keep increasing the speed of the sprite.
when it touches the ground, it seems very havey and take much time to lift the sprite up in the air.
Some how there should be some limit to dY which is constantly being added to y location.
Here is update code.
int downSpeed = 1;
int upSpeed = -2;
int dy = 0;
private void update() {
if (flapping) {
upSpeed = -2; //if flying speed
}
else {
upSpeed = 0;
}
dy += downSpeed + upSpeed;
if (dy < -10) {
dy = -10; //limit for rise speed
}
else if (dy > 8) {
dy = 8; //limit for gravity
}
y += dy; // add value in y location
if (y > GAME_HEIGHT - sprite.getHeight()) {
y = GAME_HEIGHT - sprite.getHeight(); // reset y, if touch ground
dy = 0; //reset speed, otherwise it make it very heavy to rise
}
else if ( y < 0) {
y = 0; //reset y if touch upper limit
dy = 0; //reset speed, otherwise take much time to fall (as it would be in negative)
}
}
Gravity is just a downward acceleration. An acceleration is in turn just a change in velocity (directed speed). For the following discussion, I make the following assumptions to keep things simpler:
your horizontal speed is constant
input tapping is called 'flapping'
gravity behaves normally
simplistic, rather than a scientific approach/verbiage is OK for our purposes
For each pass through the update loop, all of the various accelerations must be summed and the total added to the current speed. In your situation, there are 2 possible accelerations, gravity and possibly flapping. Gravity is constant and is a negative value (is works downward), while flapping only occurs during tapping as is positive (upwards).
Let's set gravity to -10 pixels per loop, tapping to be +25 pixels per loop, and initial height to 500. Some initial definitions are:
static final int gravity = -10; // constant downward acceleration
static final int flapping = 25; // upward acceleration whenever isFlapping is true
Boolean isFlapping = false; // Is the bird flapping
int dY = 0; // current vertical speed
y = 500; // current vertical position
Each time through the loop, without flapping, the speed calculation would be:
dY += gravity + flapping;
So the first time through, the speed calculation would be dY = 0 + (-10) + 0 = -10. The second time, dy = -10 + (-10) + 0 = -20. The 5th time, dy = -40 + (-10) = -50. Each time through, the downward speed is 10 more than the time before.
The height is simple. Each time through, the height changes by the amount of vertical acceleration. So:
y += dY;
So the first time through, the height would be y = 500 + (-10) = 490. The second time, y = 490 + (-20) = 470. And the 5th time, y = 400 + (-50) = 350. Because the rate of falling increases each time through, the bird will plummet faster and faster until splat!
That's where flapping comes in. Each time through the loop where flapping is occurring, a +25 will be applied to the dY calculation. So lets assume the bird starts flapping in 6th iteration. The dy calculation would be dy = -50 + (-10) + 25 = -35 and the height would be y = 350 + (-35) = 315. The next time through would give dy = -35 + (-10) + 25 = -20 and the height would be y = 350 + (-20) = 295. Still falling, but more slowly. The time after: that dy = -20 + (-10) + 25 = -5 and y = 295 + (-5) = 290. The time after that finally shows a gain in height: dy = -5 + (-10) + 25 = 10 and y = 290 + 10 = 300.
All that said, you'll definitely need to play with the numbers until you get a satisfying result.
TLDR: You don't want to change the height directly using gravity and flapping. Instead you want to use gravity and flapping to calculate the speed for each iteration and then use that to adjust the height.
So I'm creating a game for android that uses the roll of the device to set the position of the main character. The position is updated everytime the .onSensorChanged(event) method gets run. The problem is, even when I run my app and set my phone on the tabletop, the values change significantly (about two degrees). The sensitivity of the data collected also doesn't seem to be very precise, as difference in angle seem to be in increments of about 0.4 degrees every change. Using
Log.e(TAG, "roll: " + event.values[2]);
Sequential degree output looks like this:
roll: 2.265
roll: 2.265
roll: 2.265
roll: 1.843
roll: 2.265
roll: 2.265
roll: 2.75
roll: 2.265
I've also implemented this algorithm to limit my character's movement in the screen, but it's not enough and severely limits the speed and responsiveness of the character's movement with regards to the current roll data (I would like the character's placement to be as close to 1-1 with regards to roll as possible).
public void onSensorChanged(SensorEvent event)
{
if (event.sensor.getType() == Sensor.TYPE_ORIENTATION)
{
float newX = -(int)event.values[2] * CCDirector.sharedDirector().displaySize().width/10.0f +
CCDirector.sharedDirector().displaySize().width/2.0f;
sam.updatePosition(newX);
Log.e(TAG, "roll: " + event.values[2]);
}
}
public void updatePosition(float newX)
{
float winSizeX = CCDirector.sharedDirector().displaySize().width;
float contentWidth = this.sprite.getContentSize().width;
//tries to eliminate jumpy behaviour by the character by limiting his speed
double tolerance = 0.01;
if (Math.abs(newX - this.sprite.getPosition().x) > winSizeX * tolerance)
newX = this.sprite.getPosition().x - ((this.sprite.getPosition().x - newX) * 0.1f);
this.sprite.setPosition(newX, this.sprite.getPosition().y);
}
I'm wondering if this is normal or if it's the phone I'm testing it on (Sony Xperia Play.)
Thanks for any input.
How about moving average to smooth your data on the fly?
It will lag a little but it would be unnoticeable.
On the other hand, I would not use Euler angles (such as roll).
I have to write a compass app in Android. The only thing the user sees on the screen is a cube with a red wall which has to point north. This is not important. What's important is that I need to rotate that cube accordingly to the rotation of the device itself so that the red wall continues to point north no matter how the phone is being held. My code is simple and straightforward:
#Override
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
switch (event.sensor.getType()){
case Sensor.TYPE_ACCELEROMETER:
direction = event.values[2];
break;
case Sensor.TYPE_ORIENTATION:
if (direction < 0) {
angleX = event.values[1];
angleY = -event.values[2];
angleZ = event.values[0];
} else {
angleX = -event.values[1];
angleY = -event.values[2];
angleZ = event.values[0];
}
break;
}
}
}
I have added this extra direction variable that simply stores whether the phone's display is pointing downwards or upwards. I don't know if I need it but it seems to fix some bugs. I am using the SensorSimulator for android but whenever my pitch slider goes in the [-90, 90] interval the other variables get mixed up. It's like they get a 180 offset. But I can't detect when I am in this interval because the range of the pitch is from -90 to 90 so I can move that slider from left to write and I will always be in that interval.
This was all just to show you how far has my code advanced. I am not saying how this problem should be solved because I will only probably stir myself into a dead end. You see, I have been trying to write that app for 3 days now, and you can imagine how pissed my boss is. I have read all sorts of tutorials and tried every formula I could find or think of. So please help me. All I have to do is know how to rotate my cube, the rotation angles of which are EULER ANGLES in degrees.
Here's some code I wrote to do something pretty similar, really only caring about the rotation of the device in the roll direction. Hope it helps! It just uses the accelerometer values to determine the pitch, no need to get orientation of the view.
public void onSensorChanged(SensorEvent event) {
float x = -1 * event.values[0] / SensorManager.GRAVITY_EARTH;
float y = -1 * event.values[1] / SensorManager.GRAVITY_EARTH;
float z = -1 * event.values[2] / SensorManager.GRAVITY_EARTH;
float signedRawRoll = (float) (Math.atan2(x, y) * 180 / Math.PI);
float unsignedRawRoll = Math.abs(signedRawRoll);
float rollSign = signedRawRoll / unsignedRawRoll;
float rawPitch = Math.abs(z * 180);
// Use a basic low-pass filter to only keep the gravity in the accelerometer values for the X and Y axes
// adjust the filter weight based on pitch, as roll is harder to define as pitch approaches 180.
float filterWeight = rawPitch > 165 ? 0.85f : 0.7f;
float newUnsignedRoll = filterWeight * Math.abs(this.roll) + (1 - filterWeight) * unsignedRawRoll;
this.roll = rollSign * newUnsignedRoll;
if (Float.isInfinite(this.roll) || Float.isNaN(this.roll)) {
this.roll = 0;
}
this.pitch = filterWeight * this.pitch + (1 - filterWeight) * rawPitch;
for (IAngleListener listener : listeners) {
listener.deviceRollAndPitch(this.roll, this.pitch);
}
}
I have a code snippet to detect accelerometer movements. It works some times by properly detecting slight movements, but sometimes it detects movements when I kept my device idle too. Are there any problems with built-in accelerometer detection on Android?
I use an HTC G-1 device. My code snippet is below. How do I resolve it so I can detect small device movements but not detect anything when the device is idle?
private static final int SHAKE_THRESHOLD = 50;
public void onSensorChanged(int sensor, float[] values) {
if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
long curTime = System.currentTimeMillis();
// only allow one update every 100ms.
if ((curTime - lastUpdate) > 100) {
long diffTime = (curTime - lastUpdate);
lastUpdate = curTime;
x = values[SensorManager.DATA_X];
y = values[SensorManager.DATA_Y];
z = values[SensorManager.DATA_Z];
float speed = Math.abs(x+y+z - last_x - last_y - last_z) / diffTime * 10000;
if (speed > SHAKE_THRESHOLD) {
long curTime = System.currentTimeMillis();
long diff = (curTime - shakeTime);
shakeTime = curTime;
if (myFlagIgnoreShakeDetection==true) //Caused unneccessary accelerometer
//notification looping when device is idle
return;
// Doing something...
}
last_x = x;
last_y = y;
last_z = z;
}
}
}
Here are a few code discrepancies...
There may be a problem regarding the updating of last_x, last_y, and last_z. I believe they should be included inside the if ((curTime - lastUpdate) > 100) { statement. In other words, they are being updated every time onSensorChanged is called, not every 100 milliseconds. You should probably move the updating of those three variables into the curly brace above them.
On the line where you compute the speed, the formula ends with ... / diffTime * 10000; Are you wanting to multiply just diffTime by 10000, or the entire result? Since / and * typically have the same operator precedence in most languages I know of (such as Java), your equation will be evaluated from left to right, dividing first by diffTime then multiplying that result by 10000.
I'm guessing you mean to multiply just diffTime by 10000, thus dividing the final result by that amount. This is the difference between dividing by 10000 or multiplying by 10000, which means you are probably getting values for speed that are 10^8 greater than you should, thus tripping your threshold even when the device is idle. You need to put parentheses around the multiplication, like ... / (diffTime * 10000);, to make sure it's performed before the division takes place.
Additionally, if you are intending to scale diffTime from milliseconds to seconds, your scale factor should be 1000.
I personally, in my augmented reality library, use a rolling average for the updates:
float kFilteringFactor = (float)0.05;
rollingZ = (float) ((rawZValue * kFilteringFactor) + (rollingZ * (1.0 - kFilteringFactor)));
This tends to smooth out the data pretty well, and you can tweak the filtering factor to get the responsiveness you want.
rawZValue is the raw value coming in from the accelerometer.