프로그램 메이커 (Program maker)
프로그램 메이커는 아래 Conty 에서의 프로그램 트리 (Program Tree) 를 Python 코드를 통해 작성하고, 실행할 수 있게 합니다. 이를 통해 복잡한 로직과 멀티 쓰레드, 비동기 프로그램 등을 통해 더 유연하고 복잡한 로봇 작업 공정을 구현할 수 있습니다. 또한 뉴로메카 Conty 에서 지원하는 비전 시스템 (IndyEye, Pickit) 외 상용 비전 시스템과의 통합도 수행할 수 있습니다.
프로그램 메이커는 IndyDCP 를 통해 명령을 전달합니다. 따라서 앞 장에서의 IndyDCP 와 동일한 방법으로 indy 객체를 생성합니다. 여기서 추가적으로 JsonProgramComponent 모듈을 임포트 하면 프로그램 메이커를 사용할 수 있습니다. 또한, 프로그램 메이커에서도 마찬가지로 프로그램 구동 전과 후에 로봇과의 연결 및 해제를 connect(), disconnect() 함수를 이용하여 정상적으로 실행해주어야 합니다.
| from indy_utils import indydcp_client as client
from indy_utils.indy_program_maker import JsonProgramComponent
robot_ip = "192.168.0.2" # 예시 STEP IP 주소
robot_name = "NRMK-Indy7" # IndyRP2의 경우 "NRMK-IndyRP2"
indy = client.IndyDCPClient(robot_ip, robot_name) # indy 객체 생성
|
| indy.connect() # 로봇 연결
indy.disconnect() # 연결 해제
|
프로그램 메이커 기본 사용법
기본 사용법은 프로그램 초기화 (prog 객체 생성), 프로그램 추가, 프로그램 작성 완료를 하게 되면 JSON 타입의 프로그램을 기술하는 문자열이 출력됩니다. 이 JSON 문자열을 확장 IndyDCP 의 명령을 이용하여 로봇에게 전달하면 로봇은 전달된 프로그램을 실행합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | indy.connect()
indy.set_joint_vel_level(3)
prog = JsonProgramComponent(policy=0, resume_time=2) # Init. prgoram
prog.add_move_home()
prog.add_move_zero()
prog.add_move_home()
prog_json = prog.program_done() # Program end
indy.set_and_start_json_program(prog_json) # Execute program
indy.disconnect()
|
1
2
3
4
5
6
7
8
9
10
11
12
13 | indy.connect()
prog = JsonProgramComponent(policy=0, resume_time=2) # Init. prgoram
prog.add_endtool_do(type=0, value=0)
prog.add_wait(1)
prog.add_endtool_do(type=0, value=1)
prog_json = prog.program_done()
indy.set_and_start_json_program(prog_json) # Execute program
indy.disconnect()
|
로봇 위치 정보 획득 및 교시점 저장
프로그램 트리 상 교시점을 추가하기 위해서는 먼저 해당 교시점에서 로봇의 현재 관절 위치 또는 작업 위치를 획득해야합니다. 로봇을 교시점으로 이동시킨 뒤, 확장 IndyDCP 의 명령을 통해 로봇 위치를 얻고 이를 교시점 파일에 저장합니다.
| print(indy.get_joint_pos()) # Joint angle (degree)
print(indy.get_task_pos()) # Task-space pose (meter and degree)
|
| file_name = 'test.json'
teach_config = indy.update_teaching_data(file_name, 'j_wp1', indy.get_joint_pos())
teach_config = indy.update_teaching_data(file_name, 't_wp1', indy.get_task_pos())
|
| teach_config = indy.update_teaching_data(file_name, 'j_wp2', indy.get_joint_pos())
teach_config = indy.update_teaching_data(file_name, 't_wp2', indy.get_task_pos())
|
| teach_config = indy.update_teaching_data(file_name, 'j_wp3', indy.get_joint_pos())
teach_config = indy.update_teaching_data(file_name, 't_wp3', indy.get_task_pos())
|
| teach_config = indy.update_teaching_data(file_name, 'j_wp4', indy.get_joint_pos())
teach_config = indy.update_teaching_data(file_name, 't_wp4', indy.get_task_pos())
|
로봇 모션 명령
교시한 경유점을 이용하여 프로그램 메이커를 통해 모션 프로그램을 작성할 수 있습니다. 모션 명령에서는 로봇의 속도 레벨과 블렌딩 옵션을 설정하며, 경유점 종류에 따라 조인트 무브와 태스크 무브를 선택할 수 있습니다.
| file_name = 'test.json'
teach_config = indy.load_teaching_data(file_name)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | indy.connect()
vel = 5 # Level 1-9
blend = 20 # Blendig radius 3-27 (degree)
j_wp1 = teach_config['j_wp1']
j_wp2 = teach_config['j_wp2']
j_wp3 = teach_config['j_wp3']
j_wp4 = teach_config['j_wp4']
prog = JsonProgramComponent(policy=0, resume_time=2)
prog.add_joint_move_to(j_wp1, vel=vel, blend=blend)
prog.add_joint_move_to(j_wp2, vel=vel, blend=blend)
prog.add_joint_move_to(j_wp3, vel=vel, blend=blend)
prog.add_joint_move_to(j_wp4, vel=vel, blend=blend)
prog_json = prog.program_done()
indy.set_and_start_json_program(prog_json)
indy.disconnect()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | indy.connect()
vel = 5 # Level 1-9
blend = 0.2 # Blending radius 0.02-0.2 (meter)
t_wp1 = teach_config['t_wp1']
t_wp2 = teach_config['t_wp2']
t_wp3 = teach_config['t_wp3']
t_wp4 = teach_config['t_wp4']
prog = JsonProgramComponent(policy=0, resume_time=2)
prog.add_task_move_to(t_wp1, vel=vel, blend=blend)
prog.add_task_move_to(t_wp2, vel=vel, blend=blend)
prog.add_task_move_to(t_wp3, vel=vel, blend=blend)
prog.add_task_move_to(t_wp4, vel=vel, blend=blend)
indy.set_and_start_json_program(prog.program_done())
indy.disconnect()
|
디지털 출력 및 툴 명령
IndyCB 의 디지털 출력 제어와 툴 명령은 아래 예제처럼 실행합니다. 단, 툴 명령은 사용자가 직접 Conty 를 통해 미리 툴 설정을 해두어야 사용 가능합니다.
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 | # Simple example
indy.connect()
vel = 5
j_blend = 20
t_blend = 0.2
prog = JsonProgramComponent(policy=0, resume_time=2)
prog.add_move_home()
prog.add_joint_move_to(j_wp1, vel=vel)
# Turns on digital output of port indices from 0 to 7 (0: OFF, 1: ON)
for idx in range(0, 8):
prog.add_digital_out(idx=idx, val=1)
# Wait for set time
prog.add_wait(1)
# Tool command of tool ID and its command
# Tool should be first set up in Conty Application (Setting - Tool)
# In Conty, add tool and set application (e.g. Pick & Place)
# Edit Hold and Release output and update the tool (Refer the Indy manual)
prog.add_endtool_do(type=0, value=0)
prog.add_wait(1)
prog.add_endtool_do(type=0, value=1)
prog.add_task_move_to(t_wp2, vel=vel, blend=t_blend)
prog.add_task_move_to(t_wp3, vel=vel, blend=t_blend)
prog.add_task_move_to(t_wp4, vel=vel, blend=t_blend)
# Turns off digital output of port indices from 0 to 7 (0: OFF, 1: ON)
for idx in range(0, 8):
prog.add_digital_out(idx=idx, val=0)
prog.add_stop() # Stop program
indy.set_and_start_json_program(prog.program_done())
indy.disconnect()
|
동기/비동기 모션
아래 예제의 wait_for_program_finish() 명령어를 통해 프로그램 간의 동기/비동기 모션을 구성할 수 있습니다. JSON 문자열로 전달된 프로그램 구동이 완료되기 전까지 대기함으로써 명령어 위치에 따라 동기/비동기 모션을 수행합니다.
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 | # Async and sync example
indy.connect()
vel = 5
blend = 20
# Program loop 2 times
for i in range(0,2):
# Syncronization with Second program after initail loop
# Wait until Second program is finished
indy.wait_for_program_finish()
# First program
prog = JsonProgramComponent(policy=0, resume_time=2)
prog.add_joint_move_to(j_wp2, vel=vel, blend=blend)
prog.add_move_home()
indy.set_and_start_json_program(prog.program_done())
# Asyncronization with Second program
# Wait until First program is finished, after then below program is executed
indy.wait_for_program_finish()
# Second program
prog = JsonProgramComponent(policy=0, resume_time=2)
prog.add_task_move_to(t_wp3, vel=vel, blend=blend)
prog.add_move_home()
indy.set_and_start_json_program(prog.program_done())
indy.disconnect()
|
멀티 쓰레드
파이썬 프로그램은 기본적으로 하나의 쓰레드에서 실행됩니다. 그러므로 프로그램 메이커에서 병렬적으로 여러 프로그램을 실행하기 위해서는 파이썬 threading 모듈을 이용해야 합니다. 이 모듈을 통해 멀티 쓰레드를 구성하여 메인 쓰레드와 서브 쓰레드를 구분하고 각 프로그램을 동시에 실행할 수 있습니다. 쓰레드 구성 시에는 전역 변수를 활용하여 각 프로그램의 실행 여부를 결정하며, 프로그램 종료 시에는 stop_motion() 명령어를 통해 구동 중인 로봇을 정지시킨 다음 전역 변수를 통해 각 쓰레드를 종료합니다.
아래 예제에서는 모션 프로그램 구동 중 (메인 쓰레드) 현재 로봇의 위치 정보를 얻고 (서브 쓰레드 1), 다른 장치와의 인터페이스를 통해 디지털 입력 정보를 확인하는 작업 (서브 쓰레드 2) 작업을 수행합니다.
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
59
60
61
62
63
64
65
66 | # Global variables
GLOBAL_INDICATOR = {'position': True, 'dio': True, 'program': True, 'terminated': False}
# Monitoring current robot position at every 1 sec
def monitor_robot_position():
global GLOBAL_INDICATOR
while GLOBAL_INDICATOR['position']:
if GLOBAL_INDICATOR['terminated']:
break
sleep(0.1)
j_pos = indy.get_joint_pos()
t_pos = indy.get_task_pos()
print("Joint angles: ", j_pos)
print("Task pose", t_pos)
# Monitoring digital input at every 1 sec
def monitor_dio():
global GLOBAL_INDICATOR
while GLOBAL_INDICATOR['dio']:
if GLOBAL_INDICATOR['terminated']:
break
sleep(0.1)
dis = indy.get_di()
btn1 = dis[1]
btn2 = dis[3]
btn3 = dis[5]
print("Button 1/2/3 = [{}, {}, {}]".format(btn1, btn2, btn3))
if btn1 == 1:
# TODO: implement an action when button1 on
pass
if btn2 == 1:
# TODO: implement an action when button2 on
pass
if btn3 == 1:
# TODO: implement an action when button3 on
pass
# Inifinity loop of robot program
def run_program():
global GLOBAL_INDICATOR
while GLOBAL_INDICATOR['program']:
if GLOBAL_INDICATOR['terminated']:
break
prog = JsonProgramComponent(policy=0, resume_time=2)
prog.add_move_home()
prog.add_move_zero()
indy.set_and_start_json_program(prog.program_done())
indy.wait_for_program_finish() # Wait for finishing above json program
GLOBAL_INDICATOR['position'] = False
GLOBAL_INDICATOR['dio'] = False
GLOBAL_INDICATOR['program'] = False
# Create each thread for 'run_program', 'monitor_robot_positio', and 'monitor_dio'
th1 = threading.Thread(target=run_program)
th2 = threading.Thread(target=monitor_robot_position)
th3 = threading.Thread(target=monitor_dio)
th3.start()
th2.start()
th1.start()
|
| # Stop above program
indy.stop_motion() # Stop current move
GLOBAL_INDICATOR['position'] = False
GLOBAL_INDICATOR['dio'] = False
GLOBAL_INDICATOR['program'] = False
GLOBAL_INDICATOR['terminated'] = True
|