정의
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 |
경로 수정 모드
|
|
ref |
int |
None |
reference 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) |
프로그램 강제 종료 시 |
예제
# ---------------------------------------------------------------
# 실시간 경로 보정
# 예제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_