이전 버전(1.33.4)의 문서를 보고 있습니다. 이 버전은 계속 지원됩니다.
최신 버전 정보는 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);

제어권 및 서보 온 처리

샘플은 제어권 강제 요청과 서보 온 명령을 한 번씩 보내고, 시스템 버전과 라이브러리 버전을 출력한 다음, 1초마다 get_robot_state()를 폴링합니다.

if(true!=Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_FORCE_REQUEST)) {
  std::cout << "ManageAccessControl failure " << std::endl;
}
// std::cout << "ManageAccessControl done " << std::endl;
Drfl.set_robot_control(CONTROL_SERVO_ON);
cout << "System version: " << tSysVerion._szController << endl;
cout << "Library version: " << Drfl.get_library_version() << endl;
// Drfl.set_digital_output(GPIO_CTRLBOX_DIGITAL_INDEX_10, TRUE);
while ((Drfl.get_robot_state() != STATE_STANDBY) || !g_bHasControlAuthority)
{
  std::cout << "Drfl.get_robot_state() :  " <<Drfl.get_robot_state()<< std::endl;
  this_thread::sleep_for(std::chrono::milliseconds(1000));
}
System version: GF03050000
Library version: GL013300

이 루프에는 재시도 제한이 없습니다. 로봇 상태가 STATE_STANDBY이고 제어권 콜백이 g_bHasControlAuthority를 설정해야만 프로그램이 계속 진행됩니다.

로봇 모드 및 시스템 설정

서보 온 후 로봇 모드를 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)을 사용해 지정한 목표 포즈를 따라 위빙을 적용한 직선 용접을 실행합니다.