|
19 | 19 | //---------------------------------------------------------------------- |
20 | 20 | /*!\file |
21 | 21 | * |
22 | | - * \author Felix Exner exner@fzi.de |
23 | | - * \date 2019-04-18 |
| 22 | + * \author Marvin Große Besselmann grosse@fzi.de |
| 23 | + * \date 2021-02-18 |
24 | 24 | * |
25 | 25 | */ |
26 | 26 | //---------------------------------------------------------------------- |
27 | 27 |
|
28 | 28 | #include "ur_controllers/scaled_joint_trajectory_controller.h" |
| 29 | + |
| 30 | +namespace ur_controllers |
| 31 | +{ |
| 32 | +controller_interface::InterfaceConfiguration ScaledJointTrajectoryController::state_interface_configuration() const |
| 33 | +{ |
| 34 | + controller_interface::InterfaceConfiguration conf; |
| 35 | + conf = JointTrajectoryController::state_interface_configuration(); |
| 36 | + conf.names.push_back("speed_scaling/speed_scaling_factor"); |
| 37 | + return conf; |
| 38 | +} |
| 39 | + |
| 40 | +rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn |
| 41 | +ScaledJointTrajectoryController::on_activate(const rclcpp_lifecycle::State& state) |
| 42 | +{ |
| 43 | + TimeData time_data; |
| 44 | + time_data.time = node_->now(); |
| 45 | + time_data.period = rclcpp::Duration(0, 0); |
| 46 | + time_data.uptime = node_->now(); |
| 47 | + time_data_.initRT(time_data); |
| 48 | + return JointTrajectoryController::on_activate(state); |
| 49 | +} |
| 50 | + |
| 51 | +controller_interface::return_type ScaledJointTrajectoryController::update() |
| 52 | +{ |
| 53 | + if (state_interfaces_.back().get_name() == "speed_scaling") |
| 54 | + { |
| 55 | + scaling_factor_ = state_interfaces_.back().get_value(); |
| 56 | + } |
| 57 | + else |
| 58 | + { |
| 59 | + RCLCPP_ERROR(get_node()->get_logger(), "Speed scaling interface not found in hardware interface."); |
| 60 | + } |
| 61 | + |
| 62 | + if (get_current_state().id() == lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE) |
| 63 | + { |
| 64 | + if (!is_halted) |
| 65 | + { |
| 66 | + halt(); |
| 67 | + is_halted = true; |
| 68 | + } |
| 69 | + return controller_interface::return_type::SUCCESS; |
| 70 | + } |
| 71 | + |
| 72 | + auto resize_joint_trajectory_point = [](trajectory_msgs::msg::JointTrajectoryPoint& point, size_t size) { |
| 73 | + point.positions.resize(size); |
| 74 | + point.velocities.resize(size); |
| 75 | + point.accelerations.resize(size); |
| 76 | + }; |
| 77 | + auto compute_error_for_joint = [](JointTrajectoryPoint& error, int index, const JointTrajectoryPoint& current, |
| 78 | + const JointTrajectoryPoint& desired) { |
| 79 | + // error defined as the difference between current and desired |
| 80 | + error.positions[index] = angles::shortest_angular_distance(current.positions[index], desired.positions[index]); |
| 81 | + error.velocities[index] = desired.velocities[index] - current.velocities[index]; |
| 82 | + error.accelerations[index] = 0.0; |
| 83 | + }; |
| 84 | + |
| 85 | + // Check if a new external message has been received from nonRT threads |
| 86 | + auto current_external_msg = traj_external_point_ptr_->get_trajectory_msg(); |
| 87 | + auto new_external_msg = traj_msg_external_point_ptr_.readFromRT(); |
| 88 | + if (current_external_msg != *new_external_msg) |
| 89 | + { |
| 90 | + fill_partial_goal(*new_external_msg); |
| 91 | + sort_to_local_joint_order(*new_external_msg); |
| 92 | + traj_external_point_ptr_->update(*new_external_msg); |
| 93 | + } |
| 94 | + |
| 95 | + JointTrajectoryPoint state_current, state_desired, state_error; |
| 96 | + const auto joint_num = joint_names_.size(); |
| 97 | + resize_joint_trajectory_point(state_current, joint_num); |
| 98 | + |
| 99 | + // current state update |
| 100 | + for (auto index = 0ul; index < joint_num; ++index) |
| 101 | + { |
| 102 | + state_current.positions[index] = joint_position_state_interface_[index].get().get_value(); |
| 103 | + state_current.velocities[index] = joint_velocity_state_interface_[index].get().get_value(); |
| 104 | + state_current.accelerations[index] = 0.0; |
| 105 | + } |
| 106 | + state_current.time_from_start.set__sec(0); |
| 107 | + |
| 108 | + // currently carrying out a trajectory |
| 109 | + if (traj_point_active_ptr_ && !(*traj_point_active_ptr_)->has_trajectory_msg()) |
| 110 | + { |
| 111 | + // if sampling the first time, set the point before you sample |
| 112 | + if (!(*traj_point_active_ptr_)->is_sampled_already()) |
| 113 | + { |
| 114 | + (*traj_point_active_ptr_)->set_point_before_trajectory_msg(node_->now(), state_current); |
| 115 | + } |
| 116 | + resize_joint_trajectory_point(state_error, joint_num); |
| 117 | + |
| 118 | + // Main Speed scaling difference... |
| 119 | + // Adjust time with scaling factor |
| 120 | + TimeData time_data; |
| 121 | + time_data.time = node_->now(); |
| 122 | + rcl_duration_value_t period = (time_data.time - time_data_.readFromRT()->time).nanoseconds(); |
| 123 | + time_data.period = rclcpp::Duration(scaling_factor_ * period); |
| 124 | + time_data.uptime = time_data_.readFromRT()->uptime + time_data.period; |
| 125 | + rclcpp::Time traj_time = time_data_.readFromRT()->uptime + rclcpp::Duration(period); |
| 126 | + time_data_.writeFromNonRT(time_data); |
| 127 | + |
| 128 | + // find segment for current timestamp |
| 129 | + joint_trajectory_controller::TrajectoryPointConstIter start_segment_itr, end_segment_itr; |
| 130 | + const bool valid_point = |
| 131 | + (*traj_point_active_ptr_)->sample(traj_time, state_desired, start_segment_itr, end_segment_itr); |
| 132 | + |
| 133 | + if (valid_point) |
| 134 | + { |
| 135 | + bool abort = false; |
| 136 | + bool outside_goal_tolerance = false; |
| 137 | + const bool before_last_point = end_segment_itr != (*traj_point_active_ptr_)->end(); |
| 138 | + for (auto index = 0ul; index < joint_num; ++index) |
| 139 | + { |
| 140 | + // set values for next hardware write() |
| 141 | + joint_position_command_interface_[index].get().set_value(state_desired.positions[index]); |
| 142 | + compute_error_for_joint(state_error, index, state_current, state_desired); |
| 143 | + |
| 144 | + if (before_last_point && |
| 145 | + !check_state_tolerance_per_joint(state_error, index, default_tolerances_.state_tolerance[index], true)) |
| 146 | + { |
| 147 | + abort = true; |
| 148 | + } |
| 149 | + // past the final point, check that we end up inside goal tolerance |
| 150 | + if (!before_last_point && |
| 151 | + !check_state_tolerance_per_joint(state_error, index, default_tolerances_.goal_state_tolerance[index], true)) |
| 152 | + { |
| 153 | + outside_goal_tolerance = true; |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + if (rt_active_goal_) |
| 158 | + { |
| 159 | + // send feedback |
| 160 | + auto feedback = std::make_shared<FollowJTrajAction::Feedback>(); |
| 161 | + feedback->header.stamp = node_->now(); |
| 162 | + feedback->joint_names = joint_names_; |
| 163 | + |
| 164 | + feedback->actual = state_current; |
| 165 | + feedback->desired = state_desired; |
| 166 | + feedback->error = state_error; |
| 167 | + rt_active_goal_->setFeedback(feedback); |
| 168 | + |
| 169 | + // check abort |
| 170 | + if (abort || outside_goal_tolerance) |
| 171 | + { |
| 172 | + auto result = std::make_shared<FollowJTrajAction::Result>(); |
| 173 | + |
| 174 | + if (abort) |
| 175 | + { |
| 176 | + RCLCPP_WARN(node_->get_logger(), "Aborted due to state tolerance violation"); |
| 177 | + result->set__error_code(FollowJTrajAction::Result::PATH_TOLERANCE_VIOLATED); |
| 178 | + } |
| 179 | + else if (outside_goal_tolerance) |
| 180 | + { |
| 181 | + RCLCPP_WARN(node_->get_logger(), "Aborted due to goal tolerance violation"); |
| 182 | + result->set__error_code(FollowJTrajAction::Result::GOAL_TOLERANCE_VIOLATED); |
| 183 | + } |
| 184 | + rt_active_goal_->setAborted(result); |
| 185 | + rt_active_goal_.reset(); |
| 186 | + } |
| 187 | + |
| 188 | + // check goal tolerance |
| 189 | + if (!before_last_point) |
| 190 | + { |
| 191 | + if (!outside_goal_tolerance) |
| 192 | + { |
| 193 | + auto res = std::make_shared<FollowJTrajAction::Result>(); |
| 194 | + res->set__error_code(FollowJTrajAction::Result::SUCCESSFUL); |
| 195 | + rt_active_goal_->setSucceeded(res); |
| 196 | + rt_active_goal_.reset(); |
| 197 | + |
| 198 | + RCLCPP_INFO(node_->get_logger(), "Goal reached, success!"); |
| 199 | + } |
| 200 | + else if (default_tolerances_.goal_time_tolerance != 0.0) |
| 201 | + { |
| 202 | + // if we exceed goal_time_toleralance set it to aborted |
| 203 | + const rclcpp::Time traj_start = (*traj_point_active_ptr_)->get_trajectory_start_time(); |
| 204 | + const rclcpp::Time traj_end = traj_start + start_segment_itr->time_from_start; |
| 205 | + |
| 206 | + // TODO This will break in speed scaling we have to discuss how to handle the goal |
| 207 | + // time when the robot scales itself down. |
| 208 | + const double difference = node_->now().seconds() - traj_end.seconds(); |
| 209 | + if (difference > default_tolerances_.goal_time_tolerance) |
| 210 | + { |
| 211 | + auto result = std::make_shared<FollowJTrajAction::Result>(); |
| 212 | + result->set__error_code(FollowJTrajAction::Result::GOAL_TOLERANCE_VIOLATED); |
| 213 | + rt_active_goal_->setAborted(result); |
| 214 | + rt_active_goal_.reset(); |
| 215 | + RCLCPP_WARN(node_->get_logger(), "Aborted due goal_time_tolerance exceeding by %f seconds", difference); |
| 216 | + } |
| 217 | + } |
| 218 | + } |
| 219 | + } |
| 220 | + } |
| 221 | + } |
| 222 | + |
| 223 | + publish_state(state_desired, state_current, state_error); |
| 224 | + return controller_interface::return_type::SUCCESS; |
| 225 | +} |
| 226 | + |
| 227 | +} // namespace ur_controllers |
| 228 | + |
| 229 | +#include "pluginlib/class_list_macros.hpp" |
| 230 | +PLUGINLIB_EXPORT_CLASS(ur_controllers::ScaledJointTrajectoryController, controller_interface::ControllerInterface) |
0 commit comments