For information on the latest version, please have a look at 1.33.5.
torque_rt
This function performs real-time motor torque control from an external controller. It allows direct torque streaming to each joint motor through UDP-based real-time communication. This mode is typically used for advanced control algorithms such as impedance, admittance, or gravity-compensated torque feedback.
Definition
DRFLEx.h within class CDRFLEx, public section
bool torque_rt(float fMotorTor[NUM_JOINT],
float fTargetTime) {
return _torque_rt(_rbtCtrlUDP, fMotorTor, fTargetTime);
};
Parameter
Parameter Name |
Data Type |
Default Value |
Description |
|---|---|---|---|
fMotorTor |
float[6] |
Target torque [Nm], on the same basis as |
|
fTargetTime |
float |
Target interpolation duration [s]. |
Note
Asynchronous command.
Each command starts a new ramp from the torque the joint measures at that moment and reaches
fMotorTorafterfTargetTime. A new command restarts the ramp, so when commands arrive more often thanfTargetTime, the commanded torque never reachesfMotorTor. In a 1 ms loop, setfTargetTimeto 0.If
fTargetTime≤ controller cycle (~1 ms), the command is executed immediately without interpolation.If the next command is not received before reaching the target torque,
the last torque input is maintained.Torque commands must keep arriving. If more than 0.1 s passes between them, the controller reports a real-time motion warning, stops the robot with a quick stop and holds its position. Servo power stays on. This limit applies to the torque path specifically and is separate from, and tighter than, the input watchdog configured through set_rt_control_input.
The controller adds no gravity or friction compensation to
fMotorTor. Add thegravity_torquevalue from read_data_rt, as the example below does. That value depends on the tool weight and centre of gravity set on the controller and on the installation pose, so keep them correct.The limits set with set_velj_rt, set_accj_rt, set_velx_rt and set_accx_rt do not apply to
torque_rt. The joint and TCP speed limits of the safety settings stay monitored whiletorque_rtis active.control_modein the RT output data changes fromCONTROL_MODE_POSITION(3) toCONTROL_MODE_TORQUE(4) on the first accepted command, and returns to 3 when the robot stops, pauses or times out.The controller accepts
torque_rtwhile the robot is inSTATE_STANDBYorSTATE_MOVING. A command sent while another motion runs stops that motion and reports a warning. During jogging or hand guiding, the command is ignored without a message.A NaN value, or an abrupt jump in the commanded torque, is a fatal error: the robot stops and servo power turns off.
A value above the maximum torque the controller accepts, including infinity, is limited to that maximum, and the controller reports an information message. Check that every value is finite before sending it.
Caution
In H-series robots, the second joint has a built-in gravity compensator.
The commanded torque and the compensator’s torque are summed internally to generate motion. Therefore, when sending external torque commands, users should compensate using the relationship:\(gravity\_torque = (link\_gravity\_torque - gravity\_compensator\_torque)\)
Excessive or unstable torque commands may cause unexpected motion.
It is recommended to use soft torque feedback or compliance control to ensure safe response.
Return
Value |
Description |
|---|---|
1 |
The command was sent. This does not confirm that the controller received or applied it. |
0 |
The command datagram could not be sent. |
Note
This function is asynchronous. It returns without waiting for the controller to accept or reject the command, so a return value of 1 does not confirm that the robot acted on it. Rejected commands and motion warnings and errors are reported through set_on_log_alarm. set_on_rt_log_alarm reports only RT session messages, so register both callbacks.
Example
The example opens the controller connection with open_connection, obtains control authority, sets Auto mode and turns servo power on before it starts the RT session, because stop is sent over that connection and torque_rt is accepted only in STATE_STANDBY or STATE_MOVING.
#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstring>
#include <thread>
std::atomic<bool> g_bHasControlAuthority(false);
void OnMonitoringAccessControlCB(const MONITORING_ACCESS_CONTROL eAccess)
{
if (eAccess == MONITORING_ACCESS_CONTROL_GRANT)
g_bHasControlAuthority = true;
}
// Controller connection: stop() below is sent over it
drfl.set_on_monitoring_access_control(OnMonitoringAccessControlCB);
if (!drfl.open_connection("192.168.137.100")) return 1;
drfl.manage_access_control(MANAGE_ACCESS_CONTROL_FORCE_REQUEST);
while (!g_bHasControlAuthority)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (!drfl.set_robot_mode(ROBOT_MODE_AUTONOMOUS)) return 1;
if (!drfl.set_robot_control(CONTROL_SERVO_ON)) return 1;
while (drfl.get_robot_state() != STATE_STANDBY)
std::this_thread::sleep_for(std::chrono::milliseconds(100));
drfl.connect_rt_control("192.168.137.100", 12347);
std::string version = "v1.0";
float period = 0.001f; // 1 ms
int lossCount = 4;
// Start output streaming first. The input watchdog is set after the wait below,
// so it does not count while the example waits for the first RT data.
drfl.set_rt_control_output(version, period, lossCount);
drfl.start_rt_control();
float fMotorTor[6] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
float fTargetTime = 0.0f; // apply each command at once; a ramp would restart every 1 ms
float q[6] = {0.f};
float q_dot[6] = {0.f};
float gravity_torque[6] = {0.f};
float trg_q[6] = {0.f};
float trg_q_dot[6] = {0.f};
float kp[6] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
float kd[6] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
// Wait until RT data arrives and its time_stamp advances. Until then,
// read_data_rt() returns zeros, including gravity_torque.
while (drfl.read_data_rt()->time_stamp == 0.0)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
const double first_stamp = drfl.read_data_rt()->time_stamp;
while (drfl.read_data_rt()->time_stamp == first_stamp)
std::this_thread::sleep_for(std::chrono::milliseconds(1));
// Hold the current posture: the PD target is the measured joint position.
memcpy(trg_q, drfl.read_data_rt()->actual_joint_position, sizeof(float) * 6);
// Arm the input watchdog right before the 1 ms command loop starts.
drfl.set_rt_control_input(version, period, lossCount);
const float loop_period = 0.001f; // matches the sleep below
float elapsed = 0.0f;
while (true) {
// Copy the whole frame once. Holding the pointer is not a snapshot:
// the buffer keeps being overwritten, so field-by-field copies can mix
// values from different packets.
RT_OUTPUT_DATA_LIST frame;
memcpy(&frame, drfl.read_data_rt(), sizeof(frame));
memcpy(q, frame.actual_joint_position, sizeof(float) * 6);
memcpy(q_dot, frame.actual_joint_velocity, sizeof(float) * 6);
memcpy(gravity_torque, frame.gravity_torque, sizeof(float) * 6);
// Simple gravity + PD torque controller
for (int i = 0; i < 6; i++) {
fMotorTor[i] = gravity_torque[i]
+ kp[i] * (trg_q[i] - q[i])
+ kd[i] * (trg_q_dot[i] - q_dot[i]);
}
drfl.torque_rt(fMotorTor, fTargetTime);
// Condition to exit
elapsed += loop_period;
if (elapsed > 5.0f) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // 1 ms RT loop
}
// Exit: hold the arm against gravity, return to position control, then end the RT session.
// stop_rt_control() alone keeps the last torque until the 0.1 s timeout stops the robot.
drfl.torque_rt(gravity_torque, 0.0f);
if (!drfl.stop(STOP_TYPE_SLOW))
printf("stop failed\n");
drfl.stop_rt_control();
drfl.disconnect_rt_control();
This example demonstrates a joint-space torque control loop using proportional-derivative (PD) feedback combined with gravity compensation.
Tips
Use
torque_rtfor advanced torque-level control or compliance-based applications.Always monitor current joint torque using read_data_rt for feedback verification.
Apply smooth torque transitions to avoid abrupt mechanical stress.