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 verify system/library versions
switch to AUTONOMOUS + REAL mode
execute a full DRL-based welding sequence
configure the welding interface and motion purely via 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
This sample is implemented for DRCF_VERSION 2. Welding-related APIs are not available yet when building with DRCF_VERSION 3. Support for welding under DRCF v3 will be provided in a future update.
Setup & Global Variables
The sample defines a global controller object, several state flags, and a helper function that returns a DRL welding script as a string.
#define DRCF_VERSION 2
#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;
bool is_standby = FALSE;
std::string IP_ADDR = "192.168.137.100";
std::string getWeldingSampleDrl();
A set of callback handlers is declared to react to TP initialization, homing completion, alarms, popups, TP logs, user input, real-time monitoring data, and disconnection events.
void OnTpInitializingCompleted();
void OnHommingCompleted();
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);
void OnMonitoringStateCB(const ROBOT_STATE eState);
void OnMonitroingAccessControlCB(const MONITORING_ACCESS_CONTROL transit);
void OnLogAlarm(LPLOG_ALARM tLog);
void OnTpPopup(LPMESSAGE_POPUP tPopup);
void OnTpLog(const char* strLog);
void OnTpProgress(LPMESSAGE_PROGRESS tProgress);
void OnTpGetuserInput(LPMESSAGE_INPUT tInput);
void OnRTMonitoringData(LPRT_OUTPUT_DATA_LIST tData);
void OnDisConnected();
These globals and callbacks are used to maintain control authority and Servo On state, automatically reconnect on network loss, log alarm and popup messages from the controller, and feed welding status back to the user during execution.
Main Loop
The main loop runs after initialization and waits for a single-character command.
while (true) {
std::cout << "\ninput key : ";
char ch;
std::cin >> ch;
if (ch == 'q') {
std::cout << "Exit program..." << std::endl;
break; // exit while loop
}
switch (ch) {
case '0':
{
// DRL welding sample
Drfl.drl_start(ROBOT_SYSTEM_REAL, getWeldingSampleDrl());
}
break;
case '1':
{
// C++ welding interface + motion sample
// (configure EtherNet/IP mapping and execute welding motion)
// ...
}
break;
case '2':
{
// Reserved for additional welding tests
}
break;
default:
break;
}
}
Drfl.CloseConnection();
return 0;
q: gracefully stop the sample and close the connection.0: run the DRL-based welding script returned bygetWeldingSampleDrl().1: configure the welding interface directly via C++ structures and execute a weaving welding motion usingamovelwith a welding app type.2: reserved for future extensions.
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);
assert(Drfl.open_connection(IP_ADDR));
// ...
}
OnMonitoringStateCBsetsis_standbywhen the robot reaches STATE_STANDBY, which indicates that Servo On is complete.OnMonitroingAccessControlCBtracks whether control authority is GRANT / LOSS / DENY.OnTpInitializingCompletedimmediately issues a force request for control authority.OnLogAlarm,OnTpPopup,OnTpLogprint diagnostic information to the console.OnDisConnectedretriesopen_connection()in a loop to restore connectivity.
The access control callback is used to update g_bHasControlAuthority whenever the
controller changes the access state.
void OnMonitroingAccessControlCB(const MONITORING_ACCESS_CONTROL transit) {
std::cout << "[AccessControl] state = " << int(transit) << std::endl;
switch (transit) {
case MONITORING_ACCESS_CONTROL_GRANT:
// State where control authority is granted
g_bHasControlAuthority = true;
break;
case MONITORING_ACCESS_CONTROL_LOSS:
case MONITORING_ACCESS_CONTROL_DENY:
// State where control authority is lost or denied
g_bHasControlAuthority = false;
break;
default:
break;
}
}
When the access-control state changes, the callback prints the numeric value of
MONITORING_ACCESS_CONTROL.
[AccessControl] state = 0
Different integer values correspond to the enum constants defined in
MONITORING_ACCESS_CONTROL (REQUEST, GRANT, DENY, LOSS, …).
The robot-state callback is responsible for detecting when the robot reaches the standby state after Servo On.
void OnMonitoringStateCB(const ROBOT_STATE eState)
{
if (eState == STATE_STANDBY) {
is_standby = true;
std::cout << "Successfully Servo On !!" << std::endl;}
else
{
is_standby = false;
}
}
If the controller reports STATE_STANDBY, the sample sets is_standby = true and
prints a confirmation message.
Successfully Servo On !!
The main loop later checks is_standby together with
g_bHasControlAuthority to confirm that the system is ready for welding.
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.
assert(Drfl.open_connection(IP_ADDR));
Drfl.setup_monitoring_version(1); // (0 -> older version, 1 -> latest)
SYSTEM_VERSION tSysVerion = {'\0',};
Drfl.get_system_version(&tSysVerion);
Robot Mode & System Configuration
After Servo On, the robot mode and system are configured for welding.
std::cout << "set robot mode ret : "
<< Drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS) << std::endl;
Drfl.set_robot_system(ROBOT_SYSTEM_REAL);
set robot mode ret : 1
DRL-based Welding Script
Key 0 executes a self-contained DRL script returned by getWeldingSampleDrl().
case '0':
{
// DRL welding sample
Drfl.drl_start(ROBOT_SYSTEM_REAL, 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)
set_accx(1000.0, 322.5)
dry_run = 1
weld_con_adj = 1
arc_sensing_test = False
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(...)
app_weld_set_interface_eip_r2m_test(...)
app_weld_set_interface_eip_r2m_condition(...)
app_weld_set_interface_eip_r2m_option(...)
app_weld_set_interface_eip_m2r_process(...)
app_weld_set_interface_eip_m2r_monitoring(...)
app_weld_set_interface_eip_m2r_other(...)
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.1000, 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, ...)
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)
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 usingamovel(..., 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':
{
// 1) Move to ready joint position and get current task pose
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; // shift X by +200 mm
// 2) Basic welding options
Drfl.set_singularity_handling(SINGULARITY_AVOIDANCE_AVOID);
unsigned char dry_run = 1; // 0: real weld, 1: dry-run
unsigned char weld_con_adj = 1; // use preset welding condition
Drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS);
// 3) Ensure the welding TCP is selected
if (Drfl.get_tcp() != "Welding Torch")
Drfl.set_tcp("Welding Torch");
std::this_thread::sleep_for(200ms);
// 4) Configure EtherNet/IP R2M & M2R interface structures
CONFIG_DIGITAL_WELDING_INTERFACE_PROCESS r2m_process_data = { /* ... */ };
CONFIG_DIGITAL_WELDING_INTERFACE_MODE r2m_mode_data = { /* ... */ };
CONFIG_DIGITAL_WELDING_INTERFACE_TEST r2m_test_data = { /* ... */ };
CONFIG_DIGITAL_WELDING_INTERFACE_CONDITION r2m_condition_data = { /* ... */ };
CONFIG_DIGITAL_WELDING_INTERFACE_OPTION r2m_option_data = { /* ... */ };
CONFIG_DIGITAL_WELDING_INTERFACE_PROCESS2 m2r_process2_data = { /* ... */ };
CONFIG_DIGITAL_WELDING_INTERFACE_MONITORING m2r_monitoring_data = { /* ... */ };
CONFIG_DIGITAL_WELDING_INTERFACE_OTHER m2r_other_data = { /* ... */ };
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);
// 5) Enable digital welding & set welding condition
std::this_thread::sleep_for(500ms);
Drfl.app_weld_enable_digital(1);
std::this_thread::sleep_for(500ms);
CONFIG_DIGITAL_WELDING_CONDITION weld_condition = { 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);
std::this_thread::sleep_for(1000ms);
// 6) Configure trapezoidal weaving
CONFIG_TRAPEZOID_WEAVING_SETTING 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);
// 7) Execute welding motion with welding application type
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 defined as the current pose plus an X-offset, making it easy to shift the weld line relative to the current TCP position.
Digital welding interface mapping is done with a series of
CONFIG_DIGITAL_WELDING_INTERFACE_*structures, matching the DRL sample configuration.Weaving parameters (offsets, amplitude, timing) are configured through
CONFIG_TRAPEZOID_WEAVING_SETTINGand applied viaapp_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.