RTDE Client example
This example shows how to use the RTDEClient class for the robot’s Real-Time Data Exchange
(RTDE) interface.
The RTDE client has to be initialized with a list of keys that should be streamed from the robot and a list of keys that should be sent to the robot. The client will then start a background thread establishing communication.
In this example, those keys are stored in two text files relative to this repository’s root:
rtde_input_keys.txt and rtde_output_keys.txt. The example will read those files and use them
to initialize the RTDE client.
40const std::string OUTPUT_RECIPE = "examples/resources/rtde_output_recipe.txt";
41const std::string INPUT_RECIPE = "examples/resources/rtde_input_recipe.txt";
Creating an RTDE Client
An RTDE client can be directly created passing the robot’s IP address, a INotifier object, an
output and an input recipe. Optionally, a communication frequency can be passed as well. If that is
omitted, RTDE communication will be established at the robot’s control frequency.
75 comm::INotifier notifier;
76 const double rtde_frequency = 50; // Hz
77 rtde_interface::RTDEClient my_client(robot_ip, notifier, OUTPUT_RECIPE, INPUT_RECIPE, rtde_frequency);
78 my_client.init();
Reading data from the RTDE client
To read data received by the RTDE client, it has to be polled. See the RTDEClient section
for details on two possible strategies. In this example, we do not use background read and instead
fetch data synchronously. Hence, we pass false to the start() method.
85 auto data_pkg = std::make_unique<rtde_interface::DataPackage>(my_client.getOutputRecipe());
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 my_client.start(false); // false -> do not start background read thread.
90
91 auto start_time = std::chrono::steady_clock::now();
92 while (second_to_run <= 0 ||
93 std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start_time).count() <
94 second_to_run)
95 {
96 // Wait for a DataPackage. In a real-world application this thread should be scheduled with real-time priority in
97 // order to ensure that this is called in time.
98 bool success = my_client.getDataPackageBlocking(data_pkg);
99 if (success)
100 {
101 // Data fields in the data package are accessed by their name. Only names present in the
102 // output recipe can be accessed. Otherwise this function will return false.
103 data_pkg->getData(TARGET_SPEED_FRACTION, target_speed_fraction);
104 printFraction(target_speed_fraction, TARGET_SPEED_FRACTION);
105 }
106 else
107 {
108 // The client isn't connected properly anymore / doesn't receive any data anymore. Stop the
109 // program.
110 std::cout << "Could not get fresh data package from robot" << std::endl;
111 return 1;
112 }
The loop reuses a package built from the negotiated output recipe, keeping the normal data receive path allocation-free. Null pointers allocate a package, and foreign-recipe repair may allocate; error handling, non-data messages and reconnection are outside this guarantee. The recipe only names the fields, so the first read applies their negotiated types in place without allocation.
In our main loop, we wait for a new data package to arrive using the blocking read method. Once
received, data from the received package can be accessed using the getData() method of the
DataPackage object. This method takes the key of the data to be accessed as a parameter and
returns the corresponding value.
Note
The key used to access data has to be part of the output recipe used to initialize the RTDE
client. getData() returns false for an unknown key. If the type of the passed
variable doesn’t match the type the robot reported for that field, it throws
std::bad_variant_access.
Writing Data to the RTDE client
In this example, we use the RTDE client to oscillate the speed slider on the teach pendant between 0 and 1. While this doesn’t bear any practical use it shows how sending data to the RTDE interface works.
To send data to the RTDE client, we can use RTDEWriter object stored in the RTDE client. This
has methods implemented for each data type that can be sent to the robot. The input recipe used to
initialize the RTDE client has to contain the keys necessary to send that specific data.
129 if (!my_client.getWriter().sendSpeedSlider(speed_slider_fraction))
130 {
131 // This will happen for example, when the required keys are not configured inside the input
132 // recipe.
133 std::cout << "\033[1;31mSending RTDE data failed." << "\033[0m\n" << std::endl;
134 return 1;
135 }
Note
Many RTDE inputs require setting up the data key and a mask key. That is done internally, but the mask keys have to be part of the input recipe, as well. See the RTDE guide for more information.
Note
Every successful send... call updates the pending buffer and notifies the writer
thread. Calls may be coalesced before transmission; they are not queued as separate packages.
To submit several inputs together, use createInputDataPackage() after init(), fill the
fields and pass the package to sendPackage(). Separate helper calls can otherwise be
transmitted between updates. Neither API confirms delivery to the robot; see the
RTDE register round-trip example for verification using robot outputs.