微软机器人开发套件教程翻译3
2006-07-04 13:58
399 查看
微软机器人开发套件教程翻译
Microsoft Robotics Studio Tutorials
教程3 编写可重用的协同(orchestration)服务
MRS为编写服务提供了一个可重用设计框架,用户可以针对一个通用硬件规格编写服务,然后在不同的机器人硬件平台上进行重用。
在本教程中,你将会学习编写一个服务,该服务与一个具有基本定义的虚拟硬件服务相关联,之后,根据一个配置文件(描述文件),在运行时绑定到这些硬件服务的特定实现上。本教程将实现一个基本的随机漫游行为,该行为描述如下:
n 如果前接触传感器被触发,机器人随机旋转,并为两轮的马达提供相等的负向电力(马达倒转,对应前方遇到障碍的情况)
n 如果后接触传感器被触发,机器人随机旋转,并为两轮的马达提供相等的正向电力(马达正转,对应后方遇到障碍的情况)
这种非常原始的行为将导致轮式机器人撞到障碍物后进行转向。更加智能化,更先进的协同服务,请参见机器人教程5和探索服务(the Explorer Service)。
在本教程中你将学到:
1,了解接触传感器和差速器的通用定义,以及如何使用这些定义
2,了解如何向驱动器服务发出高级指令
3,使用接触传感器事件通知触发一个“漫游”行为
4,了解如何为具体硬件规格服务编写描述文件,这些硬件服务实现了本教程中使用的基本通用定义。
前提
硬件
本教程是基于以下机器人硬件平台的,SDK中提供了针对这些硬件的服务。你也可以使用提供了同等服务的硬件。在下面列出的所有硬件平台中,你需要的物理配置包括:至少一个接触传感器,安装两个马达的两轮差速器/制动器。
n LEGO® MINDSTORMS® RCX
n LEGO® MINDSTORMS® NXT
n fischertechnik®
n 如果使用仿真服务,需要满足仿真要求的图形加速卡
软件
本教程使用C#, 你可以使用Visual Studio 2005简易版、标准版、专业版和团队版软件进行开发。
开始
本教程中我们不创建服务,而将使用在samples/RoboticsTutorials/Tutorial3/CSharp目录中已经创建的服务。在开发环境中打开RoboticsTutorial3.csproj。如果你使用的是Visual studio,将会在VS窗口中解决方案浏览器中看到以下内容:
步骤1 在虚拟设备上建立关联关系
在RoboticsTutorial3.cs 的开始,添加以下using语句。
[align=left]using Microsoft.Ccr.Core;[/align]
[align=left]using Microsoft.Dss.Core;[/align]
[align=left]using Microsoft.Dss.Core.Attributes;[/align]
[align=left]using Microsoft.Dss.ServiceModel.Wsap;[/align]
[align=left]using Microsoft.Dss.ServiceModel.WsapServiceBase;[/align]
[align=left]using System;[/align]
[align=left]using System.Collections.Generic;[/align]
[align=left]using System.Security.Permissions;[/align]
[align=left]using xml = System.Xml;[/align]
[align=left]using contactsensor = Microsoft.Robotics.Services.ContactSensor.Proxy;[/align]
[align=left]using drive = Microsoft.Robotics.Services.Drive.Proxy;[/align]
[align=left]using motor = Microsoft.Robotics.Services.Motor.Proxy;[/align]
以上这些命名空间中的类型是在RoboticsCommon.Proxy.Dll中实现,将该动态库添加成为我们服务中的一个引用。同时,还要在服务类定义中使用以下关联方式。
[align=left][Contract(Contract.Namespace)][/align]
[align=left] [PermissionSet(SecurityAction.PermitOnly, Name="Execution")][/align]
[align=left] public class Tutorial3 : WsapServiceBase[/align]
[align=left] {[/align]
[align=left] [/align]
[align=left] [Partner("Bumper", [/align]
[align=left] Contract = contactsensor.Contract.Namespace,[/align]
[align=left] CreationPolicy = PartnerCreationPolicy.UseExisting)][/align]
[align=left] contactsensor.ContactSensorArrayOperations _contactSensorPort = new contactsensor.ContactSensorArrayOperations();[/align]
[align=left] [/align]
[align=left] [Partner("Drive",[/align]
[align=left] Contract = drive.Contract.Namespace,[/align]
[align=left] CreationPolicy = PartnerCreationPolicy.UseExisting)][/align]
[align=left] drive.DriveOperations _drivePort = new drive.DriveOperations();[/align]
[align=left] [/align]
[align=left] // target port for bumper notifications[/align]
[align=left] contactsensor.ContactSensorArrayOperations _contactNotificationPort = new contactsensor.ContactSensorArrayOperations();[/align]
与以往关联关系声明不同的是PartnerCreationPolicy的取值:
CreationPolicy = PartnerCreationPolicy.UseExisting
由于虚拟服务定义针对通用的接触传感器和驱动器,所以不存在在运行时创建和绑定所需的服务实现。作为替代,我们依靠某些已经启用的,并且实现了接触传感器契约的服务实现(例如LegoRCX Bumper服务),作为其预备契约(alternate contract)。实现了预备契约的服务将在服务实例目录中出现两次,本质上代表了两个不同的服务。SDK中提供的大多数示例服务实现了预备契约。
步骤2 使用接触传感器触发行为
要开始漫游行为,必须先触发其中一个接触传感器。为了接收传感器事件通知,需要订阅接触传感器端口并提供一个事件通知目的端口。相关的代码片断高亮显示如下:
[Contract(Contract.Namespace)]
[PermissionSet(SecurityAction.PermitOnly, Name="Execution")]
public class Tutorial3 : WsapServiceBase
{
[Partner("Bumper",
Contract = contactsensor.Contract.Namespace,
CreationPolicy = PartnerCreationPolicy.UseExisting)]
contactsensor.ContactSensorArrayOperations _contactSensorPort = new contactsensor.ContactSensorArrayOperations();
[Partner("Drive",
Contract = drive.Contract.Namespace,
CreationPolicy = PartnerCreationPolicy.UseExisting)]
drive.DriveOperations _drivePort = new drive.DriveOperations();
// target port for bumper notifications
contactsensor.ContactSensorArrayOperations _contactNotificationPort = new contactsensor.ContactSensorArrayOperations();
[ServicePort("/RoboticsTutorial3", AllowMultipleInstances=false)]
private RoboticsTutorial3Operations _mainPort = new RoboticsTutorial3Operations();
public Tutorial3(WsapServiceCreationPort creationPort) :
base(creationPort)
{
CreateSuccess();
}
protected override void Start()
{
// Listen on the main port for requests and call the appropriate handler.
ActivateWsapOperationHandlers();
// Publish the service to the local Node Directory
DirectoryInsert();
// display HTTP service Uri
LogInfo(LogGroups.Activation, "Service uri: " + base.FindServiceAliasFromScheme(Uri.UriSchemeHttp));
SubscribeToBumperSensors();
}
void SubscribeToBumperSensors()
{
_contactSensorPort.Subscribe(_contactNotificationPort);
// attach handler to update notification on bumpers
Activate(Arbiter.Receive<contactsensor.Replace>(true, _contactNotificationPort, BumperNotificationHandler));
}
在服务的Start方法中添加SubscribeToBumperSensors方法。该方法订阅了绑定到_contactSensorPort的服务。之后,激活事件端口上的事件处理器。根据哪个接触传感器被按下,该处理器触发两种不同的流程。前传感器被按下时执行TurnAndGoBackwards方法,后传感器被按下时执行TurnAndGoForward方法。
void BumperNotificationHandler(contactsensor.Update updateNotification)
{
// Since we are writing a generic wander service we dont really know
// which bumper is front, side, rear etc. We expect a navigation service to be tuned
// to a robot platform or read configuration through its initial state.
// here, we just assume the bumpers are named intuitively so we search by name
contactsensor.ContactSensor s = updateNotification.Body;
if (!s.Pressed)
return;
if (!string.IsNullOrEmpty(s.Name) &&
s.Name.ToLowerInvariant().Contains("front"))
{
SpawnIterator<double>(-1,BackUpTurnAndMove);
return;
}
if (!string.IsNullOrEmpty(s.Name) &&
s.Name.ToLowerInvariant().Contains("rear"))
{
SpawnIterator<double>(1,BackUpTurnAndMove);
return;
}
}
步骤3 向驱动器发出转向和移动指令
现在仔细研究一下当接触传感器被按下时触发的一个方法。正如在方法名字中所暗示的,BackUpTurnAndMove方法执行以下步骤:
1, 翻转方向并移动1500毫秒
2,对驱动器上的两个马达应用相同大小,但极性相反的电力以旋转机器人。
3,对两个马达应用随机的,相同大小的电力以移动机器人。
IEnumerator<ITask> BackUpTurnAndMove(double polarity)
{
// First backup a little.
yield return Arbiter.Receive(false,
StartMove(0.4*polarity),
delegate(bool result) { });
// wait
yield return Arbiter.Receive(false, TimeoutPort(1500), delegate(DateTime t) { });
// now Turn
yield return Arbiter.Receive(false,
StartTurn(),
delegate(bool result) { });
// wait
yield return Arbiter.Receive(false, TimeoutPort(1500), delegate(DateTime t) { });
// now reverse direction and keep moving straight
yield return Arbiter.Receive(false,
StartMove(_randomGen.NextDouble()*polarity),
delegate(bool result) { });
// done
yield break;
}
Port<bool> StartTurn()
{
Port<bool> result = new Port<bool>();
// start a turn
SpawnIterator<Port<bool>>(result, RandomTurn);
return result;
}
Port<bool> StartMove(double powerLevel)
{
Port<bool> result = new Port<bool>();
// start movement
SpawnIterator<Port<bool>, double>(result, powerLevel, MoveStraight);
return result;
}
IEnumerator<ITask> RandomTurn(Port<bool> done)
{
// we turn by issuing motor commands, using reverse polarity for left and right
// We could just issue a Rotate command but since its a higher level function
// we cant assume (yet) all our implementations of differential drives support it
drive.SetDrivePowerRequest setPower = new drive.SetDrivePowerRequest();
setPower.LeftWheelPower = ValidatePowerLevel(_randomGen.NextDouble());
setPower.RightWheelPower = ValidatePowerLevel(-setPower.LeftWheelPower);
bool success = false;
yield return
Arbiter.Choice(
_drivePort.SetDrivePower(setPower),
delegate(DefaultUpdateResponseType rsp) { success = true; },
delegate(W3C.Soap.Fault failure)
{
// report error but report done anyway. we will attempt
// to do the next step in wander behavior even if turn failed
LogError("Failed setting drive power");
});
done.Post(success);
yield break;
}
IEnumerator<ITask> MoveStraight(Port<bool> done, double powerLevel)
{
// Proxies dont currently have initialization constructors so we have to
// explicitly set fields. This will be fixed post June 20th CTP
drive.SetDrivePowerRequest setPower = new drive.SetDrivePowerRequest();
setPower.LeftWheelPower = ValidatePowerLevel(powerLevel);
setPower.RightWheelPower = ValidatePowerLevel(powerLevel);
yield return
Arbiter.Choice(
_drivePort.SetDrivePower(setPower),
delegate(DefaultUpdateResponseType success) { done.Post(true); },
delegate(W3C.Soap.Fault failure)
{
// report error but report done anyway. we will attempt
// to do the next step in wander behavior even if turn failed
LogError("Failed setting drive power");
done.Post(false);
});
}
步骤4 使用描述文件为虚拟服务创建在硬件的具体实现
教程中的服务不能单独运行。它依赖于对驱动器、马达和接触传感器虚拟服务的具体实现。我们声明的关联者需要运行这些服务的实例。创建这些服务可以有两个途径:
n 使用描述文档显式指定我们需要启动的服务。
n 编写针对特定机器人配置的服务,并使用指定的关联者,然后启动教程中的服务。
在以往的教程中我们已经演示了如何从代码启动具体的服务,现在重点研究如何使用描述文档。
本教程包括了所有支持平台的描述文档。下面是一个FischerTechnik机器人模块完整的描述文档。文件名为RoboticsTutorial3.FT16.manifest.xml。
该描述文档为Microsoft.Dss.Core.Manifest.ManifestType中定义的ManifestType数据类型的序列化表示。类型中包括一个创建服务列表(CreateServiceList),其中包含服务记录类型(ServiceRecordType)子项。
<?xmlversion="1.0"encoding="utf-8"?>
<Manifest
xmlns="http://schemas.microsoft.com/xw/2004/10/manifest.html"
xmlns:wsap="http://schemas.microsoft.com/xw/2004/10/wsap.html"
xmlns:drive="http://schemas.microsoft.com/robotics/2006/05/drive.html"
>
<CreateServiceList>
列表中第一个服务记录类型(ServiceRecordType)包括教程中描述的服务契约。从而使描述装载(Manifest Loader)服务使用缺省设置启动本服务。
<!--Start tutorial 3-->
<ServiceRecordType>
<wsap:Contract>http://schemas.tempuri.org/2006/06/roboticstutorial3.html</wsap:Contract>
</ServiceRecordType>
第二个服务记录类型启动通用驱动器服务。在使用该服务前必须进行相应的配置。服务通过关联者进行配置。关联者在服务记录类型下的关联者列表元素中定义。
<!--Start the Generic (hardware agnostic) drive service-->
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/robotics/2006/05/drive.html</wsap:Contract>
<wsap:PartnerList>
关联者列表中定义的第一个关联者为初始状态(Initial Stater)关联者。这是一个特殊类型的关联者,它使用一个描述服务状态的XML文件,通过装载和反序列化该文件,可以使服务的状态成员在Start方法调用前初始化为文件中的取值。在服务教程3中将讨论初始状态关联者。
<!--Initial state partner-->
<wsap:Partner>
<!-- In our environment, this maps to %LayoutDir%/config/ -->
<wsap:Service>http://localhost/mountpoint/config/RoboticsTutorial3.FT16.Drive.Config.xml</wsap:Service>
<wsap:Name>wsap:StateService</wsap:Name>
</wsap:Partner>
对剩下的关联者仅仅提到名称。正如我们所见,该描述文件创建的剩下的服务都有对应的名称,可以与本服务中同名的关联者相对应。
<wsap:Partner>
<wsap:Name>drive:BumperService</wsap:Name>
</wsap:Partner>
<wsap:Partner>
<wsap:Name>drive:LeftMotor</wsap:Name>
</wsap:Partner>
<wsap:Partner>
<wsap:Name>drive:RightMotor</wsap:Name>
</wsap:Partner>
<wsap:Partner>
<wsap:Name>drive:LeftEncoder</wsap:Name>
</wsap:Partner>
<wsap:Partner>
<wsap:Name>drive:RightEncoder</wsap:Name>
</wsap:Partner>
</wsap:PartnerList>
</ServiceRecordType>
为FischerTechnik系统装载和配置接触传感器服务。
<!--The following section creates the services that the generic drive requires as partners -->
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftbumper.html</wsap:Contract>
<wsap:PartnerList>
<!--Initial state partner-->
<wsap:Partner>
<!-- In our environment, this maps to %LayoutDir%/config/ -->
<wsap:Service>http://localhost/mountpoint/config/RoboticsTutorial3.FT16.Bumper.Config.xml</wsap:Service>
<wsap:Name>wsap:StateService</wsap:Name>
</wsap:Partner>
</wsap:PartnerList>
</ServiceRecordType>
接下来的四条记录为左右马达装载马达和编码器服务。
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftmotor.html</wsap:Contract>
<Name>drive:LeftMotor</Name>
</ServiceRecordType>
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftmotor.html</wsap:Contract>
<Name>drive:RightMotor</Name>
</ServiceRecordType>
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftencoder.html</wsap:Contract>
<Name>drive:LeftEncoder</Name>
</ServiceRecordType>
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftencoder.html</wsap:Contract>
<Name>drive:RightEncoder</Name>
</ServiceRecordType>
</CreateServiceList>
</Manifest>
试试看
编译后在MRS命名窗口中运行:
配置文件
确认在教程目录中的所有XML文件都拷贝到了以下的配置目录
C:/Microsoft Robotics Studio (June 2006)/config>
每次在本地示例目录中更新配置文件或描述文件后,需要全部拷贝,或者使用一个目录指向你本地目录的描述文件并启动dsshost。
正常运行该服务需要提供所使用机器人硬件(LegoNXT, FisherTechnik, LegoRCX等)的描述文件。设置Debug属性以启用描述文件和dsshost.exe,然后按下F5(或者单击Debug菜单,选择Start Debugging)编译运行Tutorial3服务,之后按下接触传感器。现在机器人应当启动并进行随机漫游。
如果你使用仿真描述,使用虚拟摄像机作为实体去按下仿真机器人的接触传感器。按F2切换到物理视图,然后按F3生成摄像机前半球。移动半球直到触及机器人的传感器。
总结
在该教程中,你应学到
n 针对接触传感器和差速器编写可重用的服务,实现跨硬件平台的应用
n 了解如何向驱动器服务发出高级指令
n 使用接触传感器事件通知触发一个“漫游”行为
n 了解如何为具体硬件规格服务编写描述文件,这些硬件服务实现了本教程中使用的基本通用定义。
LEGO and MINDSTORMS are trademarks of the LEGO Group. © 2006 The LEGO Group.
fischertechnik is the trademark of fischertechnik GmbH.
Microsoft Robotics Studio Tutorials
教程3 编写可重用的协同(orchestration)服务
MRS为编写服务提供了一个可重用设计框架,用户可以针对一个通用硬件规格编写服务,然后在不同的机器人硬件平台上进行重用。
在本教程中,你将会学习编写一个服务,该服务与一个具有基本定义的虚拟硬件服务相关联,之后,根据一个配置文件(描述文件),在运行时绑定到这些硬件服务的特定实现上。本教程将实现一个基本的随机漫游行为,该行为描述如下:
n 如果前接触传感器被触发,机器人随机旋转,并为两轮的马达提供相等的负向电力(马达倒转,对应前方遇到障碍的情况)
n 如果后接触传感器被触发,机器人随机旋转,并为两轮的马达提供相等的正向电力(马达正转,对应后方遇到障碍的情况)
这种非常原始的行为将导致轮式机器人撞到障碍物后进行转向。更加智能化,更先进的协同服务,请参见机器人教程5和探索服务(the Explorer Service)。
在本教程中你将学到:
1,了解接触传感器和差速器的通用定义,以及如何使用这些定义
2,了解如何向驱动器服务发出高级指令
3,使用接触传感器事件通知触发一个“漫游”行为
4,了解如何为具体硬件规格服务编写描述文件,这些硬件服务实现了本教程中使用的基本通用定义。
前提
硬件
本教程是基于以下机器人硬件平台的,SDK中提供了针对这些硬件的服务。你也可以使用提供了同等服务的硬件。在下面列出的所有硬件平台中,你需要的物理配置包括:至少一个接触传感器,安装两个马达的两轮差速器/制动器。
n LEGO® MINDSTORMS® RCX
n LEGO® MINDSTORMS® NXT
n fischertechnik®
n 如果使用仿真服务,需要满足仿真要求的图形加速卡
软件
本教程使用C#, 你可以使用Visual Studio 2005简易版、标准版、专业版和团队版软件进行开发。
开始
本教程中我们不创建服务,而将使用在samples/RoboticsTutorials/Tutorial3/CSharp目录中已经创建的服务。在开发环境中打开RoboticsTutorial3.csproj。如果你使用的是Visual studio,将会在VS窗口中解决方案浏览器中看到以下内容:
步骤1 在虚拟设备上建立关联关系
在RoboticsTutorial3.cs 的开始,添加以下using语句。
[align=left]using Microsoft.Ccr.Core;[/align]
[align=left]using Microsoft.Dss.Core;[/align]
[align=left]using Microsoft.Dss.Core.Attributes;[/align]
[align=left]using Microsoft.Dss.ServiceModel.Wsap;[/align]
[align=left]using Microsoft.Dss.ServiceModel.WsapServiceBase;[/align]
[align=left]using System;[/align]
[align=left]using System.Collections.Generic;[/align]
[align=left]using System.Security.Permissions;[/align]
[align=left]using xml = System.Xml;[/align]
[align=left]using contactsensor = Microsoft.Robotics.Services.ContactSensor.Proxy;[/align]
[align=left]using drive = Microsoft.Robotics.Services.Drive.Proxy;[/align]
[align=left]using motor = Microsoft.Robotics.Services.Motor.Proxy;[/align]
以上这些命名空间中的类型是在RoboticsCommon.Proxy.Dll中实现,将该动态库添加成为我们服务中的一个引用。同时,还要在服务类定义中使用以下关联方式。
[align=left][Contract(Contract.Namespace)][/align]
[align=left] [PermissionSet(SecurityAction.PermitOnly, Name="Execution")][/align]
[align=left] public class Tutorial3 : WsapServiceBase[/align]
[align=left] {[/align]
[align=left] [/align]
[align=left] [Partner("Bumper", [/align]
[align=left] Contract = contactsensor.Contract.Namespace,[/align]
[align=left] CreationPolicy = PartnerCreationPolicy.UseExisting)][/align]
[align=left] contactsensor.ContactSensorArrayOperations _contactSensorPort = new contactsensor.ContactSensorArrayOperations();[/align]
[align=left] [/align]
[align=left] [Partner("Drive",[/align]
[align=left] Contract = drive.Contract.Namespace,[/align]
[align=left] CreationPolicy = PartnerCreationPolicy.UseExisting)][/align]
[align=left] drive.DriveOperations _drivePort = new drive.DriveOperations();[/align]
[align=left] [/align]
[align=left] // target port for bumper notifications[/align]
[align=left] contactsensor.ContactSensorArrayOperations _contactNotificationPort = new contactsensor.ContactSensorArrayOperations();[/align]
与以往关联关系声明不同的是PartnerCreationPolicy的取值:
CreationPolicy = PartnerCreationPolicy.UseExisting
由于虚拟服务定义针对通用的接触传感器和驱动器,所以不存在在运行时创建和绑定所需的服务实现。作为替代,我们依靠某些已经启用的,并且实现了接触传感器契约的服务实现(例如LegoRCX Bumper服务),作为其预备契约(alternate contract)。实现了预备契约的服务将在服务实例目录中出现两次,本质上代表了两个不同的服务。SDK中提供的大多数示例服务实现了预备契约。
步骤2 使用接触传感器触发行为
要开始漫游行为,必须先触发其中一个接触传感器。为了接收传感器事件通知,需要订阅接触传感器端口并提供一个事件通知目的端口。相关的代码片断高亮显示如下:
[Contract(Contract.Namespace)]
[PermissionSet(SecurityAction.PermitOnly, Name="Execution")]
public class Tutorial3 : WsapServiceBase
{
[Partner("Bumper",
Contract = contactsensor.Contract.Namespace,
CreationPolicy = PartnerCreationPolicy.UseExisting)]
contactsensor.ContactSensorArrayOperations _contactSensorPort = new contactsensor.ContactSensorArrayOperations();
[Partner("Drive",
Contract = drive.Contract.Namespace,
CreationPolicy = PartnerCreationPolicy.UseExisting)]
drive.DriveOperations _drivePort = new drive.DriveOperations();
// target port for bumper notifications
contactsensor.ContactSensorArrayOperations _contactNotificationPort = new contactsensor.ContactSensorArrayOperations();
[ServicePort("/RoboticsTutorial3", AllowMultipleInstances=false)]
private RoboticsTutorial3Operations _mainPort = new RoboticsTutorial3Operations();
public Tutorial3(WsapServiceCreationPort creationPort) :
base(creationPort)
{
CreateSuccess();
}
protected override void Start()
{
// Listen on the main port for requests and call the appropriate handler.
ActivateWsapOperationHandlers();
// Publish the service to the local Node Directory
DirectoryInsert();
// display HTTP service Uri
LogInfo(LogGroups.Activation, "Service uri: " + base.FindServiceAliasFromScheme(Uri.UriSchemeHttp));
SubscribeToBumperSensors();
}
void SubscribeToBumperSensors()
{
_contactSensorPort.Subscribe(_contactNotificationPort);
// attach handler to update notification on bumpers
Activate(Arbiter.Receive<contactsensor.Replace>(true, _contactNotificationPort, BumperNotificationHandler));
}
在服务的Start方法中添加SubscribeToBumperSensors方法。该方法订阅了绑定到_contactSensorPort的服务。之后,激活事件端口上的事件处理器。根据哪个接触传感器被按下,该处理器触发两种不同的流程。前传感器被按下时执行TurnAndGoBackwards方法,后传感器被按下时执行TurnAndGoForward方法。
void BumperNotificationHandler(contactsensor.Update updateNotification)
{
// Since we are writing a generic wander service we dont really know
// which bumper is front, side, rear etc. We expect a navigation service to be tuned
// to a robot platform or read configuration through its initial state.
// here, we just assume the bumpers are named intuitively so we search by name
contactsensor.ContactSensor s = updateNotification.Body;
if (!s.Pressed)
return;
if (!string.IsNullOrEmpty(s.Name) &&
s.Name.ToLowerInvariant().Contains("front"))
{
SpawnIterator<double>(-1,BackUpTurnAndMove);
return;
}
if (!string.IsNullOrEmpty(s.Name) &&
s.Name.ToLowerInvariant().Contains("rear"))
{
SpawnIterator<double>(1,BackUpTurnAndMove);
return;
}
}
步骤3 向驱动器发出转向和移动指令
现在仔细研究一下当接触传感器被按下时触发的一个方法。正如在方法名字中所暗示的,BackUpTurnAndMove方法执行以下步骤:
1, 翻转方向并移动1500毫秒
2,对驱动器上的两个马达应用相同大小,但极性相反的电力以旋转机器人。
3,对两个马达应用随机的,相同大小的电力以移动机器人。
IEnumerator<ITask> BackUpTurnAndMove(double polarity)
{
// First backup a little.
yield return Arbiter.Receive(false,
StartMove(0.4*polarity),
delegate(bool result) { });
// wait
yield return Arbiter.Receive(false, TimeoutPort(1500), delegate(DateTime t) { });
// now Turn
yield return Arbiter.Receive(false,
StartTurn(),
delegate(bool result) { });
// wait
yield return Arbiter.Receive(false, TimeoutPort(1500), delegate(DateTime t) { });
// now reverse direction and keep moving straight
yield return Arbiter.Receive(false,
StartMove(_randomGen.NextDouble()*polarity),
delegate(bool result) { });
// done
yield break;
}
Port<bool> StartTurn()
{
Port<bool> result = new Port<bool>();
// start a turn
SpawnIterator<Port<bool>>(result, RandomTurn);
return result;
}
Port<bool> StartMove(double powerLevel)
{
Port<bool> result = new Port<bool>();
// start movement
SpawnIterator<Port<bool>, double>(result, powerLevel, MoveStraight);
return result;
}
IEnumerator<ITask> RandomTurn(Port<bool> done)
{
// we turn by issuing motor commands, using reverse polarity for left and right
// We could just issue a Rotate command but since its a higher level function
// we cant assume (yet) all our implementations of differential drives support it
drive.SetDrivePowerRequest setPower = new drive.SetDrivePowerRequest();
setPower.LeftWheelPower = ValidatePowerLevel(_randomGen.NextDouble());
setPower.RightWheelPower = ValidatePowerLevel(-setPower.LeftWheelPower);
bool success = false;
yield return
Arbiter.Choice(
_drivePort.SetDrivePower(setPower),
delegate(DefaultUpdateResponseType rsp) { success = true; },
delegate(W3C.Soap.Fault failure)
{
// report error but report done anyway. we will attempt
// to do the next step in wander behavior even if turn failed
LogError("Failed setting drive power");
});
done.Post(success);
yield break;
}
IEnumerator<ITask> MoveStraight(Port<bool> done, double powerLevel)
{
// Proxies dont currently have initialization constructors so we have to
// explicitly set fields. This will be fixed post June 20th CTP
drive.SetDrivePowerRequest setPower = new drive.SetDrivePowerRequest();
setPower.LeftWheelPower = ValidatePowerLevel(powerLevel);
setPower.RightWheelPower = ValidatePowerLevel(powerLevel);
yield return
Arbiter.Choice(
_drivePort.SetDrivePower(setPower),
delegate(DefaultUpdateResponseType success) { done.Post(true); },
delegate(W3C.Soap.Fault failure)
{
// report error but report done anyway. we will attempt
// to do the next step in wander behavior even if turn failed
LogError("Failed setting drive power");
done.Post(false);
});
}
步骤4 使用描述文件为虚拟服务创建在硬件的具体实现
教程中的服务不能单独运行。它依赖于对驱动器、马达和接触传感器虚拟服务的具体实现。我们声明的关联者需要运行这些服务的实例。创建这些服务可以有两个途径:
n 使用描述文档显式指定我们需要启动的服务。
n 编写针对特定机器人配置的服务,并使用指定的关联者,然后启动教程中的服务。
在以往的教程中我们已经演示了如何从代码启动具体的服务,现在重点研究如何使用描述文档。
本教程包括了所有支持平台的描述文档。下面是一个FischerTechnik机器人模块完整的描述文档。文件名为RoboticsTutorial3.FT16.manifest.xml。
该描述文档为Microsoft.Dss.Core.Manifest.ManifestType中定义的ManifestType数据类型的序列化表示。类型中包括一个创建服务列表(CreateServiceList),其中包含服务记录类型(ServiceRecordType)子项。
<?xmlversion="1.0"encoding="utf-8"?>
<Manifest
xmlns="http://schemas.microsoft.com/xw/2004/10/manifest.html"
xmlns:wsap="http://schemas.microsoft.com/xw/2004/10/wsap.html"
xmlns:drive="http://schemas.microsoft.com/robotics/2006/05/drive.html"
>
<CreateServiceList>
列表中第一个服务记录类型(ServiceRecordType)包括教程中描述的服务契约。从而使描述装载(Manifest Loader)服务使用缺省设置启动本服务。
<!--Start tutorial 3-->
<ServiceRecordType>
<wsap:Contract>http://schemas.tempuri.org/2006/06/roboticstutorial3.html</wsap:Contract>
</ServiceRecordType>
第二个服务记录类型启动通用驱动器服务。在使用该服务前必须进行相应的配置。服务通过关联者进行配置。关联者在服务记录类型下的关联者列表元素中定义。
<!--Start the Generic (hardware agnostic) drive service-->
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/robotics/2006/05/drive.html</wsap:Contract>
<wsap:PartnerList>
关联者列表中定义的第一个关联者为初始状态(Initial Stater)关联者。这是一个特殊类型的关联者,它使用一个描述服务状态的XML文件,通过装载和反序列化该文件,可以使服务的状态成员在Start方法调用前初始化为文件中的取值。在服务教程3中将讨论初始状态关联者。
<!--Initial state partner-->
<wsap:Partner>
<!-- In our environment, this maps to %LayoutDir%/config/ -->
<wsap:Service>http://localhost/mountpoint/config/RoboticsTutorial3.FT16.Drive.Config.xml</wsap:Service>
<wsap:Name>wsap:StateService</wsap:Name>
</wsap:Partner>
对剩下的关联者仅仅提到名称。正如我们所见,该描述文件创建的剩下的服务都有对应的名称,可以与本服务中同名的关联者相对应。
<wsap:Partner>
<wsap:Name>drive:BumperService</wsap:Name>
</wsap:Partner>
<wsap:Partner>
<wsap:Name>drive:LeftMotor</wsap:Name>
</wsap:Partner>
<wsap:Partner>
<wsap:Name>drive:RightMotor</wsap:Name>
</wsap:Partner>
<wsap:Partner>
<wsap:Name>drive:LeftEncoder</wsap:Name>
</wsap:Partner>
<wsap:Partner>
<wsap:Name>drive:RightEncoder</wsap:Name>
</wsap:Partner>
</wsap:PartnerList>
</ServiceRecordType>
为FischerTechnik系统装载和配置接触传感器服务。
<!--The following section creates the services that the generic drive requires as partners -->
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftbumper.html</wsap:Contract>
<wsap:PartnerList>
<!--Initial state partner-->
<wsap:Partner>
<!-- In our environment, this maps to %LayoutDir%/config/ -->
<wsap:Service>http://localhost/mountpoint/config/RoboticsTutorial3.FT16.Bumper.Config.xml</wsap:Service>
<wsap:Name>wsap:StateService</wsap:Name>
</wsap:Partner>
</wsap:PartnerList>
</ServiceRecordType>
接下来的四条记录为左右马达装载马达和编码器服务。
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftmotor.html</wsap:Contract>
<Name>drive:LeftMotor</Name>
</ServiceRecordType>
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftmotor.html</wsap:Contract>
<Name>drive:RightMotor</Name>
</ServiceRecordType>
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftencoder.html</wsap:Contract>
<Name>drive:LeftEncoder</Name>
</ServiceRecordType>
<ServiceRecordType>
<wsap:Contract>http://schemas.microsoft.com/2006/06/ftencoder.html</wsap:Contract>
<Name>drive:RightEncoder</Name>
</ServiceRecordType>
</CreateServiceList>
</Manifest>
试试看
编译后在MRS命名窗口中运行:
配置文件
确认在教程目录中的所有XML文件都拷贝到了以下的配置目录
C:/Microsoft Robotics Studio (June 2006)/config>
每次在本地示例目录中更新配置文件或描述文件后,需要全部拷贝,或者使用一个目录指向你本地目录的描述文件并启动dsshost。
正常运行该服务需要提供所使用机器人硬件(LegoNXT, FisherTechnik, LegoRCX等)的描述文件。设置Debug属性以启用描述文件和dsshost.exe,然后按下F5(或者单击Debug菜单,选择Start Debugging)编译运行Tutorial3服务,之后按下接触传感器。现在机器人应当启动并进行随机漫游。
如果你使用仿真描述,使用虚拟摄像机作为实体去按下仿真机器人的接触传感器。按F2切换到物理视图,然后按F3生成摄像机前半球。移动半球直到触及机器人的传感器。
总结
在该教程中,你应学到
n 针对接触传感器和差速器编写可重用的服务,实现跨硬件平台的应用
n 了解如何向驱动器服务发出高级指令
n 使用接触传感器事件通知触发一个“漫游”行为
n 了解如何为具体硬件规格服务编写描述文件,这些硬件服务实现了本教程中使用的基本通用定义。
LEGO and MINDSTORMS are trademarks of the LEGO Group. © 2006 The LEGO Group.
fischertechnik is the trademark of fischertechnik GmbH.
相关文章推荐
- 微软机器人开发套件教程翻译
- 微软机器人开发套件教程翻译-教程2
- basic4android 开发教程翻译(九)使用GPS
- [翻译]WP 平衡球游戏开发教程(一) -初识 XNA Farseer Magic
- 微信公众帐号开发教程第17篇-应用实例之智能翻译
- [041] 微信公众帐号开发教程第17篇-应用实例之智能翻译
- 微信公众账号开发教程(三) 实例入门:机器人(附源码)(转)
- 翻译微软官方HoloLens开发课程 语音识别
- enyo官方开发入门教程翻译一Key Concepts之Object Lifecycle
- 微软官方windows phone开发视频教程第一天视频(附下载地址)
- 微信公众帐号开发教程第17篇-应用实例之智能翻译
- 微软官方windows phone开发视频教程第三/四天视频(附下载地址)
- Gulp开发教程(翻译)
- 微信公共号(企业号)开发框架-gochat的从零开始教程(三): 智能机器人模版
- [041] 微信公众帐号开发教程第17篇-应用实例之智能翻译
- 微信公众帐号开发教程第17篇-应用实例之智能翻译
- 微软官网上关于驱动开发教程的文档结构
- 微软掌上电脑开发教程
- enyo官方开发入门教程翻译一Getting Started之Starting Enyo App Development
- enyo官方开发入门教程翻译一Consuming Web Services