You're reading the documentation for an older, but still supported version (1.33.4).
For information on the latest version, please have a look at 1.33.5.

6.6.5 minimal_instruction_sample.cpp

This example (5_minimal_instruction_sample.cpp) demonstrates a step-by-step instruction flow for controlling the robot using the API:

  • register monitoring callbacks,

  • open/close the controller connection,

  • request control authority,

  • turn Servo On and check system/version,

  • switch robot mode/system,

  • execute a simple joint motion (movej),

  • test inverse kinematics (ikin, ikin_norm),

  • and send/stop a DRL script.

Each function is mapped to a numeric key (1~9), so users can trigger individual operations one by one and observe how the API behaves in real time.

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

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

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

Setup & Global Variables

The example uses a global controller object and several flags to track control status.

#include "../../include/DRFLEx.h"
using namespace DRAFramework;

#undef NDEBUG
#include <assert.h>

CDRFLEx Drfl;
bool g_bHasControlAuthority = FALSE;
bool g_TpInitailizingComplted = FALSE;
bool g_mStat = FALSE;
bool g_Stop = FALSE;
bool moving = FALSE;

A DRL script string and helper flags are also defined.

string strDrl =
    "\r\n\
loop = 0\r\n\
while loop < 1003:\r\n\
 movej(posj(10,10.10,10,10.10), vel=60, acc=60)\r\n\
 movej(posj(00,00.00,00,00.00), vel=60, acc=60)\r\n\
 loop+=1\r\n";

bool bAlterFlag = FALSE;

These globals store the DRFLEx instance, track whether control authority has been granted and whether an alarm was logged, and hold a DRL script. No menu key uses the global strDrl; key 8 declares its own local strDrl.

Keyboard Menu & Main Loop

The main loop reads a key from the console and executes a corresponding instruction block.

  bool bLoop = TRUE;
  while (bLoop) {
    g_mStat = false;
    g_Stop = false;
#ifdef __XENO__
    unsigned long overrun = 0;
    const double tick = 1000000;  // 1ms
    rt_task_set_periodic(nullptr, TM_NOW, tick);
    if (rt_task_wait_period(&overrun) == -ETIMEDOUT) {
      std::cout << __func__ << ": \x1B[37m\x1B[41mover-runs: " << overrun
                << "\x1B[0m\x1B[0K" << std::endl;
    }
#else
    std::this_thread::sleep_for(std::chrono::microseconds(1000));
#endif  // __XENO__
#if 0
        static char ch = '0';
        if (ch == '7') ch = '0';
        else if (ch == '0') ch = '7';
#else
    cout << "\n\033[1;33minput key : \033[0m";
    // char ch = _getch();
    char ch;
    cin >> ch;
    // cout << ch << endl;
#endif
    switch (ch) {
      case 'q':
        bLoop = FALSE;
        Drfl.close_connection();
        std::cout << "Close Connection !! " << std::endl;
        break;
      // ...
      default:
        break;
    }
    // Sleep(100);
    this_thread::sleep_for(std::chrono::milliseconds(100));
  }

  Drfl.CloseConnection();

This structure makes the example a minimal instruction console: you can manually trigger each step of the control pipeline and see the result immediately.

Callback Registration

When the user presses 1, the example registers several monitoring and event callbacks.

case '1':
    {
        // Register callbacks
        Drfl.set_on_monitoring_data_ex(OnMonitoringDataExCB);
        Drfl.set_on_monitoring_ctrl_io_ex(OnMonitoringCtrlIOExCB);
        Drfl.set_on_log_alarm(OnLogAlarm);
        // Drfl.set_on_disconnected(OnDisConnected);
        Drfl.set_on_monitoring_state(OnMonitoringStateCB);
        // Drfl.set_on_tp_initializing_completed(OnTpInitializingCompleted);
        // 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);
        std::cout << "Register Callbacks !! " << std::endl;
    }
    break;

These callbacks receive extended monitoring data and control-box I/O data, print alarm information (OnLogAlarm) and print each robot state change (OnMonitoringStateCB). The registrations of OnDisConnected, the TP callbacks and the RT monitoring callback are commented out. OnMonitoringDataExCB and OnMonitoringCtrlIOExCB return before printing anything.

Example console output:

input key : 1
Register Callbacks !!

Connection to Controller

Pressing 2 opens a TCP/IP connection to the controller.

case '2':
    {
        // Establish connection
        assert(Drfl.open_connection("127.0.0.1"));
        std::cout << "Open Connection !! " << std::endl;
    }
    break;
  • The example uses 127.0.0.1, assuming a local emulator (DART Platform / virtual controller).

  • For a real robot controller, replace this with the actual controller IP.

  • The sample undefines NDEBUG before including assert.h, so the open_connection() call inside assert() runs in every build and the program aborts if it returns false.

Example console output:

input key : 2
Open Connection !!

Control Authority Handling

Key 3 registers a simple access-control callback and then requests control authority.

case '3':
{
    //Acquire control authority - register callback BEFORE sending request
  Drfl.set_on_monitoring_access_control([](const MONITORING_ACCESS_CONTROL eTrasnsitControl)
    {
      if(MONITORING_ACCESS_CONTROL_GRANT == eTrasnsitControl) {
        g_bHasControlAuthority = TRUE;
        std::cout << "\nSuccessfully got Control Authority !! " << std::endl;
      }else {
        std::cout << "Unintended autority.. " << std::endl;
      }
    }
  );

  // FORCE_REQUEST goes through server's async message queue (SendMessageToRC),
  // which can be delayed if the server is processing previous disconnect events.
  // Retry once if the first attempt doesn't get GRANT within 5 seconds.
  g_bHasControlAuthority = FALSE;
  Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_FORCE_REQUEST);
  std::cout << "Try to get Control Authority !!" << std::endl;

  for (int i = 0; i < 50; i++) {
      if (g_bHasControlAuthority) break;
      this_thread::sleep_for(std::chrono::milliseconds(100));
  }
  if (!g_bHasControlAuthority) {
      // Retry once - server may have been busy processing previous disconnect
      Drfl.ManageAccessControl(MANAGE_ACCESS_CONTROL_FORCE_REQUEST);
      std::cout << "Retrying..." << std::endl;
      for (int i = 0; i < 50; i++) {
          if (g_bHasControlAuthority) break;
          this_thread::sleep_for(std::chrono::milliseconds(100));
      }
  }
  if (!g_bHasControlAuthority) {
      std::cout << "Failed to get Control Authority." << std::endl;
  }
}
break;
  • The callback is registered before the request is sent.

  • MANAGE_ACCESS_CONTROL_FORCE_REQUEST forces an access request to the controller.

  • When granted, g_bHasControlAuthority is set to TRUE and a message is printed.

  • The sample waits up to 5 seconds for the grant, sends the request once more if it has not arrived, waits up to 5 seconds again, and prints a failure message if control authority is still not granted.

Example console output:

input key : 3
Try to get Control Authority !!

Successfully got Control Authority !!

Servo On & Monitoring Version

Key 4 configures the monitoring version, sets a control-box digital output, prints system information and turns Servo On.

case '4':
    {
    if (!g_bHasControlAuthority) {
        std::cout << "No control authority. Run step 3 first." << std::endl;
        break;
    }
    // Turn servo on and set monitoring version
    Drfl.setup_monitoring_version(1);
    Drfl.set_digital_output(GPIO_CTRLBOX_DIGITAL_INDEX_10, TRUE);
    SYSTEM_VERSION tSysVerion = {'\0',};
    Drfl.get_system_version(&tSysVerion);
    cout << "System version: " << tSysVerion._szController << endl;
    cout << "Library version: " << Drfl.get_library_version() << endl;

    Drfl.set_robot_control(CONTROL_SERVO_ON);

    int nRetryCount = 0;
    ROBOT_STATE ePrevState = STATE_LAST;
    int nSameStateCount = 0;
    const int RETRY_INTERVAL = 10; // retry every 10*500ms = 5 seconds
    while ((Drfl.get_robot_state() != STATE_STANDBY) || !g_bHasControlAuthority) {
        ROBOT_STATE eState = Drfl.get_robot_state();
        bool bSendCommand = false;
        if (eState != ePrevState) {
            ePrevState = eState;
            nSameStateCount = 0;
            bSendCommand = true;
        } else if (++nSameStateCount >= RETRY_INTERVAL) {
            nSameStateCount = 0;
            bSendCommand = true;
        }
        if (bSendCommand) {
            switch ((unsigned char)eState) {
              case STATE_SAFE_STOP:
                cout << ">> Recovery: SAFE_STOP -> resetting..." << endl;
                Drfl.SetSafeStopResetType(SAFE_STOP_RESET_TYPE_DEFAULT);
                Drfl.SetRobotControl(CONTROL_RESET_SAFET_STOP);
                break;
              case STATE_SAFE_OFF:
                cout << ">> Recovery: SAFE_OFF -> servo on..." << endl;
                Drfl.SetRobotControl(CONTROL_SERVO_ON);
                break;
              case STATE_SAFE_STOP2:
                cout << ">> Recovery: SAFE_STOP2 -> recovering..." << endl;
                Drfl.SetRobotControl(CONTROL_RECOVERY_SAFE_STOP);
                break;
              case STATE_SAFE_OFF2:
                cout << ">> Recovery: SAFE_OFF2 -> recovering..." << endl;
                Drfl.SetRobotControl(CONTROL_RECOVERY_SAFE_OFF);
                break;
              case STATE_NOT_READY:
                cout << ">> Recovery: NOT_READY -> servo on..." << endl;
                Drfl.SetRobotControl(CONTROL_SERVO_ON);
                break;
              default:
                cout << ">> Unknown state: " << (int)eState << endl;
                break;
            }
        }
        this_thread::sleep_for(std::chrono::milliseconds(500));
        if (++nRetryCount > 20) { // 10 second timeout
            cout << ">> Timeout waiting for STANDBY (state=" << (int)eState << ")" << endl;
            break;
        }
    }
    if (Drfl.get_robot_state() == STATE_STANDBY && g_bHasControlAuthority)
        std::cout << "Servo On !! " << std::endl;
    else
        std::cout << "Failed to reach STANDBY state." << std::endl;
    }
break;
  • Without control authority, the step prints a message and does nothing else.

  • setup_monitoring_version(1) selects the newer monitoring context.

  • set_digital_output(GPIO_CTRLBOX_DIGITAL_INDEX_10, TRUE) turns on control-box digital output 10.

  • set_robot_control(CONTROL_SERVO_ON) turns Servo On.

  • The loop checks the robot state every 500 ms. When the state changes, and every 5 seconds while it stays the same, it sends the recovery command for STATE_SAFE_STOP, STATE_SAFE_OFF, STATE_SAFE_STOP2, STATE_SAFE_OFF2 or STATE_NOT_READY.

  • The loop ends when both robot state = STANDBY and control authority are satisfied, or after about 10 seconds.

Example console output:

input key : 4
System version: GF03020000
Library version: GL013300
Servo On !!

Robot Mode & System Configuration

Key 5 switches to AUTONOMOUS mode and REAL system.

case '5':
{
  //Set robot mode and system
  assert(Drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS));
  assert(Drfl.set_robot_system(ROBOT_SYSTEM_REAL));
  std::cout << "Set Auto Mode !!" << std::endl;
}

break;
  • ROBOT_MODE_AUTONOMOUS enables programmatic motion execution.

  • ROBOT_SYSTEM_REAL indicates that the motion is executed on a real robot. To execute in virtual mode, change it to ROBOT_SYSTEM_VIRTUAL.

  • Both calls are made inside assert(), so the program aborts if either returns false.

Example console output:

input key : 5
Set Auto Mode !!

Joint Motion Execution

Key 6 executes a simple joint-space motion using movej.

case '6':
{
  //Execute motion
  std::cout << "Move Start !!  " << std::endl;
  float p1[6] = {0, 0, -90, 0, 90, 0};
  Drfl.movej(p1, 60, 30);  // Move to posj(0,0,10,0,10,0) with v=60, a=30
  // float p2[6] = {0, 0, 9, 0, 90, 0};
  // Drfl.movej(p2, 30, 30); // Move back to posj(0,0,0,0,0,0) with v=30, a=30
  //You cannot specify only selected parameters; even when using time, you must provide velocity and acceleration first.
  std::cout << "Move End !!  " << std::endl;
  LPROBOT_POSE pCur = Drfl.get_current_pose();
  std::cout << " Current Pose : "
            << pCur->_fPosition[0] << ", "
            << pCur->_fPosition[1] << ", "
            << pCur->_fPosition[2] << ", "
            << pCur->_fPosition[3] << ", "
            << pCur->_fPosition[4] << ", "
            << pCur->_fPosition[5] << std::endl;
} break;
  • The move goes to the joint position (0, 0, -90, 0, 90, 0) deg at velocity 60, acceleration 30.

  • The second movej is commented out.

  • After the move, the sample prints the current joint position returned by get_current_pose().

Example console output:

input key : 6
Move Start !!
Move End !!

Kinematics Test

Key 7 demonstrates the inverse kinematics functions ikin and ikin_norm.

case '7':
    {
  // Motion using kinematics functions
  float x1[6] = {370.9, 719.7, 651.5, 90, -180, 0}; //Task-space pose for ikin conversion
  float fIterThreshold[2] = {0.005, 0.01};
  LPROBOT_POSE res = Drfl.ikin(x1, 255); //ROBOT_POSE holds float[6] elements.
  //Store the ikin result (shallow copy)
  float q1[6] = {0,};
  for(int i=0; i<6; i++){
      q1[i] = res->_fPosition[i]; //Read elements from LPROBOT_POSE (return value)
  }
  std::cout << " Get ikin Success (Task Space -> Joint Space) !! " << std::endl;
  std::cout << " Joint Values [q1]: "
            << q1[0] << ", "
            << q1[1] << ", "
            << q1[2] << ", "
            << q1[3] << ", "
            << q1[4] << ", "
            << q1[5] << std::endl;
  this_thread::sleep_for(std::chrono::milliseconds(100));

  LPINVERSE_KINEMATIC_RESPONSE res2 = Drfl.ikin_norm(x1, 255, COORDINATE_SYSTEM_BASE, 0); //ROBOT_POSE holds float[6] elements.
  //Store the ikin_norm result (shallow copy)
  float q2[6] = {0,};
  for(int i=0; i<6; i++){
      q2[i] = res2->_fTargetPos[i]; //Read elements from LPROBOT_POSE (return value)
  }
  std::cout << " Get ikin_norm Success (Task Space -> Joint Space) !! " << std::endl;
  std::cout << " Joint Values [q2]: "
            << q2[0] << ", "
            << q2[1] << ", "
            << q2[2] << ", "
            << q2[3] << ", "
            << q2[4] << ", "
            << q2[5] << std::endl;
  // Drfl.movej(q1, 60, 30);
    }
    break;
  • ikin and ikin_norm compute a joint-space solution from the task-space pose x1; the sample passes 255 as the solution space.

  • The sample prints both results. The movej to the ikin result is commented out.

Example console output:

input key : 7
 Get ikin Success (Task Space -> Joint Space) !!

DRL Script Execution

Key 8 sends and executes a DRL script that performs two movej motions.

case '8':
    {
  // DRL
  string strDrl = "movej([0, 0, 10, 0, 10, 0], 60, 30)\nmovej([10, 20, 30, 40, 50, 60], 60, 30)\n";
  //DRL script to move between two positions using movej
  Drfl.drl_start(ROBOT_SYSTEM_REAL, strDrl); //Set ROBOT_SYSTEM to VIRTUAL for a virtual robot, and pass the DRL script
  Drfl.set_on_program_stopped(OnProgramStopped);
  std::cout << "Sent DRL Script !!" << std::endl;
    }
    break;

The registered OnProgramStopped callback handles program termination.

void OnProgramStopped(const PROGRAM_STOP_CAUSE) {
  Drfl.drl_stop();
  // Do only work under 50 msec.
  // assert(Drfl.SetRobotMode(ROBOT_MODE_MANUAL));
  cout << "program stopped" << endl;
}

OnProgramStopped calls drl_stop(). See Rules for callback functions for the functions that must not be called inside a callback.

During execution, alarm logs may appear when the DRL program stops.

input key : 8
Sent DRL Script !!
input key : Alarm Info: group(1), index(2007), param(), param(), param()
program stopped

This confirms that the DRL script was sent, the program executed, and the stop event and alarm were handled correctly.

Note

A DRL script string and helper flags are also defined at the top of the sample.
Key 8 uses its own local strDrl; the global strDrl below is not used by any menu key.

string strDrl =
    "\r\n\
loop = 0\r\n\
while loop < 1003:\r\n\
 movej(posj(10,10.10,10,10.10), vel=60, acc=60)\r\n\
 movej(posj(00,00.00,00,00.00), vel=60, acc=60)\r\n\
 loop+=1\r\n";

Xenomai / Real-time Environment Notes

When the sample is compiled with the __XENO__ macro, the following code paths are enabled.

#ifdef __XENO__
#include <rtdk.h>
#include <native/task.h>
#include <native/timer.h>
#else
//#include "stdafx.h"
//#include <Windows.h>
//#include <process.h>
//#include <conio.h>
#endif // __XENO__

In the main loop, the real-time task is scheduled with a fixed period.

#ifdef __XENO__
    unsigned long overrun = 0;
    const double tick = 1000000;  // 1ms
    rt_task_set_periodic(nullptr, TM_NOW, tick);
    if (rt_task_wait_period(&overrun) == -ETIMEDOUT) {
      std::cout << __func__ << ": \x1B[37m\x1B[41mover-runs: " << overrun
                << "\x1B[0m\x1B[0K" << std::endl;
    }
#else
    std::this_thread::sleep_for(std::chrono::microseconds(1000));
#endif  // __XENO__
  • In non-RT environments, the loop simply sleeps using std::this_thread::sleep_for.

  • In Xenomai RT environments, the loop is driven by rt_task_set_periodic and rt_task_wait_period to achieve deterministic timing and to detect possible overruns.

These blocks are independent of the DRFLEx API logic itself and are only required when deploying the sample in a strict real-time control environment.