[Home]WallAvoidance

Robo Home | Changes | Preferences | AllPages

Difference (from prior major revision) (author diff)

Changed: 65c65,66
Just to add to the section,
Just to add to the section, the following code does wall avoidance with custom events. As an example, i just made the robot to look at the center of the field and move ahead a bit whenever it is very near the wall. The code can be suitably modified to do WallSmoothing as well.


Changed: 102c103
- AxialXForm
-- AxialXForm

Related pages: Movement - Movement/CodeSnippetGoTo - WallSmoothing
I need serious help with this wall avoidance issue. This is the weakest spot of Marshmallow at the moment. Is there some simple strategies I can use that doesn't end in the robot oscillation, quite predictably, against the walls? -- PEZ

The easiest way I've found to avoid walls is to use a movement method that picks an (x,y) coordinate to move to. As long as the coordinate you pick is within the rectangle (18.0, 18.0)-(battlefieldwidth - 18.0, battlefieldheight - 18.0) you won't hit any walls. You can make the wall margin greater than 18.0 to account for the fact that bots make wide turns at high speed. Code for moving to an (x,y) coordinate is readily available. There is an extremely small piece of code to do this in DuelistMicro's source, (It has to fit into a microbot, remember? :-P) though it occaisionally drives the bot in the wrong direction (about once per round at 800x600). As far as I know this is the smallest possible code. -- David Alves


Has anyone tried David David McCoy's WallAvoidance method at [developerWorks]? It works by seeing if it is necessary to adjust for walls, and if it is necessary it factors in a bearing to the middle of the BattleField?. It seems to work quite well, but sometimes seems to go totally the wrong way... -- Jomel
Somewhat depending on your movement system the following piece of code (which I found the first time in Cigaret I think) could make for a huge improvement in wall avoidance:
setMaxVelocity(Math.abs(getTurnRemaining()) < 40 ? preferredVelocity : 0.1);
You need to do this almost every turn. The idea here is to push the brake pedal hard if you find yourself having to make a really sharp turn. I'm working with a new movement system for Marshmallow, somewhat on the basis of an AngleGenerator. This allows me to make movement descisions very often and makes for a very fluid movement. With this code plugged in I hit the wall less than 2% of the rounds. Without it I hit the wall about 5 times every round. -- PEZ
Any idea why this doesn't work:
if (getX()<20 || getY()<20 || getX()>getBattleFieldWidth()-20 || getY()>getBattleFieldHeight()-20) {
    if (nearwall=false) {
        direction*=-1;
        nearwall=true; out.println("Near wall");
    }
    else {
        out.println("Still near wall");
    }
}
else {
    nearwall=false;
}

nearwall is set to false originally, and this is the first thing in the run method. afterwards it goes ahead(100*direction). The onscannedrobot keeps it perpedicular. When i run it it hits walls, it notices when it is "still near wall", but it never says "near wall", so never changes direction until it crashes (onHitWall? changes direction too) -- Tango

if your distance is less then 18 to the wall you just hit it, because 2*18 is the length of your robot. so if you have for example a distance of 25 to a wall and you are moving with velocity 8 against the wall you will have a distance of 25-8=17 the next tick (this means you just hit the wall). as a result the chance that you are once in the range of 18-20 to a wall is really small compared to all possible situations you will hit the wall in the next tick (especially if you are moving with full speed). have a look at how Kawigi's bots do this (at least some of them) they calculate their position for the next tick and then check if this is a "dangerous" position. -- rozu

I'm working on a whole new movement system, but i just wanted to try this. The new one will use Rectange2D.contains. So the best way to improve this system is to increase the 20. I'll give 30 a try, 18+8=26, so 30 seems a good buffer. -- Tango

No, it seems that wasn't the problem. I even tried it with 50 and it didn't work. It's not that it's discovering it's near the wall too late, it's the fact that it is only running the "still near wall" code, and not the "near wall" code. (i made the distinction so it wouldn't just go backwards and fowards near the wall, and would instead wait until it was away from the wall before starting again) -- Tango

What I generally do is project my movement forward (i.e. getX() + Math.sin(getHeadingRadians())*currentDirection*30 or something) and see if that point is inside the field rectangle (it doesn't matter if you're close to the wall if you're not going toward it) -- Kawigi

Yeah, i plan to do something like that when i get around to a new movement system, i'm just trying to work out why this one doesn't work, to try and improve my java knowledge. -- Tango

If you're using ahead() and not setAhead() then your method isn't interrupting until you hit the wall, and that's the only time the remainder has a chance to look and actually see if you're near a wall. -- Kuuran

That still doesn't explain why the nested if never comes up true (ie. why it always prints "still near wall" and never "near wall". -- Tango

You used an assignment instead of a conditional. -- Kuuran

So i did, thankyou. All this for want of an equals sign... -- Tango

It's a common pitfall for all C based languages (and other languages too where you can use an assignment as an expression). In your case the compiler would have cought it if it wasn't that the variable involved is a boolean. One way to avoid this is to use the unary not operator, !, when testing booleans. Like so:

if (!nearwall) { ...
This can be read as "if not nearwall" which better expresses what you intend and thus is better code. -- PEZ

I actually knew that... not sure why i didn't use it... :-/ -- Tango

whats this WallSmoothing

Watch Shadow and SandboxDT. See how they move as the approach the wall? Thats wall smoothing. -- jim


I'm trying WallAvoidance with a CustomEvent. Anyone done this already? -- Qetu

Yeah, i worked on this for quite a while but never quite managed to get it to work for me. The problem i found was that although you could get it to work successfully in not hitting the walls, it tended to degrade my performance against any wall segmenters (or pattern matchers to a lesser extent). I rightly or wrongly concluded that this was because of the instantanious nature with which the wall avoidance was initiated, and that it involved an almost complete change of movement pattern. I suppose you could write more complex event handlers to implement an approximation to WallSmoothing, but i concluded it was easier to actually check the projected destination of my robot and adjust accordingly to avoid the problem, rather than trying to deal with it once the problem had arisen. Prevention is better than cure, or so they say anyway. Good luck to you... --Brainfade

Righto! Prevention is better than cure. -- PEZ

the two good methods seem to be: create a rectangle slightly smaller than the battlefield 1. bounce if your predicted posistion is outside of rectangle a 2. wall smooth if your predicted posistion is outside of recangle a

Just to add to the section, the following code does wall avoidance with custom events. As an example, i just made the robot to look at the center of the field and move ahead a bit whenever it is very near the wall. The code can be suitably modified to do WallSmoothing as well.

class NearWallEvent extends Condition
{
    private AdvancedRobot bot;
    private double minDistance;
    private double battleFieldWidth;
    private double battleFieldHeight;
    
    public NearWallEvent(AdvancedRobot aRobot)
    {
        this.bot = aRobot;
        this.minDistance = this.bot.getHeight();
        this.battleFieldWidth = this.bot.getBattleFieldWidth();
        this.battleFieldHeight = this.bot.getBattleFieldHeight();
    }
    
    public boolean test()
    {
        if(Math.min(this.bot.getX(), this.battleFieldWidth - this.bot.getX())<this.minDistance)
            return true;
        else if(Math.min(this.bot.getY(), this.battleFieldHeight - this.bot.getY())<this.minDistance)
            return true;
        return false;
    }
}

public void onCustomEvent(CustomEvent ce)
{
    Condition condition = ce.getCondition();
    if(condition instanceof NearWallEvent)
    {
        this.stop();
        AXFUtil.MovementMethods.lookAt(this, this.battleFieldWidth/2, this.battleFieldHeight/2);
        this.ahead(this.botHeight);
        this.resume();
    }        
}
-- AxialXForm
Yet another "any idea why this code doesn't work"?:

		if(!field.contains(getX() + getVelocity()*Math.sin(getHeadingRadians()),getY() + getVelocity()*Math.cos(getHeadingRadians()))) {
			ahead= -ahead ;
		}
		setAhead(ahead*md);

where ahead is either 1 or -1, and md is a constant. My bot seems to vibrate and get stuck(won't ever come off) on the wall. -- Nantuko Primus

Why not do:

		if(!field.contains(getX() + ahead*md*Math.sin(getHeadingRadians()),getY() + ahead*md*Math.cos(getHeadingRadians()))) {
			ahead= -ahead ;
		}
		setAhead(ahead*md);

or something like that? (I don't know what md is, maybe you'd want to use a smaller number). The problem is that when you change ahead, the sign of the velocity doesn't change for several more turns, what's important is what direction you're trying to go. -- Kawigi

Thanks! I realized the problem in Spanish class today. Thanks for the help though. -- Nantuko Primus


Why don't use AntiGravityMovement - that's very simple (you don't need to think about other algorithms - this not only avoids walls, but also other robots, and can be useful in targeting)? A good AntiGravity snippet is here: Hanji/AGravEngine Hanji/GravPoint -- Ph

My nearWall() function isn't working. Could someone tell me why? Here it is:

public boolean nearWall() {
		if(dir==-1){
		    if(getX() + 100 >= getBattleFieldWidth() && getHeading() > 270 - 45 && getHeading() < 270 + 45)
		    return true;
		    if(getX() - 100 <= 0 && getHeading() > 45 && getHeading() < 135)
		    return true;
		    if(getY() + 100 >= getBattleFieldHeight() && getHeading() > 180 - 45 && getHeading() < 180 + 45)
		    return true;
		    if(getY() - 100 <= 0 && getHeading() > 360 - 45 && getHeading() < 45)
		    return true;
	    }
	else{
		if(getX() + 100 >= getBattleFieldWidth() && getHeading() > 45 && getHeading() < 135)
		return true;
		if(getX() - 100 <= 0 && getHeading() > 270 - 45 && getHeading() < 270 + 45)
		return true;
		if(getY() + 100 >= getBattleFieldHeight() && getHeading() > 360 - 45 && getHeading() < 45)
		return true;
		if(getY() - 100 <= 0 && getHeading() > 180 - 45 && getHeading() < 180 + 45)
		return true;
	}
		return false;
	}

dir
is set to either 1 or -1 and I use it to determine whether I am going forward or backward. nearWall() tells me if I am about to crash. When it returns true, I do
dir=-dir;
. WHy does it mess up? --Bayen (edit: it's fixed now)

I'm going to take a guess here that it seems to freeze instead of going backwards. You need to adjust the if block so that it doesn't check for the first set of conditions if you are in reverse. As it is, I expect it will get near the wall while going foward, hit reverse, still be near the wall, hit reverse again, and keep reversing direction until the transmission drops out. -- Martin

Thanks Martin, that fixed it up completely! I went back and changed the code to the correct version --Bayen

private static final double northeast =  45.0;
private static final double southeast = 135.0;
private static final double southwest = 225.0;
private static final double northwest = 315.0;

public static boolean isInRange( double min, double value, double max )
{
	return ( ( min <= value ) && ( value <= max ) );
}

public static boolean isNearWall( double x, double y, double heading, double velocity, double proximity, double maxX, double maxY )
{
	boolean isNearWall;
	double minX = proximity;
	double minY = proximity;
	maxX -= proximity;
	maxY -= proximity;


	if( isInRange( minX, x, maxX ) && isInRange( minY, y, maxY ) ) isNearWall = false;
	else if( velocity == 0.0 ) isNearWall = false;
	else if( velocity > 0.0 )
	{
		if( x > maxX && isInRange( northeast, heading, southeast ) ) isNearWall = true;
		else if( y < minY && isInRange( southeast, heading, southwest ) ) isNearWall = true;
		else if( x < minX && isInRange( southwest, heading, northwest ) ) isNearWall = true;
		else if( y > maxY && !isInRange( northeast, heading, northwest ) ) isNearWall = true; // not in alternate range
		else isNearWall = false;
	}
	else
	{
		if( x < minX && isInRange( northeast, heading, southeast ) ) isNearWall = true;
		else if( y > maxY && isInRange( southeast, heading, southwest ) ) isNearWall = true;
		else if( x > maxX && isInRange( southwest, heading, northwest ) ) isNearWall = true;
		else if( y < minY && !isInRange( northeast, heading, northwest ) ) isNearWall = true; // not in alternate range
		else isNearWall = false;
	}


	return isNearWall;
}

There is some food for thought. I doubt it is code size friendly, but maybe so, and it's more readable to me. I did not test it. -- Martin

One more comment (now that I am back from the dentist): 200 seems a really high number. On an 800x600 field you will be bouncing around in a 400x200 space, 1/6th the size of the arena. Slamming the brakes (or reverse) from max velocity gives you 12 units of travel. You don't need much more than that (if any). -- Martin

Besides the 12 units, you need one extra tick (=8 units) spare. Lets assume that you are at 12.1 units from the wall, you just continue until the next tick and only then hitting the brakes (at 4.1 units from the wall) which is to late. Instead of evaluating your current position, I rather evaluate your future position. In that case the problem with 'close to the wall, but already reversing' is solved automatically. I use(d) something like this:

playField = new Rectangle2D.Double( 18, 18, getBattleFieldWidth() - 36, getBattleFieldHeight() - 36);
// currDirection is 1 if going forwards, -1 if going backwards
myHeading = getHeadingRadians();
myCheckDistance = WALLCHECK * currDirection;   // WALLCHECK = 20
// calculate future position (very short BlindMansStick)
newposx = myPos.getX() + Math.sin( myHeading) * myCheckDistance;
newposy = myPos.getY() + Math.cos( myHeading) * myCheckDistance;
// if future position not in field, bounce
if (!playField.contains( newposx, newposy)) {
    switchDir = true;
}
You have to put it in a function yourself, and change some variables to your own. I just quickly copied it from an older source. -- GrubbmGait

I originally said 20 units but 'corrected' myself with 12. You are right. -- Martin


Robo Home | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited December 31, 2006 11:31 EST by delhi-203.200.95-130.static.vsnl.net.in (diff)
Search: