Skip to content

Commit 461d05c

Browse files
srvaldmergify[bot]
authored andcommitted
Allow setting payload inertia matrix via set_payload service backwards-compatible (#1811)
Extend set_payload service with inertia matrix and transition_time (cherry picked from commit 3d18755) # Conflicts: # ur_controllers/src/gpio_controller.cpp # ur_robot_driver/src/hardware_interface.cpp # ur_robot_driver/urdf/ur.ros2_control.xacro
1 parent bb6d99e commit 461d05c

7 files changed

Lines changed: 579 additions & 10 deletions

File tree

ur_controllers/include/ur_controllers/gpio_controller.hpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ enum CommandInterfaces
8282
HAND_BACK_CONTROL_CMD = 33,
8383
HAND_BACK_CONTROL_ASYNC_SUCCESS = 34,
8484
ANALOG_OUTPUTS_DOMAIN = 35,
85+
PAYLOAD_INERTIA_IXX = 36,
86+
PAYLOAD_INERTIA_IYY = 37,
87+
PAYLOAD_INERTIA_IZZ = 38,
88+
PAYLOAD_INERTIA_IXY = 39,
89+
PAYLOAD_INERTIA_IXZ = 40,
90+
PAYLOAD_INERTIA_IYZ = 41,
91+
PAYLOAD_TRANSITION_TIME = 42,
8592
};
8693

8794
enum StateInterfaces
@@ -107,6 +114,12 @@ enum StateInterfaces
107114
PAYLOAD_STATE_COG_X = 72,
108115
PAYLOAD_STATE_COG_Y = 73,
109116
PAYLOAD_STATE_COG_Z = 74,
117+
PAYLOAD_STATE_INERTIA_IXX = 75,
118+
PAYLOAD_STATE_INERTIA_IYY = 76,
119+
PAYLOAD_STATE_INERTIA_IZZ = 77,
120+
PAYLOAD_STATE_INERTIA_IXY = 78,
121+
PAYLOAD_STATE_INERTIA_IXZ = 79,
122+
PAYLOAD_STATE_INERTIA_IYZ = 80,
110123
};
111124

112125
class GPIOController : public controller_interface::ControllerInterface
@@ -210,7 +223,8 @@ class GPIOController : public controller_interface::ControllerInterface
210223
*/
211224
bool waitForAsyncCommand(std::function<double(void)> get_value);
212225

213-
bool waitForPayloadRtdeMatch(double mass, double cx, double cy, double cz);
226+
bool waitForPayloadRtdeMatch(double mass, double cx, double cy, double cz, double ixx, double iyy, double izz,
227+
double ixy, double ixz, double iyz, double transition_time);
214228
};
215229
} // namespace ur_controllers
216230

ur_controllers/src/gpio_controller.cpp

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@ controller_interface::InterfaceConfiguration GPIOController::command_interface_c
135135

136136
config.names.emplace_back(tf_prefix + "gpio/analog_output_domain_cmd");
137137

138+
config.names.emplace_back(tf_prefix + "payload/inertia.ixx");
139+
config.names.emplace_back(tf_prefix + "payload/inertia.iyy");
140+
config.names.emplace_back(tf_prefix + "payload/inertia.izz");
141+
config.names.emplace_back(tf_prefix + "payload/inertia.ixy");
142+
config.names.emplace_back(tf_prefix + "payload/inertia.ixz");
143+
config.names.emplace_back(tf_prefix + "payload/inertia.iyz");
144+
config.names.emplace_back(tf_prefix + "payload/transition_time");
145+
138146
return config;
139147
}
140148

@@ -200,6 +208,12 @@ controller_interface::InterfaceConfiguration ur_controllers::GPIOController::sta
200208
config.names.emplace_back(tf_prefix + "payload/cog.x");
201209
config.names.emplace_back(tf_prefix + "payload/cog.y");
202210
config.names.emplace_back(tf_prefix + "payload/cog.z");
211+
config.names.emplace_back(tf_prefix + "payload/inertia.ixx");
212+
config.names.emplace_back(tf_prefix + "payload/inertia.iyy");
213+
config.names.emplace_back(tf_prefix + "payload/inertia.izz");
214+
config.names.emplace_back(tf_prefix + "payload/inertia.ixy");
215+
config.names.emplace_back(tf_prefix + "payload/inertia.ixz");
216+
config.names.emplace_back(tf_prefix + "payload/inertia.iyz");
203217

204218
return config;
205219
}
@@ -593,10 +607,26 @@ bool GPIOController::setPayload(const ur_msgs::srv::SetPayload::Request::SharedP
593607
// reset success flag
594608
command_interfaces_[CommandInterfaces::PAYLOAD_ASYNC_SUCCESS].set_value(ASYNC_WAITING);
595609

610+
<<<<<<< HEAD
596611
command_interfaces_[CommandInterfaces::PAYLOAD_MASS].set_value(req->mass);
597612
command_interfaces_[CommandInterfaces::PAYLOAD_COG_X].set_value(req->center_of_gravity.x);
598613
command_interfaces_[CommandInterfaces::PAYLOAD_COG_Y].set_value(req->center_of_gravity.y);
599614
command_interfaces_[CommandInterfaces::PAYLOAD_COG_Z].set_value(req->center_of_gravity.z);
615+
=======
616+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_MASS].set_value(static_cast<double>(req->mass));
617+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_COG_X].set_value(req->center_of_gravity.x);
618+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_COG_Y].set_value(req->center_of_gravity.y);
619+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_COG_Z].set_value(req->center_of_gravity.z);
620+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_INERTIA_IXX].set_value(req->ixx);
621+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_INERTIA_IYY].set_value(req->iyy);
622+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_INERTIA_IZZ].set_value(req->izz);
623+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_INERTIA_IXY].set_value(req->ixy);
624+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_INERTIA_IXZ].set_value(req->ixz);
625+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_INERTIA_IYZ].set_value(req->iyz);
626+
double transition_time_seconds =
627+
static_cast<double>(req->transition_time.sec) + (static_cast<double>(req->transition_time.nanosec) * 1e-9);
628+
std::ignore = command_interfaces_[CommandInterfaces::PAYLOAD_TRANSITION_TIME].set_value(transition_time_seconds);
629+
>>>>>>> 3d18755 (Allow setting payload inertia matrix via set_payload service backwards-compatible (#1811))
600630

601631
if (!waitForAsyncCommand(
602632
[&]() { return command_interfaces_[CommandInterfaces::PAYLOAD_ASYNC_SUCCESS].get_value(); })) {
@@ -613,8 +643,10 @@ bool GPIOController::setPayload(const ur_msgs::srv::SetPayload::Request::SharedP
613643

614644
if (params_.verify_payload_on_set) {
615645
if (!waitForPayloadRtdeMatch(static_cast<double>(req->mass), req->center_of_gravity.x, req->center_of_gravity.y,
616-
req->center_of_gravity.z)) {
617-
RCLCPP_WARN(get_node()->get_logger(), "setPayload reported success but RTDE payload / payload_cog do not match "
646+
req->center_of_gravity.z, req->ixx, req->iyy, req->izz, req->ixy, req->ixz, req->iyz,
647+
transition_time_seconds)) {
648+
RCLCPP_WARN(get_node()->get_logger(), "setPayload reported success but RTDE payload / payload_cog / "
649+
"payload_inertia do not match "
618650
"the "
619651
"request yet. (This might "
620652
"happen when using the mocked interface.)");
@@ -680,20 +712,47 @@ bool GPIOController::waitForAsyncCommand(std::function<double(void)> get_value)
680712
return true;
681713
}
682714

683-
bool GPIOController::waitForPayloadRtdeMatch(double mass, double cx, double cy, double cz)
715+
bool GPIOController::waitForPayloadRtdeMatch(double mass, double cx, double cy, double cz, double ixx, double iyy,
716+
double izz, double ixy, double ixz, double iyz, double transition_time)
684717
{
685718
constexpr double tol_mass = 1e-3;
686719
constexpr double tol_cog = 1e-4;
720+
constexpr double tol_inertia = 1e-4;
687721
const auto maximum_retries = params_.check_io_successfull_retries;
688722

723+
if (transition_time > 0.0) {
724+
std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<int>(transition_time * 1000)));
725+
}
726+
689727
for (int retries = 0; retries <= maximum_retries; ++retries) {
728+
<<<<<<< HEAD
690729
const auto m = state_interfaces_[StateInterfaces::PAYLOAD_STATE_MASS].get_value();
691730
const auto sx = state_interfaces_[StateInterfaces::PAYLOAD_STATE_COG_X].get_value();
692731
const auto sy = state_interfaces_[StateInterfaces::PAYLOAD_STATE_COG_Y].get_value();
693732
const auto sz = state_interfaces_[StateInterfaces::PAYLOAD_STATE_COG_Z].get_value();
694733
if (std::abs(m - mass) <= tol_mass && std::abs(sx - cx) <= tol_cog && std::abs(sy - cy) <= tol_cog &&
695734
std::abs(sz - cz) <= tol_cog) {
696735
return true;
736+
=======
737+
const auto m = state_interfaces_[StateInterfaces::PAYLOAD_STATE_MASS].get_optional();
738+
const auto sx = state_interfaces_[StateInterfaces::PAYLOAD_STATE_COG_X].get_optional();
739+
const auto sy = state_interfaces_[StateInterfaces::PAYLOAD_STATE_COG_Y].get_optional();
740+
const auto sz = state_interfaces_[StateInterfaces::PAYLOAD_STATE_COG_Z].get_optional();
741+
const auto sixx = state_interfaces_[StateInterfaces::PAYLOAD_STATE_INERTIA_IXX].get_optional();
742+
const auto siyy = state_interfaces_[StateInterfaces::PAYLOAD_STATE_INERTIA_IYY].get_optional();
743+
const auto sizz = state_interfaces_[StateInterfaces::PAYLOAD_STATE_INERTIA_IZZ].get_optional();
744+
const auto sixy = state_interfaces_[StateInterfaces::PAYLOAD_STATE_INERTIA_IXY].get_optional();
745+
const auto sixz = state_interfaces_[StateInterfaces::PAYLOAD_STATE_INERTIA_IXZ].get_optional();
746+
const auto siyz = state_interfaces_[StateInterfaces::PAYLOAD_STATE_INERTIA_IYZ].get_optional();
747+
if (m && sx && sy && sz && sixx && siyy && sizz && sixy && sixz && siyz) {
748+
if (std::abs(*m - mass) <= tol_mass && std::abs(*sx - cx) <= tol_cog && std::abs(*sy - cy) <= tol_cog &&
749+
std::abs(*sz - cz) <= tol_cog && std::abs(*sixx - ixx) <= tol_inertia &&
750+
std::abs(*siyy - iyy) <= tol_inertia && std::abs(*sizz - izz) <= tol_inertia &&
751+
std::abs(*sixy - ixy) <= tol_inertia && std::abs(*sixz - ixz) <= tol_inertia &&
752+
std::abs(*siyz - iyz) <= tol_inertia) {
753+
return true;
754+
}
755+
>>>>>>> 3d18755 (Allow setting payload inertia matrix via set_payload service backwards-compatible (#1811))
697756
}
698757
std::this_thread::sleep_for(std::chrono::milliseconds(50));
699758
}

ur_robot_driver/include/ur_robot_driver/hardware_interface.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,13 @@ class URPositionHardwareInterface : public hardware_interface::SystemInterface
262262

263263
// Payload stuff
264264
urcl::vector3d_t payload_center_of_gravity_;
265+
urcl::vector6d_t payload_inertia_;
265266
double payload_mass_;
267+
double payload_transition_time_;
266268
double payload_async_success_;
267269
double rtde_payload_mass_ = 0.0;
268270
urcl::vector3d_t rtde_payload_cog_{ 0.0, 0.0, 0.0 };
271+
urcl::vector6d_t rtde_payload_inertia_{ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
269272

270273
// Friction model parameters
271274
urcl::vector6d_t friction_model_viscous_;

ur_robot_driver/resources/rtde_output_recipe.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ actual_current
2929
tcp_offset
3030
payload
3131
payload_cog
32+
payload_inertia

ur_robot_driver/src/hardware_interface.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,26 @@ std::vector<hardware_interface::StateInterface> URPositionHardwareInterface::exp
350350
hardware_interface::StateInterface(tf_prefix + "payload", "cog.y", &rtde_payload_cog_[1]));
351351
state_interfaces.emplace_back(
352352
hardware_interface::StateInterface(tf_prefix + "payload", "cog.z", &rtde_payload_cog_[2]));
353+
<<<<<<< HEAD
354+
=======
355+
state_interfaces.emplace_back(
356+
hardware_interface::StateInterface(tf_prefix + "payload", "inertia.ixx", &rtde_payload_inertia_[0]));
357+
state_interfaces.emplace_back(
358+
hardware_interface::StateInterface(tf_prefix + "payload", "inertia.iyy", &rtde_payload_inertia_[1]));
359+
state_interfaces.emplace_back(
360+
hardware_interface::StateInterface(tf_prefix + "payload", "inertia.izz", &rtde_payload_inertia_[2]));
361+
state_interfaces.emplace_back(
362+
hardware_interface::StateInterface(tf_prefix + "payload", "inertia.ixy", &rtde_payload_inertia_[3]));
363+
state_interfaces.emplace_back(
364+
hardware_interface::StateInterface(tf_prefix + "payload", "inertia.ixz", &rtde_payload_inertia_[4]));
365+
state_interfaces.emplace_back(
366+
hardware_interface::StateInterface(tf_prefix + "payload", "inertia.iyz", &rtde_payload_inertia_[5]));
367+
// Motion primitives stuff
368+
state_interfaces.emplace_back(hardware_interface::StateInterface(tf_prefix + HW_IF_MOTION_PRIMITIVES,
369+
"execution_status", &hw_moprim_states_[0]));
370+
state_interfaces.emplace_back(hardware_interface::StateInterface(tf_prefix + HW_IF_MOTION_PRIMITIVES,
371+
"ready_for_new_primitive", &hw_moprim_states_[1]));
372+
>>>>>>> 3d18755 (Allow setting payload inertia matrix via set_payload service backwards-compatible (#1811))
353373

354374
return state_interfaces;
355375
}
@@ -407,6 +427,20 @@ std::vector<hardware_interface::CommandInterface> URPositionHardwareInterface::e
407427
hardware_interface::CommandInterface(tf_prefix + "payload", "cog.y", &payload_center_of_gravity_[1]));
408428
command_interfaces.emplace_back(
409429
hardware_interface::CommandInterface(tf_prefix + "payload", "cog.z", &payload_center_of_gravity_[2]));
430+
command_interfaces.emplace_back(
431+
hardware_interface::CommandInterface(tf_prefix + "payload", "inertia.ixx", &payload_inertia_[0]));
432+
command_interfaces.emplace_back(
433+
hardware_interface::CommandInterface(tf_prefix + "payload", "inertia.iyy", &payload_inertia_[1]));
434+
command_interfaces.emplace_back(
435+
hardware_interface::CommandInterface(tf_prefix + "payload", "inertia.izz", &payload_inertia_[2]));
436+
command_interfaces.emplace_back(
437+
hardware_interface::CommandInterface(tf_prefix + "payload", "inertia.ixy", &payload_inertia_[3]));
438+
command_interfaces.emplace_back(
439+
hardware_interface::CommandInterface(tf_prefix + "payload", "inertia.ixz", &payload_inertia_[4]));
440+
command_interfaces.emplace_back(
441+
hardware_interface::CommandInterface(tf_prefix + "payload", "inertia.iyz", &payload_inertia_[5]));
442+
command_interfaces.emplace_back(
443+
hardware_interface::CommandInterface(tf_prefix + "payload", "transition_time", &payload_transition_time_));
410444
command_interfaces.emplace_back(
411445
hardware_interface::CommandInterface(tf_prefix + "payload", "payload_async_success", &payload_async_success_));
412446

@@ -825,6 +859,7 @@ hardware_interface::return_type URPositionHardwareInterface::read(const rclcpp::
825859
readData(data_package_buffer_, "tcp_offset", tcp_offset_);
826860
readData(data_package_buffer_, "payload", rtde_payload_mass_);
827861
readData(data_package_buffer_, "payload_cog", rtde_payload_cog_);
862+
readData(data_package_buffer_, "payload_inertia", rtde_payload_inertia_);
828863

829864
// required transforms
830865
extractToolPose();
@@ -954,6 +989,8 @@ void URPositionHardwareInterface::initAsyncIO()
954989

955990
payload_mass_ = NO_NEW_CMD_;
956991
payload_center_of_gravity_ = { NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_ };
992+
payload_inertia_ = { NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_ };
993+
payload_transition_time_ = NO_NEW_CMD_;
957994

958995
friction_model_viscous_.fill(NO_NEW_CMD_);
959996
friction_model_coulomb_.fill(NO_NEW_CMD_);
@@ -1020,10 +1057,16 @@ void URPositionHardwareInterface::checkAsyncIO()
10201057

10211058
if (!std::isnan(payload_mass_) && !std::isnan(payload_center_of_gravity_[0]) &&
10221059
!std::isnan(payload_center_of_gravity_[1]) && !std::isnan(payload_center_of_gravity_[2]) &&
1023-
ur_driver_ != nullptr) {
1024-
payload_async_success_ = ur_driver_->setPayload(payload_mass_, payload_center_of_gravity_);
1060+
!std::isnan(payload_inertia_[0]) && !std::isnan(payload_inertia_[1]) && !std::isnan(payload_inertia_[2]) &&
1061+
!std::isnan(payload_inertia_[3]) && !std::isnan(payload_inertia_[4]) && !std::isnan(payload_inertia_[5]) &&
1062+
!std::isnan(payload_transition_time_) && ur_driver_ != nullptr) {
1063+
payload_async_success_ = ur_driver_->setTargetPayload(payload_mass_, payload_center_of_gravity_, payload_inertia_,
1064+
payload_transition_time_);
1065+
10251066
payload_mass_ = NO_NEW_CMD_;
10261067
payload_center_of_gravity_ = { NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_ };
1068+
payload_inertia_ = { NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_, NO_NEW_CMD_ };
1069+
payload_transition_time_ = NO_NEW_CMD_;
10271070
}
10281071

10291072
if (!std::isnan(friction_model_viscous_[0]) && ur_driver_ != nullptr) {

ur_robot_driver/test/integration_test_io_controller.py

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@
3333
import time
3434
import unittest
3535

36-
import launch_testing
3736
import pytest
3837
import rclpy
3938
from geometry_msgs.msg import Vector3
4039
from rclpy.node import Node
4140
from ur_msgs.msg import IOStates
4241

42+
from builtin_interfaces.msg import Duration as DurationMsg
43+
4344
sys.path.append(os.path.dirname(__file__))
4445
from test_common import ( # noqa: E402
4546
ControllerManagerInterface,
@@ -50,9 +51,8 @@
5051

5152

5253
@pytest.mark.launch_test
53-
@launch_testing.parametrize("tf_prefix", [(""), ("my_ur_")])
54-
def generate_test_description(tf_prefix):
55-
return generate_driver_test_description(tf_prefix=tf_prefix)
54+
def generate_test_description():
55+
return generate_driver_test_description()
5656

5757

5858
class IOControllerTest(unittest.TestCase):
@@ -159,3 +159,61 @@ def test_set_payload(self):
159159
mass=0.0, center_of_gravity=Vector3(x=0.0, y=0.0, z=0.0)
160160
)
161161
self.assertTrue(result.success, "Resetting payload via set_payload failed")
162+
163+
def test_set_payload_with_inertia(self):
164+
"""Setting mass, COG and full inertia matrix should succeed."""
165+
res = self._io_status_controller_interface.set_payload(
166+
mass=1.0,
167+
center_of_gravity=Vector3(x=0.1, y=0.0, z=0.2),
168+
ixx=0.01,
169+
iyy=0.01,
170+
izz=0.02,
171+
ixy=0.0,
172+
ixz=0.0,
173+
iyz=0.0,
174+
transition_time=DurationMsg(),
175+
)
176+
self.assertTrue(res.success)
177+
178+
def test_set_payload_with_transition_time(self):
179+
"""
180+
Setting payload with transition_time > 0 should succeed.
181+
182+
The service should wait for the transition to complete before verifying.
183+
"""
184+
res = self._io_status_controller_interface.set_payload(
185+
mass=1.0,
186+
center_of_gravity=Vector3(x=0.0, y=0.0, z=0.1),
187+
ixx=0.01,
188+
iyy=0.01,
189+
izz=0.02,
190+
transition_time=DurationMsg(sec=1, nanosec=0),
191+
)
192+
self.assertTrue(res.success)
193+
194+
def test_set_payload_updates_sequentially(self):
195+
"""Multiple sequential set_payload calls should all succeed."""
196+
payloads = [
197+
{"mass": 0.5, "cx": 0.0, "cy": 0.0, "cz": 0.1, "ixx": 0.005, "iyy": 0.005, "izz": 0.01},
198+
{"mass": 1.0, "cx": 0.1, "cy": 0.0, "cz": 0.2, "ixx": 0.01, "iyy": 0.01, "izz": 0.02},
199+
{
200+
"mass": 2.0,
201+
"cx": 0.05,
202+
"cy": 0.05,
203+
"cz": 0.15,
204+
"ixx": 0.02,
205+
"iyy": 0.02,
206+
"izz": 0.04,
207+
},
208+
{"mass": 0.0, "cx": 0.0, "cy": 0.0, "cz": 0.0, "ixx": 0.0, "iyy": 0.0, "izz": 0.0},
209+
]
210+
for p in payloads:
211+
res = self._io_status_controller_interface.set_payload(
212+
mass=p["mass"],
213+
center_of_gravity=Vector3(x=p["cx"], y=p["cy"], z=p["cz"]),
214+
ixx=p["ixx"],
215+
iyy=p["iyy"],
216+
izz=p["izz"],
217+
transition_time=DurationMsg(),
218+
)
219+
self.assertTrue(res.success, f"set_payload failed for m={p['mass']}")

0 commit comments

Comments
 (0)