Robot Turning Using PID - java

I currently have a PID algorithm to control my robots turns in an autonomous state. My robot has encoders, on each motor, which there are four of, and also a BNO055IMU. Furthermore each motor is a never rest 40 motor from Andymark, and unfortunately I am stuck with encoders that do 3 pulses. I would like to improve the accuracy of my turns either by using a different algorithm or improving my current one.
My Current Turning Code:
public void turn(int angle, Direction DIRECTION, double timeOut, int sleepTime, double kp, double ki, double kd) {
double targetAngle = imu.adjustAngle(imu.getHeading() + (DIRECTION.value * angle));
double acceptableError = 0.5;
double currentError = 1;
double prevError = 0;
double integral = 0;
double newPower;
double previousTime = 0;
timeoutClock.reset();
while (opModeIsActive() && (imu.adjustAngle(Math.abs(currentError)) > acceptableError)
&& !timeoutClock.elapsedTime(timeOut, MasqClock.Resolution.SECONDS)) {
double tChange = System.nanoTime() - previousTime;
previousTime = System.nanoTime();
tChange = tChange / 1e9;
double imuVAL = imu.getHeading();
currentError = imu.adjustAngle(targetAngle - imuVAL);
integral += currentError * ID;
double errorkp = currentError * kp;
double integralki = integral * ki * tChange;
double dervitive = (currentError - prevError) / tChange;
double dervitivekd = dervitive * kd;
newPower = (errorkp + integralki + dervitivekd);
newPower *= color;
if (Math.abs(newPower) > 1.0) {newPower /= newPower;}
driveTrain.setPower(newPower, -newPower);
prevError = currentError;
DashBoard.getDash().create("TargetAngle", targetAngle);
DashBoard.getDash().create("Heading", imuVAL);
DashBoard.getDash().create("AngleLeftToCover", currentError);
DashBoard.getDash().update();
}
driveTrain.setPower(0,0);
sleep(sleepTime);
}
NOTES:
when driveTrain.setPower(x,y); is called the left parameter is the power set to the left side and the right parameter sets the right side.
Direction is an enum that stores wither -1, or 1 to switch between left and right turns.
Dashboard.getDash.create is solely to keep a log on what is going on.
imu.adjustAngle does the following:
public double adjustAngle(double angle) {
while (angle > 180) angle -= 360;
while (angle <= -180) angle += 360;
return angle;
}
imu.getHeading() is self explanatory it gets the yaw of the robot.
My current values for pid constants. (They work pretty well.)
KP_TURN = 0.005,
KI_TURN = 0.0002,
KD_TURN = 0,
ID = 1;

Related

Is there a way to kill an entity when it is far enough

For context, I'm making a mod and I have been trying to make a wave of water blocks to damage entities. The way I'll be doing it is by tracking an invisible fireball and the fireball will go only 10 block away from the player before being killed. I can summon the fireball but I don't know how to kill it. Here's a snippet of the code, using Fabric 1.19.2 and IntelliJ
#Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
if (!user.getWorld().isClient){
double xUser = user.getEyePos().getX();
double yUser = user.getEyeY();
double zUser = user.getEyePos().getZ();
double Pitch = user.getPitch();
Pitch = (Pitch+90) / 180 * Math.PI;
double BodyYaw = user.getHeadYaw();
//converting from [
BodyYaw = (BodyYaw + 90) / 180 * Math.PI;
double theta = BodyYaw;
double phi = Pitch;
double x = xUser + 10 * Math.sin(phi) * Math.cos(theta);
double z = zUser + 10 * Math.sin(phi) * Math.sin(theta);
double y = yUser + 10 * Math.cos(phi);
Vec3d vector = new Vec3d((x-xUser)/20,(y-yUser-0.5d)/20,(z-zUser)/20);
FireballEntity fireball = new FireballEntity(world, user, vector.x, vector.y, vector.z, 0);
fireball.setPitch((float)Pitch);
fireball.setPos(xUser, yUser, zUser);
fireball.setHeadYaw((float)BodyYaw);
fireball.setInvisible(false);
fireball.setNoGravity(true);
world.spawnEntity(fireball);
}
return super.use(world, user, hand);
}

How to get direction of gravity

I need to calculate the linear acceleration based on the accelerometer, gyroscope and magnetometer. I found an application for android, which does exactly what I want to achieve:
https://play.google.com/store/apps/details?id=com.kircherelectronics.fusedlinearacceleration.
https://github.com/KEOpenSource/FusedLinearAcceleration
I'm trying to port it to a pure java. Because some elements of the code are based on virtual sensors (Gravity Sensor), I would like to achieve the same result by compute direction of gravity based on three basic sensors. I read that the force of gravity can be calculated using the Low Pass Filter (same as Android < 4.0), but this method does not give very accurate results.
From android 4.0, the force of gravity on each axis is calculated using sensor fusion. I found the code responsible for these measurements, but it is written in the CPP:
https://github.com/android/platform_frameworks_base/blob/ics-mr1/services/sensorservice/GravitySensor.cpp
Method used there is called "getRotationMatrix". The same method in SensorManager.java class: https://gitorious.org/android-eeepc/base/source/9cb3e09ec49351401cf19b5ae5092dd9ca90a538:core/java/android/hardware/SensorManager.java#L1034
public static boolean getRotationMatrix(float[] R, float[] I,
float[] gravity, float[] geomagnetic) {
// TODO: move this to native code for efficiency
float Ax = gravity[0];
float Ay = gravity[1];
float Az = gravity[2];
final float Ex = geomagnetic[0];
final float Ey = geomagnetic[1];
final float Ez = geomagnetic[2];
float Hx = Ey*Az - Ez*Ay;
float Hy = Ez*Ax - Ex*Az;
float Hz = Ex*Ay - Ey*Ax;
final float normH = (float)Math.sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
if (normH < 0.1f) {
// device is close to free fall (or in space?), or close to
// magnetic north pole. Typical values are > 100.
return false;
}
final float invH = 1.0f / normH;
Hx *= invH;
Hy *= invH;
Hz *= invH;
final float invA = 1.0f / (float)Math.sqrt(Ax*Ax + Ay*Ay + Az*Az);
Ax *= invA;
Ay *= invA;
Az *= invA;
final float Mx = Ay*Hz - Az*Hy;
final float My = Az*Hx - Ax*Hz;
final float Mz = Ax*Hy - Ay*Hx;
if (R != null) {
if (R.length == 9) {
R[0] = Hx; R[1] = Hy; R[2] = Hz;
R[3] = Mx; R[4] = My; R[5] = Mz;
R[6] = Ax; R[7] = Ay; R[8] = Az;
} else if (R.length == 16) {
R[0] = Hx; R[1] = Hy; R[2] = Hz; R[3] = 0;
R[4] = Mx; R[5] = My; R[6] = Mz; R[7] = 0;
R[8] = Ax; R[9] = Ay; R[10] = Az; R[11] = 0;
R[12] = 0; R[13] = 0; R[14] = 0; R[15] = 1;
}
}
if (I != null) {
// compute the inclination matrix by projecting the geomagnetic
// vector onto the Z (gravity) and X (horizontal component
// of geomagnetic vector) axes.
final float invE = 1.0f / (float)Math.sqrt(Ex*Ex + Ey*Ey + Ez*Ez);
final float c = (Ex*Mx + Ey*My + Ez*Mz) * invE;
final float s = (Ex*Ax + Ey*Ay + Ez*Az) * invE;
if (I.length == 9) {
I[0] = 1; I[1] = 0; I[2] = 0;
I[3] = 0; I[4] = c; I[5] = s;
I[6] = 0; I[7] =-s; I[8] = c;
} else if (I.length == 16) {
I[0] = 1; I[1] = 0; I[2] = 0;
I[4] = 0; I[5] = c; I[6] = s;
I[8] = 0; I[9] =-s; I[10]= c;
I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0;
I[15] = 1;
}
}
return true;
}
takes four arguments:
float [] R, float [] I, float [] gravity, float [] Geomagnetic.
One of them is just gravity... The code I'm working on currently is similar to
https://github.com/KEOpenSource/FusedLinearAcceleration/blob/master/FusedLinearAcceleration/src/com/kircherelectronics/fusedlinearacceleration/sensor/LinearAccelerationSensor.java,
with the exception of methods that refer to SensorManager. These are copied from android source:
https://gitorious.org/android-eeepc/base/source/9cb3e09ec49351401cf19b5ae5092dd9ca90a538:core/java/android/hardware/SensorManager.java.
I did not found any examples of how implement this in Java.
So my question is: How I can implement method (in java), based only on three basic sensors, which returns me array of gravity direction (x, y, z), similar to Android one, but without using Android API.
Gravity is a steady contribution in the accelerometer signals (x, y & z).
So, logically, to isolate the gravity values in function of time, just low-pass filter the 3 accelerometer signals, at a frequency of 2Hz, for example.
A simple FIR would do the job.
On this site
I calculated the following coefficients:
[0.000381, 0.001237, 0.002634, 0.004607, 0.007100, 0.009956, 0.012928,
0.015711, 0.017987, 0.019480, 0.020000, 0.019480, 0.017987, 0.015711,
0.012928, 0.009956, 0.007100, 0.004607, 0.002634, 0.001237, 0.000381]
based on those caracteristics:
Fa=0Hz, Fb=1Hz, Length=21Pts, Fs=100Hz, Att=60dB.
You will get a signal that will be the three values of gravity in function of time.
You can find here some FIR explaination and Java implementation.
What you want is the rotation matrix (SensorManager.getRotationMatrix). Its last three components (i.e. rotation[6], rotation[7], rotation[8]) are the vector that points straight up, thus the direction to the center of the earth is the negative of that. To subtract gravity from your accelerometer reading just multiply that vector by g (~9.8m/s^2, though you might want to know that more precisely).

How can you find the point on an ellipse that sweeps a given area?

I am working on the problem of dividing an ellipse into equal sized segments. This question has been asked but the answers suggested numerical integration so that I what I'm attempting. This code short-circuits the sectors so the integration itself should never cover more than 90 degrees. The integration itself is being done by totaling the area of intermediate triangles. Below is the code I have tried, but it is sweeping more than 90 degrees in some cases.
public class EllipseModel {
protected double r_x;
protected double r_y;
private double a,a2;
private double b,b2;
boolean flip;
double area;
double sector_area;
double radstep;
double rot;
int xp,yp;
double deviation;
public EllipseModel(double r_x, double r_y, double deviation)
{
this.r_x = r_x;
this.r_y = r_y;
this.deviation = deviation;
if (r_x < r_y) {
flip = true;
a = r_y;
b = r_x;
xp = 1;
yp = 0;
rot = Math.PI/2d;
} else {
flip = false;
xp = 0;
yp = 1;
a = r_x;
b = r_y;
rot = 0d;
}
a2 = a * a;
b2 = b * b;
area = Math.PI * r_x * r_y;
sector_area = area / 4d;
radstep = (2d * deviation) / a;
}
public double getArea() {
return area;
}
public double[] getSweep(double sweep_area)
{
System.out.println(String.format("getSweep(%f) a = %f b = %f deviation = %f",sweep_area,a,b,deviation));
double[] ret = new double[2];
double[] next = new double[2];
double t_base, t_height, swept,x_mid,y_mid;
double t_area;
sweep_area = sweep_area % area;
if (sweep_area < 0d) {
sweep_area = area + sweep_area;
}
if (sweep_area == 0d) {
ret[0] = r_x;
ret[1] = 0d;
return ret;
}
double sector = Math.floor(sweep_area/sector_area);
double theta = Math.PI * sector/2d;
double theta_last = theta;
System.out.println(String.format("- Theta start = %f",Math.toDegrees(theta)));
ret[xp] = a * Math.cos(theta + rot);
ret[yp] = (1 + (((theta / Math.PI) % 2d) * -2d)) * Math.sqrt((1 - ( (ret[xp] * ret[xp])/a2)) * b2);
next[0] = ret[0];
next[1] = ret[1];
swept = sector * sector_area;
System.out.println(String.format("- Sweeping for %f sector_area=%f",sweep_area-swept,sector_area));
int c = 0;
while(swept < sweep_area) {
c++;
ret[0] = next[0];
ret[1] = next[1];
theta_last = theta;
theta += radstep;
// calculate next point
next[xp] = a * Math.cos(theta + rot);
next[yp] = (1 + (((theta / Math.PI) % 2d) * -2d)) * // selects +/- sqrt
Math.sqrt((1 - ( (ret[xp] * ret[xp])/a2)) * b2);
// calculate midpoint
x_mid = (ret[xp] + next[xp]) / 2d;
y_mid = (ret[yp] + next[yp]) / 2d;
// calculate triangle metrics
t_base = Math.sqrt( ( (ret[0] - next[0]) * (ret[0] - next[0]) ) + ( (ret[1] - next[1]) * (ret[1] - next[1])));
t_height = Math.sqrt((x_mid * x_mid) + (y_mid * y_mid));
// add triangle area to swept
t_area = 0.5d * t_base * t_height;
swept += t_area;
}
System.out.println(String.format("- Theta end = %f (%d)",Math.toDegrees(theta_last),c));
return ret;
}
}
In the output I see the following case where it sweeps over 116 degrees.
getSweep(40840.704497) a = 325.000000 b = 200.000000 deviation = 0.166667
- Theta start = 0.000000
- Sweeping for 40840.704497 sector_area=51050.880621
- Theta end = 116.354506 (1981)
Is there any way to fix the integration formula to create a function that returns the point on an ellipse that has swept a given area? The application that is using this code divides the total area by the number of segments needed, and then uses this code to determine the angle where each segment starts and ends. Unfortunately it doesn't work as intended.
* edit *
I believe the above integration failed because the base and height formula's aren't correct.
No transformation needed use parametric equations for ellipse ...
x=x0+rx*cos(a)
y=y0+ry*sin(a)
where a = < 0 , 2.0*M_PI >
if you divide ellipse by lines from center to x,y from above equation
and angle a is evenly encreased
then the segments will have the same size
btw. if you apply affine transform you will get the same result (even the same equation)
This code will divide ellipse to evenly sized chunks:
double a,da,x,y,x0=0,y0=0,rx=50,ry=20; // ellipse x0,y0,rx,ry
int i,N=32; // divided to N = segments
da=2.0*M_PI/double(N);
for (a=0.0,i=0;i<N;i++,a+=da)
{
x=x0+(rx*cos(a));
y=y0+(ry*sin(a));
// draw_line(x0,y0,x,y);
}
This is what it looks like for N=5
[edit1]
I do not understood from your comment what exactly you want to achieve now
sorry but my English skills are horrible
ok I assume these two possibilities (if you need something different please specify closer)
0.but first some global or member stuff needed
double x0,y0,rx,ry; // ellipse parameters
// [Edit2] sorry forgot to add these constants but they are I thin straight forward
const double pi=M_PI;
const double pi2=2.0*M_PI;
// [/Edit2]
double atanxy(double x,double y) // atan2 return < 0 , 2.0*M_PI >
{
int sx,sy;
double a;
const double _zero=1.0e-30;
sx=0; if (x<-_zero) sx=-1; if (x>+_zero) sx=+1;
sy=0; if (y<-_zero) sy=-1; if (y>+_zero) sy=+1;
if ((sy==0)&&(sx==0)) return 0;
if ((sx==0)&&(sy> 0)) return 0.5*pi;
if ((sx==0)&&(sy< 0)) return 1.5*pi;
if ((sy==0)&&(sx> 0)) return 0;
if ((sy==0)&&(sx< 0)) return pi;
a=y/x; if (a<0) a=-a;
a=atan(a);
if ((x>0)&&(y>0)) a=a;
if ((x<0)&&(y>0)) a=pi-a;
if ((x<0)&&(y<0)) a=pi+a;
if ((x>0)&&(y<0)) a=pi2-a;
return a;
}
1.is point inside segment ?
bool is_pnt_in_segment(double x,double y,int segment,int segments)
{
double a;
a=atanxy(x-x0,y-y0); // get sweep angle
a/=2.0*M_PI; // convert angle to a = <0,1>
if (a>=1.0) a=0.0; // handle extreme case where a was = 2 Pi
a*=segments; // convert to segment index a = <0,segments)
a-=double(segment );
// return floor(a); // this is how to change this function to return points segment id
// of course header should be slightly different: int get_pnt_segment_id(double x,double y,int segments)
if (a< 0.0) return false; // is lower then segment
if (a>=1.0) return false; // is higher then segment
return true;
}
2.get edge point of segment area
void get_edge_pnt(double &x,double &y,int segment,int segments)
{
double a;
a=2.0*M_PI/double(segments);
a*=double(segment); // this is segments start edge point
//a*=double(segment+1); // this is segments end edge point
x=x0+(rx*cos(a));
y=y0+(ry*sin(a));
}
for booth:
x,y is point
segments number of division segments.
segment is sweep-ed area < 0,segments )
Apply an affine transformation to turn your ellipse into a circle, preferrably the unit circle. Then split that into equal sized segments, before you apply the inverse transform. The transformation will scale all areas (as opposed to lengths) by the same factor, so equal area translates to equal area.

Implementation of Projectile Motion

I have created a projectile motion simulation in Java with a user interface.
The program allows the user to enter in initial values to calculate the projectile of the object. I don't have anything currently set up to draw the projectile onto the screen.
I have a separate spring worker thread handling the simulation code in the background.
I also have added in collision detection so that when the object hits the ground it will bounce and continue doing so until the loop exits.
The equations that I have in place are not correct for what I am trying to achieve.
With the following initial conditions, here is what a plot of the outputted data yields:
Initial Conditions:
Angle: 30 degrees;
Initial Speed 8.66 m/s;
Height: 50 m;
Elasticity of object: .5 coefficient of restitution in the y direction;
Acceleration: -9.8 m/s^2;
No acceleration in the x direction
It appears that once the simulation begins, y just gets bigger and bigger, so the loop will never exit by itself.
Here is the code:
//This class will handle all time consuming activities
class Simulation extends SwingWorker<Void, Void>
{
//Execute time consuming task
protected Void doInBackground() throws Exception
{
FileWriter fstream = new FileWriter("output.txt");
BufferedWriter out = new BufferedWriter(fstream);
double angle = Double.valueOf(angleText.getText());
double radians = angle * (Math.PI/180);
double vel = Double.valueOf(speedText.getText());
double mass = Double.valueOf(massText.getText());
double y = Double.valueOf(heightText.getText());
double x = 0;
double epX = Double.valueOf(epxText.getText());
double epY = Double.valueOf(epyText.getText());
double ax = Double.valueOf(accxText.getText());
double ay = Double.valueOf(accyText.getText());
int numBounces = 0;
double deltaTime = .00000001;
double total_velocity = 0.0;
double time = 0.0;
String fs;
angle = angle * Math.PI / 180;
while(numBounces < 10)
{
//Increment Time
time = time + deltaTime;
//Calculate new values for velocity[x] and velocity[y]
double vx = (vel*Math.cos(angle)) + ax*time;;
double vy = (vel*Math.sin(angle)) + ay*time;
//Calculate new values for x and y
x = x + vx*time;
y = y + vy*time + .5*ay*(time*time);
System.out.format("%.3f\n", y);
fs = String.format("%f\t %f\t %f\t %f\t %f\t %f\t %f\t\n", ax, ay, x, y, vx, vy, time);
out.write(fs);
//If ball hits ground: y < 0
if(y < 0)
{
numBounces++;
System.out.println("Number of Bounces: " + numBounces);
//Why is this statement needed if the velocity in the y direction is already being reversed?
vy = -vy - ay*time; // vy = -vy - ay*time;
//Calculate angle
angle = Math.atan(vy/vx);
angle = angle * Math.PI / 180;
//Calculate total velocity
total_velocity = Math.sqrt((vy*vy) + (vx*vx));
//Velocity with elasticity factored in
total_velocity = Math.sqrt((epY) * total_velocity);
//New velocities for when ball makes next trip
vy = total_velocity*Math.sin(angle);
vx = total_velocity*Math.cos(angle);
out.write(fs);
}
//Draw projectile
//Thread.sleep(.00001); //Sleep for deltaTime - 10 nanoseconds or draw after n number of points
}
out.close();
return null;
}
//SwingWorker lets you execute code on the event dispatching thread. Also allows you to update the GUI
public void done()
{
try
{
/*
rangeText.setText(" " + x);
heightTText.setText(" " + y);
timeText.setText(" " + time);
*/
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
What could be the possible problem?
My guess is that it might have something to do with the angle. In a previous version of the code, where I did not factor in an angle, it worked fine. Also, I am not sure if bounds on the GUI have to be set up so that y won't go on forever.
I also have a NullPointerException.
The first problem I see is here:
//Calculate angle
angle = Math.atan(vy/vx);
angle = angle * Math.PI / 180;
Math.atan returns a value in radians already:
Returns the arc tangent of a value; the returned angle is in the range -pi/2 through pi/2.
So the * Math.PI / 180 is not going to do you any favors.
The second problem is here:
//Calculate new values for velocity[x] and velocity[y]
double vx = (vel*Math.cos(angle)) + ax*time;;
double vy = (vel*Math.sin(angle)) + ay*time;
Every pass through the loop, these values are reinitialized. Because angle, ax, ay, and time cannot change during the loop, that means you always end up with the same vx and (positive) vy. vy should be getting smaller with each loop pass, something more like:
//Calculate initial values for velocity[x] and velocity[y]
double vx = (vel*Math.cos(angle)) + ax*time;
double vy = (vel*Math.sin(angle)) + ay*time;
while(numBounces < 10) {
//Increment Time
time = time + deltaTime;
//Calculate new values for x and y
x = x + vx*time;
y = y + vy*time + .5*ay*(time*time);
//Calculate new values for velocity[x] and velocity[y]
vx += ax * time;
vy += ay * time;
You need to learn to use a debugger - in Eclipse, for instance. Then you can stop wherever you want and examine variables until you figure out exactly where you're taking a wrong turn (or not taking a right one, in this case). You'll be able to figure this out in a minute or so.
If that's not an option, start putting in console printlns of key data.
Code always has errors, and you often can't figure them out just by looking at it - no matter how hard and how long.

Polygon Intersection fails, collision "size" too big

OK, so I'm trying to make a simple asteroids clone. Everything works fine, except for the collision detection.
I have two different versions, the first one uses java.awt.geom.Area:
// polygon is a java.awt.Polygon and p is the other one
final Area intersect = new Area();
intersect.add(new Area(polygon));
intersect.intersect(new Area(p.polygon));
return !intersect.isEmpty();
This works like a charm... if you don't care about 40% CPU for only 120 asteroids :(
So I searched the net for the famous separating axis theorem, since I'm not thaaaaaat good a the math I took the implementation from here and converted it to fit my Java needs:
public double dotProduct(double x, double y, double dx, double dy) {
return x * dx + y * dy;
}
public double IntervalDistance(double minA, double maxA, double minB,
double maxB) {
if (minA < minB) {
return minB - maxA;
} else {
return minA - maxB;
}
}
public double[] ProjectPolygon(double ax, double ay, int p, int[] x, int[] y) {
double dotProduct = dotProduct(ax, ay, x[0], y[0]);
double min = dotProduct;
double max = dotProduct;
for (int i = 0; i < p; i++) {
dotProduct = dotProduct(x[i], y[i], ax, ay);
if (dotProduct < min) {
min = dotProduct;
} else if (dotProduct > max) {
max = dotProduct;
}
}
return new double[] { min, max };
}
public boolean PolygonCollision(Asteroid ast) {
int edgeCountA = points;
int edgeCountB = ast.points;
double edgeX;
double edgeY;
for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++) {
if (edgeIndex < edgeCountA) {
edgeX = xp[edgeIndex] * 0.9;
edgeY = yp[edgeIndex] * 0.9;
} else {
edgeX = ast.xp[edgeIndex - edgeCountA] * 0.9;
edgeY = ast.yp[edgeIndex - edgeCountA] * 0.9;
}
final double x = -edgeY;
final double y = edgeX;
final double len = Math.sqrt(x * x + y * y);
final double axisX = x / len;
final double axisY = y / len;
final double[] minMaxA = ProjectPolygon(axisX, axisY, points, xp,
yp);
final double[] minMaxB = ProjectPolygon(axisX, axisY, ast.points,
ast.xp, ast.yp);
if (IntervalDistance(minMaxA[0], minMaxA[1], minMaxB[0], minMaxB[1]) > 0) {
return false;
}
}
return true;
}
It works... kinda. Actually it seems that the "collision hull" of the asteroids is too big when using this code, it's like 1.2 times the size of the asteroid. And I don't have any clue why.
Here are two pictures for comparison:
http://www.spielecast.de/stuff/asteroids1.png
http://www.spielecast.de/stuff/asteroids2.png
As you can hopefully see, the asteroids in picture one are much denser than the ones in picture 2 where is use the SAT code.
So any ideas? Or does anyone knows a Polygon implementation for Java featuring intersection tests that I could use?
It looks like your second result is doing collision detection as if the polygons were circles with their radius set to the most distant point of the polygon from the center. Most collision detection stuff I've seen creates a simple bounding box (either a circle or rectangle) into which the polygon can fit. Only if two bounding boxes intersect (a far simpler calculation) do you continue on to the more detailed detection. Perhaps the appropriated algorithm is only intended as a bounding box calculator?
EDIT:
Also, from wikipedia
The theorem does not apply if one of the bodies is not convex.
Many of the asteroids in your image have concave surfaces.

Categories