您的位置:首页 > 其它

微软机器人开发套件教程翻译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.
 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息