최신 버전 정보는 1.33.5에서 확인하세요.
6.6.4 minimal_welding_sample.cpp
이 예제(4_minimal_welding_sample.cpp)는 두산로보틱스 API로 EtherNet/IP 기반 디지털 용접 공정을 설정하고 실행하는 방법을 보여 줍니다.
모니터링 / 알람 / 제어권 콜백 등록
TCP/IP 연결을 열고 제어권(Control Authority) 요청
서보 온 후 시스템/라이브러리 버전 출력
AUTONOMOUS 모드와 가상 로봇 시스템으로 전환
DRL 기반 용접 시퀀스 전체 실행
C++ API만으로 용접 인터페이스와 모션을 설정하고 위빙 용접 실행
이 예제의 경로는 다음과 같습니다.
API-DRFL/example/Linux_64/4_minimal_welding_sample.cpp
예제는 다음에서 확인하세요. Doosan Robotics API-DRFL GitHub
전체 설명 영상은 다음에서 확인하세요. Doosan Robotics Official YouTube
참고
이 샘플이 호출하는 app_weld_* 함수는 DRCF_VERSION이 2일 때(DRCF v2 컨트롤러)만 선언됩니다. 샘플은 DRCF_VERSION을 정의하지 않으며, DRFLEx.h의 기본값은 3입니다. DRCF 버전 호환성을 참고하세요.
설정 및 전역 변수
샘플은 전역 컨트롤러 객체, 여러 상태 플래그, 컨트롤러 IP 주소, DRL 용접 스크립트를 문자열로 반환하는 도우미 함수를 정의합니다.
#include <iostream>
#include <cstring>
#include <thread>
#include <chrono>
#include <termios.h>
#include <unistd.h>
#include "../../include/DRFLEx.h"
using namespace DRAFramework;
using namespace std::chrono_literals;
#undef NDEBUG
#include <assert.h>
CDRFLEx Drfl;
bool g_bHasControlAuthority = FALSE;
bool g_TpInitailizingComplted = FALSE;
bool moving = FALSE;
bool bAlterFlag = FALSE;
std::string IP_ADDR = "127.0.0.1";
std::string getWeldingSampleDrl();
콜백 핸들러들은 TP 초기화, 원점 복귀(Homing) 완료, 프로그램 정지, 모니터링 데이터에 반응합니다. 모니터링 데이터 핸들러와 OnProgramStopped는 본문이 비어 있습니다.
void OnTpInitializingCompleted() {
// Tp 초기화 이후 제어권 요청.
g_TpInitailizingComplted = TRUE;
Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_FORCE_REQUEST);
}
void OnHommingCompleted() {
// 50msec 이내 작업만 수행할 것.
cout << "homming completed" << endl;
}
void OnProgramStopped(const PROGRAM_STOP_CAUSE v) {
}
void OnMonitoringDataCB(const LPMONITORING_DATA pData) {
}
void OnMonitoringDataExCB(const LPMONITORING_DATA_EX pData) {
}
void OnMonitoringCtrlIOCB(const LPMONITORING_CTRLIO pData) {
}
void OnMonitoringCtrlIOExCB(const LPMONITORING_CTRLIO_EX2 pData) {
}
이 전역 변수와 콜백은 제어권을 추적하고, TP 초기화가 끝나거나 제어권을 잃으면 다시 요청하고, 연결이 끊기면 다시 연결하고, 알람, 팝업, TP 로그, 진행 상황, 사용자 입력 메시지를 출력합니다.
메인 루프
메인 루프는 초기화 후 실행되며 한 글자 명령을 기다립니다.
while (true) {
cout << "\ninput key : ";
char ch;
cin >> ch;
switch (ch) {
case '0':
{
// DRL 예제
Drfl.drl_start(ROBOT_SYSTEM_VIRTUAL, getWeldingSampleDrl());
}
break;
// ...
case '2':{
}
break;
default:
break;
}
}
Drfl.CloseConnection();
return 0;
0:getWeldingSampleDrl()이 반환하는 DRL 기반 용접 스크립트를 실행합니다.1: C++ 구조체로 용접 인터페이스를 직접 설정하고, 용접 애플리케이션 유형을 지정한amovel로 위빙 용접 모션을 실행합니다.2: 빈 케이스입니다.
콜백 등록
이 용접 샘플은 키를 눌러 콜백을 등록하는 최소 명령 예제와 달리, 연결을 열기 전 시작 시점에 모든 콜백을 등록합니다.
int main(int argc, char** argv) {
Drfl.set_on_homming_completed(OnHommingCompleted);
Drfl.set_on_monitoring_data(OnMonitoringDataCB);
Drfl.set_on_monitoring_data_ex(OnMonitoringDataExCB);
Drfl.set_on_monitoring_ctrl_io(OnMonitoringCtrlIOCB);
Drfl.set_on_monitoring_ctrl_io_ex(OnMonitoringCtrlIOExCB);
Drfl.set_on_monitoring_state(OnMonitoringStateCB);
Drfl.set_on_monitoring_access_control(OnMonitroingAccessControlCB);
Drfl.set_on_tp_initializing_completed(OnTpInitializingCompleted);
Drfl.set_on_log_alarm(OnLogAlarm);
Drfl.set_on_tp_popup(OnTpPopup);
Drfl.set_on_tp_log(OnTpLog);
Drfl.set_on_tp_progress(OnTpProgress);
Drfl.set_on_tp_get_user_input(OnTpGetuserInput);
Drfl.set_on_rt_monitoring_data(OnRTMonitoringData);
Drfl.set_on_program_stopped(OnProgramStopped);
Drfl.set_on_disconnected(OnDisConnected);
assert(Drfl.open_connection(IP_ADDR));
OnMonitoringStateCB는 본문이 비어 있으며, 상태 처리 코드는 주석 처리되어 있습니다.OnMonitroingAccessControlCB는 REQUEST에MANAGE_ACCESS_CONTROL_RESPONSE_YES로 응답하고, GRANT에서g_bHasControlAuthority를 설정하며, DENY나 LOSS에서 이를 해제합니다.OnTpInitializingCompleted는g_TpInitailizingComplted를 설정하고 즉시 제어권 강제 요청을 보냅니다.OnLogAlarm,OnTpPopup,OnTpLog,OnTpProgress,OnTpGetuserInput은 진단 정보를 콘솔에 출력합니다.OnDisConnected는open_connection()이 성공할 때까지 1초마다 다시 시도합니다.샘플은
assert.h를 포함하기 전에NDEBUG정의를 해제합니다. 따라서assert()안의open_connection()호출은 모든 빌드에서 실행되며, false를 반환하면 프로그램이 중단됩니다.
제어권 콜백은 컨트롤러가 제어권 상태를 바꿀 때마다 g_bHasControlAuthority를 갱신합니다. DENY나 LOSS가 오면 TP 초기화가 끝난 상태에서 강제 요청으로 제어권을 다시 요청합니다.
void OnMonitroingAccessControlCB(
const MONITORING_ACCESS_CONTROL eTrasnsitControl) {
// 50msec 이내 작업만 수행할 것.
std::cout << "OnMonitroingAccessControlCB !! " << int(eTrasnsitControl) << std::endl;
switch (eTrasnsitControl) {
case MONITORING_ACCESS_CONTROL_REQUEST:
assert(Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_RESPONSE_YES));
// Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_RESPONSE_YES);
break;
case MONITORING_ACCESS_CONTROL_GRANT:
g_bHasControlAuthority = TRUE;
break;
case MONITORING_ACCESS_CONTROL_DENY:
case MONITORING_ACCESS_CONTROL_LOSS:
g_bHasControlAuthority = FALSE;
if (g_TpInitailizingComplted) {
Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_FORCE_REQUEST);
}
break;
default:
break;
}
}
제어권 상태가 바뀌면 콜백은 MONITORING_ACCESS_CONTROL의 숫자 값(예: GRANT는 2)을 출력합니다.
OnMonitroingAccessControlCB !! 2
각 정수 값은 MONITORING_ACCESS_CONTROL에 정의된 열거형 상수(REQUEST, GRANT, DENY, LOSS 등)에 대응합니다.
로봇 상태 콜백은 등록되어 있지만 본문의 모든 문장이 주석 처리되어 있습니다.
void OnMonitoringStateCB(const ROBOT_STATE eState) {
// std::cout << "monitoring state cb " << std::endl;
// // 50msec 이내 작업만 수행할 것.
// switch ((unsigned char)eState) {
// ...
// default:
// break;
// }
// return;
}
컨트롤러의 알람 정보는 로그 알람 콜백으로 보고됩니다.
void OnLogAlarm(LPLOG_ALARM tLog) {
cout << "Alarm Info: "
<< "group(" << (unsigned int)tLog->_iGroup << "), index("
<< tLog->_iIndex << "), param(" << tLog->_szParam[0] << "), param("
<< tLog->_szParam[1] << "), param(" << tLog->_szParam[2] << ")" << endl;
}
출력 예:
Alarm Info: group(1), index(1037), param(), param(), param()
각 알람은 그룹 코드와 인덱스 코드, 그리고 최대 3개의 문자열 매개변수와 함께 기록됩니다. 이 값을 컨트롤러 매뉴얼의 알람 표와 대조하면 정확한 원인(예: 서보 온 실패, 한계 위반, 용접 인터페이스 오류)을 확인할 수 있습니다.
컨트롤러 연결
콜백을 등록한 뒤 TCP/IP로 컨트롤러 연결을 열고, 모니터링 버전을 최신으로 설정하고, 시스템 버전을 읽습니다.
assert(Drfl.open_connection(IP_ADDR));
Drfl.setup_monitoring_version(1); // (0 -> 이전 버전, 1 -> 최신 버전)
SYSTEM_VERSION tSysVerion = {'\0',};
Drfl.get_system_version(&tSysVerion);
로봇 모드 및 시스템 설정
서보 온 후 로봇 모드를 AUTONOMOUS로, 로봇 시스템을 가상 로봇으로 설정합니다.
std::cout << "set robot mode ret : " << Drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS) << std::endl;
Drfl.set_robot_system(ROBOT_SYSTEM_VIRTUAL);
set robot mode ret : 1
DRL 기반 용접 스크립트
0 키를 누르면 getWeldingSampleDrl()이 반환하는 독립 실행형 DRL 스크립트를 가상 로봇 시스템에서 실행합니다.
case '0':
{
// DRL 예제
Drfl.drl_start(ROBOT_SYSTEM_VIRTUAL, getWeldingSampleDrl());
}
break;
DRL 문자열은 용접 매개변수와 모션을 설정합니다.
std::string getWeldingSampleDrl() {
return R"(
set_singular_handling(DR_AVOID)
set_velj(60.0)
set_accj(100.0)
set_velx(250.0, 80.625, DR_OFF)
set_accx(1000.0, 322.5)
dry_run = 1 # (flag_dry_run: 실용접(0) / 모의용접(1))
weld_con_adj = 1 # 용접 조건 조정 여부 설정 (app_weld_set_weld_cond_digital에서 설정한 조건 적용(1) / 조정값 적용(0))
arc_sensing_test = False # Arc Sensing 시험을 위한 설정 (True: Arc Sensing 관련 DRL 실행 / False: Arc Sensing 관련 DRL 패스)
app_weld_set_interface_eip_r2m_process(welding_start=[1,0,0,0,4,0,1,0,0], robot_ready=[1,0,0,0,5,0,1,0,0], error_reset=[1,0,0,1,4,0,1,0,0])
app_weld_set_interface_eip_r2m_mode(welding_mode=[1,1,0,0,0,2,2,0,1], s_2t=[0,0,0,0,0,0,0,0,0], pulse_mode=[0,0,0,0,0,0,0,0,0],wm_opt1=[0,0,0,0,0,0,0,0,0])
app_weld_set_interface_eip_r2m_test(gas_test=[1,0,0,0,6,0,1,0,0], inching_plus=[1,0,0,1,0,0,1,0,0], inching_minus=[1,0,0,1,2,0,1,0,0], blow_out_torch=[0,0,0,0,0,0,0,0,0], simulation=[1,0,0,1,7,0,1,0,0], ts_opt1=[0,0,0,0,0,0,0,0,0], ts_opt2=[0,0,0,0,0,0,0,0,0])
app_weld_set_interface_eip_r2m_condition(job_num=[1,1,0,3,0,4,8,1,255], synergic_id=[1,1,0,2,0,3,4,0,15], r_wire_feed_speed=[0,0,0,0,0,0,0,0,0], voltage_correct=[0,0,0,0,0,0,0,0,0], dynamic_correct=[0,0,0,0,0,0,0,0,0])
app_weld_set_interface_eip_r2m_option(opt1=[0,0,0,0,0,0,0,0,0], opt2=[0,0,0,0,0,0,0,0,0], opt3=[0,0,0,0,0,0,0,0,0], opt4=[0,0,0,0,0,0,0,0,0], opt5=[0,0,0,0,0,0,0,0,0], opt6=[0,0,0,0,0,0,0,0,0], opt7=[0,0,0,0,0,0,0,0,0], opt8=[0,0,0,0,0,0,0,0,0], opt9=[0,0,0,0,0,0,0,0,0], opt10=[0,0,0,0,0,0,0,0,0], opt11=[0,0,0,0,0,0,0,0,0], opt12=[0,0,0,0,0,0,0,0,0], opt13=[0,0,0,0,0,0,0,0,0], opt14=[0,0,0,0,0,0,0,0,0], opt15=[0,0,0,0,0,0,0,0,0])
app_weld_set_interface_eip_m2r_process(current_flow=[1,0,0,0,0,0,1,0,0], process_active=[1,0,0,0,6,0,1,0,0], main_current=[1,0,0,0,5,0,1,0,0], machine_ready=[1,0,0,0,1,0,1,0,0], comm_ready=[0,0,0,0,0,0,0,0,0])
app_weld_set_interface_eip_m2r_monitoring(welding_voltage=[1,2,1,6,0,6,16,0.0,100.0], welding_current=[1,2,1,8,0,6,16,0.0,1000.0], wire_feed_speed=[1,2,1,10,0,6,16,0.0,40.0], wire_stick=[0,0,0,0,0,0,0,0,0], error=[1,0,0,2,0,4,1,0,0], error_num=[1,0,0,1,0,4,8,0,0])
app_weld_set_interface_eip_m2r_other(opt1=[0,0,0,0,0,0,0,0,0], opt2=[0,0,0,0,0,0,0,0,0], opt3=[0,0,0,0,0,0,0,0,0], opt4=[0,0,0,0,0,0,0,0,0], opt5=[0,0,0,0,0,0,0,0,0], opt6=[0,0,0,0,0,0,0,0,0], opt7=[0,0,0,0,0,0,0,0,0], opt8=[0,0,0,0,0,0,0,0,0], opt9=[0,0,0,0,0,0,0,0,0], opt10=[0,0,0,0,0,0,0,0,0])
wait(0.5)
app_weld_enable_digital()
wait(1)
app_weld_set_weld_cond_digital(flag_dry_run=dry_run, vel_target=3.0000, vel_min=0.0000, vel_max=100.0000, welding_mode=1, s_2t=0, pulse_mode=0, wm_opt1=0, simulation=0, ts_opt1=0, ts_opt2=0, job_num=4, synergic_id=1, r_wire_feed_speed=0, voltage_correct=0, dynamic_correct=0, r_opt1=0, r_opt2=0, r_opt3=0, r_opt4=0, r_opt5=0, r_opt6=0, r_opt7=0, r_opt8=0, r_opt9=0, r_opt10=0, r_opt11=0, r_opt12=0, r_opt13=0, r_opt14=0, r_opt15=0)
app_weld_weave_cond_trapezoidal(wv_offset=[0.000,0.000], wv_ang=0.00, wv_param=[0.000,3.0,0.000,-3.0,0.30,0.10,0.20,0.30,0.10,0.20])
ready_weld_pos = posx(-77.41, -640.26, 119.09, 54.84, 166.79, -48.59 )
movel(ready_weld_pos, radius=0.00, ref=0, mod=DR_MV_MOD_ABS, ra=DR_MV_RA_DUPLICATE, app_type=DR_MV_APP_NONE)
wait(1)
#if weld_con_adj == 1:
# app_weld_adj_welding_cond_digital(flag_reset=weld_con_adj)
#elif weld_con_adj == 0:
# app_weld_adj_welding_cond_digital(flag_reset=weld_con_adj, wv_width_ratio=0.5)
end_weld_pos = posx(306.16,-617.1,173.77,54.18,166.75,-49.15)
amovel(end_weld_pos, v=3.0000, a=70.0000, ref=0, mod=DR_MV_MOD_ABS, ra=DR_MV_RA_DUPLICATE, app_type=DR_MV_APP_WELD)
tp_log("Start welding motion")
mwait(1)
app_weld_disable_digital()
wait(1))";
};
용접 제어와 모니터링을 위한 EtherNet/IP 매핑(R2M / M2R)을 설정합니다.
디지털 용접 인터페이스를 활성화합니다(
app_weld_enable_digital()).용접 조건(속도, 모드, Job 번호, 시너직 ID 등)을 설정합니다.
토치 경로에 사다리꼴 위빙을 설정합니다.
movel로 준비 포즈로 이동한 뒤,amovel(..., app_type=DR_MV_APP_WELD)로 시작 포즈에서 끝 포즈까지 용접합니다.모션이 끝나면 용접을 비활성화합니다.
기본값 dry_run = 1은 시뮬레이션 용접(실제 아크 없음)을 합니다. 실제로 용접하려면 dry_run = 0으로 설정하고 용접기와 EtherNet/IP 매핑이 올바르게 설정되었는지 확인해야 합니다.
C++ 기반 용접 인터페이스 설정
1 키는 DRL 용접 명령을 직접 사용하지 않고 C++만으로 같은 용접 인터페이스를 설정하고 용접 모션을 실행하는 방법을 보여 줍니다.
case '1':
{
// 준비 위치를 설정하고 목표 용접 위치 조회
float ready_weld_posj[6] = {85.8353, -53.5421, -61.888, 6.7839, -76.0373, -19.6588};
Drfl.movej(ready_weld_posj, 50, 50);
LPROBOT_TASK_POSE posx = Drfl.get_current_posx();
float target_wel_posx[6];
for(int i=0; i<NUM_JOINT; i++) {
target_wel_posx[i] = posx->_fTargetPos[i];
}
target_wel_posx[0] += 200;
// DRL 예제 코드를 참고하세요.
Drfl.set_singularity_handling(SINGULARITY_AVOIDANCE_AVOID);
unsigned char dry_run = 1; // (flag_dry_run: 실용접(0) / 모의용접(1))
unsigned char weld_con_adj = 1; // 용접 조건 조정 여부 설정(app_weld_set_weld_cond_analog에서 설정한 조건 적용(1) / 조정값 적용(0))
float velx[2] = { 250.0, 80.625 };
float accx[2] = { 1000., 322.5 };
float g_op_speed = 100;
Drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS);
// 사전 TCP 설정 : EWM 용접기 기준 Welding_Torch(0, 200, 300, 90, 67.5, 0)
if (Drfl.get_tcp() != "Welding Torch")
Drfl.set_tcp("Welding Torch");
this_thread::sleep_for(200ms);
// Drfl.change_operation_speed(g_op_speed);
CONFIG_DIGITAL_WELDING_INTERFACE_PROCESS r2m_process_data = {
// 필드 순서: _bEnable / _nDataType / _nPositionalNumber / _fMinData / _fMaxData / _nByteOffset / _nBitOffset / _nComnDataType / _nMaxDigitSize
{1, 0, 0, 0, 0, 0, 4, 0, 1}, // _tWeldingStart 신호
{ 1, 0, 0, 0, 0, 0, 5, 0, 1}, // _tRobotReady 신호
{1, 0, 0, 0, 0, 1, 4, 0, 1} // _tErrorReset 신호
};
Drfl.app_weld_set_interface_eip_r2m_process(r2m_process_data);
// ...
Drfl.app_weld_set_interface_eip_r2m_mode(r2m_mode_data);
// ...
Drfl.app_weld_set_interface_eip_r2m_test(r2m_test_data);
// ...
Drfl.app_weld_set_interface_eip_r2m_condition(r2m_condition_data);
// ...
Drfl.app_weld_set_interface_eip_r2m_option(r2m_option_data);
// ...
Drfl.app_weld_set_interface_eip_m2r_process2(m2r_process2_data);
// ...
Drfl.app_weld_set_interface_eip_m2r_monitoring(m2r_monitoring_data);
// ...
Drfl.app_weld_set_interface_eip_m2r_other(m2r_other_data);
this_thread::sleep_for(500ms);
Drfl.app_weld_enable_digital(1);
this_thread::sleep_for(500ms);
CONFIG_DIGITAL_WELDING_CONDITION weld_condition = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
weld_condition._cVirtualWelding = dry_run;
weld_condition._fTargetVel = 3.f;
weld_condition._fMaxVelLimit = 100.f;
weld_condition._nWeldingMode = 1;
weld_condition._nPulseMode = 0;
weld_condition._nJobNumber = 4;
weld_condition._nSynergicID = 1;
Drfl.app_weld_set_weld_cond_digital(weld_condition);
// ...
this_thread::sleep_for(1000ms);
// ...
CONFIG_TRAPEZOID_WEAVING_SETTING weaving_trap = { //Weaving_trap 설정
0.f,
0.f,
0.f,
{ 0.f, 3.f },
{ 0.f, -3.f },
0.3f,
0.1f,
0.2f,
0.3f,
0.1f,
0.2f
};
// ...
Drfl.app_weld_weave_cond_trapezoidal(weaving_trap);
// ...
float welding_velx[2] = { 3,3 };
float welding_accx[2] = { 70,70 };
Drfl.amovel(target_wel_posx, welding_velx, welding_accx, 0, MOVE_MODE_ABSOLUTE, MOVE_REFERENCE_BASE, BLENDING_SPEED_TYPE_DUPLICATE, DR_MV_APP_WELD);
Drfl.mwait();
break;
}
작업 공간 포즈 오프셋: 용접 목표는 준비 조인트 위치로 이동한 뒤 읽은 작업 공간 포즈에 X 방향으로 200을 더한 값입니다.
디지털 용접 인터페이스 매핑은 여러
CONFIG_DIGITAL_WELDING_INTERFACE_*구조체로 설정합니다. 생략된 줄에서는 각 구조체를r2m_process_data와 같은 방식으로 정의합니다.위빙 매개변수(오프셋, 진폭, 타이밍)는
CONFIG_TRAPEZOID_WEAVING_SETTING으로 설정하고app_weld_weave_cond_trapezoidal로 적용합니다.마지막 모션은
amovel(..., DR_MV_APP_WELD)을 사용해 지정한 목표 포즈를 따라 위빙을 적용한 직선 용접을 실행합니다.