3.7.0 3.6.0 3.5.0 3.4.0 3.3.0 3.2.2 3.2.1 3.2.0 Korean English Chinese Czech Dutch French German Hungarian Italian Japanese Polish Portuguese Spanish
3.7.0 3.6.0 3.5.0 3.4.0 3.3.0 3.2.2 3.2.1 3.2.0 Korean English Chinese Czech Dutch French German Hungarian Italian Japanese Polish Portuguese Spanish

enable_alter_motion()

정의

enable_alter_motion(n,mode,ref,limit_dPOS,limit_dPOS_per)

기능

경로 수정 기능을 활성화합니다. 경로 생성의 단위 주기는 10msec이며 입력 인자 n을 설정하여 경로 생성 주기(n*10msec)를 변경할 수 있습니다. 입력 인자 mode를 통해 alter_motion()의 입력값의 의미를 2가지 모드(누적량 모드, 증분량 모드) 중 하나로 선택하여 사용할 수 있습니다.

  • 누적량 모드 : 현재의 모션 경로에 대한 절대적 증분 위치/자세만큼 경로 수정량이 반영됩니다.

  • 증분량 모드 : 현재의 절대적 증분 위치/자세에 입력된 증분 위치/자세만큼 경로 수정량이 추가되어 반영됩니다.

입력 인자 ref를 통해 기준 좌표계를 설정할 수 있습니다. 입력 인자 limit_dPOS, limit_dPOS_per를 통해 각각 누적량, 증분량의 한계치를 설정할 수 있습니다. 한계치를 벗어나는 위치 값의 한계치에 수렴된 값으로 경로 수정량이 재조정됩니다.

인수

인수명

자료형

기본값

설명

n

int

None

경로 생성 주기 (Tn = n × 10ms)

유효 범위 : 1 ~ 254 (10 ~ 2540ms)

범위를 벗어나면 경고가 발생하고 경로 수정 기능이 활성화되지 않습니다

mode

int

None

경로 수정 모드

  • DR_DPOS : 누적량

  • DR_DVEL : 증분량

ref

int

None

reference coordinate

  • DR_BASE: base coordinate

  • DR_WORLD: world coordinate

  • DR_TOOL: tool coordinate

  • user coordinate: 사용자 정의

limit_dPOS

list(float[2])

None

첫번째 값 : 이동량 제한 값[mm]

두번째 값 : 회전량 제한 값[deg]

limit_dPOS_per

list(float[2])

None

첫번째 값 : 이동량 제한 값[mm]

두번째 값 : 회전량 제한 값[deg]

알아두기

  • alter_motion()은 사용자 thread 내에서만 동작합니다.

  • ref가 None인 경우, _g_coord 적용(_g_coord 초기값은 DR_BASE이며, set_ref_coord 명령에 의해 설정 가능)

  • limit_dPOS, limit_dPOS_per를 설정하지 않을 시 누적량, 증분량의 한계를 제한하지 않습니다.

  • n × 10ms 는 실측한 최대 공급 주기 이상이어야 합니다. 산정 방법은 아래 사용 조건을 참고하십시오.

사용 조건

동작 원리

경로 수정은 보정 1 건마다 정지 → 정지 궤적을 경로 생성 주기 Tn = n × 10ms 로 계획하는 방식으로 동작합니다. 제어기는 Tn 당 보정을 한 건만 받아들입니다. 수용 창은 새 궤적이 시작하는 제어 주기에 열렸다가 그 뒤 가장 먼저 도착한 한 건을 받아들이는 즉시 닫히고, 같은 Tn 안에 이어서 도착한 값은 저장되지 않고 알람도 없이 버려집니다. 즉 최신값으로 덮어쓰는 방식이 아니라 선착순이며, 한 Tn 에 2 건이 들어오면 뒤에 온 값(더 최신 보정)이 버려집니다. 진행 중인 궤적은 새 보정이 도착해도 중단되지 않으며, 다음 궤적이 현재 궤적의 감속 구간에서 이어받아 합산됩니다. 따라서 Tn 마다 보정이 한 건씩 제때 도착하면 정지 없이 연결된 움직임이 되고, 제때 도착하지 못하면 궤적이 먼저 끝나 멈춰 선 채 다음 보정을 기다립니다.

즉 n 은 제어기가 보정 한 건을 소화하는 데 쓰는 시간을 정하는 값이므로, DRL 이 보정을 실제로 전달할 수 있는 주기에 맞춰 정해야 합니다. 순서는 다음과 같습니다.

① 보정을 전달할 수 있는 주기를 먼저 확인합니다

보정값을 만들어 내는 주기(센서 · 비전 프레임 · 상위 제어 주기)는 외부 요인으로 이미 정해져 있는 경우가 많지만, 그 값이 곧 제어기에 전달되는 주기는 아닙니다. alter_motion() 이 실제로 호출되는 간격 — 이 문서에서는 이를 공급 주기라고 부릅니다 — 은 여기에 DRL thread 가 한 바퀴를 도는 데 드는 시간, 즉 보정 계산, alter_motion() 호출 비용, 다른 thread 와의 경합, 지터가 더해진 값입니다. 실측 사례에서 wait(0.03) 으로 30ms 를 의도한 thread 의 실제 공급 주기가 단독 36ms, 모니터 thread 와 함께 돌 때 73ms 였습니다.

그러므로 공급 주기는 값을 가정하지 말고 실제로 쓸 모션 · thread 구성 그대로 돌려서 실측하십시오. 조건은 매 주기 성립해야 하므로 평균이 아니라 최대값을 씁니다. 실측 전에 실행라인 표시 기능을 OFF 해야 공급 주기가 코드 양에 흔들리지 않습니다(아래 1). 실측 절차는 아래 2 를 참고하십시오.

② 확인한 주기에 맞춰 n 을 정합니다

  • 하한(필수) — n 은 최대 공급 주기 ÷ 10ms 를 올림한 값 이상이어야 합니다. 두 모드 모두 해당합니다.

  • 누적량 모드(DR_DPOS) — 하한 위에서는 n 이 클수록 조용해지므로(공급 주기의 1.5 배에서 무보정 대비 진동 1.6 배, 2.5 배에서 1.3 배) 지연 예산이 허용하는 최대로 잡으십시오. 대가는 보정 적용 지연(약 2 × Tn) 뿐이고 정확도 손실은 없습니다.

  • 증분량 모드(DR_DVEL) — n 은 하한에 가능한 한 붙여 잡으십시오. 주기가 흔들리는 구간에서는 증분이 간헐적으로 누락될 수 있으므로, 센서가 보정이 반영된 뒤 남은 오차를 계속 재는 폐루프로 구성해 누락분이 다음 측정에서 보정되도록 하십시오(아래 4). 폐루프로 만들기 어려우면 누적량 모드를 쓰십시오.

  • n 의 유효 범위는 1 ~ 254(10 ~ 2540ms) 입니다. 범위를 벗어나면 경고가 발생하고 경로 수정 기능이 활성화되지 않습니다.

실측 공급 주기  평균 72ms / 최대 74ms

  하한 : n x 10ms >= 74ms  ->  n >= 8

  DR_DPOS : 지연 예산 250ms  ->  n = 11 (110ms, 지연 약 220ms)
            예산이 크면 n = 18 (180ms) 쪽이 더 조용합니다
  DR_DVEL : n = 8 (80ms). 하한에 붙여 잡고, 간헐적 누락을 보정하도록
            폐루프로 씁니다

③ 하한을 밑돌면 이런 문제가 생깁니다

공급이 늦은 주기마다 궤적이 먼저 끝나 보정이 멈춰 선 채 대기합니다. 변위 자체는 보존되지만 이동에 쓸 시간이 줄어 피크 속도가 1/k 배, 피크 가속도가 1/k2(k = Tn ÷ 공급 주기)로 커지고, 이것이 진동 · 소음이 됩니다. 가장 흔한 원인은 실측 생략입니다 — wait() 값을 그대로 공급 주기로 가정한 사례에서 무보정 대비 진동이 10 배가 되었습니다.

1. 실행라인 표시 기능을 OFF 하십시오

실행라인 표시 기능을 OFF 하는 것은 보정 위치를 전달하는 과정에서 같은 thread 의 다른 DRL 줄 때문에 생기는 시간 지연을 해소하기 위한 것입니다. 이 기능이 ON 이면 DRL 의 모든 줄마다 TP 로 실행 라인이 전송되고, 보정 thread 에서는 그 비용이 보정 위치를 계산해 alter_motion() 으로 전달하기까지의 시간에 그대로 쌓입니다. 그러면 공급 주기가 wait() 값이 아니라 그 thread 가 함께 실행하는 다른 DRL 줄의 수로 결정되어, 보정 로직을 몇 줄 고치기만 해도 주기가 달라지고 ① 에서 확인한 공급 주기가 무너집니다.

drl_report_line(0)      # 보정 주기를 코드량과 무관하게 만듭니다

실측 사례에서 보정 파형 계산 12 줄이 11.2ms 를 차지했고, 실행라인 표시 기능을 OFF 하자 그 차이가 1.5ms 이하로 사라졌습니다. OFF 한 뒤에는 공급 주기가 wait() 값에 거의 일정한 잔여만 더한 값이 되어 wait() 로 조절할 수 있습니다.

  • OFF 한 구간에서는 라인 별 실행 시간 표시, 변수 모니터링, 시스템 변수 업데이트, Debug 모드의 Step by Step · Brake Point 가 동작하지 않습니다. 개발 중에는 ON 으로 두었다가 운용 시 OFF 하십시오.

  • ON/OFF 를 바꾸면 공급 주기가 달라집니다. 바꿨으면 아래 실측과 n 산정을 반드시 다시 하십시오.

  • OFF 한 구간에서는 예외가 except Exception 으로 잡힙니다 (ON 인 구간에서는 except KeyboardInterrupt).

2. 공급 주기를 실측하십시오

실측은 다음 조건에서 하십시오.

  • 로봇이 멈춰 있으면 모션 생성 부하가 빠져 짧게 나옵니다. 기저 모션이 도는 상태에서 재십시오.

  • 보정값 계산을 건너뛰면 그만큼 짧게 나옵니다. 보정 배율만 0 으로 두고 계산과 호출은 그대로 돌리십시오.

  • thread 를 추가 · 삭제하거나 보정 로직을 고쳤으면 실측과 n 산정을 다시 하십시오.

  • 루프 안에서 get_current_posx() · get_desired_posx() 를 호출하면 컨트롤러 왕복이 발생해 주기가 길어집니다. 관절값으로 대체할 수 있으면 get_desired_posj() 계열을 쓰십시오.

3. 공급 주기를 인위적으로 줄이지 마십시오

공급 주기를 줄이면 위치 추종 오차는 줄지만, 같은 변위를 더 짧은 시간에 만들어야 하므로 가속도 리플이 커집니다. 소음 · 진동이 문제인 경우 주기를 줄이는 것은 역효과입니다.

주기는 짧게가 아니라 일정하게 유지하십시오. 지터가 크면 하한을 매 주기 넘나듭니다.

4. 증분량 모드(DR_DVEL)는 폐루프에서만 쓰십시오

외부 센서가 보정이 반영된 뒤 남은 오차를 계속 측정하는 폐루프라면, 받아들여지지 않은 증분만큼 오차가 줄지 않으므로 다음 측정에서 자동으로 재시도됩니다. 반대로 시간이나 엔코더로 적산한 증분(예: v × dt)을 보내는 개루프에서는 버려진 증분이 영구 손실됩니다. 알람도 나지 않고 보정량만 조용히 모자랍니다.

개루프라면 DRL 이 누계를 들고 누적량 모드로 보내십시오.

# 개루프 증분 소스를 누적량 모드로 보내는 방법
var_total = var_total + var_delta
alter_motion(posx(0, var_total, 0, 0, 0, 0))    # DR_DPOS

공구 좌표계 기준 증분이 꼭 필요한 경우가 아니면 누적량 모드를 권장합니다.

예외

예외

설명

DR_Error (DR_ERROR_TYPE)

인수들의 데이터형 오류 시

DR_Error (DR_ERROR_VALUE)

인수의 값이 유효하지 않을 시

DR_Error (DR_ERROR_RUNTIME)

C Extension 모듈 에러 발생 시

DR_Error (DR_ERROR_STOP)

프로그램 강제 종료 시

예제

Python
# ---------------------------------------------------------------
# 실시간 경로 보정
#   예제1 누적량 모드(DR_DPOS) - 절대 오프셋을 아는 경우
#   예제2 증분량 모드(DR_DVEL) - 외부 센서로 남은 오차를 재는 경우
# ---------------------------------------------------------------
drl_report_line(0)    # 보정 주기를 코드량과 무관하게 만듭니다

DT      = 0.05        # 공급 주기 기준값 [sec]. 실제 주기는 이보다 큽니다
AMP     = 5.0         # 예제1 보정 진폭 [mm]
FREQ    = 0.25        # 예제1 보정 주파수 [Hz]
GAIN    = 0.5         # 예제2 보정 이득 (0 < GAIN <= 1)
LAT_MAX = 0.25        # 예제1 허용 보정 지연 [sec]. 지연 = 약 2 x n x 10ms
LIM_POS = [50, 90]    # limit_dPOS     : 누적 보정 한계 [mm, deg]
LIM_INC = [10, 10]    # limit_dPOS_per : 1 주기 증분 한계 [mm, deg]

g_mode  = 0           # 0 = 누적량 / 1 = 증분량
g_gain  = 0.0         # 보정 배율. 0 이면 보정이 걸리지 않습니다
g_t0    = 0.0
g_calls = 0
g_prev  = 0.0
g_max   = 0.0         # 실측 공급 주기의 최대값 [sec]

def read_path_error():
    # 외부 센서(비전 등)가 측정한 "현재 남은 경로 오차" [mm] 를 반환하도록
    # 교체하십시오. 보정이 반영된 뒤의 잔여 오차여야 합니다.
    return 0.0

# thread_run(loop=True) 는 함수가 반환할 때마다 다시 호출하므로 함수 안에
# while 루프를 두지 않습니다. while 을 두면 thread_stop 이 걸리지 않습니다.
def alter_thread():
    global g_calls, g_prev, g_max
    if g_mode == 0:
        var_y = g_gain * AMP * sin(6.28318 * FREQ * (time.time() - g_t0))
        alter_motion(posx(0, var_y, 0, 0, 0, 0))       # 지금 있어야 할 오프셋
    else:
        var_e = g_gain * GAIN * read_path_error()
        alter_motion(posx(0, var_e, 0, 0, 0, 0))       # 남은 오차의 일부만큼 더
    # 호출 간격을 매 주기 재서 최대값을 남깁니다.
    var_now = time.time()
    if g_prev > 0.0:
        if var_now - g_prev > g_max:
            g_max = var_now - g_prev
    g_prev  = var_now
    g_calls = g_calls + 1
    wait(DT)

# 기저 모션 양단 자세 - 측정값(get_current_posx)이 아니라 지령값을 씁니다.
# 측정 자세에는 추종 잔차가 섞여 있어 목표로 되돌려 쓰면 오차가 누적됩니다.
movej(posj(30, 45, 45, 0, 90, 0), vel=60, acc=60)
X1 = get_desired_posx(DR_BASE)
movej(posj(-30, 45, 45, 0, 90, 0), vel=60, acc=60)
X2 = get_desired_posx(DR_BASE)

# --- 공급 주기 실측 -------------------------------------------
# 실제로 쓸 모션과 thread 구성 그대로 한 번 돌립니다.
# g_gain = 0.0 이라 보정량은 0 이지만 계산과 호출은 그대로 일어나므로,
# 로봇 궤적은 변하지 않으면서 실사용과 같은 부하로 측정됩니다.
movel(X1, vel=30, acc=60)
mwait(0)
enable_alter_motion(n=20, mode=DR_DPOS, ref=DR_BASE,
                    limit_dPOS=LIM_POS, limit_dPOS_per=LIM_INC)
g_mode  = 0
g_gain  = 0.0
g_calls = 0
g_prev  = 0.0
g_max   = 0.0
g_t0    = time.time()
th_id   = thread_run(alter_thread, loop=True)

movel(X2, vel=30, acc=60)

thread_stop(th_id)
disable_alter_motion()

var_supply = DT                                # 실측 실패 시 대비값
if g_calls > 5:
    var_supply = g_max                         # 최대 공급 주기 [sec] - n 산정 기준
tp_log("supply max = {0} ms".format(int(var_supply * 100000) / 100.0))

# 하한 : 최대 공급 주기 / 10ms 를 올림한 값
N_MIN = int(var_supply * 100)
if var_supply * 100 > N_MIN:
    N_MIN = N_MIN + 1
if N_MIN < 1:
    N_MIN = 1

# --- 예제 1. 누적량 모드 (DR_DPOS) ----------------------------
# 매 전송이 완결된 오프셋입니다. 제어기가 한 건을 받지 못해도 다음 전송이
# 그대로 복구하므로 손실이 없습니다.
# 지연 예산이 허용하는 최대로 잡습니다.
N_DPOS = int(LAT_MAX * 100 / 2)
if N_DPOS < N_MIN:
    N_DPOS = N_MIN                             # 하한이 우선입니다

movel(X1, vel=30, acc=60)
mwait(0)                                       # 정지 상태에서 보정 시작
enable_alter_motion(n=N_DPOS, mode=DR_DPOS, ref=DR_BASE,
                    limit_dPOS=LIM_POS, limit_dPOS_per=LIM_INC)
g_mode = 0
g_gain = 1.0
g_t0   = time.time()
th_id  = thread_run(alter_thread, loop=True)

movel(X2, vel=30, acc=60)                      # 보정이 중첩된 기저 모션

# 종료 순서 : 보정 정지 -> thread_stop -> disable_alter_motion
# 비활성화 후에도 thread 가 살아 있으면 alter_motion() 호출이 남아 에러가 납니다.
g_gain = 0.0
thread_stop(th_id)
disable_alter_motion()

# --- 예제 2. 증분량 모드 (DR_DVEL) ----------------------------
# 센서가 남은 오차를 계속 재는 폐루프에서만 쓰십시오. 제어기가 받지 못한
# 증분은 되돌아오지 않지만, 폐루프면 다음 측정에서 자동으로 재시도됩니다.
# n 은 하한에 붙여 잡습니다. 간헐적으로 누락되는 증분은 폐루프가 다음
# 측정에서 보정합니다.
# read_path_error() 가 폐루프가 아니라면 이 예제 대신 예제 1 을 쓰십시오.
N_DVEL = N_MIN

movel(X1, vel=30, acc=60)
mwait(0)
enable_alter_motion(n=N_DVEL, mode=DR_DVEL, ref=DR_BASE,
                    limit_dPOS=LIM_POS, limit_dPOS_per=LIM_INC)
g_mode = 1
g_gain = 1.0
g_t0   = time.time()
th_id  = thread_run(alter_thread, loop=True)

movel(X2, vel=30, acc=60)                      # 센서 보정이 중첩된 기저 모션

g_gain = 0.0
thread_stop(th_id)
disable_alter_motion()

관련 명령어

Keyword

enable / alter / motion / enable_ / enable_alter / enable_alter_