Gravity and Centripetal Force - c#

I am making an application in Unity 3d where I want to code gravity and centripetal force myself but I am getting weird results, am I doing it right? This is my code:
public void Attract(MassObject[] _allMass)
{
Vector3 F = new Vector3();
Vector3 C = new Vector3();
foreach(MassObject i in _allMass)
{
// gravity pull
F.x = GV.gravity * ((mass * i.mass) / (obj.position.x - i.obj.position.x));
F.y = GV.gravity * ((mass * i.mass) / (obj.position.y - i.obj.position.y));
F.z = GV.gravity * ((mass * i.mass) / (obj.position.z - i.obj.position.z));
// centripital force
C.x = (mass * Mathf.Pow(vel.x,2)) / (obj.position.x - i.obj.position.x);
C.y = (mass * Mathf.Pow(vel.y,2)) / (obj.position.y - i.obj.position.y);
C.z = (mass * Mathf.Pow(vel.z,2)) / (obj.position.z - i.obj.position.z);
}
vel = F + C;
Debug.Log(F);
Debug.Log(C);
}

There are a few problems with this:
You are rewriting F and C with each foreach iteration, so you end up with the for for the last object in the list. You should add all the forces together using += instead of =.
You should be using vector operations rather than manually calculating x y and z. Doing so would make the third error more obvious:
The formula is wrong. For example, this line:
F.x = GV.gravity * ((mass * i.mass) / (obj.position.x - i.obj.position.x));
with just a few things substituted is
F.x = gravity coeff * (masses multiplied) / (distance **on the x axis** between objects.)
What you really need is distance between objects, not just on the x axis. You can get this with a vector operation:
float distanceSquared = (obj.position - i.obj.position).sqrMagnitude;
Then you can use the law of universal gravitation to get the magnitude of the force of gravity:
float forceMagnitude = GV.gravity * (mass * i.mass) / distanceSquared;
Finally, you need a direction. You can get that with vector subtraction and normalization, then multiplying by the desired magnitude:
Vector3 force = (i.obj.position - obj.position).normalized * forceMagnitude;
And now force is a vector pointing from obj to i.obj with the magnitude of gravity between the two objects. Add that to F to accumulate the force from each object and things should be good.
Your understanding of physics might be off. If vel is velocity, it is wrong to set it to the sum of two forces. I think you're better off learning about this from somewhere else, though. Understanding Newton's laws is a good start.

Related

3D-Coordinate Transformation in C#

What is the Goal?:
I want to know the new Coordinates of a point after rotating the 3D-Object (Cuboid), around the anchorpoint (x,y & z) on the opposite side.
What i did:
I tried to calculate the position with the following function. I had to convert doubles to floats , because of the Autodesk Inventor API. Note: Vector is the difference from the origin /anchorpoint to the designated point.
Vector3 coordinateTransformation(Vector3 vector, float r_x, float r_y, float r_z, Vector3 origin)
{
vector.X = vector.X; //Just for demonstration
vector.Y = vector.Y * Convert.ToSingle(Math.Cos(DegreesToRadians(r_x))) - vector.Z * Convert.ToSingle(Math.Sin(DegreesToRadians(r_x)));
vector.Z = vector.Y * Convert.ToSingle(Math.Sin(DegreesToRadians(r_x))) + vector.Z * Convert.ToSingle(Math.Cos(DegreesToRadians(r_x)));
vector.X = vector.X * Convert.ToSingle(Math.Cos(DegreesToRadians(r_y))) + vector.Z * Convert.ToSingle(Math.Sin(DegreesToRadians(r_y)));
vector.Y = vector.Y; //Just for demonstration
vector.Z = vector.Z * Convert.ToSingle(Math.Cos(DegreesToRadians(r_y))) - vector.X * Convert.ToSingle(Math.Sin(DegreesToRadians(r_y)));
vector.X = vector.X * Convert.ToSingle(Math.Cos(DegreesToRadians(r_z))) - vector.Y * Convert.ToSingle(Math.Sin(DegreesToRadians(r_z)));
vector.Y = vector.X * Convert.ToSingle(Math.Sin(DegreesToRadians(r_z))) + vector.Y * Convert.ToSingle(Math.Cos(DegreesToRadians(r_z)));
vector.Z = vector.Z; //Just for demonstration
vector.X = Math.Abs(vector.X) + origin.X;
vector.Y = Math.Abs(vector.Y) + origin.Y;
vector.Z = Math.Abs(vector.Z) + origin.Z;
return vector;
}
Somehow the object does not get placed on the correct place.
Next step: On the internet i found a website which does the correct transformation.Casio Website
If i manually set vector to the calculated point on the website, everything else works fine. So i somehow have to get the exact same calculation into my code.
If you need further information, feel free to comment!
Edit:
1 : I want to place 2 Objects (e.g. Cuboids) within 1 Assembly Group in Inventor. Every Object as an anchorpoint (origin) and on the opposite side a connection point, which is described as the delta between the anchorpoint and the connection point itself. At first one Object is placed on the origin coordinates, followed by a rotation around the anchorpoint (degrees). After that the connection point coordinates of Object 1 have changed. In the next step i want to place Object 2 with its origin on the connection point of Object 1, while Object 2 has the same rotation as Object 1.
2 : Inventor uses a right-handed coordinate system
When you apply rotation to a vector manually, you'd need to update all the components (X, Y and Z) at once as follows.
Vector3 coordinateTransformation(Vector3 vector, float r_x, float r_y, float r_z, Vector3 origin)
{
// In the rotation around X axis below, `Y relies on Z` and `Z relies on Y`. So both must be updated simultaneously.
vector = new Vector3(
vector.X,
vector.Y * Convert.ToSingle(Math.Cos(DegreesToRadians(r_x))) - vector.Z * Convert.ToSingle(Math.Sin(DegreesToRadians(r_x))),
vector.Y * Convert.ToSingle(Math.Sin(DegreesToRadians(r_x))) + vector.Z * Convert.ToSingle(Math.Cos(DegreesToRadians(r_x)))
);
vector = new Vector3(
vector.X * Convert.ToSingle(Math.Cos(DegreesToRadians(r_y))) + vector.Z * Convert.ToSingle(Math.Sin(DegreesToRadians(r_y))),
vector.Y,
vector.Z * Convert.ToSingle(Math.Cos(DegreesToRadians(r_y))) - vector.X * Convert.ToSingle(Math.Sin(DegreesToRadians(r_y)))
);
vector = new Vector3(
vector.X * Convert.ToSingle(Math.Cos(DegreesToRadians(r_z))) - vector.Y * Convert.ToSingle(Math.Sin(DegreesToRadians(r_z))),
vector.X * Convert.ToSingle(Math.Sin(DegreesToRadians(r_z))) + vector.Y * Convert.ToSingle(Math.Cos(DegreesToRadians(r_z))),
vector.Z
);
vector.X = Math.Abs(vector.X) + origin.X;
vector.Y = Math.Abs(vector.Y) + origin.Y;
vector.Z = Math.Abs(vector.Z) + origin.Z;
return vector;
}
Appendix: Use of Matrix4x4
As #JonasH suggests, it's a good idea to use reliable libraries, if there are.
Since, row-vectors are used in the .NET world (while your implementation is based on column-vectors), X->Y->Z rotations can be written by straightforward matrices multiplications as follows.
Vector3 coordinateTransformation(Vector3 vector, float r_x, float r_y, float r_z, Vector3 origin)
{
var mat = Matrix4x4.CreateRotationX(DegreesToRadians(r_x))
* Matrix4x4.CreateRotationY(DegreesToRadians(r_y))
* Matrix4x4.CreateRotationZ(DegreesToRadians(r_z));
vector = Vector3.Transform(vector, mat);
vector.X = Math.Abs(vector.X) + origin.X;
vector.Y = Math.Abs(vector.Y) + origin.Y;
vector.Z = Math.Abs(vector.Z) + origin.Z;
return vector;
}
Do not mess around with vectors and angled by hand. Use some library for your vector functions and use matrices to do your transformations. For example System.Numerics or Math.Net.Numerics.
Assuming you have an euler angle to start with you can use CreateFromYawPitchRoll to create your rotation matrix. If you want to rotate around a specific point you just subtract your rotation center from all your points, apply the rotation and move the points back. This can all be done by just multiplying the transformation matrices:
var totalTransform = Matrix4x4.CreateTranslation(-rotationCenter) *
Matrix4x4.CreateFromYawPitchRoll(yawInRadians, pitchInRadians, rollInRadians) *
Matrix4x4.CreateTranslation(rotationCenter);
to apply the resulting transform you just call Vector3.Transform to produce a new, transformed vector.
This will all be a bit easier if you have some introduction to linear algebra. It is also common to do things like applying the transforms in the wrong order or something, and sometimes it is easiest to just try some different things until you get it right. I would also note that rotations are just hard to understand. I tend to prefer quaternions over eutler angles, not because the math is easier to understand, but since there are methods like CreateFromAxisAngle, that I can understand.

Rotating cubes to face the origin using Quaternions

I'm in the process of setting up a relatively simple voxel-based world for a game. The high level idea is to first generate voxel locations following a fibonacci grid, then rotate the cubes such that the outer surface of the fibonacci grid resembles a sphere, and finally size the cubes such that they roughly cover the surface of the sphere (overlap is fine). See below the code for generating the voxels along the fibonacci grid:
public static Voxel[] CreateInitialVoxels(int numberOfPoints, int radius)
{
float goldenRatio = (1 + Mathf.Sqrt(5)) / 2;
Voxel[] voxels = new Voxel[numberOfPoints];
for (int i = 0; i < numberOfPoints; i++)
{
float n = i - numberOfPoints / 2; // Center at zero
float theta = 2 * Mathf.PI * n / goldenRatio;
float phi = (Mathf.PI / 2) + Mathf.Asin(2 * n / numberOfPoints);
voxels[i] = new Voxel(new Location(theta, phi, radius));
}
return voxels;
}
This generates a sphere that looks roughly like a staircase
So, my current approach to get this looking a bit more spherical is to basically rotate each cube in each pair of axes, then combine all of the rotations:
private void DrawVoxel(Voxel voxel, GameObject voxelContainer)
{
GameObject voxelObject = Instantiate<GameObject>(GetVoxelPrefab());
voxelObject.transform.position = voxel.location.cartesianCoordinates;
voxelObject.transform.parent = voxelContainer.transform;
Vector3 norm = voxel.location.cartesianCoordinates.normalized;
float xyRotationDegree = Mathf.Atan(norm.y / norm.x) * (180 / Mathf.PI);
float zxRotationDegree = Mathf.Atan(norm.z / norm.x) * (180 / Mathf.PI);
float yzRotationDegree = Mathf.Atan(norm.z / norm.y) * (180 / Mathf.PI);
Quaternion xyRotation = Quaternion.AngleAxis(xyRotationDegree, new Vector3(0, 0, 1));
Quaternion zxRotation = Quaternion.AngleAxis(zxRotationDegree, new Vector3(0, 1, 0));
Quaternion yzRotation = Quaternion.AngleAxis(yzRotationDegree, new Vector3(1, 0, 0));
voxelObject.transform.rotation = zxRotation * yzRotation * xyRotation;
}
The primary thing that I am getting caught on is that each of these rotations seems to work fine for me in isolation, but when combining them things tend to go a bit haywire (pictures below) I'm not sure exactly what the issue is. My best guess is that I've made some sign/rotation mismatch in my rotations so they don't combine right. I can get two working, but never all three together.
Above are the pictures of one and two successful rotations, followed by the error mode when I attempt to combine them. Any help either on telling me that the approach I'm following is too convoluted, or helping me understand what the right way to combine these rotations would be would be very helpful. Cartesian coordinate conversion below for reference.
[System.Serializable]
public struct Location
{
public float theta, phi, r;
public Vector3 polarCoordinates;
public float x, y, z;
public Vector3 cartesianCoordinates;
public Location(float theta, float phi, float r)
{
this.theta = theta;
this.phi = phi;
this.r= r;
this.polarCoordinates = new Vector3(theta, phi, r);
this.x = r * Mathf.Sin(phi) * Mathf.Cos(theta);
this.y = r * Mathf.Sin(phi) * Mathf.Sin(theta);
this.z = r * Mathf.Cos(phi);
this.cartesianCoordinates = new Vector3(x, y, z);
}
}
I managed to find a solution to this problem, though it's still not clear to me what the issue with the above code is.
Unity has an extremely handy function called Quaternion.FromToRotation that will generate the appropriate rotation if you simply pass in the appropriate destination vector.
In my case I was able to just do:
voxelObject.transform.rotation = Quaternion.FromToRotation(new Vector3(0, 0, 1), voxel.location.cartesianCoordinates);

Parsing LSM6DSL raw values

I'm trying to parse the values given from a device with a LSM6DSL chip (gyroscopic and acc.) and I'm having a hard time parsing the data properly for positioning and angle.
From the vendor I've received the information that the unit is running on a resolution of 2000 for the gyro, 8g for the acc.
I receive the data in bytes that are converted by the following to shorts;
public int[] BufferToMotionData(byte[] buffer, int segments = 2)
{
int[] motionDataArray = new int[segments * 3];
int offset = Constants.BufferSizeImage + Constants.CommandLength;
for (int i = 0; i < 6; i++)
{
motionDataArray[i] = BitConverter.ToInt16(buffer, offset + (i * 2));
if (motionDataArray[i] >= Int16.MaxValue)
motionDataArray[i] -= 65535;
}
return motionDataArray;
}
(Edit; Cleaned up version)
This returns values in the range of (example) 961, -16223, -1635, 664, -269, -597.
According to the spec sheet I'm supposed to multiply each vector with it's corresponding value.. * 70f for gyro, .448f for acc.
From the documentation I understand that for the G forces these are in milliG's and gyro in millidegrees per sec?
// Gyro X,Y,Z
gx = Mathf.Deg2Rad * (motionData[0] * 70f / 1000f);
gy = Mathf.Deg2Rad * (motionData[1] * 70f / 1000f);
gz = Mathf.Deg2Rad * (motionData[2] * 70f / 1000f);
// Acc X,Y,Z
ax = motionData[3] * 0.488f / 1000f;
ay = motionData[4] * 0.488f / 1000f;
az = motionData[5] * 0.488f / 1000f;
Update(gx, gy, gz, ax, ay, az);
Update(..) is Madgwick's quaternion formul, although for velocity I use the acceleration vectors.
G force values that I'm getting at this moment after calculation;
X 0.047824 Y -0.320128 Z 0.006344
X 0.07076 Y -0.2562 Z 0.020008
X 0.099552 Y -0.063928 Z -0.13664
These look awfully low, and if applied as velocity it just runs off in a given direction, I know I'm missing a gravity correct although not entirely sure how to apply this.
I'm under the assumption that I do not need to apply drag to my velocity vector since values should be negated by the acceleration values received?
Anyone with experience with this type of chip and actually applying the values to yaw/pitch/roll (or quaternion) and applying the G forces as linear acceleration.
By looking on existing code on GitHub, it's looks like the sensitivity factor for 8g is 244 µg/digit and not 488 µg/digit as you coded it.
Also it look's like raw values are shifted and are in [-r/2,r/2] instead of [0, r]. So you have to add 500µg or 500µdps to it. (But maybe it's linked to a uint/int issue, anyway are you sure about the endianness?)
See here for acc data and here for gyro data.
Based on that, the code should look likes this:
// Gyro X,Y,Z (in rad/s)
gx = Mathf.Deg2Rad * (motionData[0] * 70000f + 500) / 1000000;
gy = Mathf.Deg2Rad * (motionData[1] * 70000f + 500) / 1000000;
gz = Mathf.Deg2Rad * (motionData[2] * 70000f + 500) / 1000000;
// Acc X,Y,Z (in g)
ax = (motionData[3] * 244f + 500) / 1000000;
ay = (motionData[4] * 244f + 500) / 1000000;
az = (motionData[5] * 244f + 500) / 1000000;
Update(gx, gy, gz, ax, ay, az);

How to rotate a vector around a particular axis

The .X3D format has an interesting rotation system. Unlike most formats containing rotation values around the X, Y and Z axis, .X3D gives a normalized direction vector and then gives a value in radians for rotation around that axis.
Example:
The axis to rotate around: 0.000000 0.465391 0.885105
Rotation around that axis (in radians): 3.141593
I have the conversion from radians to degrees, but I need the rotation values around XYZ from these values.
We can build a sequence for elementary matrix transformations to rotate about angle-axis. Let axis unit vector is w, angle Theta. Auxiliary values:
V = Sqrt(wx*wx + wz*wz)
W = Sqrt(wx*wx + wy*wy + wz*wz) //1 for unit dir vector
Cos(Alpha) = wz/V
Sin(Alpha) = wx/V
Cos(Beta) = V/W
Sin(Beta) = wy/W
Transformation sequence:
Ry(-Alpha) //rotation matrix about Y-axis by angle -Alpha
Rx(Beta)
Rz(Theta)
Rx(-Beta)
Ry(Alpha)
Note that for is axis is parallel to Y , one should use usual just rotation matrix about Y (accounting for direction sign), because V value is zero.
There is rather complex Rodrigues' rotation formula for computing the rotation matrix corresponding to a rotation by an angle Theta about a fixed axis specified by the unit vector w.
Explicit matrix here (weird formatted picture):
This Below C++ Function Rotates a Point Around A Provided Center and Uses Another Vector(Unit) as Axis to rotate Around.
This Code Below Was Made Thanks to Glenn Murray who provided the Formula Provided in Link. This is Tested and is Working Perfectly As intended.
Note: When Angle Is Positive And Unit Vector is {0,0,1} It Rotates To the Right Side, Basically Rotation Is On the Right Side, Axes are {x-forward,y-right,z-up}
void RotateVectorAroundPointAndAxis(Vector3D YourPoint, Vector3D PreferedCenter, Vector3D UnitDirection, float Angle, Vector3D& ReturnVector)
{
float SinVal = sin(Angle * 0.01745329251);
float CosVal = cos(Angle * 0.01745329251);
float OneMinSin = 1.0f - SinVal;
float OneMinCos = 1.0f - CosVal;
UnitDirection = GetUnitVector(UnitDirection, { 0,0,0 });// This Function Gets unit Vector From InputVector - DesiredCenter
float Temp = (UnitDirection.x * YourPoint.x) + (UnitDirection.y * YourPoint.y) + (UnitDirection.z * YourPoint.z);
ReturnVector.x = (PreferedCenter.x * (UnitDirection.y * UnitDirection.y)) - (UnitDirection.x * (((-PreferedCenter.y * UnitDirection.y) + (-PreferedCenter.z * UnitDirection.z)) - Temp));
ReturnVector.y = (PreferedCenter.y * (UnitDirection.x * UnitDirection.x)) - (UnitDirection.y * (((-PreferedCenter.x * UnitDirection.x) + (-PreferedCenter.z * UnitDirection.z)) - Temp));
ReturnVector.z = (PreferedCenter.z * (UnitDirection.x * UnitDirection.x)) - (UnitDirection.z * (((-PreferedCenter.x * UnitDirection.x) + (-PreferedCenter.y * UnitDirection.y)) - Temp));
ReturnVector.x = (ReturnVector.x * OneMinCos) + (YourPoint.x * CosVal);
ReturnVector.y = (ReturnVector.y * OneMinCos) + (YourPoint.y * CosVal);
ReturnVector.z = (ReturnVector.z * OneMinCos) + (YourPoint.z * CosVal);
ReturnVector.x += (-(PreferedCenter.z * UnitDirection.y) + (PreferedCenter.y * UnitDirection.z) - (UnitDirection.z * YourPoint.y) + (UnitDirection.y * YourPoint.z)) * SinVal;
ReturnVector.y += ( (PreferedCenter.z * UnitDirection.x) - (PreferedCenter.x * UnitDirection.z) + (UnitDirection.z * YourPoint.x) - (UnitDirection.x * YourPoint.z)) * SinVal;
ReturnVector.z += (-(PreferedCenter.y * UnitDirection.x) + (PreferedCenter.x * UnitDirection.y) - (UnitDirection.y * YourPoint.x) + (UnitDirection.x * YourPoint.y)) * SinVal;
}

How to rotate a Vector2?

I'm trying to rotate a Vector2 but nothing work.
I've tried the following -> didn't work:
x' = cos(angle)*x - sin(angle)*y & y' = sin(angle)*x + cos(angle)*y
I've tried using a rotation matrix -> didn't work
What am I doing wrong ? :/
angle = MathHelper.Pi;
direction.X = (int)((direction.X) * Math.Cos(angle) - direction.Y * Math.Sin(angle));
direction.Y = (int)((direction.X) * Math.Sin(angle) + direction.Y * Math.Cos(angle));
float angle = MathHelper.PiOver2;
Vector2 dir = new Vector2(direction.X, direction.Y);
Vector2.Transform(dir, Matrix.CreateRotationX(angle));
direction = new Point((int)dir.X, (int)dir.Y);
Vector2.Transform() returns a result rather than applying the changes in-place.
var transformed = Vector2.Transform(dir, Matrix.CreateRotationX(angle));
direction = new Point((int) dir.X, (int) dir.Y);
The first method you have written should work, as it's showed here also: http://www.oocities.org/davidvwilliamson/rotpoint.jpg
Remember to store the original values, and use those to determine the new values, and do not use the new x value to calculate y. Or store the products in separate variables.

Categories

Resources