Задани на лабораторные работы. ПРК / Professional Microsoft Robotics Developer Studio
.pdf
www.it-ebooks.info
Chapter 6: Extending the MRDS Visual Simulation Environment
physics engine is actively processing the frame. In the Update method, the entity calculates its transformation matrix, which is used in rendering. Custom behavior can be implemented here as well, such as setting the pose of an entity or the axle speed of a wheel, for example.
Render: This method is called once each frame after the Update method for all the other entities has completed. This is where the mesh is rendered so that the entity appears on the screen. Several entities override the default behavior for this method to implement specialized rendering effects.
State: This class is actually defined as part of the Entity class from which VisualEntity is subclassed, but it is important enough to mention it here. The EntityState contains important information about the entity, such as its name, its pose, its velocity, its physics shapes, and its rendering assets. More information is provided about the EntityState in Chapter 5.
InsertEntity, InsertEntityGlobal, RemoveEntity: These methods are used to add children entities. InsertEntity assumes that the pose of the child entity is relative to the parent entity, whereas InsertEntityGlobal assumes that the pose of the child is given in global coordinates. RemoveEntity removes a child entity. An example of a child entity is a camera that is mounted to a parent robot. The camera doesn’t have a physics shape associated with it so its position is updated in its Update method, enabling it to move with its parent entity. Other entities such as the BumperArrayEntity do have a physics shape, so they are moved by the physics engine each frame. To keep them attached to their parent entity, a joint is created between the parent entity and the child entity. This joint is stored in the ParentJoint field of the child entity. Joints are covered in more detail in Chapter 7.
PhysicsEntity: This class represents the link between this entity and the physics engine. If the entity has no physics shapes associated with it, then this member will be null. The more common case is that the entity will have one or more physics shapes associated with it and the
PhysicsEntity member is initialized with a call to CreateAndInsertPhysicsEntity after all of the physics shapes have been created and added to the State.PhysicsPrimitives list. The PhysicsEntity member contains several methods that affect the entity within the simulation environment, such as SetPose, SetLinearVelocity, SetAngularVelocity, ApplyForce, and ApplyTorque.
DeferredTaskQueue: This is a list of tasks that need to be executed during the Update method. The Update method is the only time when the physics engine is guaranteed to not be busy processing a frame. Most of the methods on the PhysicsEntity object cannot be called when the physics engine is busy. When one of these methods needs to be called outside of the Update method, it is added as a task to the DeferredTaskQueue and its execution is deferred until Update runs again.
LoadResources: This method is used to load the mesh, texture, and effect resources associated with the entity. It is typically called from the Initialize method and it stores references to the loaded meshes in the Meshes field.
The SimulationEngine Class
The SimulationEngine class defines a static member called GlobalInstance, which holds a pointer to the single instance of the SimulationEngine. The DssHost environment will not allow multiple instances of the SimulationEngine class to be instantiated. Public properties such as a reference to the graphics device and a reference to the ServiceInfo class associated with the SimulationEngine service can be accessed from GlobalInstance. Public methods such as IntersectRay and IsEntityNameInUse can also be accessed from this reference.
273
www.it-ebooks.info
Part II: Simulations
The SimulationEngine class defines another static member called GlobalInstancePort, which contains a reference to the simulation engine port that can be used to insert, update, and remove entities. These two global variables are useful to services that must interact closely with the simulation engine.
SimulationEngine.Proxy.DLL
This DLL is necessary to access the service elements of the simulation engine. Items such as contracts and port definitions should always be accessed from the Proxy DLL.
PhysicsEngine.DLL
This DLL contains the definitions for the objects used in the simulation environment that represent objects in the physics engine. The PhysicsEntity object described in the previous section is defined in this DLL, so your service must add a reference to PhysicsEngine.DLL if you want to call any of the methods on the PhysicsEntity object.
This DLL also defines the PhysicsEngine object. This object represents the physics engine scene that contains all of the entities in the simulation environment. The methods on this object are not typically called by a service directly, but the PhysicsEngine object must be passed into several of the physics engine APIs.
Microsoft.Xna.Framework.DLL
This DLL contains all of the rendering code. It provides a managed DirectX interface and eventually calls the underlying native DirectX DLLs. You must have both DirectX and XNA installed for the simulator to work properly. Some simulation services must use the XNA types, so they must have a reference to the XNA Framework DLL.
In some cases, it is necessary to convert between XNA types and their counterparts defined in
RoboticsCommon.DLL. There are several functions defined in the Microsoft.Robotics.Simulation
.Engine namespace in the TypeConversion class that make this easy. This class provides a FromXNA method for Vector3, Vector4, Quaternion, and Matrix that converts a type from XNA to its RoboticsCommon counterpart. Similar ToXNA methods are provided to convert in the opposite direction.
There are two namespaces that contain types used in the simulator: Microsoft.Xna.Framework and Microsoft.Xna.Framework.Graphics. Both are discussed in the following sections.
The Microsoft.Xna.Framework Namespace
You will see several of the basic types defined in this namespace used in various simulation services:
Vector2, Vector3, Vector4: These are functionally equivalent to the corresponding types found in RoboticsCommon.DLL but these types often have more utility methods associated with them. In addition, some APIs work with the XNA typed vectors and other APIs work with the RoboticsCommon vectors. It is sometimes necessary to convert between XNA types and their RoboticsCommon counterparts.
Matrix: This is a standard 4 × 4 matrix, but the XNA version has more utility functions associated with it than does the RoboticsCommon version.
Quaternion: The XNA Quaternion equivalent.
274
www.it-ebooks.info
Chapter 6: Extending the MRDS Visual Simulation Environment
The Microsoft.Xna.Framework.Graphics Namespace
This is where most of the graphics-related types are defined. Because there are so many types defined in this namespace, only a few are covered in this section:
GraphicsDevice: This object represents the hardware graphics device. It is initialized by the simulator, so it is rare for a service to interact directly with this object. However, if you override the Initialize method, you have to include a reference to the XNA DLL because the GraphicsDevice is passed as a parameter along with a reference to the PhysicsEngine object.
Effect, IndexBuffer, VertexBuffer, RenderState, Texture2D, TextureCube: These types are used to represent data that is directly accessed by the graphics hardware. Effects are used to tell the graphics hardware how to draw geometry. IndexBuffers and VertexBuffers hold the geometry to be drawn. RenderStates further specify how the graphics hardware should draw the objects. Texture2D and TextureCube objects hold texture map data.
It is beyond the scope of this book to completely cover the XNA graphics APIs. Numerous books and online articles cover XNA thoroughly. You can find the Microsoft XNA forums
at http://forums.xna.com, and the forum that deals specifically with the framework APIs is at http://forums.xna.com/56/ShowForum.aspx.
Adding a Reference to Microsoft.Xna.Framework.DLL
With the exception of Microsoft.Xna.Framework.DLL, all the DLLs mentioned in this section can be found in c:\Microsoft Robotics Studio (1.5)\bin. The
Microsoft.Xna.Framework.DLL is installed in the .NET Global Assembly Cache (GAC). You must find it in a subdirectory of the Windows directory in order to add it as a reference. Look under \WINDOWS\assembly\GAC_32\Microsoft.Xna.Framework. You may find multiple copies installed in subdirectories under this directory. Choose the most recent version. Windows Explorer does not allow you to browse to this directory, so you will find it easier to use a command window to access it. You can then copy the DLL to your own directory and then add it as a reference. To make this easier, install the entire XNA SDK. Then it will contain a copy of the Xna.Framework.DLL that you can use as a reference. By default, MRDS only installs the run-time version of the XNA DLLs, so the steps outlined above are necessary.
Using Statements and DLL References
In summary, add the following DLLs as references to your service to enable it to work with the simulator:
Microsoft.Xna.Framework
PhysicsEngine
RoboticsCommon
SimulationCommon
SimulationEngine
SimulationEngine.proxy
275
www.it-ebooks.info
Part II: Simulations
Add the following using statements to properly resolve references to objects in these DLLs:
#region Simulation namespaces
using Microsoft.Robotics.Simulation;
using Microsoft.Robotics.Simulation.Engine;
using engineproxy = Microsoft.Robotics.Simulation.Engine.Proxy; using Microsoft.Robotics.Simulation.Physics;
using Microsoft.Robotics.PhysicalModel; using xna = Microsoft.Xna.Framework;
using xnagrfx = Microsoft.Xna.Framework.Graphics; #endregion
Building Your Own SRS-Robo-Magellan
Simulation
In the following sections, you’ll begin building an SRS Robo-Magellan simulation from the ground up. If you are the type of person who likes to skip to the end of the book to see how it all turns out, feel free to go to the Chapter6 directory of the sample software and build and run the solution to see the simulated Corobot in action. Chapter 7 includes a referee and orchestration service to complete
the Robo-Magellan simulation.
This chapter describes how to build a custom robot, the CoroWare Corobot, along with its sensors and associated simulation services. You’ll also define an environment to test the Corobot. The next chapter describes how to use the robot you’ve defined in a custom simulation scenario, the simulated SRS Robo-Magellan scenario.
If you prefer to follow the step-by-step implementation of these services, it’s recommended that you make a new directory parallel to the Chapter6 directory. You can call it something like MyChapter6.
Simulation Services
A simulation service is a service that creates, manipulates, or reads data from entities in the simulation environment. It typically specifies the SimulationEngine service as a partner, and it references the DLLs listed in the previous sections. A simulation service follows the same rules as other services and it only communicates with the SimulationEngine service through a SimulationEnginePort. It can insert, replace, or delete entities by sending messages to this port. It can also subscribe to a particular entity by name so that it receives a notification when that entity is inserted, replaced, or deleted.
Simulation services that are running on the same node as the SimulationEngine service can take advantage of a shortcut that dramatically improves the performance of setting and retrieving entity data. When a service is running on the same node as the simulator and it receives an insert notification for a particular entity, the body of the insert notification message contains a reference to the actual entity in the simulation environment. This object is “live,” meaning its fields are updated as each frame is processed in the simulator. In this case, it is only necessary for a simulation service to receive a single notification when an entity is inserted in the environment, after which it can use the reference to that entity for all subsequent operations. Because simulation services typically need to interact with their associated entities frequently, this can speed things up substantially.
276
www.it-ebooks.info
Chapter 6: Extending the MRDS Visual Simulation Environment
A typical simulation scenario has one or more of these services running on the same node as the simulator and one or more higher-level orchestration services that may or may not be running on the same node. Only the simulation services interact directly with entities in the simulator. The simulation and orchestration services for the Robo-Magellan simulation are shown in Figure 6-2.
Corobot Service
Dashboard
Service
Simulation |
|
|
Engine |
QuadDiffDrive |
|
Service |
||
|
SimMagellan
Service
SimulatedIR
Referee Service |
|
SimWebCam |
|
Simulation Services |
Orchestration Services |
Figure 6-2
The SimulationEngine service, the Dashboard service, and the SimulatedWebCam service are provided as part of the SDK. The rest of the services will be developed in this chapter and in Chapter 7.
Notice that neither the Dashboard service nor the SimMagellan service interacts directly with the simulator. These services rely on information passed to them from the simulation services. The orchestration services can run on a different node or even a different machine from the simulator, but the simulation services must run on the same node.
Creating a Simulation Service
The first simulation service you’ll create is the Corobot service. This service will add sky, ground, and a few other simulation entities to the simulation environment. Eventually, it will contain the definition for a new robot entity modeled after the CoroWare Corobot.
One way to create a new simulation service is to use the DssNewService utility. Make sure that you have installed the ProMRDS samples from the CD. Open an MRDS command prompt by clicking Start
Microsoft Robotics Studio (1.5)
Command Prompt. Create a new directory and run DssNewService as follows (bold text indicates text that you type):
C:\Microsoft Robotics Studio (1.5)> C:\Microsoft Robotics Studio (1.5)>cd ProMRDS
C:\Microsoft Robotics Studio (1.5)\ProMRDS>mkdir MyChapter6 C:\Microsoft Robotics Studio (1.5)\ProMRDS>cd MyChapter6 C:\Microsoft Robotics Studio (1.5)\ProMRDS\MyChapter6>dssnewservice
/Service:”Corobot” /Namespace:”ProMRDS.Simulation.Corobot” /year:”2007” /month:”07”
277
www.it-ebooks.info
Part II: Simulations
The /Service parameter specifies the name of the service. The /Namespace parameter specifies the namespace that will be used in the service. The /year and /month options are typically not necessary because they default to the current year and month. They are used to make the contract identifier for the service. In this case, you want the contract identifier to match the Corobot service in the Chapter6 directory so you specify the year and month in which this contract was created.
After executing the command, a directory called Corobot is created with all of the files necessary to build a complete service. The contract identifier associated with the service is as follows:
“http://schemas.tempuri.org/2007/07/corobot.html”
Note that several files have been generated for you:
AssemblyInfo.cs: This file identifies the assembly as a Dss service.
Corobot.cs: This file contains the service implementation.
Corobot.manifest.xml: This is a manifest that can be used to start the Corobot service. Visual Studio is set up to run this manifest when you press F5 to start debugging your service. This is set up in the file Corobot.csproj.user.
CorobotTypes.cs: This file contains the contract identifier and the definition of the state associated with the service, and the operations defined on the main port.
Go ahead and build and run the service. At this point, it doesn’t do much, but you can start a browser window and navigate to http://localhost:50000 and select Service Directory from the left side of the window and verify that the /corobot service is indeed running. You can even click it to retrieve its state, although you haven’t put anything interesting in the service yet.
Now you’ll modify the service so that it can interact with the simulator:
1. Begin by adding the following using statements to the top of Corobot.cs as described in the preceding section:
#region Simulation namespaces
using Microsoft.Robotics.Simulation;
using Microsoft.Robotics.Simulation.Engine;
using engineproxy = Microsoft.Robotics.Simulation.Engine.Proxy; using Microsoft.Robotics.Simulation.Physics;
using Microsoft.Robotics.PhysicalModel; using xna = Microsoft.Xna.Framework;
using xnagrfx = Microsoft.Xna.Framework.Graphics; #endregion
2. Now add references to the following DLLs. Remember that the XNA DLL has to be handled specially according to the instructions provided in the section “Microsoft Xna.Framework.DLL,” earlier in this chapter.
Microsoft.Xna.Framework
PhysicsEngine
RoboticsCommon
SimulationCommon
SimulationEngine
SimulationEngine.proxy
278
www.it-ebooks.info
Chapter 6: Extending the MRDS Visual Simulation Environment
3.
4.
After you have added a reference to a new external DLL, select the DLL so that its properties are displayed. Set the CopyLocal and Specific Version properties to False. Build the service again to ensure that no mistakes were made. Change the Description attribute, which precedes the CorobotService class, to something more descriptive if you like.
At the top of the CorobotService class, add the following partner specification:
[Partner(“Engine”,
Contract = engineproxy.Contract.Identifier,
CreationPolicy = PartnerCreationPolicy.UseExistingOrCreate)] private engineproxy.SimulationEnginePort _engineStub =
new engineproxy.SimulationEnginePort();
This identifies the SimulationEngine service as a partner to the Corobot service and instructs DSS to start this service if it isn’t already running. At this point, when you run the service, it will at least start up the simulation engine even though the simulation environment is empty.
5. Now you’ll add code to the Start method to set up the simulation environment. The first thing to do is define the Main Camera initial view. Insert the following code after the call to base.Start. This code defines a CameraView message, sets it EyePosition and LookAtPoint and then sends it to the SimulationEngine service. This is all pretty straightforward until the last line where you post the CameraView message to the SimulationEngine
.GlobalInstancePort. The SimulationEngine service holds a static global pointer to its own main operation port. It is often convenient for other simulation services to interact with the SimulationEngine through this port.
// MainCamera initial view CameraView view = new CameraView();
view.EyePosition = new Vector3(-1.65f, 1.63f, -0.29f); view.LookAtPoint = new Vector3(0, 0, 0); SimulationEngine.GlobalInstancePort.Update(view);
6. Adding a sky to the simulation environment is as easy as adding the following two lines of code:
// Add a SkyDome.
SkyDomeEntity sky = new SkyDomeEntity(“skydome.dds”, “sky_diff.dds”); SimulationEngine.GlobalInstancePort.Insert(sky);
Here, you create a new SkyDomeEntity and specify a visual texture of “skydome.dds” and a lighting texture of “sky_diff.dds”. When the entity has been created, you insert it into the simulation environment.
7. Now you’ll add a directional light to simulate the sun. Without this light source, entities in the simulation environment are only lit by ambient light and by the light from the SkyDome entity.
LightSourceEntity sun = new LightSourceEntity(); sun.State.Name = “Sun”;
sun.Type = LightSourceEntityType.Directional; sun.Color = new Vector4(0.8f, 0.8f, 0.8f, 1); sun.Direction = new Vector3(0.5f, -.75f, 0.5f); SimulationEngine.GlobalInstancePort.Insert(sun);
279
www.it-ebooks.info
Part II: Simulations
8. The next step is to add a ground entity. If you forget to add this entity, your other simulation entities will fall into nothingness as soon as they are inserted in the environment. The ground is a good thing.
// create a large horizontal plane, at zero elevation. HeightFieldEntity ground = new HeightFieldEntity(
“Ground”, // name “Gravel.dds”, // texture image
new MaterialProperties(“ground”, 0.2f, // restitution
0.5f, // dynamic friction 0.5f) // static friction
);
SimulationEngine.GlobalInstancePort.Insert(ground);
Here, you create a HeightFieldEntity with zero-elevation height specified for each height field sample, which gives you a nice flat ground plane. Notice that you’re using a gravel texture for the ground plane instead of the infinite carpet texture that most of the MRDS Simulation Tutorials use. There is something vaguely unsettling about a world of infinite carpet. You also specify a material for the ground, consisting of restitution and dynamic and static friction values, as described in Chapter 5.
9. Finally, add a giant box to the environment. Giant boxes can be handy. This particular giant box is positioned so that its center is exactly 29 inches + 1 meter in the +Z direction from the origin of the simulation world. The origin of all SingleShapeEntities is at their center, so you must specify a Y offset equal to half the height of the box to keep the box from being partially buried in the ground. You might surmise that the reason the box is offset 29 inches + 1 meter in the +Z direction is that you really want its edge to be about 29 inches from the origin. This will be useful later.
Vector3 dimensions = new Vector3(2f, 2f, 2f); // meters SingleShapeEntity box = new SingleShapeEntity(
new BoxShape(
new BoxShapeProperties( 100, // mass in kilograms. new Pose(), // relative pose dimensions)), // dimensions
new Vector3(0, 1f, 29f * 2.54f / 100f + 1f));
//Name the entity. All entities must have unique names box.State.Name = “box”;
//Insert entity in simulation. SimulationEngine.GlobalInstancePort.Insert(box);
Let’s see — you’ve got a sky, a ground, and a giant box. Build and run your service and verify that it looks something like what is shown in Figure 6-3.
280
www.it-ebooks.info
Chapter 6: Extending the MRDS Visual Simulation Environment
Figure 6-3
If the scene does not look like you expect, you can start the Simulation Editor by pressing F5 and then examine the list of entities in the upper-right pane. If any of the entities have a red exclamation point next to them, they did not initialize properly for some reason. This is often due to a missing texture or mesh file. Select the entity with a problem and look at the InitError property to find more information about the error.
If your entity fails to show up in the list at all, the simulation engine could not create it. This is often because you have forgotten to specify an entity name or you have duplicated a name already in use. Examine the DssHost window to see whether it contains any error messages that can shed some light on the problem.
Congratulations! You have just created your first simulation environment. Now let’s build a robot. If you ran into any trouble building your service or if your results are different, compare your Corobot.cs file with the corresponding file in the Chapter6 directory.
Defining a Custom Robot Entity
Granted, a world of infinite gravel and a giant box is not very exciting, so let’s define a new robot entity to move some of that gravel around (Figure 6-4 shows the CoroWare Corobot in the real world). In this section, you’ll model a new robot entity similar to the Corobot manufactured by CoroWare. You can find more information about this robot at www.corobot.net.
281
www.it-ebooks.info
Part II: Simulations
Figure 6-4
The features of this robot that are most interesting in the simulator are its four-wheel differential drive, its front and rear infrared distance sensors, and its front-mounted camera.
The first step in creating this robot in the simulator is to define a new class that inherits from
VisualEntity as follows:
///<summary>
///An entity which represents a Corobot. This entity is created facing
///in the +Z direction meaning that its front has positive Z
///coordinates. Front and Rear IR sensors are included along with
///a front-facing camera.
///</summary>
[DataContract]
[DataMemberConstructor]
public class CorobotEntity : VisualEntity
{
}
The [DataContract] attribute indicates that this entity has state that needs to be serialized when it is sent across nodes. The [DataMemberConstructor] attribute indicates that the nondefault constructor defined in the class should also appear in the generated proxy class. All of the other two-wheel differential drive entities in the simulator inherit from DifferentialDriveEntity. This entity will be different enough that it makes sense to incorporate the functionality of DifferentialDriveEntity without subclassing it.
The next thing you need to do is define constructors for this entity. Every simulation entity must have a default constructor that has no parameters. This constructor is called when the entity is deserialized, which occurs when the entity is inserted into the simulator from a remote node or when an entity is
282
