Thursday, July 3, 2008

Using MRDS/MSRS to create simulated hardware entities

My goal is to use a hardware differential drive on a simulated bot. Now the problem was I could not seem to connect the bot to the hardware drive I programmed in. As you can imagine it was a frustrating WEEK that I wasted trying to get it to work, however, thanks to the personal effort of Trevor Taylor, the author of "Professional Microsoft Robotics Development Studio", I managed to figure out the problem.

Here are the forums I used:

Firstly, create the program that controls your robot. I called mine Actuator (my creative team is on holiday and I will have a better name soon), and all it has is the differential drive. So far, I have only imported RoboticsCommon.Proxy and the only namespace is:
using drive =
Microsoft.Robotics.Services.Drive.Proxy;

Then I created a perform method of type IEnumerator<ITask>, in which all actions can be spawned. Be careful, however, that you time the robots actions appropriately when calling the methods in this fashion. If you travel a long distance, make the timeout operation longer. I think it is more effective that actuators react based on sensor information, but the point of this blog is to get you to a certain point and let you figure out the rest.

private IEnumerator<ITask> perform()
{
//drive a set distance
SpawnIterator<double, double>
(2.0, 1.0, driveRobotDistance);
//wait
yield return Arbiter.Receive
(false, TimeoutPort(2000),
delegate(DateTime t) { });
//turn
SpawnIterator<double, double>
(90.0, 1.0, turnRobot);
//wait
yield return Arbiter.Receive
(false, TimeoutPort(2000),
delegate(DateTime t) { });
//drive again
SpawnIterator<double, double>
(2.0, 1.0, driveRobotDistance);

yield break;
}

private IEnumerator<ITask> driveRobotDistance
(double distance, double power)
{
drive.DriveDistanceRequest driveRequest =
new drive.DriveDistanceRequest
(distance, power);

yield return Arbiter.Choice
(
_drivePort.DriveDistance(driveRequest),
delegate
(DefaultUpdateResponseType response)
{
},
delegate(Fault fault)
{
LogError
(null,
"Unable to drive robot",
fault);
}
);
}

private IEnumerator<ITask> turnRobot
(double angle, double power)
{
drive.RotateDegreesRequest request =
new drive.RotateDegreesRequest
(angle, power);

yield return Arbiter.Choice(
_drivePort.RotateDegrees(request),
delegate
(DefaultUpdateResponseType response)
{
},
delegate(Fault fault)
{
LogError
(null,
"Unable to turn robot",
fault);
}
);
}

That's all there is to this drive. But the point remains that in code some physical differential drive is being told to drive and we can see in what manner to make other physical entities work, as well as react.

Well, that's all there is on this side. Now we need to create an environment. Go to the visual simulation editor and create entities in the same manner as I describe in my post on how to create a custom simulation environment [HERE]. There are a few changes though:
  1. Make sure that the lego has the Simulated differential drive contract (not actually a change, just a reminder. Don't try make it the physical drive.)
  2. (I left this out the last time - how embarrassing!) Make sure that you add wheels to the LegoNXTTribot. You must edit the Left and Right wheel's entity state and load a mesh:

Save the manifest, but make sure you know where the manifest is stored. Then load up the DSS Manifest Editor. This is the power of MRDS / MSRS. You have created a hardware project. You have created a simulated 'version' of the hardware's environment. Now to plug them into each other.

In the manifest editor, load the manifest of the simulation environment. It should look something like this:

What you should notice is that the simulated differential drive actually has an entity: the LegoNXTTribot. I am still not sure in what manner we can do this in code, but at least the entity is there, and we can modify it with the VSE.

Now drag and drop the Actuator file on top of the simulated differential drive, not the entity. This creates an instance of the Actuator for the whole manifest. (To make sure it is not added to the entity, click the entity, it should not highlight any relationships). Then either: drag and drop the existing simulated differential drive on to the Actuator's DRIVE object, or you can just drag a simulated differential drive object from the services section and make sure it is the same one as previously loaded. It should look like this:

Then you can either save the manifest over the manifest for your Actuator application and run from code, or you can run the manifest from the editor by pressing F5. But that is the basics of loading a simulation environment and then plugging in your hardware devices. Happy Botting©!

7 incoming messages (comments):

Cory Dambach said...

Your description of the state of the documentation is perfect, I spent about a week as well looking at samples and the info in Sara Morgan's Programming MRS book, but the introductory documentation on simulations doesn't emphasize the strength of the manifest approach enough, your post is exactly what I was looking for and a confirmation of what I thought was going on, but just didn't know where to look. Hope that makes sense, I'm very new to Programming Robotics...Thanks

Quintin said...

Hi Cory,

Trevor did mention once that perhaps it should have been in the textbook ;P

This particular post is my pride and joy of this blog, because it was the real problem I was having and it was missing from everywhere! I am glad it has helped you!

Waldemar said...

Hello,
I maked it everything just like you. But i my actuator.cs the is a feailure such as:
Errors
1 The name '_drivePort' does not exist in the current context
2 The name '_drivePort' does not exist in the current context.

Why??

Quintin said...

Hi Waldemar,

I am not sure where this is failing. Can you tell me at what point your error is occuring and quote the specific portions of your code? Also, just check that you have declared a variable called _drivePort - I know this is trivial but if you can show me that code I can get a better idea of what is going on.

Please also remember I did this with the CTP version of MRDS so I may be a bit out of date, but I would really like to try help.

Waldemar said...

protected override void Start()
{

base.Start();

SpawnIterator(perform);

}

private IEnumerator perform()
{
//drive a set distance
SpawnIterator
(2.0, 1.0, driveRobotDistance);
//wait
yield return Arbiter.Receive
(false, TimeoutPort(2000),
delegate(DateTime t) { });
//turn
SpawnIterator
(90.0, 1.0, turnRobot);
//wait
yield return Arbiter.Receive
(false, TimeoutPort(2000),
delegate(DateTime t) { });
//drive again SpawnIterator
(2.0, 1.0, driveRobotDistance);

yield break;
}

private IEnumerator driveRobotDistance
(double distance, double power)
{
drive.DriveDistanceRequest driveRequest =
new drive.DriveDistanceRequest
(distance, power);

yield return Arbiter.Choice
(
_drivePort.DriveDistance(driveRequest),
delegate
(DefaultUpdateResponseType response)
{
},
delegate(Fault fault)
{
LogError
(null,
"Unable to drive robot",
fault);
}
);
}

private IEnumerator turnRobot
(double angle, double power)
{
drive.RotateDegreesRequest request =
new drive.RotateDegreesRequest
(angle, power);

yield return Arbiter.Choice(
_drivePort.RotateDegrees(request),
delegate
(DefaultUpdateResponseType response)
{
},
delegate(Fault fault)
{
LogError
(null,
"Unable to turn robot",
fault);
}
);
}
that's the code in .cs file

in type.cs:

[DataContract]
public class ActuatorState
{

private double _drivePort;
[DataMember]
public double drivePort
{
get { return _drivePort; }
set { _drivePort = value; }
}

}

Because there is little space to write the code is dislocated.
Thanks for any help

Quintin said...

Good day Waldermar,

The location and type of your _drivePort variable is incorrect.

Firstly, you need to declare the _drivePort in your actuator.cs file.

Secondly, declare it like this:

[Partner("Drive", Contract = drive.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.CreateAlways)]
private drive.DriveOperations _drivePort = new drive.DriveOperations();

Waldemar said...

Hello,
thanks for advice, your are super

Respect