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:
87 // Once RTDE communication is started, we have to make sure to read from the interface buffer, as
88 // otherwise we will get pipeline overflows. Therefor, do this directly before starting your main
89 // loop.
90 g_my_robot->getUrDriver()->startRTDECommunication();
91 rtde_interface::DataPackage data_pkg(g_my_robot->getUrDriver()->getRTDEOutputRecipe());
92 while (!(passed_positive_part && passed_negative_part))
93 {
94 // Read latest RTDE package. This will block for a hard-coded timeout (see UrDriver), so the
95 // robot will effectively be in charge of setting the frequency of this loop.
96 // In a real-world application this thread should be scheduled with real-time priority in order
97 // to ensure that this is called in time.
98 if (!g_my_robot->getUrDriver()->getDataPackage(data_pkg))
99 {
100 URCL_LOG_WARN("Could not get fresh data package from robot");
101 return 1;
102 }
103 // Read current joint positions from robot data
104 if (!data_pkg.getData("actual_q", g_joint_positions))
105 {
106 // This throwing should never happen unless misconfigured
107 std::string error_msg = "Did not find 'actual_q' in data sent from robot. This should not happen!";
108 throw std::runtime_error(error_msg);
109 }
110
111 if (first_pass)
112 {
113 joint_target = g_joint_positions;
114 first_pass = false;
115 }
The first read operation will initialize the target buffer with the current robot position. Next, the target joint configuration is calculated:
117 // Open loop control. The target is incremented with a constant each control loop
118 if (passed_positive_part == false)
119 {
120 increment = increment_constant;
121 if (g_joint_positions[5] >= 2)
122 {
123 passed_positive_part = true;
124 }
125 }
126 else if (passed_negative_part == false)
127 {
128 increment = -increment_constant;
129 if (g_joint_positions[5] <= 0)
130 {
131 passed_negative_part = true;
132 }
133 }
134 joint_target[5] += increment;
To send the control command, the robot’s ReverseInterface is used via the
writeJointCommand() function:
135 // Setting the RobotReceiveTimeout time is for example purposes only. This will make the example running more
136 // reliable on non-realtime systems. Use with caution in productive applications.
137 bool ret = g_my_robot->getUrDriver()->writeJointCommand(joint_target, comm::ControlMode::MODE_SERVOJ,
138 RobotReceiveTimeout::millisec(100));
139 if (!ret)
140 {
141 URCL_LOG_ERROR("Could not send joint command. Make sure that the robot is in remote control mode and connected "
142 "with a network cable.");
143 return 1;
144 }