ros2_control - rolling
Loading...
Searching...
No Matches
joint_limits_rosparam.hpp
1// Copyright 2020 PAL Robotics S.L.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
16
17#ifndef JOINT_LIMITS__JOINT_LIMITS_ROSPARAM_HPP_
18#define JOINT_LIMITS__JOINT_LIMITS_ROSPARAM_HPP_
19
20#include <fmt/compile.h>
21
22#include <limits>
23#include <string>
24#include <vector>
25
26#include "joint_limits/joint_limits.hpp"
27#include "rclcpp/node.hpp"
28#include "rclcpp_lifecycle/lifecycle_node.hpp"
29
30namespace // utilities
31{
39template <typename ParameterT>
40auto auto_declare(
41 const rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & param_itf,
42 const std::string & name, const ParameterT & default_value)
43{
44 if (!param_itf->has_parameter(name))
45 {
46 auto param_default_value = rclcpp::ParameterValue(default_value);
47 param_itf->declare_parameter(name, param_default_value);
48 }
49 return param_itf->get_parameter(name).get_value<ParameterT>();
50}
51} // namespace
52
53namespace joint_limits
54{
89 const std::string & joint_name,
90 const rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & param_itf,
91 const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr & logging_itf)
92{
93 const std::string param_base_name = fmt::format(FMT_COMPILE("joint_limits.{}"), joint_name);
94 try
95 {
96 auto_declare<bool>(param_itf, param_base_name + ".has_position_limits", false);
97 auto_declare<double>(
98 param_itf, param_base_name + ".min_position", std::numeric_limits<double>::quiet_NaN());
99 auto_declare<double>(
100 param_itf, param_base_name + ".max_position", std::numeric_limits<double>::quiet_NaN());
101 auto_declare<bool>(param_itf, param_base_name + ".has_velocity_limits", false);
102 auto_declare<double>(
103 param_itf, param_base_name + ".max_velocity", std::numeric_limits<double>::quiet_NaN());
104 auto_declare<bool>(param_itf, param_base_name + ".has_acceleration_limits", false);
105 auto_declare<double>(
106 param_itf, param_base_name + ".max_acceleration", std::numeric_limits<double>::quiet_NaN());
107 auto_declare<bool>(param_itf, param_base_name + ".has_deceleration_limits", false);
108 auto_declare<double>(
109 param_itf, param_base_name + ".max_deceleration", std::numeric_limits<double>::quiet_NaN());
110 auto_declare<bool>(param_itf, param_base_name + ".has_jerk_limits", false);
111 auto_declare<double>(
112 param_itf, param_base_name + ".max_jerk", std::numeric_limits<double>::quiet_NaN());
113 auto_declare<bool>(param_itf, param_base_name + ".has_effort_limits", false);
114 auto_declare<double>(
115 param_itf, param_base_name + ".max_effort", std::numeric_limits<double>::quiet_NaN());
116 auto_declare<bool>(param_itf, param_base_name + ".angle_wraparound", false);
117 auto_declare<bool>(param_itf, param_base_name + ".has_soft_limits", false);
118 auto_declare<double>(
119 param_itf, param_base_name + ".k_position", std::numeric_limits<double>::quiet_NaN());
120 auto_declare<double>(
121 param_itf, param_base_name + ".k_velocity", std::numeric_limits<double>::quiet_NaN());
122 auto_declare<double>(
123 param_itf, param_base_name + ".soft_lower_limit", std::numeric_limits<double>::quiet_NaN());
124 auto_declare<double>(
125 param_itf, param_base_name + ".soft_upper_limit", std::numeric_limits<double>::quiet_NaN());
126 }
127 catch (const std::exception & ex)
128 {
129 RCLCPP_ERROR(logging_itf->get_logger(), "%s", ex.what());
130 return false;
131 }
132 return true;
133}
134
147inline bool declare_parameters(const std::string & joint_name, const rclcpp::Node::SharedPtr & node)
148{
149 return declare_parameters(
150 joint_name, node->get_node_parameters_interface(), node->get_node_logging_interface());
151}
152
166 const std::string & joint_name, const rclcpp_lifecycle::LifecycleNode::SharedPtr & lifecycle_node)
167{
168 return declare_parameters(
169 joint_name, lifecycle_node->get_node_parameters_interface(),
170 lifecycle_node->get_node_logging_interface());
171}
172
232 const std::string & joint_name,
233 const rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & param_itf,
234 const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr & logging_itf,
235 JointLimits & limits)
236{
237 const std::string param_base_name = fmt::format(FMT_COMPILE("joint_limits.{}"), joint_name);
238 try
239 {
240 if (
241 !param_itf->has_parameter(param_base_name + ".has_position_limits") &&
242 !param_itf->has_parameter(param_base_name + ".min_position") &&
243 !param_itf->has_parameter(param_base_name + ".max_position") &&
244 !param_itf->has_parameter(param_base_name + ".has_velocity_limits") &&
245 !param_itf->has_parameter(param_base_name + ".max_velocity") &&
246 !param_itf->has_parameter(param_base_name + ".has_acceleration_limits") &&
247 !param_itf->has_parameter(param_base_name + ".max_acceleration") &&
248 !param_itf->has_parameter(param_base_name + ".has_deceleration_limits") &&
249 !param_itf->has_parameter(param_base_name + ".max_deceleration") &&
250 !param_itf->has_parameter(param_base_name + ".has_jerk_limits") &&
251 !param_itf->has_parameter(param_base_name + ".max_jerk") &&
252 !param_itf->has_parameter(param_base_name + ".has_effort_limits") &&
253 !param_itf->has_parameter(param_base_name + ".max_effort") &&
254 !param_itf->has_parameter(param_base_name + ".angle_wraparound"))
255 {
256 RCLCPP_ERROR(
257 logging_itf->get_logger(),
258 "No joint limits specification found for joint '%s' in the parameter server "
259 "(param name: %s).",
260 joint_name.c_str(), param_base_name.c_str());
261 return false;
262 }
263 }
264 catch (const std::exception & ex)
265 {
266 RCLCPP_ERROR(logging_itf->get_logger(), "%s", ex.what());
267 return false;
268 }
269
270 // Position limits
271 if (param_itf->has_parameter(param_base_name + ".has_position_limits"))
272 {
273 limits.has_position_limits =
274 param_itf->get_parameter(param_base_name + ".has_position_limits").as_bool();
275 if (
276 limits.has_position_limits && param_itf->has_parameter(param_base_name + ".min_position") &&
277 param_itf->has_parameter(param_base_name + ".max_position"))
278 {
279 limits.min_position = param_itf->get_parameter(param_base_name + ".min_position").as_double();
280 limits.max_position = param_itf->get_parameter(param_base_name + ".max_position").as_double();
281 }
282 else
283 {
284 limits.has_position_limits = false;
285 }
286
287 if (
288 !limits.has_position_limits &&
289 param_itf->has_parameter(param_base_name + ".angle_wraparound"))
290 {
291 limits.angle_wraparound =
292 param_itf->get_parameter(param_base_name + ".angle_wraparound").as_bool();
293 }
294 }
295
296 // Velocity limits
297 if (param_itf->has_parameter(param_base_name + ".has_velocity_limits"))
298 {
299 limits.has_velocity_limits =
300 param_itf->get_parameter(param_base_name + ".has_velocity_limits").as_bool();
301 if (limits.has_velocity_limits && param_itf->has_parameter(param_base_name + ".max_velocity"))
302 {
303 limits.max_velocity = param_itf->get_parameter(param_base_name + ".max_velocity").as_double();
304 }
305 else
306 {
307 limits.has_velocity_limits = false;
308 }
309 }
310
311 // Acceleration limits
312 if (param_itf->has_parameter(param_base_name + ".has_acceleration_limits"))
313 {
314 limits.has_acceleration_limits =
315 param_itf->get_parameter(param_base_name + ".has_acceleration_limits").as_bool();
316 if (
317 limits.has_acceleration_limits &&
318 param_itf->has_parameter(param_base_name + ".max_acceleration"))
319 {
320 limits.max_acceleration =
321 param_itf->get_parameter(param_base_name + ".max_acceleration").as_double();
322 }
323 else
324 {
325 limits.has_acceleration_limits = false;
326 }
327 }
328
329 // Deceleration limits
330 if (param_itf->has_parameter(param_base_name + ".has_deceleration_limits"))
331 {
332 limits.has_deceleration_limits =
333 param_itf->get_parameter(param_base_name + ".has_deceleration_limits").as_bool();
334 if (
335 limits.has_deceleration_limits &&
336 param_itf->has_parameter(param_base_name + ".max_deceleration"))
337 {
338 limits.max_deceleration =
339 param_itf->get_parameter(param_base_name + ".max_deceleration").as_double();
340 }
341 else
342 {
343 limits.has_deceleration_limits = false;
344 }
345 }
346
347 // Jerk limits
348 if (param_itf->has_parameter(param_base_name + ".has_jerk_limits"))
349 {
350 limits.has_jerk_limits =
351 param_itf->get_parameter(param_base_name + ".has_jerk_limits").as_bool();
352 if (limits.has_jerk_limits && param_itf->has_parameter(param_base_name + ".max_jerk"))
353 {
354 limits.max_jerk = param_itf->get_parameter(param_base_name + ".max_jerk").as_double();
355 }
356 else
357 {
358 limits.has_jerk_limits = false;
359 }
360 }
361
362 // Effort limits
363 if (param_itf->has_parameter(param_base_name + ".has_effort_limits"))
364 {
365 limits.has_effort_limits =
366 param_itf->get_parameter(param_base_name + ".has_effort_limits").as_bool();
367 if (limits.has_effort_limits && param_itf->has_parameter(param_base_name + ".max_effort"))
368 {
369 limits.has_effort_limits = true;
370 limits.max_effort = param_itf->get_parameter(param_base_name + ".max_effort").as_double();
371 }
372 else
373 {
374 limits.has_effort_limits = false;
375 }
376 }
377
378 return true;
379}
380
396 const std::string & joint_name, const rclcpp::Node::SharedPtr & node, JointLimits & limits)
397{
398 return get_joint_limits(
399 joint_name, node->get_node_parameters_interface(), node->get_node_logging_interface(), limits);
400}
401
417 const std::string & joint_name, const rclcpp_lifecycle::LifecycleNode::SharedPtr & lifecycle_node,
418 JointLimits & limits)
419{
420 return get_joint_limits(
421 joint_name, lifecycle_node->get_node_parameters_interface(),
422 lifecycle_node->get_node_logging_interface(), limits);
423}
424
440 const std::string & joint_name, const std::vector<rclcpp::Parameter> & parameters,
441 const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr & logging_itf,
442 JointLimits & updated_limits)
443{
444 const std::string param_base_name = fmt::format(FMT_COMPILE("joint_limits.{}"), joint_name);
445 bool changed = false;
446
447 // update first numerical values to make later checks for "has" limits members
448 for (auto & parameter : parameters)
449 {
450 const std::string param_name = parameter.get_name();
451 try
452 {
453 if (param_name == param_base_name + ".min_position")
454 {
455 changed = updated_limits.min_position != parameter.get_value<double>();
456 updated_limits.min_position = parameter.get_value<double>();
457 }
458 else if (param_name == param_base_name + ".max_position")
459 {
460 changed = updated_limits.max_position != parameter.get_value<double>();
461 updated_limits.max_position = parameter.get_value<double>();
462 }
463 else if (param_name == param_base_name + ".max_velocity")
464 {
465 changed = updated_limits.max_velocity != parameter.get_value<double>();
466 updated_limits.max_velocity = parameter.get_value<double>();
467 }
468 else if (param_name == param_base_name + ".max_acceleration")
469 {
470 changed = updated_limits.max_acceleration != parameter.get_value<double>();
471 updated_limits.max_acceleration = parameter.get_value<double>();
472 }
473 else if (param_name == param_base_name + ".max_deceleration")
474 {
475 changed = updated_limits.max_deceleration != parameter.get_value<double>();
476 updated_limits.max_deceleration = parameter.get_value<double>();
477 }
478 else if (param_name == param_base_name + ".max_jerk")
479 {
480 changed = updated_limits.max_jerk != parameter.get_value<double>();
481 updated_limits.max_jerk = parameter.get_value<double>();
482 }
483 else if (param_name == param_base_name + ".max_effort")
484 {
485 changed = updated_limits.max_effort != parameter.get_value<double>();
486 updated_limits.max_effort = parameter.get_value<double>();
487 }
488 }
489 catch (const rclcpp::exceptions::InvalidParameterTypeException & e)
490 {
491 RCLCPP_WARN(logging_itf->get_logger(), "Please use the right type: %s", e.what());
492 }
493 }
494
495 for (auto & parameter : parameters)
496 {
497 const std::string param_name = parameter.get_name();
498 try
499 {
500 if (param_name == param_base_name + ".has_position_limits")
501 {
502 updated_limits.has_position_limits = parameter.get_value<bool>();
503 if (updated_limits.has_position_limits)
504 {
505 if (std::isnan(updated_limits.min_position) || std::isnan(updated_limits.max_position))
506 {
507 RCLCPP_WARN(
508 logging_itf->get_logger(),
509 "PARAMETER NOT UPDATED: Position limits can not be used, i.e., "
510 "'has_position_limits' flag can not be set, if 'min_position' "
511 "and 'max_position' are not set or not have valid double values.");
512 updated_limits.has_position_limits = false;
513 }
514 else if (updated_limits.min_position >= updated_limits.max_position)
515 {
516 RCLCPP_WARN(
517 logging_itf->get_logger(),
518 "PARAMETER NOT UPDATED: Position limits can not be used, i.e., "
519 "'has_position_limits' flag can not be set, if not "
520 "'min_position' < 'max_position'");
521 updated_limits.has_position_limits = false;
522 }
523 else
524 {
525 changed = true;
526 }
527 }
528 }
529 else if (param_name == param_base_name + ".has_velocity_limits")
530 {
531 updated_limits.has_velocity_limits = parameter.get_value<bool>();
532 if (updated_limits.has_velocity_limits && std::isnan(updated_limits.max_velocity))
533 {
534 RCLCPP_WARN(
535 logging_itf->get_logger(),
536 "PARAMETER NOT UPDATED: 'has_velocity_limits' flag can not be set if 'min_velocity' "
537 "and 'max_velocity' are not set or not have valid double values.");
538 updated_limits.has_velocity_limits = false;
539 }
540 else
541 {
542 changed = true;
543 }
544 }
545 else if (param_name == param_base_name + ".has_acceleration_limits")
546 {
547 updated_limits.has_acceleration_limits = parameter.get_value<bool>();
548 if (updated_limits.has_acceleration_limits && std::isnan(updated_limits.max_acceleration))
549 {
550 RCLCPP_WARN(
551 logging_itf->get_logger(),
552 "PARAMETER NOT UPDATED: 'has_acceleration_limits' flag can not be set if "
553 "'max_acceleration' is not set or not have valid double values.");
554 updated_limits.has_acceleration_limits = false;
555 }
556 else
557 {
558 changed = true;
559 }
560 }
561 else if (param_name == param_base_name + ".has_deceleration_limits")
562 {
563 updated_limits.has_deceleration_limits = parameter.get_value<bool>();
564 if (updated_limits.has_deceleration_limits && std::isnan(updated_limits.max_deceleration))
565 {
566 RCLCPP_WARN(
567 logging_itf->get_logger(),
568 "PARAMETER NOT UPDATED: 'has_deceleration_limits' flag can not be set if "
569 "'max_deceleration' is not set or not have valid double values.");
570 updated_limits.has_deceleration_limits = false;
571 }
572 else
573 {
574 changed = true;
575 }
576 }
577 else if (param_name == param_base_name + ".has_jerk_limits")
578 {
579 updated_limits.has_jerk_limits = parameter.get_value<bool>();
580 if (updated_limits.has_jerk_limits && std::isnan(updated_limits.max_jerk))
581 {
582 RCLCPP_WARN(
583 logging_itf->get_logger(),
584 "PARAMETER NOT UPDATED: 'has_jerk_limits' flag can not be set if 'max_jerk' is not set "
585 "or not have valid double values.");
586 updated_limits.has_jerk_limits = false;
587 }
588 else
589 {
590 changed = true;
591 }
592 }
593 else if (param_name == param_base_name + ".has_effort_limits")
594 {
595 updated_limits.has_effort_limits = parameter.get_value<bool>();
596 if (updated_limits.has_effort_limits && std::isnan(updated_limits.max_effort))
597 {
598 RCLCPP_WARN(
599 logging_itf->get_logger(),
600 "PARAMETER NOT UPDATED: 'has_effort_limits' flag can not be set if 'max_effort' is not "
601 "set or not have valid double values.");
602 updated_limits.has_effort_limits = false;
603 }
604 else
605 {
606 changed = true;
607 }
608 }
609 else if (param_name == param_base_name + ".angle_wraparound")
610 {
611 updated_limits.angle_wraparound = parameter.get_value<bool>();
612 if (updated_limits.angle_wraparound && updated_limits.has_position_limits)
613 {
614 RCLCPP_WARN(
615 logging_itf->get_logger(),
616 "PARAMETER NOT UPDATED: 'angle_wraparound' flag can not be set if "
617 "'has_position_limits' flag is set.");
618 updated_limits.angle_wraparound = false;
619 }
620 else
621 {
622 changed = true;
623 }
624 }
625 }
626 catch (const rclcpp::exceptions::InvalidParameterTypeException & e)
627 {
628 RCLCPP_WARN(
629 logging_itf->get_logger(), "PARAMETER NOT UPDATED: Please use the right type: %s",
630 e.what());
631 }
632 }
633
634 return changed;
635}
636
672 const std::string & joint_name,
673 const rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & param_itf,
674 const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr & logging_itf,
675 SoftJointLimits & soft_limits)
676{
677 const std::string param_base_name = fmt::format(FMT_COMPILE("joint_limits.{}"), joint_name);
678 try
679 {
680 if (
681 !param_itf->has_parameter(param_base_name + ".has_soft_limits") &&
682 !param_itf->has_parameter(param_base_name + ".k_velocity") &&
683 !param_itf->has_parameter(param_base_name + ".k_position") &&
684 !param_itf->has_parameter(param_base_name + ".soft_lower_limit") &&
685 !param_itf->has_parameter(param_base_name + ".soft_upper_limit"))
686 {
687 RCLCPP_DEBUG(
688 logging_itf->get_logger(),
689 "No soft joint limits specification found for joint '%s' in the parameter server "
690 "(param name: %s).",
691 joint_name.c_str(), param_base_name.c_str());
692 return false;
693 }
694 }
695 catch (const std::exception & ex)
696 {
697 RCLCPP_ERROR(logging_itf->get_logger(), "%s", ex.what());
698 return false;
699 }
700
701 // Override soft limits if complete specification is found
702 if (param_itf->has_parameter(param_base_name + ".has_soft_limits"))
703 {
704 if (
705 param_itf->get_parameter(param_base_name + ".has_soft_limits").as_bool() &&
706 param_itf->has_parameter(param_base_name + ".k_position") &&
707 param_itf->has_parameter(param_base_name + ".k_velocity") &&
708 param_itf->has_parameter(param_base_name + ".soft_lower_limit") &&
709 param_itf->has_parameter(param_base_name + ".soft_upper_limit"))
710 {
711 soft_limits.k_position =
712 param_itf->get_parameter(param_base_name + ".k_position").as_double();
713 soft_limits.k_velocity =
714 param_itf->get_parameter(param_base_name + ".k_velocity").as_double();
715 soft_limits.min_position =
716 param_itf->get_parameter(param_base_name + ".soft_lower_limit").as_double();
717 soft_limits.max_position =
718 param_itf->get_parameter(param_base_name + ".soft_upper_limit").as_double();
719 return true;
720 }
721 }
722
723 return false;
724}
725
740 const std::string & joint_name, const rclcpp::Node::SharedPtr & node,
741 SoftJointLimits & soft_limits)
742{
743 return get_joint_limits(
744 joint_name, node->get_node_parameters_interface(), node->get_node_logging_interface(),
745 soft_limits);
746}
747
762 const std::string & joint_name, const rclcpp_lifecycle::LifecycleNode::SharedPtr & lifecycle_node,
763 SoftJointLimits & soft_limits)
764{
765 return get_joint_limits(
766 joint_name, lifecycle_node->get_node_parameters_interface(),
767 lifecycle_node->get_node_logging_interface(), soft_limits);
768}
769
785 const std::string & joint_name, const std::vector<rclcpp::Parameter> & parameters,
786 const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr & logging_itf,
787 SoftJointLimits & updated_limits)
788{
789 const std::string param_base_name = fmt::format(FMT_COMPILE("joint_limits.{}"), joint_name);
790 bool changed = false;
791
792 for (auto & parameter : parameters)
793 {
794 const std::string param_name = parameter.get_name();
795 try
796 {
797 if (param_name == param_base_name + ".has_soft_limits")
798 {
799 if (!parameter.get_value<bool>())
800 {
801 RCLCPP_WARN(
802 logging_itf->get_logger(),
803 "Parameter 'has_soft_limits' is not set, therefore the limits will not be updated!");
804 return false;
805 }
806 }
807 }
808 catch (const rclcpp::exceptions::InvalidParameterTypeException & e)
809 {
810 RCLCPP_INFO(logging_itf->get_logger(), "Please use the right type: %s", e.what());
811 }
812 }
813
814 for (auto & parameter : parameters)
815 {
816 const std::string param_name = parameter.get_name();
817 try
818 {
819 if (param_name == param_base_name + ".k_position")
820 {
821 changed = updated_limits.k_position != parameter.get_value<double>();
822 updated_limits.k_position = parameter.get_value<double>();
823 }
824 else if (param_name == param_base_name + ".k_velocity")
825 {
826 changed = updated_limits.k_velocity != parameter.get_value<double>();
827 updated_limits.k_velocity = parameter.get_value<double>();
828 }
829 else if (param_name == param_base_name + ".soft_lower_limit")
830 {
831 changed = updated_limits.min_position != parameter.get_value<double>();
832 updated_limits.min_position = parameter.get_value<double>();
833 }
834 else if (param_name == param_base_name + ".soft_upper_limit")
835 {
836 changed = updated_limits.max_position != parameter.get_value<double>();
837 updated_limits.max_position = parameter.get_value<double>();
838 }
839 }
840 catch (const rclcpp::exceptions::InvalidParameterTypeException & e)
841 {
842 RCLCPP_INFO(logging_itf->get_logger(), "Please use the right type: %s", e.what());
843 }
844 }
845
846 return changed;
847}
848
849} // namespace joint_limits
850
851#endif // JOINT_LIMITS__JOINT_LIMITS_ROSPARAM_HPP_
Definition data_structures.hpp:39
bool check_for_limits_update(const std::string &joint_name, const std::vector< rclcpp::Parameter > &parameters, const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr &logging_itf, JointLimits &updated_limits)
Check if any of updated parameters are related to JointLimits.
Definition joint_limits_rosparam.hpp:439
bool declare_parameters(const std::string &joint_name, const rclcpp::node_interfaces::NodeParametersInterface::SharedPtr &param_itf, const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr &logging_itf)
Declare JointLimits and SoftJointLimits parameters for joint with joint_name using node parameters in...
Definition joint_limits_rosparam.hpp:88
bool get_joint_limits(const std::string &joint_name, const rclcpp::node_interfaces::NodeParametersInterface::SharedPtr &param_itf, const rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr &logging_itf, JointLimits &limits)
Populate a JointLimits instance from the node parameters.
Definition joint_limits_rosparam.hpp:231
Store joint limits values from YAML definition or URDF <limits> tag.
Definition joint_limits.hpp:36
Store soft joint limits values from the URDF <safety_controller> tag.
Definition joint_limits.hpp:130