Skip to content

C# 응용 예제

사용자가 NURI SDK 를 활용하여, 다른 언어 환경에서 사용하고자 하는 경우를 위해 단계별 가이드를 제공합니다.

프로젝트 생성 및 구성하기

C# 에서 C++ 라이브러리를 사용하기 위해서는 동적연결 라이브러리로 프로젝트를 생성해야합니다. 프로젝트 이름은 추후 사용할 라이브러리의 이름이 됩니다.

새 프로젝트 생성 창에서 C++/Windows -> DLL(동적 연결 라이브러리) 를 선택하여 프로젝트를 생성합니다.



프로젝트 생성이 완료되었으면, 해당 솔루션이 만들어진 폴더 내부에 NURI SDK 에 선언되어 있는 C++ 함수들을 사용하기 위해 아래 그림과 같이 기존 NURI SDK 내부에 있던 external, include, lib 폴더를 복사하여 붙여넣습니다.


이후 솔루션 내에도 붙여넣은 폴더들을 추가합니다.


솔루션 속성 설정

프로젝트 내에 필요한 파일 및 폴더를 추가했다면, 빌드를 위한 솔루션 속성을 설정합니다. 구성 환경은 Release 또는 Debug 상관 없지만, NURI SDK는 64bit 환경에서 빌드된 라이브러리 이므로, 플랫폼은 x64 로 설정해주어야 합니다. 아래 설정 방법은 구성 환경은 Release, 플랫폼은 x64 인 상황에서의 설정 방법입니다.

  • 구성 속성 -> 일반 -> C++ 언어 표준 -> ISO C++17 표준(/std:c++17)


  • 구성 속성 -> 고급 -> 문자집합 -> 멀티바이트 문자 집합 사용


  • 구성 속성 -> 디버깅 -> 환경 -> 프로젝트 경로\lib\Windows\cpp\Release\64bit;


  • 구성 속성 -> C/C++ -> 추가 포함 디렉터리 -> 프로젝트 경로\external, 프로젝트 경로\include



  • 구성 속성 -> 링커 -> 입력 -> 추가 종속성 -> 프로젝트 경로\lib\Windows\cpp\Release\64bit 폴더 내 lib 파일들의 이름을 입력



여기까지 설정했다면, 적용 버튼을 눌러 설정을 완료합니다.

함수 작성

사용할 헤더(.h) 파일과 소스(.cpp) 파일을 생성하고, C# 환경에서 사용할 함수를 정의합니다.

C++ 함수를 사용하기 위해선 __declspec(dllexport) 선언이 필요합니다. 여기에선 간단히 하기위해 헤더(.h)파일에 사용자 정의로 'NURI_API' 라는 이름으로 선언하여 사용합니다.

헤더(.h) 파일 내용

#pragma once

#ifdef NURICSHARP_EXPORTS
#define NURI_API        __declspec(dllexport)
#else
#define NURI_API        __declspec(dllimport)
#endif

사용자가 원하는 대로 함수를 생성하거나 만들수 있으며, extern "C" 내부에 내용을 작성해야 외부에서 호출할 수 있습니다.

소스(.cpp) 파일 내용

#include "pch.h"
#include "stdio.h"
#include "nuri.h"
#include <iostream>
#include <thread>
#include <string>
#include "include/nuri/base.h"
#include "include/nuri/model.h"
#include "include/nuri/robot.h"
#include "include/nuri/utility.h"
#pragma warning(disable:4996)

using namespace nuri;
NuriRobot* robot;

std::error_code ec;
std::string id;
extern "C"
{
    /* ===================================================
    @brief 로봇과의 연결을 위한 함수
    @return 성공 : 0 / 실패 : -1
    @param [in]ip       연결할 로봇 IP
    =================================================== */
    NURI_API int connectRobot(const char* ip)
    {
        try
        {
            std::string ipAddress = ip;

            robot = new nuri::NuriRobot(ipAddress);

            return 0;
        }
        catch (const std::exception& e)
        {
            return -1;
        }
    }

    /* ===================================================
    @brief 로봇과의 연결을 해제하기 위한 함수
    @return 성공 : 0 / 실패 : -1
    =================================================== */
    NURI_API int disconnectFromRobot()
    {
        try
        {
            robot->disconnectFromRobot(ec);

            return 0;

        }
        catch (const std::exception& e)
        {
            return -1;
        }
    }

    /* ===================================================
    @brief Move J (joint Motion) 입력
    @return 성공 : 0 / 실패 : -1
    @param [in] array   타겟 위치
           [in] length  배열 크기
           [in] speed   모션 속도
           [in] radius  반지름
    =================================================== */
    NURI_API int addMoveAbsJ(double* array, int length, int speed, int radius)
    {
        try {
            JointPosition jointPos(std::vector<double>(array, array + length));
            MoveAbsJCommand moveAbsJCommand(jointPos, speed, radius);
            robot->moveAppend({ moveAbsJCommand }, id, ec);

            return 0;

        }
        catch (const std::exception& e) {
            return -1;
        }
    }

  /* ===================================================
    @brief 모션 모드 설정
    @param [in]motionControlMode
        Idle = 0,
        NrtCommand = 1,
        NrtRLTask = 2,
        RtCommand = 3
    =================================================== */
    NURI_API int setMotionControlMode(int motionControlMode)
    {
        try
        {
            robot->setMotionControlMode(static_cast<MotionControlMode>(motionControlMode), ec);

            return 0;
        }
        catch (const std::exception& e) {
            return -1;
        }
    }

    /* ===================================================
    @brief 모터 상태 설정
    @return 성공 : 0 / 실패 : -1
    @param [in] on : true 모터 on / false 모터 off
    =================================================== */
    NURI_API int setPowerState(bool on)
    {
        try
        {
            robot->setPowerState(on, ec);

            return 0;
        }
        catch (const std::exception& e) {
            return -1;
        }
    }

    /* ===================================================
    @brief 모션 재생
    @return 성공 : 0 / 실패 : -1
    =================================================== */
    NURI_API int moveStart()
    {
        try
        {
            robot->moveStart(ec);

            return 0;
        }
        catch (const std::exception& e) {
            return -1;
        }
    }

    /* ===================================================
    @brief 모션 정지
    @return 성공 : 0 / 실패 : -1
    =================================================== */
    NURI_API int stop()
    {
        try
        {
            robot->stop(ec);

            return 0;
        }
        catch (const std::exception& e) {
            return -1;
        }
    }

    /* ===================================================
    @brief 모션 초기화
    @return 성공 : 0 / 실패 : -1
    =================================================== */
    NURI_API int moveReset()
    {
        try
        {
            robot->moveReset(ec);

            return 0;
        }
        catch (const std::exception& e) {
            return -1;
        }
    }

    /* ===================================================
    @brief 동작 모드 설정
    @param [in]operateMode
        manual    = 0,      ///< manual
        automatic = 1,      ///< automatic
        unknown   = Unknown ///< Unknown (abnormalities occurred)
    =================================================== */
    NURI_API int setOperateMode(int operateMode)
    {
        try
        {
            robot->setOperateMode(static_cast<OperateMode>(operateMode), ec);

            return 0;
        }
        catch (const std::exception& e) {
            return -1;
        }
    }

    /* ===================================================
    @brief 현재 동작 상태 확인
    =================================================== */
    NURI_API int operationState()
    {
        switch (robot->operationState(ec))
        {
        case OperationState::idle:
            return 0;

        case OperationState::jog:
            return 1;

        case OperationState::rtControlling:
            return 2;

        case OperationState::drag:
            return 3;

        case OperationState::rlProgram:
            return 4;

        case OperationState::demo:
            return 5;

        case OperationState::dynamicIdentify:
            return 6;

        case OperationState::frictionIdentify:
            return 7;

        case OperationState::loadIdentify:
            return 8;

        case OperationState::moving:
            return 9;

        default: // OperationState::unknown:
            return -1;
        }
    }
}

함수 작성이 끝났다면, 솔루션을 빌드하여 프로젝트이름.dll 을 생성합니다.


C#에서 사용하기

앞선 과정에서, .dll 파일을 생성했다면 이제 그 파일을 사용하고자하는 C# 폴더 내부로 옮깁니다. 생성한 dll 파일은 NuriSDK.dll 파일내의 함수를 호출하도록 만들어진 것이므로 NuriSDK.dll 파일도 C# 폴더 내부로 같이 옮겨줍니다. 본 예제에서는 Unity 를 사용했으므로 Plugins 폴더 아래에 파일을 복사해줍니다.

일반적인 C# 프로젝트를 생성하는 경우, Release 또는 Debug 폴더 내에 dll 파일을 복사해 줍니다.


파일을 옮겼다면, 선언 했던 함수들을 호출하는 코드를 작성합니다. C#에서 DllImport 를 사용하기 위해서는 System.Runtime.InteropServices 를 선언해야합니다.

using System;
using System.Runtime.InteropServices;
...

그 이후, dll 에서 생성했던 함수의 이름을 선언합니다. 함수를 선언할 땐 dll에서 사용했던 함수명 및 리턴 값, 함수에 사용되는 인자의 갯수와 종류를 맞추어 작성해야합니다.

[DllImport("nuricsharp")]
public static extern int connectRobot(string ip);

[DllImport("nuricsharp")]
public static extern int disconnectFromRobot();

[DllImport("nuricsharp")]
public static extern int addMoveAbsJ(IntPtr array, int length, int speed, int radius);

[DllImport("nuricsharp")]
public static extern int setMotionControlMode(int motionControlMode);

[DllImport("nuricsharp")]
public static extern int setPowerState(bool on);
...

아래는 MoveAbsJ 모션 재생을 포함한 사용 예제입니다.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using UnityEngine;

public class nuricsharp
{
    [DllImport("nuricsharp")]
    public static extern int connectRobot(string ip);

    [DllImport("nuricsharp")]
    public static extern int disconnectFromRobot();

    [DllImport("nuricsharp")]
    public static extern int setPowerState(bool on);

    [DllImport("nuricsharp")]
    public static extern int setMotionControlMode(int motionControlMode);

    [DllImport("nuricsharp")]
    public static extern int setOperateMode(int operateMode);

    [DllImport("nuricsharp")]
    public static extern int addMoveAbsJ(IntPtr array, int length, int speed, int radius);

    [DllImport("nuricsharp")]
    public static extern int moveStart();

    [DllImport("nuricsharp")]
    public static extern int stop();

    [DllImport("nuricsharp")]
    public static extern int operationState();
}

public enum OperateMode
{
    manual = 0,
    automatic = 1,
    unknown = -1
}

public enum MotionControlMode
{
    Idle = 0,
    NrtCommand = 1,
    NrtRLTask = 2,
    RtCommand = 3
}

public class NuriSDKTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }

    public void OnClick()
    {
        string ip = "192.168.20.3";
        int res;

        // 로봇 연결
        res = nuricsharp.connectRobot(ip);

        // 연결 실패시 더이상 진행하지 않음
        if (!res.Equals(0))
        {
            Debug.Log("failed");
            return;
        }

        // 모션 재생을 위해 NrtCommand 모드로 설정
        nuricsharp.setMotionControlMode((int)MotionControlMode.NrtCommand);

        // 모터를 켜기 위해서는 동작 모드가 자동 이여야 한다.
        nuricsharp.setOperateMode((int)OperateMode.automatic);

        // 모터 ON
        nuricsharp.setPowerState(true);

        // 모션 설정
        // 모든 길이는 m, 각도는 radian 기준임.
        List<double> target = new List<double>() { -0.3452, -0.0213, -1.74533, 0.0159, -1.570, 0 };
        double[] array = target.ToArray();
        GCHandle gch = GCHandle.Alloc(array, GCHandleType.Pinned);

        try
        {
            IntPtr pointer = gch.AddrOfPinnedObject();
            res = nuricsharp.addMoveAbsJ(pointer, array.Length, 500, 0);
        }
        finally
        {
            gch.Free();
        }

        // 모션 재생
        res = nuricsharp.moveStart();
        if (!res.Equals(0))
        {
            Debug.Log("failed : " + res);
            return;
        }

        // 모션이 끝날때까지 기다림
        waitRobot();

        // 모션 완료 후
        // 모터 OFF
        nuricsharp.setPowerState(false);

        // 동작 모드 수동 모드로 설정
        nuricsharp.setOperateMode((int)OperateMode.manual);


        // 로봇 연결 해제
        nuricsharp.disconnectFromRobot();
    }

    // 모션이 진행중인지 확인
    public void waitRobot()
    {
        Thread.Sleep(2000);
        bool running = true;
        while(running)
        {
            int res = nuricsharp.operationState();
            if(res.Equals(0) || res.Equals(-1))
            {
                running = false;
            }
        }
    }
}

NURI SDK 에 대한 자세한 설명은 SDK 매뉴얼을 참고하시기 바랍니다.

Sdk매뉴얼

PDF 형태의 사용자 매뉴얼은 SDK 구매 시 제공되며, 매뉴얼을 제공받지 못하거나 분실한 고객은 메일(sales@neuromeka.com)을 통해 요청해주시기 바랍니다.