6.6.4 minimal_welding_sample.cpp

This example (4_minimal_welding_sample.cpp) demonstrates how to configure and execute a digital welding process over EtherNet/IP using the Doosan Robotics API:

  • register monitoring / alarm / access control callbacks

  • open a TCP/IP connection and request control authority

  • turn Servo On and print system/library versions

  • switch to AUTONOMOUS mode and the virtual robot system

  • execute a full DRL-based welding sequence

  • configure the welding interface and motion purely via the C++ API and run a weave weld

This example is located in the following path:
API-DRFL/example/Linux_64/4_minimal_welding_sample.cpp

See the example on the Doosan Robotics API-DRFL GitHub.

Watch the full walk-through on the Doosan Robotics Official YouTube.

Note

The app_weld_* functions that this sample calls are declared only when DRCF_VERSION is 2 (DRCF v2 controllers). The sample sets DRCF_VERSION to 2 unless the compiler defines it, and stops the build with an error for any other value. See DRCF Version Compatibility.

Setup & Global Variables

The sample defines a global controller object, several state flags, the controller IP address and a helper function that returns a DRL welding script as a string.

#include <iostream>
#include <cstring>
#include <thread>
#include <chrono>
#include <termios.h>
#include <unistd.h>

// The welding API used in this sample exists only for DRCF 2.
#ifndef DRCF_VERSION
#define DRCF_VERSION 2
#endif
#if DRCF_VERSION != 2
#error "4_minimal_welding_sample.cpp uses the welding API, which is available only with DRCF_VERSION 2"
#endif
#include "../../include/DRFLEx.h"
using namespace DRAFramework;
using namespace std::chrono_literals;


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();

A set of callback handlers reacts to TP initialization, homing completion, program stop and monitoring data. The monitoring data handlers and OnProgramStopped have empty bodies.

void OnTpInitializingCompleted() {
  // Tp 초기화 이후 제어권 요청.
  g_TpInitailizingComplted = TRUE;
  Drfl.manage_access_control(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_EX pData) {

}

These globals and callbacks track control authority, request it again when the TP finishes initializing or the authority is lost, reconnect when the connection drops, and print alarm, popup, TP log, progress and user-input messages.

Main Loop

The main loop runs after initialization and waits for a single-character command.

bool bLoop = true;
while (bLoop) {
  cout << "\ninput key : ";
  char ch;
  cin >> ch;
  switch (ch) {
    case 'q':
      bLoop = false;
      break;
    case '0':
    {
      // drl sample.
      Drfl.drl_start(ROBOT_SYSTEM_VIRTUAL, getWeldingSampleDrl());
    }
    break;
    // ...
    case '2':{


    }
    break;
    default:
      break;
  }
}

Drfl.close_connection();
return 0;
  • q : leave the loop, close the connection and exit.

  • 0 : run the DRL-based welding script returned by getWeldingSampleDrl().

  • 1 : configure the welding interface directly via C++ structures and execute a weaving welding motion using amovel with a welding app type.

  • 2 : an empty case.

Callback Registration

Unlike the minimal instruction example (where callbacks are registered on a key press), this welding sample registers all callbacks at startup before opening the connection.

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);

  if (!Drfl.open_connection(IP_ADDR)) {
    std::cout << "open_connection failed" << std::endl;
    return 1;
  }
  • OnMonitoringStateCB has an empty body; its state handling is commented out.

  • OnMonitroingAccessControlCB answers a REQUEST with MANAGE_ACCESS_CONTROL_RESPONSE_YES, sets g_bHasControlAuthority on GRANT, and clears it on DENY or LOSS.

  • OnTpInitializingCompleted sets g_TpInitailizingComplted and immediately issues a force request for control authority.

  • OnLogAlarm, OnTpPopup, OnTpLog, OnTpProgress and OnTpGetuserInput print diagnostic information to the console.

  • OnDisConnected retries open_connection() every second until it succeeds.

  • If open_connection() returns false, the program prints open_connection failed and exits.

The access control callback updates g_bHasControlAuthority whenever the controller changes the access state. On DENY or LOSS, once the TP has finished initializing, it requests control authority again with a force request.

void OnMonitroingAccessControlCB(
    const MONITORING_ACCESS_CONTROL eTrasnsitControl) {
  // 50msec 이내 작업만 수행할 것.
  std::cout << "OnMonitroingAccessControlCB !! " << int(eTrasnsitControl) << std::endl;
  switch (eTrasnsitControl) {
    case MONITORING_ACCESS_CONTROL_REQUEST:
      if (!Drfl.manage_access_control(MANAGE_ACCESS_CONTROL_RESPONSE_YES))
        std::cout << "manage_access_control failed" << std::endl;
      // Drfl.manage_access_control(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.manage_access_control(MANAGE_ACCESS_CONTROL_FORCE_REQUEST);
      }
      break;
    default:
      break;
  }
}

When the access-control state changes, the callback prints the numeric value of MONITORING_ACCESS_CONTROL, for example 2 for GRANT.

OnMonitroingAccessControlCB !! 2

Different integer values correspond to the enum constants defined in MONITORING_ACCESS_CONTROL (REQUEST, GRANT, DENY, LOSS, …).

The robot-state callback is registered, but every statement in its body is commented out.

void OnMonitoringStateCB(const ROBOT_STATE eState) {
    // std::cout << "monitoring state cb " << std::endl;
    // // 50msec 이내 작업만 수행할 것.
  // switch ((unsigned char)eState) {
  // ...
  //   default:
  //     break;
  // }
  // return;
}

Alarm information from the controller is reported through the log-alarm callback.

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;
}

Example output:

Alarm Info: group(1), index(1037), param(), param(), param()

Each alarm is logged with a group and index code plus up to three string parameters. These values can be cross-referenced with the alarm table in the controller manual to identify the exact cause (e.g., Servo On failure, limit violation, welding interface error, etc.).

Connection to Controller

After registering callbacks, the controller connection is opened via TCP/IP, the monitoring version is set to the latest and the system version is read.

if (!Drfl.open_connection(IP_ADDR)) {
  std::cout << "open_connection failed" << std::endl;
  return 1;
}

Drfl.setup_monitoring_version(1); // (0 -> older version, 1 -> latest)

SYSTEM_VERSION tSysVerion = {'\0',};
Drfl.get_system_version(&tSysVerion);

Control Authority & Servo On Handling

The sample sends one force request for control authority and one Servo On command, prints the system and library versions, and then polls get_robot_state() every second.

if(true!=Drfl.manage_access_control(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

The loop has no retry limit. The program continues only when the robot state is STATE_STANDBY and the access-control callback has set g_bHasControlAuthority.

Robot Mode & System Configuration

After Servo On, the robot mode is set to AUTONOMOUS and the robot system to the virtual robot.

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-based Welding Script

Key 0 executes a self-contained DRL script returned by getWeldingSampleDrl() on the virtual robot system.

case '0':
{
  // drl sample.
  Drfl.drl_start(ROBOT_SYSTEM_VIRTUAL, getWeldingSampleDrl());
}
break;

The DRL string configures welding parameters and motions:

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))";
};
  • Configure EtherNet/IP mapping (R2M / M2R) for welding control and monitoring.

  • Enable the digital welding interface (app_weld_enable_digital()).

  • Set welding conditions (speed, mode, job number, synergic ID, etc.).

  • Configure trapezoidal weaving for the torch path.

  • Move to a ready pose via movel, then weld from start to end pose using amovel(..., app_type=DR_MV_APP_WELD).

  • Disable welding at the end of the motion.

By default, dry_run = 1 performs a simulation weld (no real arc). To perform real welding, users must set dry_run = 0 and ensure that the welding machine and EtherNet/IP mapping are configured correctly.

C++-based Welding Interface Configuration

Key 1 demonstrates how to set up the same welding interface purely via C++ and execute a welding motion without using DRL welding commands directly.

case '1':
{
  // Set ready pos and get Target Welding position
  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;

  // Refer to drl Sample Code.
  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_MANUAL); // set_tcp is accepted only in manual mode.
  // 사전 TCP 설정 : EWM 용접기 기준 Welding_Torch(0, 200, 300, 90, 67.5, 0)
  if (Drfl.get_tcp() != "Welding Torch")
    Drfl.set_tcp("Welding Torch");
  Drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS);
  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;
}
  • Task pose offset: the welding target is the task pose read after the move to the ready joint position, plus 200 on X.

  • TCP: the sample switches to Manual mode, sets the Welding Torch TCP if it is not already active, and returns to Auto mode, because set_tcp is accepted only in Manual mode.

  • Digital welding interface mapping is done with a series of CONFIG_DIGITAL_WELDING_INTERFACE_* structures; the elided lines define each structure in the same way as r2m_process_data.

  • Weaving parameters (offsets, amplitude, timing) are configured through CONFIG_TRAPEZOID_WEAVING_SETTING and applied via app_weld_weave_cond_trapezoidal.

  • The final motion uses amovel(..., DR_MV_APP_WELD), which executes a linear weld with weaving along the specified target pose.