Skip to content

작업공간 어드미턴스 제어

시스템 및 제어기 구성

IndySDK에서 제공하는 위치 제어기, 어드미턴스 궤적 생성기, 필터 및 공유메모리 등을 활용해서 작업공간 임피던스 제어기를 구성한다.

본 예제를 통해서 다음을 살펴볼 수 있다.

  • F/T센서의 좌표계 변환방법
  • H-infinity 위치제어기 사용방법
  • 명목 시스템 초기화 방법
  • 정동역학 기반 위치제어기 사용방법
  • 작접공간내 3차원 어드미턴스 궤적 생성기 사용방법
  • 저역필터의 사용방법

예제 코드 작성

헤더 파일 작성

작업공간 제어 컴포넌트 (Task Control Component)를 생성한 후, 공유메모리와 필터등을 사용하기 위해서 다음의 해더파일을 추가하고 필요한 코드를 작성한다.

1
2
3
4
5
6
7
8
9
// for the use of shared memeory
#include "NRMKFramework/Indy/Sharedmemory/SharedData.h"
#include "NRMKFramework/Indy/Sharedmemory/SharedData.h"
NRMKFramework::ShmemManager * indyShm = NULL;
NRMKIndy::SharedData::RobotControlSharedData ctrData;

// for the use of lowpass filter and filtered derviative
#include <Util/Filter.h>
#include <math.h>

작업공간 어드미턴스 제어기를 설계하기 전에 생성된 TaskControlComponent 클래스에 다음과 같이 멤버함수와 타입 및 변수들을 새로 정의한다

1
2
3
4
5
// new type definitaions
typedef typename ROBOT::JointVec JointVec;
typedef typename ROBOT::JointMat JointMat;
typedef NRMKFoundation::FilterLowPass<double> LowPassFilter;
typedef NRMKFoundation::FilterDerivative<double> FilteredDerivative;
1
2
3
4
5
// member functions
void readFTSensor(ROBOT & robot);
void initNominalSystem(ROBOT & robot);
void computeJDet(ROBOT & robot);
void setBias() {_ftBias = _ftSensor;}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private:
    // getting nominal robot dynamics
    JointMat            _Mn, _Cn;
    JointVec            _Gn;

private:
    // getting admittance trajectory
    TaskPosition        _tposPxy;
    TaskVelocity        _tvelPxy;
    TaskAcceleration    _taccPxy;

private:
    // with respect to ft sensor
    LieGroup::Wrench    _ftBias;
    LieGroup::Wrench    _ftSensor;
    LieGroup::Rotation  _ft_sensor_orientation;
    LieGroup::Wrench    _ftTransformed, _ftTransformed_filtered;
    LowPassFilter       _ftLowPass[6];

private:
    // reset control, instaneous time, control gains
    bool                _reset_ctrl;
    double              _delT;
    TaskVec             _kp, _kv, _ki;

다음으로 공유 메모리에서 F/T센서 정보를 획득하기 위해서 다음과 같이 생성자에서 포인터 변수에 메모리를 할당한다 (자세한 내용은 IndyFramework 또는 F/T센서 이용 참고).

1
2
3
4
5
6
7
8
TaskControlComponent::TaskControlComponent()
: AbstractController(USERNAME, EMAIL, SERIAL)
, _robotNom(NULL)
{
    indyShm = new NRMKFramework::ShmemManager(INDY_SHM_NAME, INDY_SHM_LEN);

    for(int i=0; i<6; i++) _ftBias[i] = 0;
}

종료 시 할당된 메모리를 해제하기 위해서 다음과 같이 공유메모리와 명목 로봇에 할당된 메모리를 해제한다.

1
2
3
4
5
6
7
TaskControlComponent::~TaskControlComponent()
{
    if (_robotNom != NULL) delete _robotNom;

    if (indyShm != NULL)
        delete indyShm;
}

소스 파일 작성

작업공간 어드미턴스 유닛에 의해서 생성된 어드미턴스 궤적을 API로 제공되는 위치제어기를 사용하여 로봇이 추종하도록 제어기를 설계한다. 본 예제에서는 API로 제공되는 위치제어기인 H-infinity 제어기와 Forward Dynamic 제어기를 각각 활용하여 어드미턴스 제어를 구현한다.

이를 위해서 먼저 initialize() 함수를 다음과 같이 작성한다.

  • 로봇의 끝단에 배치된 센서의 좌표계를 기준 좌표계와 일치 시킬 수 있도록 한다.
  • 사용하려고 하는 제어기를 초기화 한다.
  • 샘플링 시간(delt)을 전역변수로 사용할 수 있도록 _delT에 업데이트한다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void TaskControlComponent::initialize(ROBOT & robot, double delt)
{
  // create a nominal robot
    _robotNom = AbstractController::createRobotObj();

  // align ft sensor coordinates with robot's tool frame's coordinates
    _ft_sensor_orientation = LieGroup::Rotation(-1,0,0,0,-1,0,0,0,1);
    robot.setTFTSensor(_ft_sensor_orientation, LieGroup::Displacement::Zero());

  // initialize position controller
#if defined(HINF_CTR)
    robot.initHinfController(delt);
#else
    robot.initForwardDynController(delt);
#endif

  // update sampling time
  _delT = delt;
}

제어이득 값을 전역에서 사용하기 위해서 다음과 같이 setGains() 함수에서 제어 이득을 전역 변수에 업데이트 한다.

1
2
3
4
5
6
void TaskControlComponent::setGains(TaskVec const & kp, TaskVec const & kv, TaskVec const & ki)
{
    _kp = kp;
    _kv = kv;
    _ki = ki;
}

작업이 끝날 때마다 초기화가를 수행하기 위해서 reset() 함수를 다음과 같이 작성한다. 본 예제에서는 작업이 끝날 때마다 F/T센서 값의 초기화와 제어기 내부 초기화용 변수를 초기화 한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void TaskControlComponent::reset()
{
    // get ft sensor values from shared memory
    NRMKIndy::SharedData::getControlData(*indyShm, ctrData);
    for (int i=0; i<6; i++) _ftSensor[i] = ctrData.Fext[i];

    // compensate ft sensor bias
    setBias();

    for(int j=0; j<JOINT_DOF; j++){
        for(int i=0; i<2; i++){
            _ftLowPass[j].filtered(i) = 0.0;
            _ftLowPass[j].raw(i) = 0.0;
        }
    }

    // reset flag defined for controller reset
    _reset_ctrl = true;
}

어드미턴스 제어기는 다음과 같이 computeControlTorq() 함수에 작성된다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
int TaskControlComponent::computeControlTorq(ROBOT & robot, LieGroup::Vector3D const & gravDir, TaskPosition const & posDesired, TaskVelocity const & velDesired, TaskAcceleration const & accDesired, JointVec & torque)
{
    if (indyShm == NULL)
    {
        printf("Shared memory is missed\n");
    }

  // reset admittance trajectory and position controller
    if (_reset_ctrl)
    {
      printf("initialized\n");

      //reset nominal system "robotNom"; the function is defined below
      resetNominalSystem(robot);
      //reset admittancd trajectory
      robot.resetAdmittanceTraj(_delT);
      //reset position controllers
#if defined(HINF_CTR)
      robot.resetHinfController();
#else
      robot.resetForwardDynController();
#endif
      _reset_ctrl = false;
    }

    // read ft sensor value; the function is defined below
    readFTSensor(robot);

    // change ft sensor direction x,y,z,u,v,w to u,v,w,x,y,z
    for (int i=0; i<3; i++) _ftTransformed[i] = _ftSensor[i+3];
    for (int i=0; i<3; i++) _ftTransformed[i+3] = _ftSensor[i];

    // lowpass filtering of ft sensor values
    LieGroup::Wrench _ftTransFiltered, _ftTransFilteredLow;
    for (int i=0; i<INDY_NUM_TASK_AXES; i++)
    {
      _ftLowPass[i].filter(_ftTransformed[i], _ftTransformed_filtered[i]);
    }

    // calculate admittance trajecotry
    // based on desired trajectories and ft sensor values
    robot.updateAdmittanceTraj(posDesired, velDesired, accDesired, _tposPxy,
    _tvelPxy, _taccPxy, _ftTransformed, _ftTransformed_filtered, -1);

    // set control gains and update admittance trajecotry to controllers
#if defined(HINF_CTR)
    robot.setHinfControlGain(_kp, _kv, _ki);
    robot.HinfController(gravDir, _tposPxy, _tvelPxy, _taccPxy);
#else
    robot.setForwardDynControlGain(_kp, _kv, _ki);
    robot.ForwardDynController(*_robotNom, _Mn, _Cn, _Gn, _tposPxy, _tvelPxy,
    gravDir);
#endif

    torque = robot.tau();

    return 0;   //return 1: Close to Singularity
}

다음으로 computeControlTorq() 에서 사용된 resetNominalSystem() 함수와 readFTSensor() 함수는 다음과 같이 각각 정의된다.

아래와 같이 resetNominalSystem() 함수를 정의하여 작업완료 후 재시작 시 명목 로봇 시스템 robotNom 을 실제 로봇 시스템 robot 의 값으로 업데이트 한다.

1
2
3
4
5
6
7
8
9
void TaskControlComponent::resetNominalSystem(ROBOT & robot)
{
    _robotNom->setTtarget(robot.Ttarget().R(), robot.Ttarget().r());
    _robotNom->setTReference(robot.Ttarget().R(),robot.Ttarget().r());
    _robotNom->q() = robot.q();
    _robotNom->qdot() = robot.qdot();
    _robotNom->update();
    _robotNom->updateJacobian();
}

아래와 같이 readFTSensor() 함수를 정의하여 F/T 센서 값을 로봇의 베이스 좌표계와 일치하도록 변환 한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void TaskControlComponent::readFTSensor(ROBOT & robot)
{
    NRMKIndy::SharedData::getControlData(*indyShm, ctrData);
    TaskPosition _tposT;
    TaskVelocity _tvelT;
    LieGroup::Wrench _ftSensorTemp;
    _ftSensorTemp.setZero();

    robot.computeFK(_tposT, _tvelT);

    for (int i=0; i<6; i++) _ftSensorTemp[i] = ctrData.Fext[i];
    _ftSensorTemp = _ftSensorTemp - _ftBias;

    LieGroup::HTransform T_ft_target = robot.Ttarget().icascade(robot.Tft());
    LieGroup::Wrench _ftSensorTemp2 = T_ft_target.itransform(_ftSensorTemp);
    LieGroup::Wrench _ftSensorTransformed = LieGroup::Wrench(_tposT.R()*_ftSensorTemp2.f(), _ftSensorTemp2.n());

    for (int i=0; i<3; i++) _ftSensor[i] = _ftSensorTransformed[i+3];
    for (int i=0; i<3; i++) _ftSensor[i+3] = _ftSensorTransformed[i];
}