Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Задани на лабораторные работы. ПРК / Professional Microsoft Robotics Developer Studio

.pdf
Скачиваний:
126
Добавлен:
20.04.2015
Размер:
16.82 Mб
Скачать

www.it-ebooks.info

Chapter 9: Adventures in Simulation

Each player is identified by the number on its top platform.

From a simulation standpoint, one of the interesting things about this simulation environment is the way it detects when a sumobot has left the ring. The ring itself is several centimeters high and it is placed in the middle of a trigger volume that is slightly lower than the ring. In Figure 9-5, rendering of the trigger entity has been enabled so that you can see it.

Figure 9-5

When a sumobot leaves the ring, it intersects with the trigger shape, the Referee service is notified, and the round is ended.

Communicating with the Sumo Referee Service

Most of the examples in this book have a manifest that starts a number of services, and those services continue to run as long as the node is running. In the case of the sumo competition, sumo player services are started and ended dynamically.

When the sumo player service runs, it starts up an instance of the SimulatedIRobotLite service. This service is analogous to the hardware service that would be running on an actual hardware robot. It provides an interface between the sumo player service and the hardware.

The sample sumo player service provided with the sumo package is \Microsoft Robotics Studio

(1.5)\samples\simulation\competitions\simulatedsumoservices\sumoplayer\ sumoplayer.csproj. You can examine the ConnectWithRobot method in sumoplayer.cs to see how the sumo player service configures and connects to the SimulatedIRobotLite service.

When the SimulatedIRobotLite service receives the Connect message, it sends a message to the SimulatedSumoReferee service announcing that a new player is about to join the match. The Referee service adds a new sumobot entity to the simulation environment and sets up a ServiceForwarder to send messages back to the SimulatedIRobotLite service.

423

www.it-ebooks.info

Part II: Simulations

This method of sending an announcement message to another service so that it can send messages back is a pattern that is often used in systems with dynamic services, so we’ll look at it in a little more detail. In the SimulatedIRobotLite service, the AnnounceToReferee method is called in response to a Connect message from the sumo player service. The method first verifies that the sumo Referee service is running and that it has a valid port to send messages to that service.

Then a PlayerAnnounceRequest is constructed to send to the Referee service. This class contains the sumo player name, an image that represents the sumo player service, and the ServiceInfo.Service string that identifies the SimulatedIRobotLite service. It also contains the name of the entity specified as a partner to the SimulatedIRobotLite service. The referee uses this entity name to name the player entity that is created in the simulation environment:

//let the referee know that we would like to join the match. referee.PlayerAnnounce announce = new referee.PlayerAnnounce(); announce.Body = new referee.PlayerAnnounceRequest(ServiceInfo.Service); announce.Body.Name = _state.Name;

announce.Body.Image = _state.RobotImage;

//our entity instance name is passed as a partner

PartnerType entityPartner = Dss.ServiceModel.DsspServiceBase.DsspServiceBase.FindPartner(

new System.Xml.XmlQualifiedName(Microsoft.Robotics.Simulation.Partners.Entity, Microsoft.Robotics.Simulation.Contract.Identifier), ServiceInfo.PartnerList);

if (entityPartner == null)

{

LogError(

“Invalid entity name specified as a partner to the iRobotLite Service”);

}

else

announce.Body.EntityName = entityPartner.Service.ToString();

// find the alternate contract service info for the refereeannounce service foreach (ServiceInfoType altService in AlternateContractServiceInfo)

{

if (altService.Contract.Contains(“simulatedsumorefereeannounce”))

{

announce.Body.AnnounceURI = altService.Service; break;

}

}

_refereePort.Post(announce);

referee.PlayerAnnounceResponse announceRsp = null; Fault failure = null;

yield return Arbiter.Choice(announce.ResponsePort, delegate(referee.PlayerAnnounceResponse rsp)

{

announceRsp = rsp;

},

424

www.it-ebooks.info

Chapter 9: Adventures in Simulation

delegate(Fault f)

{

failure = f;

LogError(“Announce failed, shutting down”, f);

}

);

When the referee receives the announce request, it sets up a port to send messages back to the SimulatedIRobotLite service as follows:

refereeannounce.SimulatedSumoRefereeAnnounceOperations port = ServiceForwarder<refereeannounce.SimulatedSumoRefereeAnnounceOperations>

(announce.Body.AnnounceURI);

When both players have been connected and the match is started, the Referee service sends a PlayerButtonPress message to the SimulatedIRobotLite service associated with each sumobot, indicating that the Play button has been clicked, and the sumobot begins running its strategy for the round.

When the match has concluded and a new match begins, the referee sends a Drop message to the sumo player service, as well as its associated SimulatedIRobotLite service, and they terminate.

The code for the sample sumo player and for the SimulatedIRobotLite services is included with the MRDS Sumo package. Unfortunately, the source code for the SimulatedSumoReferee service has not been publicly released yet. Look for it in a future MRDS release.

The Sample Sumo Behavior

Before you learn how to make your own sumo behavior, it pays to study the behavior implemented in the sample sumo player. The behavior implemented in the sample sumo relies on a state machine with the following states:

Uninitialized: This is the initial state. No state information has been received from the robot yet.

Initializing: A state packet has been received and the sumo player service is initializing the hardware.

Ready: The hardware has been initialized and the sumobot is ready to begin a round.

Pending: The Play button has been pressed and the sumobot is waiting five seconds before entering the Wander state and starting to move.

Contact: One of the bumper sensors indicated that the sumobot is in contact with an opponent.

AvoidBoundary: One of the infrared sensors on the bottom of the sumobot detected that the sumobot is too close to the edge of the ring.

Tracking: The opponent has been spotted; move toward it.

Wander: The sumobot doesn’t know what else to do and wanders around the sumo ring until something interesting happens.

Blind: The sumobot has not received a sensor packet for some time. Stop movement and spin slowly to avoid leaving the sumo ring.

425

www.it-ebooks.info

Part II: Simulations

Most of the processing for this state machine takes place in the TimerHandler method in sumoplayer.cs, which is called at regular intervals. Three times out of four, the TimerHandler begins by requesting sensor state from the hardware. On the fourth time, an image is requested from the webcam. Both the sensor handler method (IRobotStateHandler) and the image handler method (ValidateFrameHandler) execute asynchronously when the data is returned from the hardware.

The sumo player service starts in the Uninitialized state. Once it has made a request to initialize the hardware, it moves to the Initializing state and then to the Ready state when the hardware confirms that it is properly initialized. At that point, the robot beeps once to indicate that it is ready for action. On the real hardware, booting the hardware and making it to the Ready state can take a minute or so. In the simulator, it is very fast.

When the hardware robot is in the Ready state, it is placed in the sumo ring, and the Play button is clicked when the referee starts the round. In the simulator, the referee places the simulator model in the ring and then passes a message to the SimulatedIRobotLite service associated with each player, which causes the Play button to be clicked. The next time the sumo player service retrieves the hardware state, it detects that the button has been pressed and enters the Pending state. The Pending state lasts for five seconds during which the robot can’t move. In the real world, this gives the robot owner time to get out of the ring before the robots start moving.

The sumo player service sets the state to Pending in the RobotUpdateButtonsHandler following code. It then enforces the five-second wait with a call to InternalDrivingMilliseconds, which specifies a wheel speed of 0 for both the left and right wheels and a duration of 5000 milliseconds. The InternalDrivingMilliseconds method is important because it schedules the movement of the robot for the next period of time. If the sumo ring border or an opponent is detected, the duration of the movement can be preempted. In this case, you are instructing the robot to stay still for the next five seconds. It can still gather webcam images and do processing during this time, so if it is facing its opponent it will be ready to attack immediately after the time limit expires:

public void RobotUpdateButtonsHandler(irobot.ReturnPose pose)

{

// Ignore buttons before the iRobot is initialized if (_state.SumoMode < SumoMode.Ready)

return;

if ((pose.ButtonsCreate & create.ButtonsCreate.Play) == create.ButtonsCreate.Play && _state.SumoMode < SumoMode.Pending)

{

_state.SumoMode = SumoMode.Pending; LogVerbose(LogGroups.Console, “Sumo Mode: Pending”);

//play tones to indicate that the competition has begun _robotPort.RoombaPlaySong(3);

//wait 5 seconds before moving InternalDrivingMilliseconds(0, 0, 5000.0);

//Set Advanced LED On and Power LED to bright Green.

_robotPort.RoombaSetLeds(irobot.RoombaLedBits.CreatePlay, 0, 255);

}

}

426

www.it-ebooks.info

Chapter 9: Adventures in Simulation

The TimerHandler runs about every 12.5 milliseconds, and each time it runs, it requests new sensor state or image state. If the time has expired from the previous InternalDrivingMilliseconds command, it plots its next move. If it is in the Pending state, it moves to the Wander state and makes a call to SetWanderDrive, which causes the robot to drive forward with a slight curve to the right for the next 250 milliseconds. The robot remains in this Wander state until something interesting happens.

In the Wander state, the robot eventually crosses the outer band in the sumo ring. When this happens, the next state packet returned from the hardware will be processed by the RobotUpdateFloorSensorsHandler method. This method takes immediate action to preempt the current movement. It wouldn’t be wise to wait until the duration of the last movement completed because by then the robot could be out of the ring and the round would be lost. This method immediately calls InternalDrivingMilliseconds to spin the robot in a direction away from the edge of the ring. This call cancels the previous driving command. When the spin command expires, the TimerHandler method drives the robot away from the edge and puts it back into the

Wander state.

This way of handling drive commands works well because it enables the robot to choose its movement by setting the motor drive speeds for a period of time, but it also allows for important events to interrupt this movement when necessary.

Another event that can interrupt movement commands is when the robot detects the presence of an opponent. This occurs when the ProcessImage method detects a large white area in the webcam image. It determines whether the center of the white region is to the left or the right, and then adjusts the wheel speed to move the robot toward its opponent and changes the state to Tracking.

When the sumobot makes contact with its opponent, the RobotUpdateBumpersHandler changes the state to Contact and pushes against the opponent with full power.

In the Tracking state or the Contact state, if the robot loses its opponent, it returns to the Wander state.

The only state we haven’t covered is the Blind state. The robot enters this state if it receives a sensor packet that is older than a certain time threshold. This indicates that there is a problem transferring the sensor data or the robot hardware is running slowly and the sensor data is too old to be trusted. In this case, the robot enters the Blind state and stops moving forward until it begins to receive sensor state data that is not stale. This prevents the robot from relying on outdated sensor state data to make its decisions, which in turn prevents it from going out of the ring.

That covers the behavior that is provided with the sample sumo player. Of course, you are free to implement anything you like for your winning strategy. Take some time to open the sumo player project, run it, and set some breakpoints so that you understand how the robot makes decisions and handles important events such as crossing the outer border and detecting its opponent.

Creating a New Sumo Player

Now that you are familiar with the sample sumo player service, you can build your own. Microsoft has provided a utility called MakeSumoPlayer that makes this process simple. From the Microsoft Robotics Developer Studio Command Prompt window, type the following: MakeSumoPlayer /name:Cyclone.

427

www.it-ebooks.info

Part II: Simulations

This utility takes the source code for the sample sumo player service in \Microsoft Robotics Studio (1.5)\samples\simulation\competitions\simulatedsumoservices and copies it to a new sumo player service with the specified name in the same directory. All of the necessary variables and namespaces are updated to make a new and completely independent service.

Compile the new sumo player service that you have created and run the sumo referee by typing sumoreferee at the MRDS command prompt. A new sumo player called Cyclone should now show up in the drop-down box, and you can pair it with the old sample sumo player for a match. At this point, the behavior of the new sumo player is the same as the sample sumo player. Let’s make some changes to make things interesting.

You can customize your new sumo player by giving it a new name and an image that will be used in the sumo referee user interface. Change the initial value of the _name field to define a new name for

your sumobot. You can change the image by editing the PlayerImage.bmp file in the Resources subdirectory. Be sure to keep the image at 64 by 64 pixels.

A copy of the Cyclone sumo player has been provided in the Chapter9 directory with a few modifications to the out-of-bounds handling. Open this project and look at the RobotUpdateFloorSensorsHandler method. A minor change or two has created a major change in the behavior of the robot.

This is the old code, which handled the case when the robot first encounters the colored band at the edge of the ring:

if (_state.Sensors.LineDetected)

{

_state.SumoMode = SumoMode.AvoidBoundary;

if (_state.Sensors.LineLeft && !_state.Sensors.LineRight && !_state.Sensors.LineFrontRight) InternalDrivingMilliseconds(300, -300, 100.0);

else if (_state.Sensors.LineRight && !_state.Sensors.LineLeft && !_state.Sensors.LineFrontLeft)

InternalDrivingMilliseconds(-300, 300, 100.0);

else

InternalDrivingMilliseconds(-200, 200, 400.0);

}

Sensors.LineDetected is true if any of the floor sensors have detected the outer boundary of the ring. In this case, the old behavior was to spin the robot for a period of time and then move it away from the boundary. The new behavior is shown here:

if (_state.Sensors.LineDetected || _state.SumoMode == SumoMode.FollowLine)

{

bool lineRight = _state.Sensors.LineRight || _state.Sensors.LineFrontRight; bool lineLeft = _state.Sensors.LineLeft || _state.Sensors.LineFrontLeft; bool lineMiddle = !lineLeft && lineRight;

if (_state.SumoMode != SumoMode.FollowLine)

428

www.it-ebooks.info

Chapter 9: Adventures in Simulation

{

if (!lineMiddle)

{

// spin until our orientation is right to follow the line InternalDrivingMilliseconds(-100, 120, 1000.0);

}

else

{

_state.SumoMode = SumoMode.FollowLine; InternalDrivingMilliseconds(100, 200, 1000);

}

}

else

{

if (lineMiddle)

{

// right on the line, continue by curving slightly to the left InternalDrivingMilliseconds(100, 200, 1000);

}

else if (!lineRight)

{

// curve to the right InternalDrivingMilliseconds(200, 50, 1000);

}

else if (lineLeft)

{

// curve to the left InternalDrivingMilliseconds(50, 250, 1000);

}

}

}

A new state called FollowLine has been added. If the robot is not in the FollowLine state when it encounters the edge of the ring, it turns until the right side of the robot is over the colored outer ring and the left side of the robot is not. At that point, it enters the FollowLine state. In this state, it follows along the inside edge of the outer boundary. If it detects that it is still over the boundary, it moves with a slight curve to the left to roughly follow the contour of the ring. If the right sensors move left of the boundary, the robot curves to the right to re-acquire the line. If the left sensors move right of

the boundary, then the robot curves to the left to re-acquire the line.

This causes the robot to circle the ring looking for its prey. The new FollowLine state is defined in the CycloneTypes.cs file. Compile and run the new cyclone service either by pressing F5 to debug or by running the sumo Referee service and selecting the Cyclone player. You should see a very noticeable difference in the behavior of the two opponents as one wanders randomly across the ring while the other follows the outer boundary, as shown in Figure 9-6.

429

www.it-ebooks.info

Part II: Simulations

Figure 9-6

Where to Go from Here

Is the new strategy better or worse than the previous strategy? Well, the jury is still out on that one, but you now understand how to build your own sumobot and implement your own strategy. You have everything you need to organize your own simulated sumo competition whereby each participant creates a unique sumo player and competes.

You can also take it to the next level by following the instructions on the Microsoft Robotics Developer Studio website to build a hardware version of the sumobot. After you compile your service for Windows CE, you will be able to see it running your strategy in a real sumo ring.

Building a Six-Legged Walker

If you’ve ever dreamed about building a giant mechanical cockroach, then this project is for you. You’ll build on the previous chapter that discussed articulated entities in the simulation environment to learn how to build a robotic leg with three degrees of freedom and replicate it five times to build a hexapod that can walk in any direction.

In this section, you’ll learn how to define the hexapod entity and then how to define a service that enables the hexapod to walk. The code for this project is in the ProMRDS\Chapter9 directory under

HexapodEntity and HexapodDifferentialDrive. We’ll look at HexapodEntity first. Figure 9-7 shows what our hexapod entity will look like.

430

www.it-ebooks.info

Chapter 9: Adventures in Simulation

Figure 9-7

The Hexapod Entity

The HexapodEntity service was initially created by copying and renaming the files from SimulationTutorial1. All references to SimulationTutorial1 were replaced with references to HexapodEntity, and the namespace was changed appropriately.

This service takes a data-driven approach to define the hexapod. An array called ShapeDescriptors is initialized with information about all of the shapes that make up the entity. Then another array called Relationships is initialized with all of the information about the joints that join the shapes together. The method that builds the entity consists of fairly generic code that reads the information from the arrays and puts the entity together. One advantage of this approach is that it is much easier to change the shapes and joints without having to modify any code.

Building the Main Body and Legs

The following code shows how the shapes in the main body and the front right leg are defined:

public struct HexapodShapeDescriptor

{

public Shapes ShapeID; public string Name; public double xPosition; public double yPosition; public double zPosition; public double xSize; public double ySize; public double zSize; public double radius; public double xRotation; public double yRotation; public double zRotation; public double mass; public string mesh;

(continued)

431

www.it-ebooks.info

Part II: Simulations

(continued)

public HexapodShapeDescriptor( Shapes _ShapeID,

string _Name, double _xPosition, double _yPosition, double _zPosition, double _xSize, double _ySize, double _zSize, double _radius, double _xRotation, double _yRotation, double _zRotation, double _mass, string _mesh)

{

ShapeID = _ShapeID; Name = _Name; xPosition = _xPosition; yPosition = _yPosition; zPosition = _zPosition; xSize = _xSize;

ySize = _ySize; zSize = _zSize; radius = _radius;

xRotation = _xRotation; yRotation = _yRotation; zRotation = _zRotation; mass = _mass;

mesh = _mesh;

}

}

//units are in meters

//Adjustable parameters:

const float _bodyLength = 0.3f; const float _bodyWidth = 0.2f; const float _bodyHeight = 0.08f; const float _upperLegRadius = 0.03f;

const float _lowerLegRadius = 0.025f; const float _upperLegLength = 0.1f; const float _lowerLegLength = 0.15f;

HexapodShapeDescriptor[] ShapeDescriptors = new HexapodShapeDescriptor[]

{

new HexapodShapeDescriptor(Shapes.Box, “Hexapod”, 0,

_upperLegLength + _lowerLegLength, 0,

_bodyWidth, _bodyHeight, _bodyLength, 0, 0, 0, 0, 1, “HexapodBody.obj”),

new HexapodShapeDescriptor(Shapes.Sphere, “FRShoulder”,

432