Microsoft Visual Simulation 튜토리얼 Microsoft 시뮬레이션개발환경튜토리얼은아래의항목으로구성됩니다. 목차 1. 시뮬레이션튜토리얼 Part1 1. 시뮬레이션튜토리얼 Part 1 시뮬레이션튜토리얼 1 (C#) 시뮬레이션런타임에대한소개 MSRS 는

Similar documents
Microsoft Visual Simulation 사용자가이드및튜토리얼 Microsoft 시뮬레이션개발환경가이드및튜토리얼은아래의항목으로구성됩니다. 목차 1. 시작하기 2. 사용자가이드 3. 시뮬레이션툴및유틸리티 4. 시뮬레이션튜토리얼 1. 시작하기 Microsoft V

ISP and CodeVisionAVR C Compiler.hwp

<4D F736F F F696E74202D20C1A63034B0AD202D20C7C1B7B9C0D3B8AEBDBAB3CABFCD20B9ABB9F6C6DBC0D4B7C2>

Windows 8에서 BioStar 1 설치하기

PowerPoint 프레젠테이션

RVC Robot Vaccum Cleaner

Microsoft Word - ntasFrameBuilderInstallGuide2.5.doc

chap 5: Trees

API - Notification 메크로를통하여어느특정상황이되었을때 SolidWorks 및보낸경로를통하여알림메시지를보낼수있습니다. 이번기술자료에서는메크로에서이벤트처리기를통하여진행할예정이며, 메크로에서작업을수행하는데유용할것입니다. 알림이벤트핸들러는응용프로그램구현하는데있어

MVVM 패턴의 이해

q 이장에서다룰내용 1 객체지향프로그래밍의이해 2 객체지향언어 : 자바 2

JAVA PROGRAMMING 실습 08.다형성

Microsoft PowerPoint - CSharp-10-예외처리

< 목차 > Ⅰ. 개요 3 Ⅱ. 실시간스팸차단리스트 (RBL) ( 간편설정 ) 4 1. 메일서버 (Exchange Server 2007) 설정변경 4 2. 스팸차단테스트 10

gnu-lee-oop-kor-lec06-3-chap7

Windows Server 2012

PowerPoint 프레젠테이션

슬라이드 1

Visual Basic 반복문

Install stm32cubemx and st-link utility

다른 JSP 페이지호출 forward() 메서드 - 하나의 JSP 페이지실행이끝나고다른 JSP 페이지를호출할때사용한다. 예 ) <% RequestDispatcher dispatcher = request.getrequestdispatcher(" 실행할페이지.jsp");

1) 인증서만들기 ssl]# cat > // 설명 : 발급받은인증서 / 개인키파일을한파일로저장합니다. ( 저장방법 : cat [ 개인키

윈도우시스템프로그래밍

슬라이드 1

(Microsoft PowerPoint - \301\24613\260\255 - oFusion \276\300 \261\270\274\272)

PowerPoint Presentation

PowerPoint Presentation

View Licenses and Services (customer)

제8장 자바 GUI 프로그래밍 II

NTD36HD Manual

PowerPoint Template

Microsoft Word - src.doc

PowerPoint 프레젠테이션

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100

untitled

Analytics > Log & Crash Search > Unity ios SDK [Deprecated] Log & Crash Unity ios SDK. TOAST SDK. Log & Crash Unity SDK Log & Crash Search. Log & Cras

사용설명서를 읽기 전에 안드로이드(Android)용 아이디스 모바일은 네트워크 연결을 통해 안드로이드 플랫폼 기반의 모바일 기기에서 장치 (DVR, NVR, 네트워크 비디오 서버, 네트워크 카메라) 에 접속하여 원격으로 영상을 감시할 수 있는 프로그램입니다. 장치의 사

Microsoft PowerPoint - ch03ysk2012.ppt [호환 모드]

목차 윈도우드라이버 1. 매뉴얼안내 운영체제 (OS) 환경 윈도우드라이버준비 윈도우드라이버설치 Windows XP/Server 2003 에서설치 Serial 또는 Parallel 포트의경우.

슬라이드 1

Microsoft PowerPoint - java1-lab5-ImageProcessorTestOOP.pptx

1) 인증서만들기 ssl]# cat > // 설명 : 발급받은인증서 / 개인키파일을한파일로저장합니다. ( 저장방법 : cat [ 개인키

사용설명서를 읽기 전에 ios용 아이디스 모바일은 네트워크 연결을 통해 ios 플랫폼 기반의 모바일 기기(iOS 버전 6.0 이상의 ipod Touch, iphone 또는 ipad)에서 장치(DVR, 네트워크 비디오 서버 및 네트워크 카메라)에 접속하여 원격으로 영상을

아이콘의 정의 본 사용자 설명서에서는 다음 아이콘을 사용합니다. 참고 참고는 발생할 수 있는 상황에 대처하는 방법을 알려 주거나 다른 기능과 함께 작동하는 방법에 대한 요령을 제공합니다. 상표 Brother 로고는 Brother Industries, Ltd.의 등록 상

Microsoft Word - Armjtag_문서1.doc

PowerPoint 프레젠테이션

쉽게 풀어쓴 C 프로그래밍

아래 항목은 최신( ) 이미지를 모두 제대로 설치하였을 때를 가정한다

API STORE 키발급및 API 사용가이드 Document Information 문서명 : API STORE 언어별 Client 사용가이드작성자 : 작성일 : 업무영역 : 버전 : 1 st Draft. 서브시스템 : 문서번호 : 단계 : Docum

슬라이드 1

SBR-100S User Manual

Microsoft PowerPoint - 04-UDP Programming.ppt

목차 BUG offline replicator 에서유효하지않은로그를읽을경우비정상종료할수있다... 3 BUG 각 partition 이서로다른 tablespace 를가지고, column type 이 CLOB 이며, 해당 table 을 truncate

Studuino소프트웨어 설치

쉽게 풀어쓴 C 프로그래밍

Secure Programming Lecture1 : Introduction

Microsoft Word - Crackme 15 from Simples 문제 풀이_by JohnGang.docx

C++ Programming

이도경, 최덕재 Dokyeong Lee, Deokjai Choi 1. 서론

산선생의 집입니다. 환영해요

Spring Boot/JDBC JdbcTemplate/CRUD 예제

슬라이드 1

3D MAX + WEEK 9 Hansung Univ. Interior Design

*Revision History 날짜 내용 최초작성 Tel Fax [2] page

슬라이드 1

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

Microsoft PowerPoint SDK설치.HelloAndroid(1.5h).pptx

PowerPoint Presentation

PowerPoint Presentation

04_오픈지엘API.key

C# Programming Guide - Types

PowerPoint 프레젠테이션

untitled

IRISCard Anywhere 5

iii. Design Tab 을 Click 하여 WindowBuilder 가자동으로생성한 GUI 프로그래밍환경을확인한다.

PowerChute Personal Edition v3.1.0 에이전트 사용 설명서

슬라이드 1

Chapter 1

슬라이드 1

어댑터뷰

<4D F736F F F696E74202D203137C0E55FBFACBDC0B9AEC1A6BCD6B7E7BCC72E707074>

System Recovery 사용자 매뉴얼

제11장 프로세스와 쓰레드

Dialog Box 실행파일을 Web에 포함시키는 방법

<4D F736F F F696E74202D20B5A5C0CCC5CDBAA3C0CCBDBA5F3130C1D6C2F75F32C2F7BDC32E >

슬라이드 1

소프트웨어공학 Tutorial #2: StarUML Eun Man Choi

PowerPoint 프레젠테이션

슬라이드 1

Microsoft PowerPoint - chap01-C언어개요.pptx

02 C h a p t e r Java

목차 BUG DEQUEUE 의 WAIT TIME 이 1 초미만인경우, 설정한시간만큼대기하지않는문제가있습니다... 3 BUG [qp-select-pvo] group by 표현식에있는컬럼을참조하는집합연산이존재하지않으면결괏값오류가발생할수있습니다... 4

Modern Javascript

1. 자바프로그램기초 및개발환경 2 장 & 3 장. 자바개발도구 충남대학교 컴퓨터공학과

예제 1.1 ( 관계연산자 ) >> A=1:9, B=9-A A = B = >> tf = A>4 % 4 보다큰 A 의원소들을찾을경우 tf = >> tf = (A==B) % A

H3050(aap)

Something that can be seen, touched or otherwise sensed

초보자를 위한 C# 21일 완성

윈도우시스템프로그래밍

Microsoft PowerPoint Android-SDK설치.HelloAndroid(1.0h).pptx

Transcription:

Microsoft Visual Simulation 튜토리얼 Microsoft 시뮬레이션개발환경튜토리얼은아래의항목으로구성됩니다. 목차 1. 시뮬레이션튜토리얼 Part1 1. 시뮬레이션튜토리얼 Part 1 시뮬레이션튜토리얼 1 (C#) 시뮬레이션런타임에대한소개 MSRS 는물리엔진에기반한다양한가상환경에서로봇어플리케이션을개발할수있는환경을제공합니다. 이번튜토리얼은시뮬레이션엔진서비스와렌더링윈도우를어떻게시작하는지에대해보여주며, 두개의객체를가상공간에추가하는방법을보여줍니다. 그림 1 시뮬레이션실행화면 1

그림 2 시뮬레이션의물리적표현모드 시뮬레이션실행모듈은다음과같은컴포넌트로구성되어있습니다. 시뮬레이션엔진서비스 엔터티들의렌더링과물리엔진에서의시뮬레이션시간을처리합니다. 시뮬레이션공간전체의상태를트래킹하고서비스와시뮬레이션앞단부분을처리합니다. Managed 물리엔진랩퍼 저수준의물리엔진의 API 에대해 C# 형태의인터페이스를노출합니다. AGEIA PhysX 기술 AGEIA 물리프로세서를통한하드웨어가속기능을제공합니다 ( 별도의하드웨어카드추가시 ) 엔터티 시뮬레이션공간상에서의하드웨어및물리객체를나타냅니다. 다양한엔터티들이미리정의되어있으며, 이러한엔터티들을조합하여새로운형태의엔터티들을생성할수있고다양한형태의로봇플랫폼을개발할수있습니다. 사전요구사항하드웨어 시뮬레이션을구동하기위해서는 PC 의그래픽카드가이를지원하는카드이어야가능합니다. 지원되는그래픽카드는아래의링크를통해확인가능합니다. http://channel9.msdn.com/wiki/default.aspx/channel9.simulationfaq 2

소프트웨어 시뮬레이션프로그래밍을위해서는아래의툴중한가지가필요합니다. Microsoft Visual C# 2005 Express Edition. Microsoft Visual Studio Standard Edition. Microsoft Visual Studio Professional Edition. 또는 Microsoft Visual Studio Team System. 시작하기 이번튜토리얼의소스코드는아래의폴더에미리작성되어있습니다. Samples\SimulationTutorials\Tutorial1 Visual Studio 에서 SimulationTutorial1.csproj 파일을로딩함으로써튜토리얼을시작합니다. 프로젝트를빌드한후 MSRS Commnad 창에서아래와같이실행시켜봄으로써결과를확인해볼수있습니다. dsshost /port:50000 /manifest:"samples\config\simulationtutorial1.manifest.xml" Visual Studio 에서프로젝트를로딩한경우, 해당프로젝트속성에위의실행명령어가등록되어있으므로, 개발툴에서 F5 키를눌러직접실행을시키면해당프로그램을실행시킬수있습니다. 각각의코드구성에대한설명은아래와같습니다. Step 1: 참조구성 시뮬레이션을실행시키기위해서는아래의 dll 파일들을반드시참조에추가해놓아야하며, 이러한 dll 파일들은 MSRS 의설치폴더안에 bin 폴더에존재합니다. RoboticsCommon.DLL PhysicalModel namespace 를포함하고있으며, 물리적인로봇속성을모델링하기위한공통값들이정의되어있습니다. PhysicsEngine.DLL Native physics engine dll 에대한 Managed C++ 랩퍼입니다. SimulationCommon.DLL 공통타입들이정의되어있습니다. SimulationEngine.DLL 랜더링엔진, 시뮬레이션상태관리와서비스앞단처리를수행합니다. SimulationEngine.Proxy.DLL 시뮬레이션엔진상태의 Proxy 이며엔진이파트너서비스로호출될때사용됩니다. 각참조항목들은 Copy Local Property 가 False 로설정되어있어야합니다. 3

그림 3 SimulationTutorial1 참조항목 SimulationTutorial1.cs 파일에는다음과같은 Using 구분이사용됩니다. using Microsoft.Ccr.Core; using Microsoft.Dss.Core; using Microsoft.Dss.Core.Attributes; using Microsoft.Dss.ServiceModel.Dssp; using Microsoft.Dss.ServiceModel.DsspServiceBase; using System; using System.Collections.Generic; 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 System.ComponentModel; Step 2: 시뮬레이션엔진시작하기 시뮬레이션엔진은시뮬레이션공간을랜더링하는윈도우를실행시킵니다. 이모듈이 MSRS 의서비스형태로되어있기때문에, 이모듈을구동하기위해서는해당서비스를파트너로등록해놓아야합니다. [DisplayName("Simulation Tutorial 1")] [Description("Simulation Tutorial 1 Service")] [Contract(Contract.Identifier)] public class SimulationTutorial1 : DsspServiceBase [Partner("Engine", Contract = engineproxy.contract.identifier, CreationPolicy = PartnerCreationPolicy.UseExistingOrCreate)] private engineproxy.simulationengineport _enginestub = 4

new engineproxy.simulationengineport(); // Main service port [ServicePort("/SimulationTutorial1", AllowMultipleInstances=false)] private SimulationTutorial1Operations _mainport = new SimulationTutorial1Operations(); public SimulationTutorial1(DsspServiceCreationPort creationport) : base(creationport) 이미해당엔진이매니페스트를통해실행되는경우도있기때문에파트너속성에서 CreationPolicy.UseExistingOrCreate 로지정하였음을유의하시기바랍니다. _enginestub 변수는단순히파트너를로딩시키기위한목적이외에는다른용도로사용되지않습니다. 대신에튜토리얼에서는시뮬레이션엔진에대한정적참조를사용하며직접메시지를전달합니다. 이러한통신은서비스처리방식에서예외적인부분이며, 성능을극대화하기위해적용된방식입니다. Step 3: 엔터티를환경에추가하기 Start 메소드에서 SetupCamera 와 PopulateWorld 메소드가호출됩니다. SetupCamera 에서는카메라의방향과위치를지정하며, PopulateWorld 에서는간단한환경엔터티들을추가합니다. protected override void Start() base.start(); // Orient sim camera view point SetupCamera(); // Add objects (entities) in our simulated world PopulateWorld(); SetupCamera 는아래와같이정의됩니다. private void SetupCamera() // Set up initial view CameraView view = new CameraView(); view.eyeposition = new Vector3(-1.65f, 1.63f, -0.29f); view.lookatpoint = new Vector3(-0.68f, 1.38f, -0.18f); SimulationEngine.GlobalInstancePort.Update(view); Sky 추가하기 Sky 객체는다음과같이추가됩니다. void AddSky() // Add a sky using a static texture. We will use the sky texture // to do per pixel lighting on each simulation visual entity SkyDomeEntity sky = new SkyDomeEntity("skydome.dds", "sky_diff.dds"); SimulationEngine.GlobalInstancePort.Insert(sky); 5

// Add a directional light to simulate the sun. 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); SkyEntity 클래스는파라메터로 2 개의파일이름을필요로합니다. Sky 비트맵을위한텍스쳐파일 세부적인하늘을묘사하기위한고해상도이미지 단순화된큐브맵형태의텍스쳐파일 픽셀단위광원효과를위한파일 LightEntity 클래스는광원효과를시뮬레이션하기위해사용됩니다. 평지추가 이제평지를시뮬레이션공간에추가합니다. 평지는다른객체를추가하기전에미리추가되어야하며, 그렇지안으면다른객체들이모두평지아래로떨어져버립니다. void AddGround() // create a large horizontal plane, at zero elevation. HeightFieldEntity ground = new HeightFieldEntity( "simple ground", // name "03RamieSc.dds", // texture image new MaterialProperties("ground", 0.2f, // restitution 0.5f, // dynamic friction 0.5f) // static friction ); SimulationEngine.GlobalInstancePort.Insert(ground); Ground 엔터티는미리정의되어있는 HeightFieldEntity 를사용하며, 이객체는다양한 Height 들의배열로구성됩니다. 위의예제는평지를구성한예를보여줍니다. Ground 엔터티를생성한다음에는반드시 Insert 메소드를통해시뮬레이션엔진에등록시켜주어야합니다. 단순한물체추가 다음으로는단순한박스를추구하는코드입니다. 만약별도의메쉬파일이제공되지않는다면, 시뮬레이션엔진은기본적인색상으로해당객체를랜더링합니다. void AddBox(Vector3 position) Vector3 dimensions = new Vector3(0.2f, 0.2f, 0.2f); // meters // create simple movable entity, with a single shape 6

SingleShapeEntity box = new SingleShapeEntity( new BoxShape( new BoxShapeProperties( 100, // mass in kilograms. new Pose(), // relative pose dimensions)), // dimensions position); box.state.massdensity.mass = 0; box.state.massdensity.density = 0; // Name the entity. All entities must have unique names box.state.name = "box"; // Insert entity in simulation. SimulationEngine.GlobalInstancePort.Insert(box); 미리정의되어있는 SingleShapeEntity 는시뮬레이션공간에객체를추가하는데있어서좋은출발점이될수있으며, 구, 박스또는캡슐형태의객체를추가할수있습니다. 이러한모양의객체를추가할때, 질량과상대적위치, 그리고해당물체의크기를같이지정합니다. SingleShapeEntity 를사용함에있어서유의할사항은아래와같습니다. 질량과밀도가정의되지않은 Shape 은정적이며, 움직일수없고무한의질량을가진것으로가정됩니다. Shape 은물리엔진에서엔터티로묘사되며, 만약메쉬파일이제공된다면, 랜더링은물리적표현과별개로처리됩니다. Pose 클래스는객체의위치와방향을같이나타냅니다. 이값은해당엔터티내에서상대적인위치값으로표시됩니다. 모든엔터티들은고유의식별되는이름을반드시가져야합니다. 만약이름이중복되는경우에는해당엔터티가표시되지않습니다. 메쉬기반의텍스쳐엔터티 시뮬레이션엔진은실제와유사한사진기반의형태로시뮬레이션공간상에서사물을쉽게표현할수있습니다. 이렇게표현하기위해서는파일기반의 DirectX 메쉬파일이필요합니다. 아래의예제에서는이러한메쉬파일에기반하여객체를랜더링하는간단한사례를보여줍니다. void AddTexturedSphere(Vector3 position) SingleShapeEntity entity = new SingleShapeEntity( new SphereShape( new SphereShapeProperties(10, // mass in kg new Pose(), // pose of shape within entity 1)), //default radius position); entity.state.assets.mesh = "earth.obj"; entity.sphereshape.spherestate.material = new MaterialProperties("sphereMaterial", 0.5f, 0.4f, 0.5f); 7

// Name the entity entity.state.name = "detailed sphere"; // Insert entity in simulation. SimulationEngine.GlobalInstancePort.Insert(entity); 파일기반의메쉬파일을사용하는데있어서유의할사항은아래와같습니다. Asset 멤버에서정의되어있는모든파일경로는 store\media 내에있는것으로간주됩니다. 만약오직파일이름만사용하는경우에는경로는무시되며, 해당파일을 store\media 폴더밑에모두복사해두어야합니다. 메쉬를사용하는경우, 물리 Shape 을계산하기위해 TriangleMeshEnvironmentEntity 또는 SimplifiedConvexMeshEnvironmentEntity 와같은고급엔터티기능을사용할수있습니다. Step 4: 튜토리얼실행 SimulationTutorial1.csproj 파일을로드한후컴파일과정을거쳐실행시킵니다. 정상적으로실행된다면, 먼저비주얼윈도우화면이표시되고, 이튜토리얼의앞부분에있는그림과같은화면이표시됩니다. 단순한시뮬레이션공간을이동시켜보기위해단축키를눌러화면을이동시켜봅니다. 단축키 F1 도움말을숨김 F2 랜더링모드를변경함 카메라이동 A 왼쪽으로이동 D 오른쪽으로이동 W 앞으로이동 S 뒤로이동 Q Y 축을따라위로이동 E Y 축을따라아래로이동 마우스컨트롤 카메라의방향을마우스를통해이동시킬수있습니다. Xbox360 게임패드컨트롤 왼쪽엄지손가락의컨트롤을통해카메라의위치를이동시킬수있습니다. 오른쪽엄지손가락의컨트롤을통해카메라의방향을이동시킬수있습니다. 8

시뮬레이션튜토리얼 2 (C#) 시뮬레이션서비스를통한복합엔터티 이번튜토리얼에서는일반적인엔터티를어떻게생성하고모듈화된시뮬레이션로봇을어떻게생성하는지에대해설명합니다. 그림 1 시뮬레이션실행화면 9

그림 2 시뮬레이션의물리적표현화면 시작하기 이번튜토리얼은 Samples\SimulationTutorials\Tutorial2 밑에소스코드가해당폴더에있는 SimulationTutorial2.csproj 파일을로드하여프로젝트를실행할수있습니다. 해당프로젝트를실행하기위해서는 F5 키를눌러 Visual Studo 에서바로실행할수도있으며, 아래와같이 MSRS 실행창에서명령어를통해실행할수도있습니다. bin\dsshost /port:50000 /manifest:"samples\config\simulationtutorial2.manifest.xml" Step 1: 참조추가 시뮬레이션을실행하기위해서는아래와같은 dll 파일을참조에추가해놓아야합니다. SimulatedBumper.Y2006.M05.Proxy SimulatedDifferentialDrive.2006.M06.Proxy SimulatedLRF.Y2006.M05.Proxy SimulatedWebcam.Y2006.M09.Proxy 또한다음의 Using 문장을프로그램의상단에추가해야합니다. using drive = Microsoft.Robotics.Services.Simulation.Drive.Proxy; using lrf = Microsoft.Robotics.Services.Simulation.Sensors.LaserRangeFinder.Proxy; 10

using bumper = Microsoft.Robotics.Services.Simulation.Sensors.Bumper.Proxy; using simwebcam = Microsoft.Robotics.Services.Simulation.Sensors.SimulatedWebcam.Proxy; 서비스시작 튜토리얼 1 에서보다확장되어좀더복잡한형태의엔터티들이시뮬레이션에추가됩니다. private void SetupCamera() // Set up initial view CameraView view = new CameraView(); view.eyeposition = new Vector3(2.491269f, 0.598689f, 1.046625f); view.lookatpoint = new Vector3(1.873792f, 0.40983f, 0.2830455f); SimulationEngine.GlobalInstancePort.Update(view); private void PopulateWorld() AddSky(); AddGround(); AddCameras(); AddTable(new Vector3(1, 0.5f, -2)); AddPioneer3DXRobot(new Vector3(1, 0.1f, 0)); AddLegoNxtRobot(new Vector3(2, 0.1f, 0)); //AddIRobotCreateRobot(new Vector3(2, 0.1f, 0)); // uncomment this to add an irobot Create robot Table 엔터티는프로그램에의해생성된다중 Shape 엔터티입니다. 그리고 Pioneer3DX 와 LegoNxt 는디퍼런셜드라이드기반의로봇으로서각각레이저파인더와 2 개의범퍼센서그리고한개의전방범퍼센서를포함하고있습니다. Step 2: 다중 Shape 을통한환경엔터티정의 그림 3 시뮬레이션에서의 Table 객체표현 11

그림 4 - 시뮬레이션에서의 Table 의물리적표현 복잡한형태의물체를손쉽게생성하기위해기본적인 Shape 들의집합을사용할수있습니다. 위의 Table 예에서는여러개의 Box Shape 을결합하여하나의엔터티로생성을한예입니다. void AddTable(Vector3 position) // create an instance of our custom entity TableEntity entity = new TableEntity(position); // Name the entity entity.state.name = "table:"+guid.newguid().tostring(); // Insert entity in simulation. SimulationEngine.GlobalInstancePort.Insert(entity); TableEntity 클래스는 MultiShapeEntity 클래스로부터유도되었으며, 여러개의박스를결합하여테이블을생성합니다. 엔터티정의는 SimulationTutorial2.cs 파일의아래부분에기술되어있습니다. /// <summary> /// An entity for approximating a table. /// </summary> [DataContract] public class TableEntity : MultiShapeEntity /// <summary> /// Default constructor. /// </summary> public TableEntity() /// <summary> /// Custom constructor, programmatically builds physics primitive shapes to describe /// a particular table. /// </summary> /// <param name="position"></param> public TableEntity(Vector3 position) 12

State.Pose.Position = position; State.Assets.Mesh = "table_01.obj"; float tableheight = 0.65f; float tablewidth = 1.05f; float tabledepth = 0.7f; float tablethinkness = 0.03f; float legthickness = 0.03f; float legoffset = 0.05f; // add a shape for the table surface BoxShape tabletop = new BoxShape( new BoxShapeProperties(30, new Pose(new Vector3(0, tableheight, 0)), new Vector3(tableWidth, tablethinkness, tabledepth)) ); // add a shape for the left leg BoxShape tableleftleg = new BoxShape( new BoxShapeProperties(10, // mass in kg new Pose( new Vector3(-tableWidth/2 + legoffset, tableheight/2, 0)), new Vector3(legThickness, tableheight + tablethinkness, tabledepth)) ); BoxShape tablerightleg = new BoxShape( new BoxShapeProperties(10, // mass in kg new Pose( new Vector3(tableWidth / 2 - legoffset, tableheight / 2, 0)), new Vector3(legThickness, tableheight + tablethinkness, tabledepth)) ); BoxShapes = new List<BoxShape>(); BoxShapes.Add(tableTop); BoxShapes.Add(tableLeftLeg); BoxShapes.Add(tableRightLeg); public override void Update(FrameUpdate update) base.update(update); 자체의엔터티를생성하는데있어서유의할점은아래와같습니다. 물리적파라메터와 Shape 을기술하기위해커스텀생성자를사용합니다. 만약시뮬레이션상태를캡쳐하고상태파일을로드할경우, 기본생성자가구동되고동일한엔터티가시리얼라이즈된값을이용해재생성될것입니다. 복잡한동작이필요치않다면이미존재하는엔터티를이용해새로운엔터티를생성합니다. MultiShapeEntity 는 Box 와 Sphere 와같은다양한형상의객체들에대한리스트를지원합니다. 13

Step 3: 모듈화된로봇과시뮬레이션하드웨어서비스생성 그림 5 Pioneer3DX 로봇의시뮬레이션화면 그림 6 - Pioneer3DX 로봇의물리적표현화면 시뮬레이션런타임은몇개의사전에정의되어있는휠로봇기반의로봇들을실행시킵니다. 이러한로봇들은디퍼런셜드라이브와몇개의센서들을포함하고있습니다. 이번시뮬레이션튜토리얼에서는 2 개의로봇들을사용할예정이며, 첫번째로 Pioneer3DX 로봇에대한인스턴스를아래와같이생성하여사용합니다. 이러한로봇은기본적으로 DifferentialDriveEntity 를확장하여생성한로봇엔터티입니다. void AddPioneer3DXRobot(Vector3 position) Pioneer3DX robotbaseentity = CreateMotorBase(ref position); // Create Laser entity and start simulated laser service LaserRangeFinderEntity laser = CreateLaserRangeFinder(); // insert laser as child to motor base robotbaseentity.insertentity(laser); // Create bumper array entity and start simulated bumper service BumperArrayEntity bumperarray = CreateBumperArray(); // insert as child of motor base robotbaseentity.insertentity(bumperarray); 14

// create Camera Entity ans start SimulatedWebcam service CameraEntity camera = CreateCamera(); // insert as child of motor base robotbaseentity.insertentity(camera); // Finally insert the motor base and its two children // to the simulation SimulationEngine.GlobalInstancePort.Insert(robotBaseEntity); 모듈화된시뮬레이션로봇은아래와같이 3 단계를거쳐생성됩니다. 1. 디퍼런셜드라이브기반의기본모터를생성합니다. Pioneer3DX 와 LegoNXT 의디퍼런셜드라이브는기본적으로동일한클래스를상속받아생성되었으며, 두로봇사이에는차이가없습니다. 2. 가상하드웨어에대해 LaserRangeFinderEntity 와레이저파인더시뮬레이션서비스의인스턴스를생성합니다. 3. 두개의범퍼에대한 BumperArrayEntity 와범퍼시뮬레이션서비스의인스턴스를생성합니다. 4. CameraEntity 와웹캠서비스에대한시뮬레이션서비스의인스턴스를생성합니다. 위의과정을통해레이저, 범퍼, 카메라를생성한후에는모터기반엔터티에해당엔터티들을자식엔터티로추가해주어야합니다. robotbaseentity.insertentity(bumperarray); 디퍼런셜드라이브엔터티 부모엔터티는 Pioneer3DX 로봇의물리형상을가진디퍼런셜모터클래스로부터생성됩니다. CreateEntityPartner 메소드는엔터티가런타임시에바인딩될필요가있다는것을서비스에게알려주어서비스파트너를생성시킵니다. private Pioneer3DX CreateMotorBase(ref Vector3 position) // use supplied entity that creates a motor base // with 2 active wheels and one caster Pioneer3DX robotbaseentity = new Pioneer3DX(position); // specify mesh. robotbaseentity.state.assets.mesh = "Pioneer3dx.bos"; // specify color if no mesh is specified. robotbaseentity.chassisshape.state.diffusecolor = new Vector4(0.8f, 0.25f, 0.25f, 1.0f); // the name below must match manifest robotbaseentity.state.name = "P3DXMotorBase"; // Start simulated arcos motor service drive.contract.createservice(constructorport, Microsoft.Robotics.Simulation.Partners.CreateEntityPartner( "http://localhost/" + robotbaseentity.state.name) ); 15

return robotbaseentity; Laser Range Finder 엔터티 아래의코드는레이저엔터티와관련된서비스가어떻게시작되는지에대해간략히보여줍니다. 엔터티의이름과파트너이름이매칭되어야한다는것에유의하시기바랍니다. private LaserRangeFinderEntity CreateLaserRangeFinder() // Create a Laser Range Finder Entity. // Place it 30cm above base CenterofMass. LaserRangeFinderEntity laser = new LaserRangeFinderEntity( new Pose(new Vector3(0, 0.30f, 0))); laser.state.name = "P3DXLaserRangeFinder"; laser.laserbox.state.diffusecolor = new Vector4(0.25f, 0.25f, 0.8f, 1.0f); // Create LaserRangeFinder simulation service and specify // which entity it talks to lrf.contract.createservice( ConstructorPort, Microsoft.Robotics.Simulation.Partners.CreateEntityPartner( "http://localhost/" + laser.state.name)); return laser; 센서엔터티에관해한가지흥미로운부분은상대적인위치가모터본체위에항상위치해야한다는것입니다. 모듈화된로봇의경우센서와추가적인하드웨어들은부모엔터티의용어로기술되어야하며, 결합된형태가아닌독립적인형태로기술될필요가있습니다. Bumper Array 엔터티 이번섹션에서는모터본체의앞뒤에위치한두개의 Shape 을사용하여 Bumper Array 를생성하는방법에대해보여줍니다. Pioneer 메쉬파일은 10 개의범포를사용하는것으로보여주고있지만, 시뮬레이션에서는단지 2 개의범퍼만사용합니다. 범퍼 Shpae 의상대적인위치에유의하시기바랍니다. private BumperArrayEntity CreateBumperArray() // Create a bumper array entity with two bumpers BoxShape frontbumper = new BoxShape( new BoxShapeProperties("front", 0.001f, new Pose(new Vector3(0, 0.05f, -0.25f)), new Vector3(0.40f, 0.03f, 0.03f) ) ); frontbumper.state.diffusecolor = new Vector4(0.1f, 0.1f, 0.1f, 1.0f); BoxShape rearbumper = new BoxShape( 16

new BoxShapeProperties("rear", 0.001f, new Pose(new Vector3(0, 0.05f, 0.25f)), new Vector3(0.40f, 0.03f, 0.03f) ) ); rearbumper.state.diffusecolor = new Vector4(0.1f, 0.1f, 0.1f, 1.0f); // The physics engine will issue contact notifications only // if we enable them per shape frontbumper.state.enablecontactnotifications = true; rearbumper.state.enablecontactnotifications = true; // put some force filtering so we only get notified for significant bumps //frontbumper.state.contactfilter = new ContactNotificationFilter(1,1); //rearbumper.state.contactfilter = new ContactNotificationFilter(1, 1); BumperArrayEntity bumperarray = new BumperArrayEntity(frontBumper, rearbumper); // entity name, must match manifest partner name bumperarray.state.name = "P3DXBumpers"; // start simulated bumper service bumper.contract.createservice( ConstructorPort, Microsoft.Robotics.Simulation.Partners.CreateEntityPartner( "http://localhost/" + bumperarray.state.name)); return bumperarray; Bumper Array 엔터티는물리공간에서다른물체와충돌을알리기위해 ContractNotification 방식을적용합니다. 범퍼가충돌을감지하기위해서는아래와같은속성이설정되어야합니다. frontbumper.state.enablecontactnotifications = true; 카메라엔터티 이본섹션에서는실시간을시물레이션공간상의영상을캡쳐해서전송하는카메라를어떻게추가시키는지에설명을합니다. 카메라는모터본체에부착되어있으며, 이로인해로봇이움직일때같이움직이도록되어있습니다. 시뮬레이션엔진은 CameraEntity 를생성하는데있어서다중인스턴스를지원하며, 아래와같이 2 가지의모드로작동될수있습니다. 1. 실시간모드 CameraEntity 는임의의카메라로부터전체영역의영상을실시간으로랜더링할수있습니다. 이모드는웹캠센서를모델링하는데유용하게사용됩니다. 실시간카메라는영상의랜더링을중복적으로수행하기때문에높은랜더링자원을소모합니다. 2. 비실시간모드 CameraEntity 는시뮬레이션상에서오직활성화될때만랜더링을합니다. 따라서랜더링자원이많오소요되지않습니다. 17

private CameraEntity CreateCamera() // low resolution, wide Field of View CameraEntity cam = new CameraEntity(320, 240); cam.state.name = "robocam"; // just on top of the bot cam.state.pose.position = new Vector3(0.0f, 0.5f, 0.0f); // camera renders in an offline buffer at each frame // required for service cam.isrealtimecamera = true; // Start simulated webcam service simwebcam.contract.createservice( ConstructorPort, Microsoft.Robotics.Simulation.Partners.CreateEntityPartner( "http://localhost/" + cam.state.name) ); return cam; Step 4: 시뮬레이션서비스와엔터티파트너쉽 서비스와하드웨어시뮬레이션엔터티와의파트너링 CreateService 메소드는런터임시에해당서비스정보의요소로서파트너요소를제공하는것과함께주어진 Contract 에대한서비스인스턴스를생성하도록합니다. SimulatedDifferentialDrive 서비스의구현은시뮬레이션서비스를어떻게찾고엔터티파트너를어떻게이용하는지에대해구체적으로보여줍니다. 아래코드는 CreateMotorNase 메소드에서사용된코드입니다. // Start simulated arcos motor service drive.contract.createservice(constructorport, Microsoft.Robotics.Simulation.Partners.CreateEntityPartner( "http://localhost/" + robotbaseentity.state.name) ); 시뮬레이션서비스디자인패턴 시뮬레이션서비스들은엔터티생성과정시비동기특성을확실하게하기위해특별한시작패턴을따라야합니다. 엔터티가서비스가시작된후에시뮬레이션에추가될수도있기때문에, 서비스는엔터티 Notification 이도착할때까지 DSSP 오퍼레이션프로세싱을허용하지않도록유의해야합니다. 아래코드는 SimulatedDifferentialDrive 서비스에서사용된코드로서이러한패턴의사례를보여주고있습니다. protected override void Start() // Find our simulation entity that represents the "hardware" or real-world service. // To hook up with simulation entities we do the following steps // 1) have a manifest or some other service create us, specifying a partner named SimulationEntity // 2) in the simulation service (us) issue a subscribe to the simulation engine 18

looking for // an instance of that simulation entity. We use the Entity.State.Name for the match so it must be // exactly the same. See SimulationTutorial2 for the creation process // 3) Listen for a notification telling us the entity is available // 4) cache reference to entity and communicate with it issuing low level commands. _simengine = simengine.simulationengine.globalinstanceport; _notificationtarget = new simengine.simulationengineport(); if (_state == null) CreateDefaultState(); // PartnerType.Service is the entity instance name. _simengine.subscribe(serviceinfo.partnerlist, _notificationtarget); // dont start listening to DSSP operations, other than drop, until notification of entity Activate(new Interleave( new TeardownReceiverGroup ( Arbiter.Receive<simengine.InsertSimulationEntity>(false, _notificationtarget, InsertEntityNotificationHandlerFirstTime), Arbiter.Receive<DsspDefaultDrop>(false, _mainport, DefaultDropHandler) ), new ExclusiveReceiverGroup(), new ConcurrentReceiverGroup() )); void CreateDefaultState() _state = new diffdrive.drivedifferentialtwowheelstate(); _state.leftwheel = new Microsoft.Robotics.Services.Motor.Proxy.WheeledMotorState(); _state.rightwheel = new Microsoft.Robotics.Services.Motor.Proxy.WheeledMotorState(); _state.leftwheel.motorstate = new Microsoft.Robotics.Services.Motor.Proxy.MotorState(); _state.rightwheel.motorstate = new Microsoft.Robotics.Services.Motor.Proxy.MotorState(); void InsertEntityNotificationHandlerFirstTime(simengine.InsertSimulationEntity ins) InsertEntityNotificationHandler(ins); base.start(); // Listen on the main port for requests and call the appropriate handler. MainPortInterleave.CombineWith( new Interleave( new TeardownReceiverGroup(), new ExclusiveReceiverGroup( 19

Arbiter.Receive<simengine.InsertSimulationEntity>(true, _notificationtarget, InsertEntityNotificationHandler), Arbiter.Receive<simengine.DeleteSimulationEntity>(true, _notificationtarget, DeleteEntityNotificationHandler) ), new ConcurrentReceiverGroup() ) ); void InsertEntityNotificationHandler(simengine.InsertSimulationEntity ins) _entity = (simengine.differentialdriveentity)ins.body; _entity.servicecontract = Contract.Identifier; // create default state based on the physics entity if(_entity.chassisshape!= null) _state.distancebetweenwheels = _entity.chassisshape.boxstate.dimensions.x; _state.leftwheel.motorstate.powerscalingfactor = _entity.motortorquescaling; _state.rightwheel.motorstate.powerscalingfactor = _entity.motortorquescaling; 초기화하는데있어서핵심적인코드영역은아래와같습니다. 1. Drop 오퍼레이션또는 Entity Notification 에대해한번기다리도록간단한 interleave 를활성화합니다. InsertEntityNotificationHandlerFirstTime 핸들러가단한번사용된것에유의하시기바랍니다. 2. Notification 헨들러가한번사용됨에있어서, 엔터티인스턴스와서비스상태를구성하기위해서정상적인 Notification 헨들러를호출합니다. 3. 베이스클래스인 Start 메소드가호출되며서비스가해당오퍼레이션헨들러를활성화하고서비스디렉토리에추가합니다. 4. 엔터티들이동시삭제또는추가되는동시성상황으로부터오퍼레이션을보호하기위해, 엔터티 Notification 을수신하는단순한 Interleave 를메인포트 Interleave 에결합시킵니다. Step 5: 튜토리얼실행 코드를컴파일하고실행시키면윈도우창과 Simple Dashboard 이름의윈도우창이실행되는것을확인하실수있습니다. 20

그림 7 SimpleDashboard 에대한물리적표현화면 Simple Dashboard 는별도의서비스프로그램으로서매니페스트에서별도의서비스로등록되어있습니다. 따라서 Simple Dashboard 서비스는별도로실행가능한프로그램이며, 이번튜토리얼에서는시뮬레이션로봇을제어하기위해사용되었습니다. Simple Dashboard 프로그램을통해네트워크로로봇에접속할수있으며, 로봇에주행명령을전송할수있습니다. Drive 기반주행명령에대해서는로보틱스튜토리얼을참조하시기바랍니다. 그리고 Simple Dashboard 에서는레이저파인더의값을읽어와서화면에표시해주는기능도지원합니다. 로봇에접속하기위해 Simple Dashboard 창에서아래의명령을수행하시기바랍니다. 1. Machine 입력창에 localhost 를입력하고 Connect 버튼을클릭합니다. 2. Connect 버튼클릭후에리스트박스창에 2 개의서비스목록을확인할수있습니다. 3. 화면하단의 Laser Range Finder 부분에서 Connect 버튼을클릭합니다. 21

그림 8 Simple Dashboard Simple Dashboard 를이용해로봇을제어할수있으며아래와같이수행합니다. 1. 화면우측상단의서비스목록중에서 /simulateddifferentialdrive/ 로시작하는항목을더블클릭합니다. 2. 좌측의버튼중에서 Drive 버튼을클릭합니다. 3. Drive 버튼위에있는원형안에십자모양이그려져있는부분을마우스로클릭하여움직입니다. 이도형은컨트롤러를대신하여로봇을조정하도록합니다. 4. 만약조이스틱이나컨트롤러가부착되어있다면, Direct Input Device 항목에표시되며, 연결된디바이스를통해서도로봇을제어할수있습니다. 22

시뮬레이션튜토리얼 3 (C#) 상태정보와매니페스트를이용한시물레이션생성 이번튜토리얼에서는시뮬레이션엔진의상태를파일로저장하고시뮬레이션구동시이러한상태파일부터시작정보를읽어서초기값을설정하는시나리오를설명합니다. 이번시나리오는별도의샘플소스코드를필요로하지않으며, 기존의튜토리얼을활용하여상태정보를저장하고활용하는과정을소개합니다. 시작하기 이번튜토리얼을시작하기위해서는시물레이션튜토리얼 1 번과 2 번을실행한후실행중인시뮬레이션상태를파일로저장하는것이필요합니다. 상태를저장하는방법은아래의 Step1 을참고하기바랍니다. Step 1: 시뮬레이션실행상태를파일로저장하기 윈도우시작메뉴에서 MSRS Command 창을실행시키고아래의시뮬레이션튜토리얼 1 번을실행시키는명령어를수행합니다. dsshost.exe /port:50000 /tcpport:50001 /manifest:"samples\config\simulationtutorial1.manifest.xml" 웹브라우저를실행한후아래의주소로접속하여서비스가정상적으로실행중인지를체크합니다. http://localhost:50000/simulationengine 연결후에는아래의결과와같은페이지를볼수있습니다. 23

그림 1 Serialized XML 형태로표현된시뮬레이션상태 Serialized XML 형태로표현된시뮬레이션엔진의상태는시뮬레이션환경에있는모든엔터티들의현재속성을보여줍니다. 스크롤다운한후 SerializedEntities XML 노드를확인해보면아래와같습니다. 24

그림 2 SerializedEntities 노드 첫번째로이동가능한엔터티는 SingleShapeEntity 이며, 이엔터티의시리얼라이즈된상태값은첫번째시뮬레이션튜토리얼 (SimulationTutorial1.cs) 에포함되어있는아래의코드부분에서설정한값을보여줍니다. void AddBox(Vector3 position) Vector3 dimensions = new Vector3(0.2f, 0.2f, 0.2f); // meters // create simple movable entity, with a single shape SingleShapeEntity box = new SingleShapeEntity( new BoxShape( new BoxShapeProperties( 100, // mass in kilograms. new Pose(), // relative pose dimensions)), // dimensions position); box.state.massdensity.mass = 0; box.state.massdensity.density = 0; // Name the entity. All entities must have unique names box.state.name = "box"; // Insert entity in simulation. SimulationEngine.GlobalInstancePort.Insert(box); 시뮬레이션엔진서비스의웹주소에대해 HTTP GET 형태로값을읽어보면, 시뮬레이션엔진서비스의내부에값이유지되는상태값을확인해볼수있습니다. 웹브라우저에보이는내용을 MSRS 설치폴더밑에 \store 폴더안에 SimulationEngineState.xml 로저장해놓습니다. 25

그림 3 XML 파일로저장되어있는시뮬레이션상태값 이제는실행차에서 Ctrl+C 를눌러노드의실행을중지시키고아래의명령어를실행시킵니다. 1. Samples 아래에새로운폴더를생성합니다. cd Samples md MySimulationTutorial3 cd MySimulationTutorial3 2. 위에서 Store 폴더안에저장해놓은파일을생성해놓은폴더안에복사해놓습니다. copy..\..\store\simulationenginestate.xml MySim3.SimulationState.xml Step 2: 시뮬레이션상태파일을결합하기 이제두번째시뮬레이션튜토리얼예제를실행합니다. 위의 Step1 과정과동일하게웹브라우저로접속후실행상태를 XML 파일로저장해놓습니다. 두번째시뮬레이션튜토리얼은아래와같이실행시킵니다. dsshost.exe /p:50000 /t:50001 /manifest:"samples\config\simulationtutorial2.manifest.xml" 실행상태는웹브라우저를통해서아래의주소로접속하여확인입니다. 26

http://localhost:50000/simulationengine 이제실행상태를중지하고웹브라우저의내용을 XML 파일로저장합니다. 파일은위에서만든폴더에생성하며, 파일이름은 MySim3.SimulationState.XML 파일로저장합니다. 그림 4 XML 형태로저장된파일의내용중 Pioneer3DX 부분 이제나중에저장된 XML 파일과 Step 1 에서저장된 XML 파일을 Visual Studio 를통해파일을엽니다. 그리고나중에저장된파일중에서 Pioneer3DX 와이에연관되어있는 LaserRangeFinderEntity, BumperArrayEntity, CameraEntity 관련 XML 노드들을복사하여, 첫번째 XML 파일인 SimulationEngineState.XML 의 <SerializedEntities> 노드안에복사해놓습니다. Pioneer3DX 는아래와같이시작되며, LaserRangeFinderEntity 와나머지항목들도복사해놓고파일을저장합니다. <Pioneer3DX xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:xsd="http://www.w3.org/2001/xmlschema" xmlns="http://schemas.microsoft.com/robotics/2006/04/simulationengine.html"> Step 3: 저장된 XML 파일로서비스실행하기 이제 SimulationTutorial3.manifest.xml 파일을새롭게생성하여, 위의단계에서생성한 XML 파일을이용해서서비스를구동합니다. MSRS 설치폴더밑에 Samples\Config 폴더에는 SimulationTutorial3.manifest.xml 이름의파일이샘플로이미생성되어있으며, 해당파일의내용은아래와같습니다. <?xml version="1.0"?> <Manifest xmlns="http://schemas.microsoft.com/xw/2004/10/manifest.html" xmlns:dssp="http://schemas.microsoft.com/xw/2004/10/dssp.html" xmlns:simcommon="http://schemas.microsoft.com/robotics/2006/04/simulation.html" > <!--Simulation Tutorial 3 manifest. Uses simulation engine state file as configuration 27

for simulation engine --> <CreateServiceList> <!--Start simulation engine service--> <ServiceRecordType> <dssp:contract>http://schemas.microsoft.com/robotics/2006/04/simulationengine.html</dssp:c ontract> <dssp:partnerlist> <dssp:partner> <dssp:service>simulationtutorial3.simulationenginestate.xml</dssp:service> <dssp:name>dssp:stateservice</dssp:name> </dssp:partner> </dssp:partnerlist> </ServiceRecordType> <!-- Start simulated motor service--> <ServiceRecordType> <dssp:contract>http://schemas.microsoft.com/robotics/simulation/services/2006/05/simulated differentialdrive.html</dssp:contract> <dssp:partnerlist> <dssp:partner> <!--The partner name must match the entity name--> <dssp:service>http://localhost/p3dxmotorbase</dssp:service> <dssp:name>simcommon:entity</dssp:name> </dssp:partner> </dssp:partnerlist> </ServiceRecordType> <!-- Start simulated laser range finder service--> <ServiceRecordType> <dssp:contract>http://schemas.microsoft.com/robotics/simulation/services/2006/05/simulated lrf.html</dssp:contract> <dssp:partnerlist> <dssp:partner> <!--The partner name must match the entity name--> <dssp:service>http://localhost/p3dxlaserrangefinder</dssp:service> <dssp:name>simcommon:entity</dssp:name> </dssp:partner> </dssp:partnerlist> </ServiceRecordType> <!-- Start simulated bumber service--> <ServiceRecordType> <dssp:contract>http://schemas.microsoft.com/robotics/simulation/services/2006/05/simulated bumper.html</dssp:contract> <dssp:partnerlist> <dssp:partner> <!--The partner name must match the entity name--> 28

<dssp:service>http://localhost/p3dxbumpers</dssp:service> <dssp:name>simcommon:entity</dssp:name> </dssp:partner> </dssp:partnerlist> </ServiceRecordType> </CreateServiceList> </Manifest> 위의예제에서는동일한폴더에존재하는 SimulationEngineState.xml 파일을참조하도록되어있습니다. Step 4: 실행하기 이제아래의명령어를 MSRS 실행창에서실행시킵니다. Dsshost.exe /port:50000 /tcpport:50001 /manifest: samples\config\simulationtutorial3.manifest.xml 또한 SimpleDashboard 서비스를아래와같이실행시킨후 SimpleDashboard 에서해당 Pioneer3DX 로봇이정상적으로제어되는지를확인합니다. dsshost.exe /p:51000 /t:51001 /m:"samples\config\simpledashboard.manifest.xml" 29

시뮬레이션튜토리얼 4 (C#) 조인트와다관절 Arm 구현 이번튜토리얼에서는시뮬레이션상에서 6 자유도를가지는조인트 (Joint) 와다관절 Arm (Articulated Arms) 을구현하는예제를소개합니다. 그림 1 다관절 Arm 을시뮬레이션에서구현한예제 30

그림 2 - 다관절 Arm 의물리적랜더링예제 시작하기 이번시뮬레이션튜토리얼은해당샘플코드가이미존재하며 samples\simulationtutorials\tutorial4 폴더를참조합니다. Simulationtutorial4.csproj 파일을비주얼스튜디오를이용해서로드합니다. 프로젝트참조 이번시물레이션프로그램이실행되기위해서는두번째시뮬레이션프로젝트에추가적으로아래와같이 3 개의 Proxy dll 파일을참조에추가해야합니다. RoboticsCommon.Proxy 다관절 Arm 과같은서비스에대한서비스인터페이스정의 SimulatedLBR3Arm.2006.M07.Proxy 시뮬레이션 LBR3 Arm 서비스에대한프록시라이브러리 SimulationCommon.Proxy 공통시뮬레이션타입에대한프록시라이브러리. 결과값을시뮬레이션 Arm 에보내기위해사용됩니다. 31

그림 3 튜토리얼 4 의참조구성 아래의 using 문이파일상단에추가되어야합니다. using arm = Microsoft.Robotics.Services.ArticulatedArm.Proxy; using Microsoft.Robotics.PhysicalModel; using physicalmodelproxy = Microsoft.Robotics.PhysicalModel.Proxy; PhysicalModel 네임스페이스의타입적용 Microsoft.Robotics.PhysicalModel 네임스페이스에서는로봇의물리특성을기술하는데필요한타입들이정의됩니다. 이러한타입들은시뮬레이션에서엔터티와시뮬레이션서비스들을기술하기위해사용되기때문에, Microsoft.Robotics.Simulation.Physics 의네임스페이스와타입들이 PhysicalModel 네임스페이스로단일화되었습니다. 하지만, Vector3 와 Quaternion 과같은공통타입들이정의되는 DirectX 에대한런타임에서의의존성을피하기위하여, 새로운기본타입들이새로운네임스페이스에이러한이름으로정의되었습니다. 이러한새로운모델타입을사용하는시뮬레이션서비스또는실제서비스에연결되는서비스들에있어서는추가적으로복잡한작업이요구되지는않습니다. 단지일반적으로메시지를전달하드시원격서비스에대한프록시에링크를거는수준입니다. Quaternion 이포함되는연산을수행하는서비스나시뮬레이션서비스들에있어서는이러한작업들이큰문제가되지는않으며, 아래와같은주된이유를가집니다. 프록시에서는정적루틴을넘어서변경이안되기때문에, Vector 와 Quaternion 연산에대해서정적 Helper 메소드가 physicalmodelproxy 네임스페이스에서는더이상유효하지가않습니다. 이것은개발자가 RoboticsCommon.dll 과 RoboticsCommonProxy.dll 파일둘다를참조에포함해야한다는것을의미합니다. 이름들이같은프록시네임스페이스에대해서이름의혼동을피하기위하여 Using 문장을기술할때, 별칭 (Alias) 을사용하여축약된형태를사용하는것을권장합니다. 이러한이슈를다룬좋은예제는 SimulatedLBR3Arm 서비스이며 Samples\Simulation\ArticulatedArms\LBR3 폴더안에소스코드가있습니다. Step 1: 서비스를생성하고시작하기 32

서비스시작 이번튜토리얼은두번째튜토리얼과매우유사한방식으로시작되며, PopulateWorld 메소드를호출하고몇개의엔터티를추가합니다. protected override void Start() base.start(); // Orient sim camera view point SetupCamera(); // Add objects (entities) in our simulated world PopulateWorld(); private void SetupCamera() // Set up initial view CameraView view = new CameraView(); view.eyeposition = new Vector3(1.8f, 0.598689f, -1.28f); view.lookatpoint = new Vector3(0, 0, 0.2830455f); SimulationEngine.GlobalInstancePort.Update(view); private void PopulateWorld() AddSky(); AddGround(); AddDomino(new Vector3(0.3f, 0, 0.3f), 10); SpawnIterator(AddArticulatedArm); 시뮬레이션 Arm 서비스와엔터티 AddArticulatedArm 메소드는산업용다관절로봇 Arm 에대한시뮬레이션엔터티들과엔터티들을제어하기위한관련서비스들을생성합니다. 해당메소드가다중의비동기연산을순차적으로수행해야하기때문에 Iterater 방식이적용되었습니다. IEnumerator<ITask> AddArticulatedArm() Vector3 position = new Vector3(0, 0, 0); // Create an instance of our custom arm entity. // Source code for this entity can be found under // Samples\Simulation\Entities\Entities.cs KukaLBR3Entity entity = new KukaLBR3Entity(position); // Name the entity entity.state.name = "LBR3Arm"; // Insert entity in simulation. SimulationEngine.GlobalInstancePort.Insert(entity); 33

// create simulated arm service DsspResponsePort<CreateResponse> resultport = CreateService( Microsoft.Robotics.Services.Simulation.LBR3Arm.Proxy.Contract.Identifier, Microsoft.Robotics.Simulation.Partners.CreateEntityPartner("http://localhost/" + entity.state.name)); // asynchronously handle service creation result. yield return Arbiter.Choice(resultPort, delegate(createresponse rsp) _armserviceport = ServiceForwarder<arm.ArticulatedArmOperations>(rsp.Service);, delegate(fault fault) LogError(fault); ); if (_armserviceport == null) yield break; // we re-issue the get until we get a response with a fully initialized state do yield return Arbiter.Receive(false, TimeoutPort(1000), delegate(datetime signal) ); yield return Arbiter.Choice( _armserviceport.get(new GetRequestType()), delegate(arm.articulatedarmstate state) _cachedarmstate = state;, delegate(fault f) LogError(f); ); // exit on error if (_cachedarmstate == null) yield break; while (_cachedarmstate.joints == null); // Start a timer that will move the arm in a loop // Comment out the line below if you want to control // the arm through SimpleDashboard //Spawn<DateTime>(DateTime.Now, MoveArm); 두번째튜토리얼에서의 AddModularRobot 과유사하게엔터티가생성되고시뮬레이션엔진과엔터티에대한서비스에더해져서추가됩니다. 그리고아래의작업들이추가적으로적용됩니다. 34

1. CreateService 메소드에대한결과가비동기적으로처리되며, 신규서비스와의통신을위해포트가생성됩니다. 2. GET DSSP 연산이해당상태를조회합니다. 다관절 Arm 의상태정의는각각의 Arm 들을연결하는조인트들을기술하는조인트인스턴스의목록을포함합니다. 3. MoveArm 메소드가증식되면서호출됩니다. MoveArm 메소드는시뮬레이션 Arm 서비스에 Arm 들을연결하는다양한조인트를제어하기위한요청데이터를생성합니다. 시뮬레이션서비스는다관절 Arm 서비스의추상화된인터페이스를사용하기때문에, 이러한메소드는아래와같은서비스구현으로동작합니다. void MoveArm(DateTime signal) _angleinradians += 0.005f; float angle = (float)math.sin(_angleinradians); // Create set pose operation. Notice we have specified // null for the response port to eliminate a roundtrip arm.setjointtargetpose setpose = new arm.setjointtargetpose( new arm.setjointtargetposerequest(), null); // specify by name, which joint to orient setpose.body.jointname = _cachedarmstate.joints[0].state.name; setpose.body.targetorientation = new physicalmodelproxy.axisangle( new physicalmodelproxy.vector3(1, 0, 0), angle); // issue request to arm service for joint0 move. _armserviceport.post(setpose); // now move other joints. setpose.body.jointname = _cachedarmstate.joints[1].state.name; _armserviceport.post(setpose); setpose.body.jointname = _cachedarmstate.joints[2].state.name; _armserviceport.post(setpose); setpose.body.jointname = _cachedarmstate.joints[3].state.name; _armserviceport.post(setpose); setpose.body.jointname = _cachedarmstate.joints[4].state.name; _armserviceport.post(setpose); // re- issue timer request so we wake up again Activate(Arbiter.Receive(false, TimeoutPort(15), MoveArm)); 메소드는 SetJointTargetPose 데이터를 15 밀리초단위로생성하며, 점진적으로조인트를해당축주위로이동시킵니다. 시뮬레이션 LBR3 로봇 Arm 에대해서는모든조인트들이단일자유도를가지도록설정됩니다. Step 2: 조인트구성 다음단계로진행하기전에, 조인트들이물리엔진에서시뮬레이션상으로어떻게기술되는지에대해살펴보도록합니다. 6 자유도와같은세부적인기술자료는 AGEIA PhysX 의기술문서를참고하길바라며, 조인트속성의데이터타입은 Samples\Common\PhysicalModel.cs 파일에정의되어 35

있습니다. /// <summary> /// Joint properties /// </summary> [DataContract] public class JointProperties /// <summary> /// Joint name. Must be unique for all joints between an entity pair /// </summary> [DataMember] [Description("The descriptive identifier for the joint.")] public string Name; /// <summary> /// Pair of entities joined through this joint /// </summary> [DataMember] [Description("The pair of entities connected through this joint.")] public EntityJointConnector[] Connectors = new EntityJointConnector[2]; /// <summary> /// Maximum force supported by the joint /// </summary> [DataMember] [Description("The maximum force supported by the joint.")] public float MaximumForce; /// <summary> /// Maximum torque supported by the joint /// </summary> [DataMember] [Description("The maximum torque supported by the joint.")] public float MaximumTorque; /// <summary> /// Enables collision modelling between entities coupled by the joint /// </summary> [DataMember] [Description("Enables collision between entities couples by the joint.")] public bool EnableCollisions; /// <summary> /// Underlying physics mechanism to compensate joint simulation errors /// </summary> [DataMember] [Description("Underlying physics mechanism to compensate joint simulation errors.")] public JointProjectionProperties Projection; /// <summary> /// If set, defines a joint with translation/linear position drives 36

/// </summary> [DataMember] [Description("Identifies if the joint supports translation/linear position drives.")] public JointLinearProperties Linear; /// <summary> /// If set, defines a joint with angular drives. /// </summary> [DataMember] [Description("Identifies if the joint supports angular drives.")] public JointAngularProperties Angular; //... Constructors omitted for clarity 시뮬레이션엔진은오진한가지타입의조인트만을노출시키며, 이러하조인트는 6 자유도조인트로알려져있습니다. 조인트는 2 개의물리엔터티를연결시키며, 상대엔터티에대한동작을제한시킬수있습니다. 조인트의기본요소는아래와같습니다. 자유도는 Unlock, Lock 또는 Limited 상태를가집니다. 엔터티들은로컬포인트를기준으로연결됩니다. 각각의엔터티에대해기준축이정의됩니다. 조인트법선 (normal) 은해당조인트가향하는방향을보여줍니다. Unlock 자유도에대해옵션사항으로드라이브와스프링메커니즘을적용할수있습니다. 구형조인트예제 구형조인트를구성하기위해서는선형자유도를고정시키고 ( 또는 JointProperties.Linear 관련코드를전혀기술하지않음 ) 각자유도 (Angular degree of freedom) 항목을해제시킵니다. void MoveArm(DateTime signal) _angleinradians += 0.005f; float angle = (float)math.sin(_angleinradians); // Create set pose operation. Notice we have specified // null for the response port to eliminate a roundtrip arm.setjointtargetpose setpose = new arm.setjointtargetpose( new arm.setjointtargetposerequest(), null); // specify by name, which joint to orient setpose.body.jointname = _cachedarmstate.joints[0].state.name; setpose.body.targetorientation = new physicalmodelproxy.axisangle( new physicalmodelproxy.vector3(1, 0, 0), angle); // issue request to arm service for joint0 move. _armserviceport.post(setpose); // now move other joints. setpose.body.jointname = _cachedarmstate.joints[1].state.name; 37

_armserviceport.post(setpose); setpose.body.jointname = _cachedarmstate.joints[2].state.name; _armserviceport.post(setpose); setpose.body.jointname = _cachedarmstate.joints[3].state.name; _armserviceport.post(setpose); setpose.body.jointname = _cachedarmstate.joints[4].state.name; _armserviceport.post(setpose); // re- issue timer request so we wake up again Activate(Arbiter.Receive(false, TimeoutPort(15), MoveArm)); JointAngularProperties angular = new JointAngularProperties(); angular.twistmode = JointDoFMode.Free; angular.swing1mode = JointDoFMode.Free; angular.swing2mode = JointDoFMode.Free; angular.twistdrive = new JointDriveProperties(JointDriveMode.Position, new SpringProperties(500, 10, 0), 1000); angular.swingdrive = new JointDriveProperties(JointDriveMode.Position, new SpringProperties(500, 10, 0), 1000); JointProperties sphericaljoint = new JointProperties(angular, null, null); 위의코드에서는각자유도항목들이해제되었으며, Twist 자유도에대해서만드라이브메커니즘메적용되었습니다. 이제조인트는 MoveArm 에서보여지는 SetJointtargetPose 를통해서제어됩니다. SetJointTargetVelocity 를이용해조인트드라이브를제어하기위해서는다음과같이드라이브모드를설정해야합니다. angular.twistdrive = new JointDriveProperties(JointDriveMode.Velocity, new SpringProperties(500, 10, 0), 1000); Step 3: 엔터티구현에대한이해와조인트정의 MSRS 에서지원되는모든엔터티에대해서는에제의형태로소스코드가제공됩니다. samples\simulation\entities\entities.cs 에는내장되어있는엔터티의구현사례가제공되어있으며, 컴파일되어 SimulationEngine.dll 안에포함되어있습니다. 이번튜토리얼은 KukaLBR3Entity 에초점을맞추고있으며, 7 개의 Arm 링크와 6 개의조인트로구성되어있습니다. 아래의코드는전체코드중의일부이며, 조인트리스트와 ArticulatedArmEntity 인스턴스항목을참조하기바랍니다. /// <summary> /// Models KUKA LBR3 robotic arm /// </summary> [DataContract] [CLSCompliant(true)] public class KukaLBR3Entity : VisualEntity const float ARM_MASS = 1f; const float ARM_THICKNESS = 0.03f; const float ARM_LENGTH = 0.075f; 38

const float DELTA = 0.000f; // approximation of the Lbr3 arms for arms 1->5 // base is considered arm/link 0, and the end effectors are 6 and 7 float ARM_LENGTH2 = ARM_LENGTH + ARM_THICKNESS * 2; float MIDPOINT_Y = ARM_LENGTH / 2f + ARM_THICKNESS; const int _jointcount = 7; /// <summary> /// Number of joints /// </summary> [DataMember] public int JointCount get return _jointcount; List<Joint> _joints = new List<Joint>(); /// <summary> /// Joints /// </summary> [DataMember] public List<Joint> Joints get return _joints; set _joints = value; List<ArmLinkEntity> _links = new List<ArmLinkEntity>(); /// <summary> /// Default constructor /// </summary> public KukaLBR3Entity() /// <summary> /// Initialization constructor /// </summary> /// <param name="position"></param> public KukaLBR3Entity(Vector3 position) State.Pose.Position = position; 엔터티초기화 다른모든엔터티와유사하게, KukaLBR3 엔터티는초기화루틴안에서데이터구조를맨먼저구성합니다. 그리고그다음으로해당강체에대한물리인스턴스를생성하는 Initalized 메소드를 39

호출합니다. 초기화루틴은조인트가모든링크엔터티가생성되고나서조인트들이생성되어야하기때문에약간복잡합니다. 조인트들은사용되기위해서인스턴스를필요로하기때문에, 물리엔진에추가되고활성화되어져야합니다. 엔터티의초기화메소드는엔터티가어떻게생성되었는지에따라서두가지방식으로구성됩니다. 1. 프로그램에의한생성 초기화생성자를통해엔터티가생성되었다면, 메소드는모든자식링크엔터티들을자동으로생성하고모든조인트파라메터를설정합니다. 2. Deserialization 만약엔터티가 Descerialization 되어있다면, 모든파라메터는설정되지만, 초기화과정은적당한물리객체를생성하기위해호출되어져야합니다. Initialized 메소드는아래와같습니다. /// <summary> /// Initialization /// </summary> /// <param name="device"></param> /// <param name="physicsengine"></param> public override void Initialize(xnagrfx.GraphicsDevice device, PhysicsEngine physicsengine) try InitError = string.empty; // all joints will be essentially of the same type: // A single DoF free, around the Joint Axis. A drive will be associated with // that DoF. Each joint will have a different joint axis and joint normal for (int i = 0; i < _jointcount; i++) PhysicsJoint jointinstance = null; if (_joints.count < i + 1) JointAngularProperties commonangular = new JointAngularProperties(); commonangular.twistmode = JointDoFMode.Free; commonangular.twistdrive = new JointDriveProperties( JointDriveMode.Position, new SpringProperties(500000, 100000, 0), 1000000); jointinstance = PhysicsJoint.Create(new JointProperties(commonAngular, null, null)); // joints must be names jointinstance.state.name = "Joint" + i.tostring(); Joints.Add(jointInstance); else jointinstance = PhysicsJoint.Create(_joints[i].State); _joints[i] = jointinstance; 40

if (_baselink == null) // programmatically build articulated arm CreateBase(_joints[0]); CreateArm1(_joints[0]); CreateArm2(_joints[1]); CreateArm3(_joints[2]); CreateArm4(_joints[3]); CreateArm5(_joints[4]); CreateArm6(_joints[5]); CreateArm7(_joints[6]); // if we were deserialized, _links will be null // Rebuilt it from the individual named fields if (_links == null _links.count == 0) _links = new List<ArmLinkEntity>(); _links.add(_baselink); _links.add(_arm1link); _links.add(_arm2link); _links.add(_arm3link); _links.add(_arm4link); _links.add(_arm5link); _links.add(_arm6link); _links.add(_arm7link); // set the physics shape on each child entity for (int i = 1; i < (_links.count - 1); i++) // Links 1 to 6 use the same capsule shape to approximate the detailed // geometry of the LBR3 arm links. Notice that in the local pose for the // shape, we translate it up by half its total height since, by default // physics engine sets the center of the shape to be its center of mass if (_links[i].capsuleshape == null) _links[i].capsuleshape = new CapsuleShape( new CapsuleShapeProperties(ARM_MASS, new Pose(new Vector3(0, MIDPOINT_Y, 0)), ARM_THICKNESS, ARM_LENGTH)); 41

// initialize will load the graphics mesh base.initialize(device, physicsengine); // initialize children foreach (VisualEntity c in _links) c.parent = this; c.initialize(device, physicsengine); // fix the base link so its immovable. Remove this if you plan to have the arm // attached through yet a different joint, to some other entity _baselink.physicsentity.iskinematic = true; // for each child arm, set the lower and upper joint for (int i = 0; i < _links.count; i++) ArmLinkEntity link = _links[i] as ArmLinkEntity; if (i < _jointcount) link.upperjoint = _joints[i]; // assume children.count == _joints.count + 1 if (i > 0) link.lowerjoint = _joints[i - 1]; // set the entity name in upper connector if (link.upperjoint!= null) link.upperjoint.state.connectors[0].entity = link; if (link.lowerjoint!= null) link.lowerjoint.state.connectors[1].entity = link; them // second pass inserts joints into the simulation, which initializes and activates // We have to do this after the joint connectors are all attached to entities for (int i = 0; i < _jointcount; i++) physicsengine.insertjoint((physicsjoint)_joints[i]); catch (Exception ex) HasBeenInitialized = false; InitError = ex.tostring(); 42

조인트들은 PhysicsJoint.Create 문장을통해물리엔진에서명확히초기화되어져야합니다. 시각적인표현을위한 3D 설계데이터의활용 모든 Arm 형태는기반 Arm 을제외하고는동일한캡슐모양을가집니다. 시각화를위해 3 개의메쉬파일이사용되었습니다. Lbr3_j0.obj 기반 Arm 을위해사용되는메쉬파일 Lbr3_j1.obj link1 부터 link4 까지의 Arm 을위해사용되는메쉬파일 Lbr3_j5.obj link5 를위해사용되는메쉬파일 Lbr3_j6.obj 구형 link6 을위해사용되는메쉬파일 그림 4 캡술모양 Lbr3_j1.obj 파일은유사한링크들의시각적표현을위해회전되고변환되어사용됩니다. Arm 의프로그램화된구현 아래단계에서보여지는메소드는맨처음의기반 Arm 과첫번째 Arm 두개를연결시키는조인트에필요한메소드입니다. void CreateBase(Joint joint) PhysicsJoint commonjoint = (PhysicsJoint)joint; float mass = 1; float radius = 0.03f; CapsuleShape baseshape = new CapsuleShape(new CapsuleShapeProperties(mass, new Pose(new Vector3(0, radius + 0.015f / 2, 0)), radius, 0.015f)); 43

// create connect point for base. Top and center of its shape. commonjoint.state.connectors[0] = new EntityJointConnector(null, new Vector3(0, 0, 1), new Vector3(0, 1, 0), // joint rotates around the Y(vertical axis) new Vector3(0, baseshape.capsulestate.radius * 2 + baseshape.capsulestate.dimensions.y, 0)); ArmLinkEntity link = new ArmLinkEntity("base", "lbr3_j0.obj", new Pose(new Vector3(0, -radius - 0.005f, 0), TypeConversion.FromXNA(xna.Quaternion.CreateFromAxisAngle(new xna.vector3(0, 1, 0), 0)))); link.state.pose.position = State.Pose.Position; link.upperjoint = commonjoint; _baselink = link; _baselink.capsuleshape = baseshape; _links.add(link); ArmLinkEntity _arm1link; /// <summary> /// Link 1 /// </summary> [DataMember] public ArmLinkEntity Arm1Link get return _arm1link; set _arm1link = value; void CreateArm1(Joint joint) PhysicsJoint commonjoint = (PhysicsJoint)joint; commonjoint.state.connectors[1] = new EntityJointConnector(null, new Vector3(0, 0, 1), new Vector3(0, 1, 0), new Vector3(0, 0, 0)); ArmLinkEntity link = new ArmLinkEntity("arm1", "lbr3_j1.obj", new Pose(new Vector3(0, -MIDPOINT_Y, 0), TypeConversion.FromXNA(xna.Quaternion.CreateFromAxisAngle(new xna.vector3(0, 1, 0), 0)))); link.state.pose.position = State.Pose.Position; _arm1link = link; _links.add(link); 아래그림은기반부분과첫번째 Arm 의연결상태를물리적관점에서표현한그림입니다. 둘다 44

모두조인트축이빨간색화살표로표시됩니다. 그림 5 기반부분과첫번째 Arm 연결 void CreateArm2(Joint joint) PhysicsJoint commonjoint = (PhysicsJoint)joint; commonjoint.state.connectors[0] = new EntityJointConnector( null, new Vector3(0, 1, 0), new Vector3(0, 0, -1), new Vector3(0, ARM_LENGTH2, 0)); commonjoint.state.connectors[1] = new EntityJointConnector( null, new Vector3(0, 1, 0), new Vector3(0, 0, -1), new Vector3(0, 0, 0)); ArmLinkEntity link = new ArmLinkEntity( "arm2", "lbr3_j1.obj", new Pose(new Vector3(0, MIDPOINT_Y, 0), Quaternion.RotationAxis(new Vector3(1, 0, 0), (float)math.pi))); link.state.pose.position = State.Pose.Position; _arm2link = link; _links.add(link); 위의코드에서두번의 new EntityJointConnector 호출은정상적인엔터티연결을위해매우중요한부분입니다. 해당객체내에서두번째와세번째파라메터로사용된값들은조인트들의방향을나타내며, 각각법선방향과로컬축방향을나타냅니다. 45

Step 4: 튜토리얼실행 튜토리얼예제는아래와같이실행될수있습니다. MSRS 실행창에서아래의명령어를실행하시기바랍니다. bin\dsshost /port:50000 /tcpport:50001 /manifest:"samples\config\simulationtutorial4.manifest.xml" 또한웹브라우저를실행하여아래의연결주소를통해실행상태를확인할수있습니다. http://localhost:50000/directory 그림 6 디렉토리서비스 위의서비스목록중에서 /simulatedlbr3 항목을클릭하면, 다음과같은개별조인트들의상태값을확인할수있습니다. 46

그림 7 시뮬레이션 Arm 의상태 초기 Obj 파일에대한변환 시뮬레이션파일은자동으로 Obj 파일을이진파일형태로변환합니다. 따라서맨처음실행될때변환에따른시간지연이발생할수있습니다. 47

그림 8 시뮬레이션으로표현된 Arm 이번튜토리얼을실행하면자동으로 SimpleDashboard 서비스가같이실행됩니다. SimpleDashboard 실행후서버명에 localhost 를입력하고 Connect 버튼을클릭합니다. 48

그림 9 SimpleDashboard 를실행한화면 화면하단에서 Articulated Arm 영역에있는 Connect 버튼을클릭하면, 우측에관련된조인트목록이표시됩니다. 49

그림 10 SimpleDashboard 에서조인트목록을가져온결과 목록에표시되는 Joint 항목들을선택한후 Angle 부분에각도값 (-90 ~ 90) 을입력합니다. 각도값입력후 Apply Changes 버튼을클릭합니다. 1. Joint1 에대해 50 값을입력후 Apply Changes 버튼을클릭합니다. 2. Joiny0 에대해 -90 도값을입력후 Apply Changes 버튼을클릭합니다. 3. 아래와같이 Arm 이이동후블록이차례로넘어지는결과를확인할수있습니다. 50

그림 11 시뮬레이션 Arm 의실행결과 51