Редактирование шаблона.
Двигаясь далее, изменим сервис так, чтобы он мог взаимодействовать с симулятором:
1. Вверху файла добавим следующие using операторы.
a) using-объявление (вводящее в данном пространстве имён (namespace) локальный синоним для элемента другого пространства)
b) using-директива (делающая доступными в данном пространстве имён (namespace) все имена из другого пространства имён)
#region Simulation namespaces
using Microsoft.Robotics.Simulation; // using-объявление
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-директива
using xnagrfx = Microsoft.Xna.Framework.Graphics;
#endregion
2. Далее, необходимо задать ссылки на другие используемые службы: выбирается из списка dll-файлов (панель Solution Explorer → References)

рис.2. Добавление ссылок на DLL
Добавим ссылки на следующие DLL:
Microsoft.Xna.Framework
PhysicsEngine
RoboticsCommon
SimulationCommon
SimulationEngine
SimulationEngine.proxy
Microsoft.Xna.Framework.DLL. Эта библиотека содержит весь код визуализации. Она обеспечивает управляемый DirectX интерфейс и наконец вызывает лежащие в основе собственные библиотеки DirectX.
PhysicsEngine.DLL. Эта DLL содержит описание для объектов, использующихся в среде симуляции, которая изображает (воплощает, представляет) объекты в физическом движке.
Robotics.Common.DLL. Эта DLL определяет ряд общих типов и базовых контрактов, которые могут использовать как аппаратное обеспечение, так и сервисы моделирования.
Simulation.Common.DLL. Эта DLL определяет типы, которые используются только в среде моделирования. Типы, определенные для движка моделирования содержатся в пространстве имен Microsoft.Robotics
SimulationEngine.DLL. Эта DLL содержит большую часть функциональности симулятора, с точки зрения программиста, наиболее важные типы, они содержатся в пространстве имен Microsoft.Robotics.Simulation.
SimulationEngine.Proxy.DLL. Эта библиотека необходима для получения доступа к элементам сервиса движка симуляции. Такие элементы как контракты и определения портов должны всегда быть доступны из ProxyDLL.
3. После добавления ссылок на новые внешние библиотеки установим свойства CopyLocal и Specific Version в False. Построим сервис снова, чтобы гарантировать отсутствие ошибок.
4. Наверху класса CorobotService установим партнерство.
Contract = engineproxy.Contract.Identifier,
CreationPolicy = PartnerCreationPolicy.UseExistingOrCreate)]
private engineproxy.SimulationEnginePort _engineStub =
new engineproxy.SimulationEnginePort();
Это определяет службы SimulationEngine в качестве партнера для сервиса Corobot и дает указания DSS запустить этот сервис, если он ещё не запущен. На этой стадии, при запуске службы он будет, по крайней мере, запускать движок симуляции, хотя среда моделирования пуста.
5. Добавим код к методу Start чтобы настроить среду моделирования. Первое что нужно сделать – определить начальный обзор MainCamera. Вставим следующий код после вызова base.Start.
// начальный вид MainCamera
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);
Этот код определяет сообщение CameraView, устанавливает её EyePosition и LookAtPoint и потом посылает его сервису SimulationEngine. Это всё довольно просто до последней строчки, где размещается сообщение CameraView для SimulationEngine.GlobalInstancePort. Сервис GlobalInstancePort содержит статический глобальный указатель на его собственный главный порт управления. Это часто бывает неудобно для других служб симуляции взаимодействовать с SimulationEngine через этот порт.
6. Добавление неба к среде симуляции происходит простым добавлением следующих двух строк кода:
// Добавление небосвода.
SkyDomeEntity sky = new SkyDomeEntity(“skydome.dds”, “sky_diff.dds”);
SimulationEngine.GlobalInstancePort.Insert(sky);
Здесь создается новая сущность SkyDomeEntity и определяется внешняя текстура "skydome.dss" и текстура освещения "sky_diff.dss".
7. Сейчас добавим направленный свет, чтобы имитировать солнце. Без источника света, объекты в среде симуляции освещены только внешним светом и светом сущности SkyDome.
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);
8. На следующем шаге добавим сущность "земля". Если не добавить эту сущность, все другие объекты симуляции будут падать в небытие, как только будут вставлены в среду.
// создание большой горизонтальной плоскости при нулевой высоте.
HeightFieldEntity ground = new HeightFieldEntity(
“Ground”, // название
“Gravel.dds”, // изображение текстуры
new MaterialProperties(“ground”,
0.2f, // восстановление начального положения (реституция)
0.5f, // трение движения
0.5f) // трение покоя
);
SimulationEngine.GlobalInstancePort.Insert(ground);
9. Наконец, добавим в среду гигантский ящик. Он должен быть маневренный (портативный). Этот гигантский ящик расположен так, что его центр - точно 29 дюймов + 1 метр в положительном направлении оси Z от начала отсчёта моделируемого мира.
Начало координат всех объектов SingleShapeEntities это их центр, поэтому следует определить смещение, равное половине высоты ящика, чтобы избежать того, что он будет частично похоронен в земле.
Vector3 dimensions = new Vector3(2f, 2f, 2f); // метры
SingleShapeEntity box = new SingleShapeEntity(
new BoxShape(
new BoxShapeProperties(
100, // масса в кг.
new Pose(), // относительное положение
dimensions)), // измерения
new Vector3(0, 1f, 29f * 2.54f / 100f + 1f));
// Название объекта. Все объекты должны иметь уникальное название.
box.State.Name = “box”;
// Вставка объекта в среду симуляции
SimulationEngine.GlobalInstancePort.Insert(box);
Теперь у нас есть небо, земля и гигантский ящик (рис.3)

рис. 3. VSE
Определение собственного объекта - робота.
Первый шаг в создании робота в симуляторе - это определить новый класс (унаследованный от VisualEntity) следующим образом:
/// < summary >
/// Объект, который представляет Corobot. . Этот объект создан поворотом в ///положительном направлении оси Z, которое означает, что его передняя ///сторона имеет положительную Z координату. Передний и задний ИК- ///сенсоры включены наряду с фронтальной камерой.
/// < /summary >
[DataContract]
[DataMemberConstructor]
public class CorobotEntity : VisualEntity
{
}
Атрибут [DataContract] указывает, что этот объект имеет состояние, которое необходимо сериализовать (процесс перевода какой-либо структуры данных в последовательность битов), когда он передается по узлам.
Атрибут [DataMemberConstructor] указывает, что конструктор не по умолчанию, определенный в классе, должен также появиться в созданном proxy-классе. Все другие двухколесные двигатели дифференциальной передачи являются наследниками DifferentialDriveEntity.
Следующая вещь, которую необходимо сделать - это определить конструктор для этого объекта. Каждый моделируемый объект должен иметь конструктор по умолчанию, который не имеет параметров. Этот конструктор вызывается, при десериализации объекта (восстановление начального состояния структуры данных из битовой последовательности), это случается когда объект вставлен в среду симуляции из удаленного узла или из SimulationEditor, или когда целая сцена загружена в симулятор.
Конструктор Corobot:
/// < summary >
/// Конструктор по умолчанию используется когда его объект преобразован в ///параллельную форму.
/// < /summary >
public CorobotEntity()
{
}
/// < summary >
/// Инициализация конструктора используется когда его объект построен путем ///программирования
/// < /summary >
/// < param name=”initialPos” > < /param >
public CorobotEntity(string name, Vector3 initialPos)
{
base.State.Name = name;
base.State.Pose.Position = initialPos;
}
Конструктор не по умолчанию дает возможность определить объекту название и начальное положение.
Конструктор делает единственное действие - изменяет его состояние с передаваемыми параметрами. Параметры не должны быть инициализированы, когда конструктор по умолчанию вызван в течении
десериализации, потому что состоянию объекта будет восстановлено значение, которое он имел, когда объект был сериализован.
private static float InchesToMeters(float inches)
{
return (float)(inches * 2.54 / 100.0);
}
static float mass = 3.63f; // kg
static float chassisClearance = InchesToMeters(1.5f);
static float wheelGap = InchesToMeters(3f / 8f);
static float wheelWidth = InchesToMeters(2.2f);
static float wheelDiameter = InchesToMeters(4.75f);
static float wheelMass = 0.1f; // kg
static float platformClearance = InchesToMeters(5.75f);
static Vector3 platformDimensions = new Vector3(
InchesToMeters(11.0f),
InchesToMeters(3.0f),
InchesToMeters(8.5f));
static Vector3 chassisDimensions = new Vector3(
platformDimensions.X - 2 * wheelWidth - 2 * wheelGap,
InchesToMeters(2.5f),
platformDimensions.Z);
static Vector3 wheelFRPosition = new Vector3(
chassisDimensions.X / 2.0f + wheelGap + wheelWidth / 2.0f,
wheelDiameter / 2.0f,
-InchesToMeters(5.75f - 2.125f));
static Vector3 wheelFLPosition = new Vector3(
-wheelFRPosition.X,
wheelFRPosition.Y,
wheelFRPosition.Z);
static Vector3 wheelRRPosition = new Vector3(
wheelFRPosition.X,
wheelFRPosition.Y,
-wheelFRPosition.Z);
static Vector3 wheelRLPosition = new Vector3(
-wheelFRPosition.X,
wheelFRPosition.Y,
-wheelFRPosition.Z);
Коробка двигателя называется шасси, и верхний ящик, содержащий процессор, называется платформой. Высота шасси над землей определяется chassisClearance, а высота платформы - platformClearance. Ширина шасси рассчитывается так, чтобы быть широкой, чтобы позволить внешним концам колес быть даже на сторонах платформы.
Положение переднего правого колеса вычислено, и положение остальных трех получено из него. Это дает информацию, чтобы определить основные физические формы, которые составляют физическую модель Corobot.
Колеса – это больше, чем просто фигуры - это целые объекты, с их собственной сеткой и физическими формами. Необходимо место чтобы хранить все четыре колеса, когда они будут созданы. Добавим код после конструктора.
// переменные объекта
WheelEntity _wheelFR;
WheelEntity _wheelFL;
WheelEntity _wheelRR;
WheelEntity _wheelRL;
[Category(“Wheels”)]
[DataMember]
public WheelEntity FrontRightWheel
{
get { return _wheelFR; }
set { _wheelFR = value; }
}
[Category(“Wheels”)]
[DataMember]
public WheelEntity FrontLeftWheel
{
get { return _wheelFL; }
set { _wheelFL = value; }
}
[Category(“Wheels”)]
[DataMember]
public WheelEntity RearRightWheel
{
get { return _wheelRR; }
set { _wheelRR = value; }
}
[Category(“Wheels”)]
[DataMember]
public WheelEntity RearLeftWheel
{
get { return _wheelRL; }
set { _wheelRL = value; }
}
Атрибут Category только собирает в группу эти свойства вместе так, чтобы они были лучше организованы при просмотре в Simulation Editor.
Каждое свойство WheelEntity имеет атрибут [DataMember]. Этот атрибут сообщает proxy-генератору, какое из свойств должно быть сериализовано, когда объект посылается через удаленный узел или когда его сохраняют на диске. Только общие (public) свойства могут быть отмечены этим атрибутом.
Объекты колёс, шасси и фигуры платформы создаются в методе Initialize.
Добавим следующий код для переопределения метода Initialize основного класса
public override void Initialize(
xnagrfx.GraphicsDevice device,
PhysicsEngine physicsEngine)
{
try
{
// шасси
BoxShapeProperties chassisDesc = new BoxShapeProperties(
“chassis”,
mass / 2.0f,
new Pose(new Vector3(
0,
chassisClearance + chassisDimensions.Y / 2.0f,
0)),
chassisDimensions);
chassisDesc.Material =
new MaterialProperties(“chassisMaterial”, 0.0f, 0.5f, 0.5f);
BoxShape chassis = new BoxShape(chassisDesc);
chassis.State.Name = “ChassisShape”;
base.State.PhysicsPrimitives.Add(chassis);
// платформа
BoxShapeProperties platformDesc = new BoxShapeProperties(
“platform”,
mass / 2.0f,
new Pose(new Vector3(
0,
platformClearance + platformDimensions.Y / 2.0f,
0)),
platformDimensions);
platformDesc.Material = chassisDesc.Material;
BoxShape platform = new BoxShape(platformDesc);
platform.State.Name = “PlatformShape”;
base.State.PhysicsPrimitives.Add(platform);
Первое, что делает метод Initialize - создает два ящика, чтобы изобразить шасси и платформу. Предполагается, что масса обоих одинакова, поэтому общая масса делится между ними. Им задают измерения и определенное положение. Эти позиции относительны началу координат объекта. Обеим фигурам задано схожее вещественное описание, которое определяет реституцию нулевого и среднего уровня статического трения. После создания каждой фигуры, добавляем к списку физических примитивов следующее:
base.CreateAndInsertPhysicsEntity(physicsEngine);
base.PhysicsEntity.SolverIterationCount = 128;
Когда физическая сущность создана, она передает все объекты в список физических примитивов в физическом движке, который создает его собственное представление для них, основанное на атрибутах, определенных в BoxShape объектах.
SolverIterationCount определяет как много итераций физического движка будет использовано чтобы выйти из ограничений на объект, такие как точки соприкосновения или места соединения. Чем выше число, тем точнее будет результат.
// колеса
WheelShapeProperties wheelFRprop = new WheelShapeProperties(
“FrontRightWheel”, wheelMass, wheelDiameter / 2.0f);
WheelShapeProperties wheelFLprop = new WheelShapeProperties(
“FrontLeftWheel”, wheelMass, wheelDiameter / 2.0f);
WheelShapeProperties wheelRRprop = new WheelShapeProperties(
“RearRightWheel”, wheelMass, wheelDiameter / 2.0f);
WheelShapeProperties wheelRLprop = new WheelShapeProperties(
“RearLeftWheel”, wheelMass, wheelDiameter / 2.0f);
wheelFRprop.Flags |= WheelShapeBehavior.OverrideAxleSpeed;
wheelFLprop.Flags |= WheelShapeBehavior.OverrideAxleSpeed;
wheelRRprop.Flags |= WheelShapeBehavior.OverrideAxleSpeed;
wheelRLprop.Flags |= WheelShapeBehavior.OverrideAxleSpeed;
wheelFRprop.InnerRadius = 0.7f * wheelDiameter / 2.0f;
wheelFLprop.InnerRadius = 0.7f * wheelDiameter / 2.0f;
wheelRRprop.InnerRadius = 0.7f * wheelDiameter / 2.0f;
wheelRLprop.InnerRadius = 0.7f * wheelDiameter / 2.0f;
wheelFRprop.LocalPose = new Pose(wheelFRPosition);
wheelFLprop.LocalPose = new Pose(wheelFLPosition);
wheelRRprop.LocalPose = new Pose(wheelRRPosition);
wheelRLprop.LocalPose = new Pose(wheelRLPosition);
Далее, для каждого колеса определены WheelShapeProperties. Флаг OverrideAxesSpeed сообщает физическому движку то, что не нужно вычислять скорость оси, основанную на вращающем моменте мотора и трении. Это оказывается лучший способ, чтобы смоделировать типы моторов, которыми обычно управляют колесные роботы.
LocalPose каждой фигуры полезна, когда объект содержит множество фигур. Она определяет позицию и ориентацию каждой фигуры относительно начала координат объекта.
В приведенном выше коде каждое положение инициализируется с вектором положения и часть ориентации положения по умолчанию равна 0.
_wheelFR = new WheelEntity(wheelFRprop);
_wheelFR.State.Name = base.State.Name + “ FrontRightWheel”;
_wheelFR.Parent = this;
_wheelFR.Initialize(device, physicsEngine);
_wheelFL = new WheelEntity(wheelFLprop);
_wheelFL.State.Name = base.State.Name + “ FrontLeftWheel”;
_wheelFL.Parent = this;
_wheelFL.Initialize(device, physicsEngine);
_wheelRR = new WheelEntity(wheelRRprop);
_wheelRR.State.Name = base.State.Name + “ RearRightWheel”;
_wheelRR.Parent = this;
_wheelRR.Initialize(device, physicsEngine);
_wheelRL = new WheelEntity(wheelRLprop);
_wheelRL.State.Name = base.State.Name + “ RearLeftWheel”;
_wheelRL.Parent = this;
_wheelRL.Initialize(device, physicsEngine);
После того как WheelShapeProperties были определены, можно создать WheelEntities. Каждому WheelEntity задано имя, основанное на имени родительского объекта. Родительская ссылка каждого WheelEntity установлена в CorobotEntity.
WheelEntity использует ссылку необычным способом. Вместо вызова CreateAndInsrtPhysicsEntity, WheelEntity вызывает InsertShape его родителя PhysicsEngine. Это добавляет фигуру WheelEntity к набору фигур, которые составляют его родителя. Что касается физического движка, фигуры колес это только часть родительского объекта. Это сокращает число требуемых вычислений физического движка, потому что он не вычисляет взаимодействия между колесами, шасси и платформой. Он предполагает, что они неподвижно соединены.
Следует явно вызывать метод Initialize для каждго WheelEntity, потому что эти объектыне вставлены в родительский объект как потомки, использующие метод InsertEntity.
base.Initialize(device, physicsEngine);
{
catch (Exception ex)
{
// очистить
if (PhysicsEntity != null)
PhysicsEngine.DeleteEntity(PhysicsEntity);
HasBeenInitialized = false;
InitError = ex.ToString();
}
}
Наконец, вызываем base.Initialize метод, который, между прочим, загружает любые сетки и текстуры связанные с объектом. Если сетка не задана в State.Assets.Mesh, простая сетка создастся из фигур в объекте.
Сейчас метод Initialize завершен и осталось добавить объект Corobot в среду моделирования. Добавим следующие строки кода к методу Start, после кода, который вставляет гигантский ящик.
// создаем Corobot
SimulationEngine.GlobalInstancePort.Insert(
new CorobotEntity(“Corobot”, new Vector3(0, 0, 0)));
Этот код создал новый CorobotEntity с именем "Corobot" в начало координат среды моделирования. Скомпилируем и запустим сервис.

рис.4. Corobot
Заключение
В данной курсовой работе был рассмотрен пример заполнения моделируемого мира: добавление неба, земли и программное создание виртуального робота Corobot. В дальнейшем это может использоваться как манифест в VPL. , который позволит привязывать блок сервиса с общим контрактом к конкретной реализации.
Список литературы
-
Kyle Johns, Trevor Taylor. Professional Microsoft Robotics Developer Studio
-
http://robotics.computerscience.ru/dokuwiki/doku.php/playground/dss2
-
http://msdn.microsoft.com/ru-ru/magazine/cc546547.aspx
