|
|
# Group 8
|
|
|
|
|
|
## Lab Notebook 9
|
|
|
|
|
|
**Date:** 19/5 - 2015
|
|
|
|
|
|
**Duration:** 20-24 hours
|
|
|
|
|
|
**Participants:**
|
|
|
|
|
|
- Frederik Jerløv
|
|
|
- Casper Christensen
|
|
|
- Mark Sejr Gottenborg
|
|
|
- Peder Detlefsen
|
|
|
|
|
|
## Goal
|
|
|
The goal of this exercise is to construct and program a sumo wrestling LEGO robot [1]. Then at the end of the lecture, Thursday 21th of May, we are going to make the robot fight each other in a arena seen on Figure 1.
|
|
|
|
|
|
![Image of the robot arena](http://legolab.cs.au.dk/DigitalControl.dir/NXT/Lesson9.dir/moresumosonboard.jpg)
|
|
|
|
|
|
## Plan
|
|
|
Our plan is to follow the exercises exactly as described, then test out some different ideas for a robot sumo wrestler.
|
|
|
|
|
|
## BumperCar
|
|
|
|
|
|
### Exercise 1
|
|
|
When starting the robot it just drive forwards. If we then hit the bumper, it will stop and drive backwards plus turn a little but. If we just hold the bumper in, it will keep move back and turn a but.
|
|
|
This is because the arbitrator decide which of the 2 behavior should be run DriveForward (lowest priority) or DetectWall. So if `takeControl` of DetectWall does not return true we just run DriveForwar. Then when we push the bumper `takeControl` of DetectWall becomes true and it call suppress on DriveForward, which make it stop running forward. Then it call DetectWalls `action` method which make it drive back and turn a bit. When we keep the bumper pressed the `takeControl` of DetectWall is always true, meaning we will always run the `action` method in DetectWall.
|
|
|
|
|
|
### Exercise 2
|
|
|
Show exit behavior code here
|
|
|
Exit doesn’t get activated immediately. We have to press Escape for a little while before the behavior changes. Quickly pressing Escape while in DriveForward will stop the motors and start them again right away. Escape while in DetectWall will have to wait for the blocking rotate to finish.
|
|
|
Note, A fast press on the bumper would yield true for `takeControl` and suppress the other behaviors. We would expect the behavior for when a bumper is hit to take action. This does not seem to happen. It looks like the arbitrator now go through all behavior again to which `takeControl` now is true. We have the same effect if it was the ESCAPE button that was quickly pressed.
|
|
|
|
|
|
### Exercise 3
|
|
|
On Figure 2 we see an image showing a code snippet of the Arbitrator. It shows how iterator from the different behaviors and calling `takeControl` on each in order to see if the given behavior wants control.
|
|
|
|
|
|
![Code illustrating Arbitrator takeControl loop](http://i.imgur.com/NaxT8c0.png)
|
|
|
|
|
|
So to answer question wether the DriveForward `takeControl` is called when the triggering condition of DetectWall is true, we have to say, NO. The reason for this is that when it finds a behavior where its `takeControl` is true, it sets this to the highestPriority to this behavior. Then we call `break`, which means we do not check behavior with lower priority .
|
|
|
|
|
|
### Exercise 4
|
|
|
In [2] the Lejos people says that `takeControl` should return quickly and not perform long calculation. Since a call to get the distance from the Ultrasonic Sensor takes some time to perform we instead make a thread run in the background and update the distance. This would mean that we in our `takeControl` we can now return quickly because we just read the latest value from the Ultrasonic Sensor. Below is the snippet of the thread which read the value and then sleep for 20 ms.
|
|
|
|
|
|
```Java
|
|
|
private Thread sonicThread = new Thread() {
|
|
|
public void run() {
|
|
|
while (true) {
|
|
|
distance = sonar.getDistance();
|
|
|
try {
|
|
|
Thread.sleep(20);
|
|
|
} catch (InterruptedException e) {
|
|
|
// TODO Auto-generated catch block
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
```
|
|
|
|
|
|
Below is the new `takeControl` which instead read the variable distance which is the latest value we have read through our thread.
|
|
|
|
|
|
```
|
|
|
public boolean takeControl() {
|
|
|
return touch.isPressed() || distance < 25;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### Exercise 5
|
|
|
In this exercise we should add a 1 second move back before it do the rest of the action. So we just set the motors to run backwards and sleep for 1000 ms. Then we do the action as usual. Below is the code snippet.
|
|
|
|
|
|
```Java
|
|
|
public void action() {
|
|
|
_suppressed = false;
|
|
|
BumperCar.leftMotor.backward();
|
|
|
BumperCar.rightMotor.backward();
|
|
|
try {
|
|
|
Thread.sleep(1000);
|
|
|
} catch (InterruptedException e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
BumperCar.leftMotor.rotate(-180, true);// start Motor.A rotating // backward
|
|
|
BumperCar.rightMotor.rotate(-360); // rotate C farther to make the turn
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### Exercise 6
|
|
|
We were not completely sure what we were supposed to do, but what we did was to make the motors non-blocking so when the bumper is pressed again we would reset the `action` method. But if we look on the code in the Arbitrator we can see suppress is only called if the priority is higher than the active one. See code below.
|
|
|
|
|
|
```Java
|
|
|
int active = _active;// local copy: avoid out of bounds error in 134
|
|
|
if (active != NONE && _highestPriority > active)
|
|
|
{
|
|
|
_behavior[active].suppress();
|
|
|
}
|
|
|
```
|
|
|
This mean that if we hit the bumper while it is turning it will not suppress and start over, because the priority is the same. So below is shown how we changed the `action` method:
|
|
|
|
|
|
|
|
|
```Java
|
|
|
public void action() {
|
|
|
_suppressed = false;
|
|
|
BumperCar.leftMotor.backward();
|
|
|
BumperCar.rightMotor.backward();
|
|
|
try {
|
|
|
Thread.sleep(1000);
|
|
|
} catch (InterruptedException e) {
|
|
|
e.printStackTrace();
|
|
|
}
|
|
|
BumperCar.leftMotor.rotate(-180, true);// start Motor.A rotating // backward
|
|
|
BumperCar.rightMotor.rotate(-360, true); // rotate C farther to make the turn
|
|
|
|
|
|
while(BumperCar.leftMotor.isRotating() || BumperCar.rightMotor.r.isRotating())
|
|
|
{
|
|
|
if(touch.isPressed())
|
|
|
{
|
|
|
action();
|
|
|
}
|
|
|
if(_suppressed)
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
BumperCar.leftMotor.stop();
|
|
|
BumperCar.rightMotor.stop();
|
|
|
}
|
|
|
```
|
|
|
So when we start the turning part we keep checking if the bumper is hit again. If the bumper gets hit we run action from start. If suppress is called (some one with higher priority wants to run) we stop the loop. Else we will run in the while loop until we are done turning.
|
|
|
|
|
|
### Motivation Functions
|
|
|
|
|
|
### A Behavior-based Control Program for the Sumo Wrestler Robot
|
|
|
We used the files `Behavior.java`[10], `Arbitrator.java`[11] and `BumperCar.java`[12] supplied in the lesson description as the basis for our sumo wrestler robot and BumperCar contains our own modifications for RoboWarV2.1 as well.
|
|
|
|
|
|
Before starting we first sat down and talked about some different ideas for the robot. The list below shows a short description of what we talked about.
|
|
|
|
|
|
1. We could surround the robot with a white circled paper so that opponents might believe it's the edge of the matching thus tricking it to turn around
|
|
|
2. We could use low gearing and a heavy built robot to simply push the others over the edge.
|
|
|
3. Use a third motor to built a flipper…. If bump … Flip
|
|
|
4. Use Ultrasonic Sound Sensor to look for people and push them out.
|
|
|
5. Use Light Sensor to avoid falling out of arena.
|
|
|
|
|
|
#### Gearbot
|
|
|
First we implemented gearing so the bot would have the double amount of pushing power which was supposed to lead us to victory by slowly pushing the other bots over the edge.
|
|
|
Unfortunately this was a rogue modification and thus we had to try another idea.
|
|
|
See picture for the modification.
|
|
|
|
|
|
![Robot with very low gearing on the wheels. Notice the tiny geer on the motor axis compared to the big geer on the wheel axis.](http://i.imgur.com/W33zzBCl.jpg)
|
|
|
|
|
|
#### RoboWar V1
|
|
|
Based on the ideas above we decided to make a robot that would be able to “flip” people. So we made a some sort of ramp, and together with a third motor we created an “elevator” mechanism which should be used to lift up the opponent and then knock them out of the arena. See Figure 3. Then we would use a UltraSonic Sound Sensor to move towards the opponent. If we can’t see an opponent we would just spin around and try look for one. Also we used 2 Light Sensor in order to not drive out of the arena ourself.
|
|
|
|
|
|
![Image of the robot](http://i.imgur.com/QpTATH6.jpg)
|
|
|
|
|
|
In this version of the robot we used the Arbitrator and Behavior from [2]. We then implemented 4 different behaviors.
|
|
|
1. Spin Behavior, used for spinning around to look for opponent.
|
|
|
2. AvoidWhiteLine Behavior, used for not driving out of the arena.
|
|
|
3. Attack Behavior, used for driving forward towards an opponent when we found one.
|
|
|
4. Exit Behavior, used for exiting the program without restarting the NXT.
|
|
|
|
|
|
The priority is as follows: Exit, AvoidWhiteLine, Attack, Spin. The exit is irrelevant for the battle. The top priority is therefore staying in the arena, since if we drive out of arena we are done. Next after survivability we also want to push the opponent out of the arena with the attack. If we are not close to the edge or we can’t see someone, spin around until we see an opponent that we can attack.
|
|
|
|
|
|
The Spin behaviors `takeControl()` just return true, since it would always like to be ran if nothing else to do. The ‘action()’ method is shown below.
|
|
|
|
|
|
```Java
|
|
|
public void action() {
|
|
|
LCD.drawString("Spin", 0, 3);
|
|
|
_suppressed = false;
|
|
|
Motor.A.setSpeed(300);
|
|
|
Motor.C.setSpeed(300);
|
|
|
|
|
|
Motor.A.forward();
|
|
|
Motor.C.backward();
|
|
|
|
|
|
while (!_suppressed) {
|
|
|
Thread.yield(); // don't exit till suppressed
|
|
|
}
|
|
|
|
|
|
Motor.A.stop();
|
|
|
Motor.C.stop();
|
|
|
Motor.A.setSpeed(800);
|
|
|
Motor.C.setSpeed(800);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
So what it does is simply to slow down the motor and spin around until we suppress it. It can be suppressed if we want to exit, we see a white line or we found an opponent. The reason we set down the speed is because when it spinned around at full speed it had a hard time finding the other robots.
|
|
|
|
|
|
The AvoidWhiteLine `takeControl` look like this.
|
|
|
|
|
|
```Java
|
|
|
public boolean takeControl() {
|
|
|
return rightLight.readValue() > 40 || leftLight.readValue() > 40;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
It return true of one of the sensors enter the white line. The values are fixed, but maybe they should be read before start of each turn. The `action` method looks like this.
|
|
|
|
|
|
```Java
|
|
|
public void action() {
|
|
|
_suppressed = false;
|
|
|
|
|
|
LCD.drawString("Drive backward", 0, 3);
|
|
|
Motor.A.backward();
|
|
|
Motor.C.backward();
|
|
|
long now = System.currentTimeMillis();
|
|
|
while (!_suppressed && (System.currentTimeMillis() < now + 500)) {
|
|
|
Thread.yield(); // don't exit till suppressed
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
This just drives back for half a second.
|
|
|
|
|
|
Last we have the Attack behavior. Its `takeControl()` method return true if an object is closer than 60 cm. We here used the code from Exercise 4, where we had a thread saving the value of the sensor.
|
|
|
|
|
|
```Java
|
|
|
public boolean takeControl() {
|
|
|
LCD.drawInt(distance, 3, 0, 1);
|
|
|
return distance < 60;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The `action()` method does nothing in particularly, it just moves forward towards the target.
|
|
|
|
|
|
```Java
|
|
|
public void action() {
|
|
|
LCD.drawString("Drive forward", 0, 3);
|
|
|
|
|
|
_suppressed = false;
|
|
|
Motor.A.forward();
|
|
|
Motor.C.forward();
|
|
|
|
|
|
while (!_suppressed) {
|
|
|
Thread.yield(); // don't exit till suppressed
|
|
|
}
|
|
|
|
|
|
Motor.A.stop();
|
|
|
Motor.C.stop();
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The full source code for the robot can be found here[3]. We had a lot of trouble to get the Attack behavior to function properly. Most of the time it did not notice the robot. It might have had something to do with the angle of the sensor. Below we have some videos of this version of the robot.
|
|
|
|
|
|
[Video of the robot trying to attack a tin can, but does not perform very ood[4]](https://drive.google.com/file/d/0B6hyEQ0rRjUMWHEtWUZRdUpLakU/view?usp=sharing)
|
|
|
|
|
|
[Video of the robot getting in a front to front challange, and succeed in winning a battle by almost flipping the opponent[5]](https://drive.google.com/file/d/0B6hyEQ0rRjUMeHhPY2o4V0NhQ1U/view?usp=sharing)
|
|
|
|
|
|
#### RoboWar V2
|
|
|
This robot is building on the same idea as described in the start. This robot instead use the Arbitrator presented in [1]. It also use the Light Sensor to keep in the arena. It use the front to try flip people out of the arena but I don’t use a Ultrasonic Sensor
|
|
|
|
|
|
This version have 3 behaviors.
|
|
|
1. DriveForward, used to drive forward.
|
|
|
2. DetectWhite, used for not drive out of the arena.
|
|
|
3. Exit, used for exiting.
|
|
|
|
|
|
The priority is as follows, Exit, DetectWhite, DriveForward. Again exit is irrelevant. So the main priority is to stay in the arena and drive around to see if we can knock out opponents.
|
|
|
|
|
|
#####DriveForward
|
|
|
Always wants control and drives the BumperCar forward.
|
|
|
|
|
|
#####DetectWhite
|
|
|
It detects if the Left or the Right sensor gets below the value 50, and if one of them does. Changes the priority from 0 to 100. When action is called it drives backwards for 200 milliseconds. If the Left one was detected rotate right. If the Right one was detected rotate left, otherwise do a 180.
|
|
|
|
|
|
#####Exit
|
|
|
If the escape button is down then is gets a priority of 200 and do a system exit when Arbiter calls its action.
|
|
|
|
|
|
The full source code can be found here[6]. The robot performed well even though it just drive somewhat random around and tries to flip opponents out of the arena. Below is some videos of robot.
|
|
|
|
|
|
[Video of the robot lose the first round, best of 3[7]](https://drive.google.com/file/d/0B6hyEQ0rRjUMZ25lN0FhTlRjZkE/view?usp=sharing)
|
|
|
|
|
|
[Video of the robot win second round, best of 3[8]](https://drive.google.com/file/d/0B6hyEQ0rRjUMTHNiTWxZTkE5amc/view?usp=sharing)
|
|
|
|
|
|
[Video of the robot win last round, best of 3[9]](https://drive.google.com/file/d/0B6hyEQ0rRjUMdjdsOHdIZ1BiUm8/view?usp=sharing)
|
|
|
|
|
|
#### RoboWar V2.1
|
|
|
This robot is an extension of the RoboWar V2. Here we added an UltraSonic Sensor in order to better attack people since it looks like the winning strategy of a robot fight.
|
|
|
The priority is as follows Exit, DriveFowards, DetectRobot and DetectWhite.
|
|
|
|
|
|
![The upgraded robot with ultrasonic sensor on the sides on top of the light sensors.](http://i.imgur.com/T6x2kjEl.jpg)
|
|
|
|
|
|
|
|
|
#####DetectRobot
|
|
|
This behavior uses 2 sonars and samples the values 3 times before making a decision to avoid acting on noisy data. If one of the distances mean values goes under 30 it wants control with a priority of 50. When the action gets executed it turns a bit to the left or right to turn towards the opponent.
|
|
|
|
|
|
To get the mean distance for each sensor we modified the thread that continuously reads the sensor data to also keep the latest three readings in an array and calculate the average for each array. This was done in the following way:
|
|
|
|
|
|
```Java
|
|
|
public void run() {
|
|
|
while (true) {
|
|
|
leftDistArray.add(sonarLeft.getDistance());
|
|
|
rightDistArray.add(sonarRight.getDistance());
|
|
|
if (leftDistArray.size() > 3) leftDistArray.remove(0);
|
|
|
if (rightDistArray.size() > 3) rightDistArray.remove(0);
|
|
|
int sum = 0;
|
|
|
for (Integer i : leftDistArray) {
|
|
|
sum += i;
|
|
|
}
|
|
|
distanceLeft = sum / leftDistArray.size();
|
|
|
sum = 0;
|
|
|
for (Integer j : rightDistArray) {
|
|
|
sum += j;
|
|
|
}
|
|
|
distanceRight = sum / rightDistArray.size();
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
Our `takeControl()` method remained mostly the same except that we now use a 2 second cooldown period after each detection where the DetectRobot behavior will not run to prevent it from turning too much.
|
|
|
|
|
|
```Java
|
|
|
public int takeControl() {
|
|
|
if (System.currentTimeMillis() < detectTime + cooldownMs)
|
|
|
return 0;
|
|
|
detectedLeft = distanceLeft < 30;
|
|
|
detectedRight = distanceRight < 30;
|
|
|
if (detectedLeft || detectedRight)
|
|
|
return 50;
|
|
|
return 0;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The `action()` method is quite simple since all it have to do is to turn either a bit left or right and save the time when it made the detection. We found that with our type of robot it was important not to turn our back on the opponent for too long which made us set the motor speed to 2000 every time we turned or moved backwards.
|
|
|
|
|
|
We tried out this new behaviour against some fellow students as seen in the following video.
|
|
|
|
|
|
[Video of Robowar 2.1](http://youtu.be/ZErLRsoqaRc)[13]
|
|
|
|
|
|
## Conclusion
|
|
|
|
|
|
By doing the exercises and building a sumo wrestler robot we learned that there are a lot of different ways you can construct a good candidate. Furthermore, it seemed equally important to have a clever construction that could either push enemies off the platform or tip them over as having well thought out behaviors to prevent enemies from doing those things to you.
|
|
|
|
|
|
## References
|
|
|
[1], Exercise material lab 9(http://legolab.cs.au.dk/DigitalControl.dir/NXT/ Lesson9.dir/Lesson.html)
|
|
|
|
|
|
[2], The leJOS Tutorial, Behavior Programming(http://www.lejos.org/nxt/nxj/tutorial/ Behaviors/BehaviorProgramming.htm)
|
|
|
|
|
|
[3], RoboWar V1 source code(https://gitlab.au.dk/lego-group-8/lego/ tree/master/lesson9/Robowar_v1)
|
|
|
|
|
|
[4], Video of the robot trying to attack a tin can, but does not perform very ood(https://drive.google.com/file/d/0B6hyEQ0rRjUMWHEtWUZRdUpLakU/view?usp=sharing)
|
|
|
|
|
|
[5], Video of the robot getting in a front to front challenge, and succeed in winning a battle by almost flipping the opponent(https://drive.google.com/file/d/ 0B6hyEQ0rRjUMeHhPY2o4V0NhQ1U/view?usp=sharing)
|
|
|
|
|
|
[6], Robot V2 source code(https://gitlab.au.dk/lego-group-8/lego/ tree/master/lesson9/Robowar_v2)
|
|
|
|
|
|
[7], Video of the robot lose the first round, best of 3 (https://drive.google.com/file/d/0B6hyEQ0r RjUMZ25lN0FhTlRjZkE/view?usp=sharing)
|
|
|
|
|
|
[8], Video of the robot win second round, best of 3(https://drive.google.com/file/d/0B6hyEQ0 rRjUMTHNiTWxZTkE5amc/view?usp=sharing)
|
|
|
|
|
|
[9], Video of the robot win last round, best of 3(https://drive.google.com/file/d/0B6hyEQ0 rRjUMdjdsOHdIZ1BiUm8/view?usp=sharing)
|
|
|
|
|
|
[10], Code for Behavior.java (https://gitlab.au.dk/lego-group-8/lego/tree/ 14e71c3a30af6ec8ef14f734fb81937e3a7382fc/lesson9/Robowar_v2/Behavior.java)
|
|
|
|
|
|
[11], Code for Arbitrator.java (https://gitlab.au.dk/lego-group-8/lego/tree/ 14e71c3a30af6ec8ef14f734fb81937e3a7382fc/lesson9/Robowar_v2/Arbitrator.java)
|
|
|
|
|
|
[12], Code for BumperCar.java with own modifications (https://gitlab.au.dk/lego-group-8/lego/tree/ 14e71c3a30af6ec8ef14f734fb81937e3a7382fc/lesson9/Robowar_v2/BumperCar.java)
|
|
|
|
|
|
[13], Video of RoboWar 2.1(http://youtu.be/ZErLRsoqaRc) |