Is it possible to turn gps ground speed and vehical heading to acceleration and yaw angle in NED [duplicate] - matlab

I am developing an android application to calculate position based on Sensor's Data
Accelerometer --> Calculate Linear Acceleration
Magnetometer + Accelerometer --> Direction of movement
The initial position will be taken from GPS (Latitude + Longitude).
Now based on Sensor's Readings i need to calculate the new position of the Smartphone:
My Algorithm is following - (But is not calculating Accurate Position): Please help me improve it.
Note: My algorithm Code is in C# (I am sending Sensor Data to Server - Where Data is stored in the Database. I am calculating the position on Server)
All DateTime Objects have been calculated using TimeStamps - From 01-01-1970
var prevLocation = ServerHandler.getLatestPosition(IMEI);
var newLocation = new ReceivedDataDTO()
{
LocationDataDto = new LocationDataDTO(),
UsersDto = new UsersDTO(),
DeviceDto = new DeviceDTO(),
SensorDataDto = new SensorDataDTO()
};
//First Reading
if (prevLocation.Latitude == null)
{
//Save GPS Readings
newLocation.LocationDataDto.DeviceId = ServerHandler.GetDeviceIdByIMEI(IMEI);
newLocation.LocationDataDto.Latitude = Latitude;
newLocation.LocationDataDto.Longitude = Longitude;
newLocation.LocationDataDto.Acceleration = float.Parse(currentAcceleration);
newLocation.LocationDataDto.Direction = float.Parse(currentDirection);
newLocation.LocationDataDto.Speed = (float) 0.0;
newLocation.LocationDataDto.ReadingDateTime = date;
newLocation.DeviceDto.IMEI = IMEI;
// saving to database
ServerHandler.SaveReceivedData(newLocation);
return;
}
//If Previous Position not NULL --> Calculate New Position
**//Algorithm Starts HERE**
var oldLatitude = Double.Parse(prevLocation.Latitude);
var oldLongitude = Double.Parse(prevLocation.Longitude);
var direction = Double.Parse(currentDirection);
Double initialVelocity = prevLocation.Speed;
//Get Current Time to calculate time Travelling - In seconds
var secondsTravelling = date - tripStartTime;
var t = secondsTravelling.TotalSeconds;
//Calculate Distance using physice formula, s= Vi * t + 0.5 * a * t^2
// distanceTravelled = initialVelocity * timeTravelling + 0.5 * currentAcceleration * timeTravelling * timeTravelling;
var distanceTravelled = initialVelocity * t + 0.5 * Double.Parse(currentAcceleration) * t * t;
//Calculate the Final Velocity/ Speed of the device.
// this Final Velocity is the Initil Velocity of the next reading
//Physics Formula: Vf = Vi + a * t
var finalvelocity = initialVelocity + Double.Parse(currentAcceleration) * t;
//Convert from Degree to Radians (For Formula)
oldLatitude = Math.PI * oldLatitude / 180;
oldLongitude = Math.PI * oldLongitude / 180;
direction = Math.PI * direction / 180.0;
//Calculate the New Longitude and Latitude
var newLatitude = Math.Asin(Math.Sin(oldLatitude) * Math.Cos(distanceTravelled / earthRadius) + Math.Cos(oldLatitude) * Math.Sin(distanceTravelled / earthRadius) * Math.Cos(direction));
var newLongitude = oldLongitude + Math.Atan2(Math.Sin(direction) * Math.Sin(distanceTravelled / earthRadius) * Math.Cos(oldLatitude), Math.Cos(distanceTravelled / earthRadius) - Math.Sin(oldLatitude) * Math.Sin(newLatitude));
//Convert From Radian to degree/Decimal
newLatitude = 180 * newLatitude / Math.PI;
newLongitude = 180 * newLongitude / Math.PI;
This is the Result I get --> Phone was not moving. As you can see speed is 27.3263111114502 So there is something wrong in calculating Speed but I don't know what
ANSWER:
I found a solution to calculate position based on Sensor: I have posted an Answer below.
If you need any help, please leave a comment
this is The results compared to GPS (Note: GPS is in Red)

As some of you mentioned you got the equations wrong but that is just a part of the error.
Newton - D'Alembert physics for non relativistic speeds dictates this:
// init values
double ax=0.0,ay=0.0,az=0.0; // acceleration [m/s^2]
double vx=0.0,vy=0.0,vz=0.0; // velocity [m/s]
double x=0.0, y=0.0, z=0.0; // position [m]
// iteration inside some timer (dt [seconds] period) ...
ax,ay,az = accelerometer values
vx+=ax*dt; // update speed via integration of acceleration
vy+=ay*dt;
vz+=az*dt;
x+=vx*dt; // update position via integration of velocity
y+=vy*dt;
z+=vz*dt;
the sensor can rotate so the direction must be applied:
// init values
double gx=0.0,gy=-9.81,gz=0.0; // [edit1] background gravity in map coordinate system [m/s^2]
double ax=0.0,ay=0.0,az=0.0; // acceleration [m/s^2]
double vx=0.0,vy=0.0,vz=0.0; // velocity [m/s]
double x=0.0, y=0.0, z=0.0; // position [m]
double dev[9]; // actual device transform matrix ... local coordinate system
(x,y,z) <- GPS position;
// iteration inside some timer (dt [seconds] period) ...
dev <- compass direction
ax,ay,az = accelerometer values (measured in device space)
(ax,ay,az) = dev*(ax,ay,az); // transform acceleration from device space to global map space without any translation to preserve vector magnitude
ax-=gx; // [edit1] remove background gravity (in map coordinate system)
ay-=gy;
az-=gz;
vx+=ax*dt; // update speed (in map coordinate system)
vy+=ay*dt;
vz+=az*dt;
x+=vx*dt; // update position (in map coordinate system)
y+=vy*dt;
z+=vz*dt;
gx,gy,gz is the global gravity vector (~9.81 m/s^2 on Earth)
in code my global Y axis points up so the gy=-9.81 and the rest are 0.0
measure timings are critical
Accelerometer must be checked as often as possible (second is a very long time). I recommend not to use timer period bigger than 10 ms to preserve accuracy also time to time you should override calculated position with GPS value. Compass direction can be checked less often but with proper filtration
compass is not correct all the time
Compass values should be filtered for some peak values. Sometimes it read bad values and also can be off by electro-magnetic polution or metal enviroment. In that case the direction can be checked by GPS during movement and some corrections can be made. For example chech GPS every minute and compare GPS direction with compass and if it is constantly of by some angle then add it or substract it.
why do simple computations on server ???
Hate on-line waste of traffic. Yes you can log data on server (but still i think file on device will be better) but why to heck limit position functionality by internet connection ??? not to mention the delays ...
[Edit 1] additional notes
Edited the code above a little. The orientation must be as precise as it can be to minimize cumulative errors.
Gyros would be better than compass (or even better use them both). Acceleration should be filtered. Some low pass filtering should be OK. After gravity removal I would limit ax,ay,az to usable values and throw away too small values. If near low speed also do full stop (if it is not a train or motion in vacuum). That should lower the drift but increase other errors so an compromise has to be found between them.
Add calibration on the fly. When filtered acceleration = 9.81 or very close to it then the device is probably stand still (unless its a flying machine). Orientation/direction can be corrected by actual gravity direction.

Acceleration sensors and gyros are not suited for position calculation.
After some seconds the errors become incredible high. (I hardly remember that the double integration is the problem).
Look at this Google tech talk video about sensor fusioning,
he explains in very detail why this is not possible.

After solving the position I calculated using Sensors I would like to post my code here in case anyone needs in future:
Note: This was only checked on Samsung Galaxy S2 phone and only when person was walking with the phone, it has not been tested when moving in car or on bike
This is the result I got when compared when compared with GPS, (Red Line GPS, Blue is Position calculated with Sensor)
The code is not very efficient, but I hope my sharing this code will help someone and point them in the right direction.
I had two seperate classes:
CalculatePosition
CustomSensorService
public class CalculatePosition {
static Double earthRadius = 6378D;
static Double oldLatitude,oldLongitude;
static Boolean IsFirst = true;
static Double sensorLatitude, sensorLongitude;
static Date CollaborationWithGPSTime;
public static float[] results;
public static void calculateNewPosition(Context applicationContext,
Float currentAcceleration, Float currentSpeed,
Float currentDistanceTravelled, Float currentDirection, Float TotalDistance) {
results = new float[3];
if(IsFirst){
CollaborationWithGPSTime = new Date();
Toast.makeText(applicationContext, "First", Toast.LENGTH_LONG).show();
oldLatitude = CustomLocationListener.mLatitude;
oldLongitude = CustomLocationListener.mLongitude;
sensorLatitude = oldLatitude;
sensorLongitude = oldLongitude;
LivePositionActivity.PlotNewPosition(oldLongitude,oldLatitude,currentDistanceTravelled * 1000, currentAcceleration, currentSpeed, currentDirection, "GPSSensor",0.0F,TotalDistance);
IsFirst = false;
return;
}
Date CurrentDateTime = new Date();
if(CurrentDateTime.getTime() - CollaborationWithGPSTime.getTime() > 900000){
//This IF Statement is to Collaborate with GPS position --> For accuracy --> 900,000 == 15 minutes
oldLatitude = CustomLocationListener.mLatitude;
oldLongitude = CustomLocationListener.mLongitude;
LivePositionActivity.PlotNewPosition(oldLongitude,oldLatitude,currentDistanceTravelled * 1000, currentAcceleration, currentSpeed, currentDirection, "GPSSensor", 0.0F, 0.0F);
return;
}
//Convert Variables to Radian for the Formula
oldLatitude = Math.PI * oldLatitude / 180;
oldLongitude = Math.PI * oldLongitude / 180;
currentDirection = (float) (Math.PI * currentDirection / 180.0);
//Formulae to Calculate the NewLAtitude and NewLongtiude
Double newLatitude = Math.asin(Math.sin(oldLatitude) * Math.cos(currentDistanceTravelled / earthRadius) +
Math.cos(oldLatitude) * Math.sin(currentDistanceTravelled / earthRadius) * Math.cos(currentDirection));
Double newLongitude = oldLongitude + Math.atan2(Math.sin(currentDirection) * Math.sin(currentDistanceTravelled / earthRadius)
* Math.cos(oldLatitude), Math.cos(currentDistanceTravelled / earthRadius)
- Math.sin(oldLatitude) * Math.sin(newLatitude));
//Convert Back from radians
newLatitude = 180 * newLatitude / Math.PI;
newLongitude = 180 * newLongitude / Math.PI;
currentDirection = (float) (180 * currentDirection / Math.PI);
//Update old Latitude and Longitude
oldLatitude = newLatitude;
oldLongitude = newLongitude;
sensorLatitude = oldLatitude;
sensorLongitude = oldLongitude;
IsFirst = false;
//Plot Position on Map
LivePositionActivity.PlotNewPosition(newLongitude,newLatitude,currentDistanceTravelled * 1000, currentAcceleration, currentSpeed, currentDirection, "Sensor", results[0],TotalDistance);
}
}
public class CustomSensorService extends Service implements SensorEventListener{
static SensorManager sensorManager;
static Sensor mAccelerometer;
private Sensor mMagnetometer;
private Sensor mLinearAccelertion;
static Context mContext;
private static float[] AccelerometerValue;
private static float[] MagnetometerValue;
public static Float currentAcceleration = 0.0F;
public static Float currentDirection = 0.0F;
public static Float CurrentSpeed = 0.0F;
public static Float CurrentDistanceTravelled = 0.0F;
/*---------------------------------------------*/
float[] prevValues,speed;
float[] currentValues;
float prevTime, currentTime, changeTime,distanceY,distanceX,distanceZ;
float[] currentVelocity;
public static CalculatePosition CalcPosition;
/*-----FILTER VARIABLES-------------------------*-/
*
*
*/
public static Float prevAcceleration = 0.0F;
public static Float prevSpeed = 0.0F;
public static Float prevDistance = 0.0F;
public static Float totalDistance;
TextView tv;
Boolean First,FirstSensor = true;
#Override
public void onCreate(){
super.onCreate();
mContext = getApplicationContext();
CalcPosition = new CalculatePosition();
First = FirstSensor = true;
currentValues = new float[3];
prevValues = new float[3];
currentVelocity = new float[3];
speed = new float[3];
totalDistance = 0.0F;
Toast.makeText(getApplicationContext(),"Service Created",Toast.LENGTH_SHORT).show();
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mAccelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mMagnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
//mGyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mLinearAccelertion = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
sensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
sensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_NORMAL);
//sensorManager.registerListener(this, mGyro, SensorManager.SENSOR_DELAY_NORMAL);
sensorManager.registerListener(this, mLinearAccelertion, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
public void onDestroy(){
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_SHORT).show();
sensorManager.unregisterListener(this);
//sensorManager = null;
super.onDestroy();
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;
Sensor mSensor = event.sensor;
if(mSensor.getType() == Sensor.TYPE_ACCELEROMETER){
AccelerometerValue = values;
}
if(mSensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION){
if(First){
prevValues = values;
prevTime = event.timestamp / 1000000000;
First = false;
currentVelocity[0] = currentVelocity[1] = currentVelocity[2] = 0;
distanceX = distanceY= distanceZ = 0;
}
else{
currentTime = event.timestamp / 1000000000.0f;
changeTime = currentTime - prevTime;
prevTime = currentTime;
calculateDistance(event.values, changeTime);
currentAcceleration = (float) Math.sqrt(event.values[0] * event.values[0] + event.values[1] * event.values[1] + event.values[2] * event.values[2]);
CurrentSpeed = (float) Math.sqrt(speed[0] * speed[0] + speed[1] * speed[1] + speed[2] * speed[2]);
CurrentDistanceTravelled = (float) Math.sqrt(distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ);
CurrentDistanceTravelled = CurrentDistanceTravelled / 1000;
if(FirstSensor){
prevAcceleration = currentAcceleration;
prevDistance = CurrentDistanceTravelled;
prevSpeed = CurrentSpeed;
FirstSensor = false;
}
prevValues = values;
}
}
if(mSensor.getType() == Sensor.TYPE_MAGNETIC_FIELD){
MagnetometerValue = values;
}
if(currentAcceleration != prevAcceleration || CurrentSpeed != prevSpeed || prevDistance != CurrentDistanceTravelled){
if(!FirstSensor)
totalDistance = totalDistance + CurrentDistanceTravelled * 1000;
if (AccelerometerValue != null && MagnetometerValue != null && currentAcceleration != null) {
//Direction
float RT[] = new float[9];
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(RT, I, AccelerometerValue,
MagnetometerValue);
if (success) {
float orientation[] = new float[3];
SensorManager.getOrientation(RT, orientation);
float azimut = (float) Math.round(Math.toDegrees(orientation[0]));
currentDirection =(azimut+ 360) % 360;
if( CurrentSpeed > 0.2){
CalculatePosition.calculateNewPosition(getApplicationContext(),currentAcceleration,CurrentSpeed,CurrentDistanceTravelled,currentDirection,totalDistance);
}
}
prevAcceleration = currentAcceleration;
prevSpeed = CurrentSpeed;
prevDistance = CurrentDistanceTravelled;
}
}
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
public void calculateDistance (float[] acceleration, float deltaTime) {
float[] distance = new float[acceleration.length];
for (int i = 0; i < acceleration.length; i++) {
speed[i] = acceleration[i] * deltaTime;
distance[i] = speed[i] * deltaTime + acceleration[i] * deltaTime * deltaTime / 2;
}
distanceX = distance[0];
distanceY = distance[1];
distanceZ = distance[2];
}
}
EDIT:
public static void PlotNewPosition(Double newLatitude, Double newLongitude, Float currentDistance,
Float currentAcceleration, Float currentSpeed, Float currentDirection, String dataType) {
LatLng newPosition = new LatLng(newLongitude,newLatitude);
if(dataType == "Sensor"){
tvAcceleration.setText("Speed: " + currentSpeed + " Acceleration: " + currentAcceleration + " Distance: " + currentDistance +" Direction: " + currentDirection + " \n");
map.addMarker(new MarkerOptions()
.position(newPosition)
.title("Position")
.snippet("Sensor Position")
.icon(BitmapDescriptorFactory
.fromResource(R.drawable.line)));
}else if(dataType == "GPSSensor"){
map.addMarker(new MarkerOptions()
.position(newPosition)
.title("PositionCollaborated")
.snippet("GPS Position"));
}
else{
map.addMarker(new MarkerOptions()
.position(newPosition)
.title("Position")
.snippet("New Position")
.icon(BitmapDescriptorFactory
.fromResource(R.drawable.linered)));
}
map.moveCamera(CameraUpdateFactory.newLatLngZoom(newPosition, 18));
}

As per our discussion, since your acceleration is continuously changing, the equations of motion that you have applied shall not give you an accurate answer.
You may have to keep updating your position and velocities as and when you get a new reading for acceleration.
Since this would be highly inefficient, my suggestion would be to call the update function every few seconds and use the average value of acceleration during that period to get the new velocity and position.

I am not quite sure, but my best guess would be around this part:
Double initialVelocity = prevLocation.Speed;
var t = secondsTravelling.TotalSeconds;
var finalvelocity = initialVelocity + Double.Parse(currentAcceleration) * t;
if lets say at the prevLocation the speed was: 27.326... and t==0 and currentAcceleration ==0 (as you said you were idle) the finalvelocity would come down to
var finalvelocity = 27.326 + 0*0;
var finalvelocity == 27.326
If the finalvelocity becomes the speed of the currentlocation, so that previouslocation = currentlocation. This would mean that your finalvelocity might not go down. But then again, there's quite a bit of assumptions here.

Seems like you are making it hard on yourself. You should be able to simply use the Google Play Service Location API and easily access location, direction, speed, etc. accurately.
I would look into using that instead of doing work server side for it.

Related

Problem rotating an object on its local orientation

Hello i am new in the forum! I hope i am in the right section! Im trying to rotate a camera (that rapresent the player POV) using the mouse delta and im rotating the camera in local coordinates not world coordinates and i want avoid gimbal lock effect. I read somewhere on the internet that for that purpose i have to use quaternions, and i read how to do that. The problem is that axis rotations works well moving in local orientation but one of the axis is losing its local orientation and it rotate following the world coordinates orientation. I will post the code and i hope someone can help me and telling me where im doing things wrong. Thanks!
public class Player : MonoBehaviour {
[Header("Camera")]
[SerializeField] private Camera _camera;
[SerializeField] private Vector2 _xMinMaxRotation = new Vector2(-90, 90);
[SerializeField] private Vector2 _yMinMaxRotation = new Vector2(-90, 90);
[SerializeField] private float _mouseXSensistivity = 1;
[SerializeField] private float _mouseYSensistivity = 1;
[SerializeField] private float _mouseZSensistivity = 1;
[SerializeField] private float _xStartRotation = 0;
[SerializeField] private float _yStartRotation = 0;
private Vector2 _mouseDelta;
private float _rotY, _rotX, _rotZ;
//public GameObject head;
// Start is called before the first frame update
void Start() {
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update() {
_mouseDelta = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
MoveCamera();
}
private void MoveCamera() {
_rotX += _mouseDelta.x * _mouseXSensistivity * Time.deltaTime * 100;
_rotX = Mathf.Clamp(_rotX, _xMinMaxRotation.x, _xMinMaxRotation.y);
_rotY += _mouseDelta.y * _mouseYSensistivity * Time.deltaTime * 100;
_rotY = Mathf.Clamp(_rotY, _yMinMaxRotation.x, _yMinMaxRotation.y);
//Calculation for RotZ
if (Input.GetKey(KeyCode.Q)) {
_rotZ += +_mouseZSensistivity * Time.deltaTime * 50;
if (_rotZ > 25) _rotZ = 25;
}
else {
if (_rotZ > 0) {
_rotZ -= 2 * _mouseZSensistivity * Time.deltaTime * 50;
if (_rotZ < 0) _rotZ = 0;
}
}
if (Input.GetKey(KeyCode.E)) {
_rotZ += -_mouseZSensistivity * Time.deltaTime * 50;
if (_rotZ < -25) _rotZ = -25;
}
else {
if (_rotZ < 0) {
_rotZ -= 2 * -_mouseZSensistivity * Time.deltaTime * 50;
if (_rotZ > 0) _rotZ = 0;
}
}
Quaternion currentRotation = Quaternion.identity;
currentRotation = currentRotation * Quaternion.AngleAxis(_rotX, transform.up);
currentRotation = currentRotation * Quaternion.AngleAxis(-_rotY, transform.right);
currentRotation = currentRotation * Quaternion.AngleAxis(_rotZ, transform.forward);
_camera.transform.localRotation = currentRotation;
//head.transform.position = _camera.transform.position;
//head.transform.rotation = _camera.transform.rotation;
}
The last part with quaternions is where im trying to calculate angles in order to properly rotate in local coordinates.
You don’t need to use quaternions at all.
You can use transform.EulerAngles instead of the transform.rotation or transform.localEulerAngles instead of transform.LocalRotation.
I messed up the capitalization I’m sure.
Say you wanted to rotate the camera 10 degrees along the local x axis. That would look something like
transform.localEulerAngles = transform.localEulerAngles.Add(10,0,0);
That’s it as far as I know. If you wanna read more about this,
transfrom.localEulerAngles
If your question was completely different, let me know and I can change or remove my answer.

Snapped GO's Rotation not working properly

I'm currently trying to snap objects (cubes, pyramids, torus) together. I assigned each face of the objects bounding box a "snap allowed" variable and on collision try to find the closest two sides to each other. I then move the main object to the collision side and rotate it accordingly.
The code is as follows:
private static Vector3[] sides = {
Vector3.up,
Vector3.forward,
Vector3.right,
Vector3.down,
Vector3.back,
Vector3.left
};
private void Snap() {
if (!snappedObject)
return;
if (lastSnapping > LeftController.GetLastSnappingToggle()) {
//we dont calculate new sides, only use the old ones
//but we have to check whether we are now further away
float dis = Vector3.Distance(GetWorldPositionFace(this.gameObject, lastSelf),
GetWorldPositionFace(snappedObject, lastOther));
float max = Mathf.Max(Mathf.Abs(size.x), Mathf.Max(Mathf.Abs(size.y), Mathf.Abs(size.z)));
if (dis > max)
return;
ApplyToOther(lastSelf, lastOther, snappedObject);
} else {
//we need to find both new closest sides
MeshSnapping other = snappedObject.GetComponent<MeshSnapping>();
float otherDis = float.MaxValue;
int otherSide = -1;
//find the closest side from the other object
for (int i = 0; i < NUM_SIDES; i++) {
float dis = Vector3.Distance(transform.position, GetWorldPositionFace(snappedObject, i));
if (dis < otherDis && other.sidesAllowed[i]) {
otherDis = dis;
otherSide = i;
}
}
//find the closest side of our object
float selfDis = float.MaxValue;
int selfSide = 0;
for (int i = 0; i < NUM_SIDES; i++) {
float dis = Vector3.Distance(GetWorldPositionFace(this.gameObject, i),
GetWorldPositionFace(snappedObject, otherSide));
if (dis < selfDis && sidesAllowed[i]) {
selfDis = dis;
selfSide = i;
}
}
//are we to far away or at a prohibited side?
float max = Mathf.Max(Mathf.Abs(size.x), Mathf.Max(Mathf.Abs(size.y), Mathf.Abs(size.z)));
if (selfDis > max)
return;
ApplyToOther(selfSide, otherSide, snappedObject);
//save the sides for next iteration
lastSelf = selfSide;
lastOther = otherSide;
}
lastSnapping = Time.time;
}
private void OnCollisionEnter(Collision collision) {
snappedObject = collision.gameObject;
}
private void OnCollisionExit(Collision collision) {
snappedObject = null;
}
private Vector3 GetWorldPositionFace(GameObject other, int i) {
//get the side in local coordinates, scaled to size
Vector3 otherLocalSize = other.transform.localScale;
Vector3 otherSidePoint = new Vector3(otherLocalSize.x * sides[i].x, otherLocalSize.y * sides[i].y, otherLocalSize.z * sides[i].z) / 2f;
//rotate it according to world position
Vector3 dir = (other.transform.rotation * otherSidePoint);
//actually move it to world position
Vector3 center = other.transform.position + dir;
return center;
}
private void ApplyToOther(int selfI, int otherI, GameObject other) {
//first get the midpoint of face of other go
Vector3 edge = GetWorldPositionFace(other, otherI);
Vector3 dir = edge - other.transform.position;
RotateSides(selfI, otherI, dir);
selfI = (selfI + NUM_SIDES / 2) % NUM_SIDES;
//get midpoint of face of self go
edge += GetWorldPositionFace(this.gameObject, selfI) - transform.position;
//now move towards the combination
transform.position = edge;
}
private void RotateSides(int selfI, int otherI, Vector3 dir) {
//rotate self side towards this point
switch (selfI) {
case 0: transform.up = -dir; break;
case 1: transform.forward = -dir; break;
case 2: transform.right = -dir; break;
case 3: transform.up = dir; break;
case 4: transform.forward = dir; break;
case 5: transform.right = dir; break;
}
}
I can find every midpoint of the bounding box by transforming the direction vector, applying the objects current rotation and position to it (see GetWorldPositionFace() ). After finding the best combination, ApplyToOther() moves the objects to position and rotates it according to the selected face normals. So far so good, but the result is not aligned. As you can see, the front faces do not face in the same direction, i.e. I want to rotate the upper cube around the transform.up axis by this amount. This would be the result that I want.
But, if I add
float angle = Vector3.Angle(transform.forward, snappedObject.transform.forward);
transform.Rotate(transform.up, angle);
to the RotateSides() function, the result is this. The rotation axis is wrong.
Using
Quaternion.FromToRotation(transform.up, snappedObject.transform.up)
did not work either.
What did I miss? Thanks for your help!
I figured out my problem. By setting the transform.forward and the transform.up seperately (e.g. with transform.rotate around axis), only one of them was correct. Using Quaternion.LookRotation() solves this.

Apply scale/translation after image target is lost Vuforia Unity

I have an 3D model attached to a image target. What I want is to scale the model according to the camera's position, i.e. when I get closer to the model I want it to get larger and when I get far away it should get smaller. I searched the forums and tried two different solutions:
Solution 1:
I created a script and attached to the 3d model:
public class AR_Capsule : MonoBehaviour {
float minimumDistance = 0;
float maximumDistance = 3;
float minimumDistanceScale = 0.3f;
float maximumDistanceScale = 0.1f;
void Update()
{
float distance = (transform.position - Camera.main.transform.position).magnitude;
float norm = (distance - minimumDistance) / (maximumDistance - minimumDistance);
norm = Mathf.Clamp01(norm);
Vector3 minScale = Vector3.one * maximumDistanceScale;
Vector3 maxScale = Vector3.one * minimumDistanceScale;
transform.localScale = Vector3.Lerp(maxScale, minScale, norm);
}
}
Solution 2:
public class AR_Capsule : MonoBehaviour {
public float speed = 10f;
void Update()
{
transform.Translate(0, 0,(-1 * Input.acceleration.z * speed * Time.deltaTime));
}
}
Both solutions work when the image target is visible. But when I lose the target from sight, then the object stays still and camera position is always (0.0, 0.0, 0.0).
Do you have any idea why the object freezes when the target is lost?
I am developing for Epson Moverio bt-300 and working with vuforia eyewear sample app.
Thanks.
I found the issue. The problem is about getting the correct camera position. Since I am using a eyewear Camera.main.transform.position will give always (0,0,0). Instead I used Camera.main.transform.GetChild(1).transform.position which work fine. Below is the working code:
initialDist = Vector3.Distance(transform.position, Camera.main.transform.GetChild(1).transform.position);
distance = Vector3.Distance(lastMarkerpos, Camera.main.transform.GetChild(1).transform.position);
if((transform.localScale.x * (initialDist / distance)) > 0.05 && (transform.localScale.x * (initialDist / distance)) < 2)
{
transform.localScale = new Vector3(transform.localScale.x * (initialDist / distance), transform.localScale.y * (initialDist / distance), transform.localScale.z * (initialDist / distance));
}

HoloLens Draw a graph

What is the best way to draw a graph for the HoloLens in unity?
I am new to this platform and have no idea which packages will work and which dont, the graph gets data dynamically.
EDIT: I have tried LineRenderer but it seems very limited in version 5.4 of Unity
A possible Solution for drawing a 3D-Graph is using a particle system:
Simple Example for a Component Script for a particle system:
public class Graph: MonoBehaviour {
//Particle-Resolution of the Graph
[Range(10, 100)]
public int resolution = 10;
private int currentResolution;
private ParticleSystem.Particle[] points;
void Start()
{
currentResolution = resolution;
points = new ParticleSystem.Particle[resolution];
float increment = 1f / (resolution - 1);
for (int i = 0; i < resolution; i++)
{
float x = i * increment;
points[i].position = new Vector3(x, 0f, 0f);
points[i].startColor = new Color(0f, 0f, 0f);
points[i].startSize = 0.1f;
}
}
void Update()
{
if ((currentResolution != resolution) || (points == null))
{
CreatePoints();
}
FunctionDelegate f = functionDelegates[(int)function];
for (int i = 0; i < points.Length; i++)
{
Vector3 p = points[i].position;
p.y = Sine(p.x);
points[i].position = p;
Color c = points[i].GetCurrentColor(GetComponent<ParticleSystem>());
c.g = p.y;
c.r = 1f - p.y;
points[i].startColor = c;
}
GetComponent<ParticleSystem>().SetParticles(points, points.Length);
}
private static float Sine(float x)
{
return 0.5f + 0.5f * Mathf.Sin(2 * Mathf.PI * x + Time.timeSinceLevelLoad);
}
}
A good tutorial for drawing 2D/3D graphs (including this example) with a particle system from CatLikeCoding (Jasper Flick). Refer to: http://catlikecoding.com/unity/tutorials/graphs/. It's a bit outdated and you must use startSize/startColor instead the depreceated color/size-Properties in this case.
But i'have testet it with the hololens allready and it worked fine. Some experiments with the HoloToolkit shaders for a better performance are necessary if you have a big amount of particles :-)
If you have further questions: Just ask me.

2D projectile trajectory prediction (unity3d)

(Using unity3d 4.3 2d, it uses box2d like physics).
I have problems with predicting trajectory
I'm using:
Vector2 startPos;
float power = 10.0f;
float interval = 1/30.0f;
GameObject[] ind;
void Start (){
transform.rigidbody2D.isKinematic = true;
ind = new GameObject[dots];
for(int i = 0; i<dots; i++){
GameObject dot = (GameObject)Instantiate(Dot);
dot.renderer.enabled = false;
ind[i] = dot;
}
}
void Update (){
if(shot) return;
if(Input.GetAxis("Fire1") == 1){
if(!aiming){
aiming = true;
startPos = Input.mousePosition;
ShowPath();
}
else{
CalculatePath();
}
}
else if(aiming && !shot){
transform.rigidbody2D.isKinematic = false;
transform.rigidbody2D.AddForce(GetForce(Input.mous ePosition));
shot = true;
aiming = false;
HidePath();
}
}
Vector2 GetForce(Vector3 mouse){
return (new Vector2(startPos.x, startPos.y)- new Vector2(mouse.x, mouse.y))*power;
}
void CalculatePath(){
ind[0].transform.position = transform.position; //set frist dot to ball position
Vector2 vel = GetForce(Input.mousePosition); //get velocity
for(int i = 1; i < dots; i++){
ind[i].renderer.enabled = true; //make them visible
Vector3 point = PathPoint(transform.position, vel, i); //get position of the dot
point.z = -1.0f;
ind[i].transform.position = point;
}
}
Vector2 PathPoint(Vector2 startP, Vector2 startVel, int n){
//Standard formula for trajectory prediction
float t = interval;
Vector2 stepVelocity = t*startVel;
Vector2 StepGravity = t*t*Physics.gravity;
Vector2 whattoreturn = ((startP + (n * stepVelocity)+(n*n+n)*StepGravity) * 0.5f);
return whattoreturn;
}
Using this, I get wrong trajectory.
1. It's like gravity doesn't drag trajectory down at all, and yes i know that gravity is weak because:
t*t*Physics.gravity = 0.03^2 * vector2(0, -9.8) = vector2(0, -0.00882)
But that is the formula :S
2. Since gravity is low, velocity is too strong.
Here is the video:
http://tinypic.com/player.php?v=1z50w3m&s=5
Trajectory formula form:
http://www.iforce2d.net/b2dtut/projected-trajectory
What should I do?
I found that if I set
StepGravity to something stronger like (0, -0.1)
and devide startVel by 8
I get nearly right trajectory, but i don't want that, I need true trajectory path.
Users from answer.unity3d.com said I should ask here, because here is a bigger group of mathematical coders.
And I searched a lot about this problem (that how I found that formula).
you're only calculating the effect of gravity over 1/30th of a second for each step - you need to do it cumulatively. Step 1 should end with a velocity of 0.09G, Step 2 with .18G, step3 with .27G etc.
Here's a very simple example that draws the ballistic trajectory based on start velocity and a supplied time:
using UnityEngine;
using System.Collections;
public class grav : MonoBehaviour {
public Vector3 StartVelocity;
public float PredictionTime;
private Vector3 G;
void OnDrawGizmos()
{
if (G == Vector3.zero)
{
// a hacky way of making sure this gets initialized in editor too...
// this assumes 60 samples / sec
G = new Vector3(0,-9.8f,0) / 360f;
}
Vector3 momentum = StartVelocity;
Vector3 pos = gameObject.transform.position;
Vector3 last = gameObject.transform.position;
for (int i = 0; i < (int) (PredictionTime * 60); i++)
{
momentum += G;
pos += momentum;
Gizmos.DrawLine(last, pos);
last = pos;
}
}
}
In you version you'd want draw your dots where I'm drawing the Gizmo, but it's the same idea unless I'm misunderstanding your problem.