Example driver
In the examples
subfolder you will find a minimal example of a running driver. It starts an
instance of the UrDriver
class and prints the RTDE values read from the controller. To run it make
sure to
have an instance of a robot controller / URSim running at the configured IP address (or adapt the address to your needs)
run it from the package’s main folder (the one where the README.md file is stored), as for simplicity reasons it doesn’t use any sophisticated method to locate the required files.
This page will walk you through the full_driver.cpp example.
Initialization
At first, we create a ExampleRobotWrapper
object giving it the robot’s IP address, script file and RTDE
recipes.
62 bool headless_mode = true;
63 g_my_robot = std::make_unique<ExampleRobotWrapper>(robot_ip, OUTPUT_RECIPE, INPUT_RECIPE, headless_mode,
64 "external_control.urp");
65 if (!g_my_robot->isHealthy())
66 {
67 URCL_LOG_ERROR("Something in the robot initialization went wrong. Exiting. Please check the output above.");
68 return 1;
69 }
70 // --------------- INITIALIZATION END -------------------
Robot control loop
This example reads the robot’s joint positions, increments / decrements the 5th axis and sends that back as a joint command for the next cycle. This way, the robot will move its wrist first until a positive limit and then back to 0.
To read the joint data, the driver’s RTDE client is used:
86 // Once RTDE communication is started, we have to make sure to read from the interface buffer, as
87 // otherwise we will get pipeline overflows. Therefor, do this directly before starting your main
88 // loop.
89 g_my_robot->ur_driver_->startRTDECommunication();
90 while (!(passed_positive_part && passed_negative_part))
91 {
92 // Read latest RTDE package. This will block for a hard-coded timeout (see UrDriver), so the
93 // robot will effectively be in charge of setting the frequency of this loop.
94 // In a real-world application this thread should be scheduled with real-time priority in order
95 // to ensure that this is called in time.
96 std::unique_ptr<rtde_interface::DataPackage> data_pkg = g_my_robot->ur_driver_->getDataPackage();
97 if (!data_pkg)
98 {
99 URCL_LOG_WARN("Could not get fresh data package from robot");
100 return 1;
101 }
102 // Read current joint positions from robot data
103 if (!data_pkg->getData("actual_q", g_joint_positions))
104 {
105 // This throwing should never happen unless misconfigured
106 std::string error_msg = "Did not find 'actual_q' in data sent from robot. This should not happen!";
107 throw std::runtime_error(error_msg);
108 }
109
110 if (first_pass)
111 {
112 joint_target = g_joint_positions;
113 first_pass = false;
114 }
The first read operation will initialize the target buffer with the current robot position. Next, the target joint configuration is calculated:
116 // Open loop control. The target is incremented with a constant each control loop
117 if (passed_positive_part == false)
118 {
119 increment = increment_constant;
120 if (g_joint_positions[5] >= 2)
121 {
122 passed_positive_part = true;
123 }
124 }
125 else if (passed_negative_part == false)
126 {
127 increment = -increment_constant;
128 if (g_joint_positions[5] <= 0)
129 {
130 passed_negative_part = true;
131 }
132 }
133 joint_target[5] += increment;
To send the control command, the robot’s ReverseInterface is used via the
writeJointCommand()
function:
134 // Setting the RobotReceiveTimeout time is for example purposes only. This will make the example running more
135 // reliable on non-realtime systems. Use with caution in productive applications.
136 bool ret = g_my_robot->ur_driver_->writeJointCommand(joint_target, comm::ControlMode::MODE_SERVOJ,
137 RobotReceiveTimeout::millisec(100));
138 if (!ret)
139 {
140 URCL_LOG_ERROR("Could not send joint command. Is the robot in remote control?");
141 return 1;
142 }