.. _torque_rt: 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** |br| ``DRFLEx.h`` within class `CDRFLEx`, public section .. code-block:: cpp bool torque_rt(float fMotorTor[NUM_JOINT], float fTargetTime) { return _torque_rt(_rbtCtrlUDP, fMotorTor, fTargetTime); }; **Parameter** .. list-table:: :widths: 25 20 20 35 :header-rows: 1 * - **Parameter Name** - **Data Type** - **Default Value** - **Description** * - fMotorTor - float[6] - - - Target torque [Nm], on the same basis as ``gravity_torque`` in :ref:`read_data_rt `, so the two can be added directly. Every value must be finite. * - 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 ``fMotorTor`` after ``fTargetTime``. A new command restarts the ramp, so when commands arrive more often than ``fTargetTime``, the commanded torque never reaches ``fMotorTor``. In a 1 ms loop, set ``fTargetTime`` to 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, |br| 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 :ref:`set_rt_control_input `. - The controller adds no gravity or friction compensation to ``fMotorTor``. Add the ``gravity_torque`` value from :ref:`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 :ref:`set_velj_rt `, :ref:`set_accj_rt `, :ref:`set_velx_rt ` and :ref:`set_accx_rt ` do not apply to ``torque_rt``. The joint and TCP speed limits of the safety settings stay monitored while ``torque_rt`` is active. - ``control_mode`` in the RT output data changes from ``CONTROL_MODE_POSITION`` (3) to ``CONTROL_MODE_TORQUE`` (4) on the first accepted command, and returns to 3 when the robot stops, pauses or times out. - The controller accepts ``torque_rt`` while the robot is in ``STATE_STANDBY`` or ``STATE_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**. |br| 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: |br| :math:`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** .. list-table:: :widths: 20 80 :header-rows: 1 * - **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 :ref:`set_on_log_alarm `. :ref:`set_on_rt_log_alarm ` reports only RT session messages, so register both callbacks. **Example** The example opens the controller connection with :ref:`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``. .. code-block:: cpp #include #include #include #include #include std::atomic 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_rt`` for **advanced torque-level control** or **compliance-based applications**. - Always monitor current joint torque using :ref:`read_data_rt ` for feedback verification. - Apply smooth torque transitions to avoid abrupt mechanical stress.