PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/joint_trajectory_controller/include/joint_trajectory_controller/joint_trajectory_controller_impl.h

https://github.com/benwr/ros_controllers
C Header | 712 lines | 501 code | 102 blank | 109 comment | 58 complexity | 1055ad4d77585d19f773241d3fdfa27d MD5 | raw file
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) 2013, PAL Robotics S.L.
  3. // Copyright (c) 2008, Willow Garage, Inc.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright notice,
  8. // this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of PAL Robotics S.L. nor the names of its
  13. // contributors may be used to endorse or promote products derived from
  14. // this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  20. // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  21. // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  22. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  23. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  24. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  25. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  26. // POSSIBILITY OF SUCH DAMAGE.
  27. //////////////////////////////////////////////////////////////////////////////
  28. /// \author Adolfo Rodriguez Tsouroukdissian, Stuart Glaser
  29. #ifndef JOINT_TRAJECTORY_CONTROLLER_JOINT_TRAJECTORY_CONTROLLER_IMP_H
  30. #define JOINT_TRAJECTORY_CONTROLLER_JOINT_TRAJECTORY_CONTROLLER_IMP_H
  31. namespace joint_trajectory_controller
  32. {
  33. namespace internal
  34. {
  35. template <class Enclosure, class Member>
  36. inline boost::shared_ptr<Member> share_member(boost::shared_ptr<Enclosure> enclosure, Member &member)
  37. {
  38. actionlib::EnclosureDeleter<Enclosure> d(enclosure);
  39. boost::shared_ptr<Member> p(&member, d);
  40. return p;
  41. }
  42. std::vector<std::string> getStrings(const ros::NodeHandle& nh, const std::string& param_name)
  43. {
  44. using namespace XmlRpc;
  45. XmlRpcValue xml_array;
  46. if (!nh.getParam(param_name, xml_array))
  47. {
  48. ROS_ERROR_STREAM("Could not find '" << param_name << "' parameter (namespace: " << nh.getNamespace() << ").");
  49. return std::vector<std::string>();
  50. }
  51. if (xml_array.getType() != XmlRpcValue::TypeArray)
  52. {
  53. ROS_ERROR_STREAM("The '" << param_name << "' parameter is not an array (namespace: " <<
  54. nh.getNamespace() << ").");
  55. return std::vector<std::string>();
  56. }
  57. std::vector<std::string> out;
  58. for (int i = 0; i < xml_array.size(); ++i)
  59. {
  60. if (xml_array[i].getType() != XmlRpcValue::TypeString)
  61. {
  62. ROS_ERROR_STREAM("The '" << param_name << "' parameter contains a non-string element (namespace: " <<
  63. nh.getNamespace() << ").");
  64. return std::vector<std::string>();
  65. }
  66. out.push_back(static_cast<std::string>(xml_array[i]));
  67. }
  68. return out;
  69. }
  70. boost::shared_ptr<urdf::Model> getUrdf(const ros::NodeHandle& nh, const std::string& param_name)
  71. {
  72. boost::shared_ptr<urdf::Model> urdf(new urdf::Model);
  73. std::string urdf_str;
  74. // Check for robot_description in proper namespace
  75. if (nh.getParam(param_name, urdf_str))
  76. {
  77. if (!urdf->initString(urdf_str))
  78. {
  79. ROS_ERROR_STREAM("Failed to parse URDF contained in '" << param_name << "' parameter (namespace: " <<
  80. nh.getNamespace() << ").");
  81. return boost::shared_ptr<urdf::Model>();
  82. }
  83. }
  84. // Check for robot_description in root
  85. else if (!urdf->initParam("robot_description"))
  86. {
  87. ROS_ERROR_STREAM("Failed to parse URDF contained in '" << param_name << "' parameter");
  88. return boost::shared_ptr<urdf::Model>();
  89. }
  90. return urdf;
  91. }
  92. typedef boost::shared_ptr<const urdf::Joint> UrdfJointConstPtr;
  93. std::vector<UrdfJointConstPtr> getUrdfJoints(const urdf::Model& urdf, const std::vector<std::string>& joint_names)
  94. {
  95. std::vector<UrdfJointConstPtr> out;
  96. for (unsigned int i = 0; i < joint_names.size(); ++i)
  97. {
  98. UrdfJointConstPtr urdf_joint = urdf.getJoint(joint_names[i]);
  99. if (urdf_joint)
  100. {
  101. out.push_back(urdf_joint);
  102. }
  103. else
  104. {
  105. ROS_ERROR_STREAM("Could not find joint '" << joint_names[i] << "' in URDF model.");
  106. return std::vector<UrdfJointConstPtr>();
  107. }
  108. }
  109. return out;
  110. }
  111. std::string getLeafNamespace(const ros::NodeHandle& nh)
  112. {
  113. const std::string complete_ns = nh.getNamespace();
  114. std::size_t id = complete_ns.find_last_of("/");
  115. return complete_ns.substr(id + 1);
  116. }
  117. } // namespace
  118. template <class SegmentImpl, class HardwareInterface>
  119. inline void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  120. starting(const ros::Time& time)
  121. {
  122. // Update time data
  123. TimeData time_data;
  124. time_data.time = time;
  125. time_data.uptime = ros::Time(0.0);
  126. time_data_.initRT(time_data);
  127. // Hold current position
  128. setHoldPosition(time_data.uptime);
  129. // Initialize last state update time
  130. last_state_publish_time_ = time_data.uptime;
  131. // Hardware interface adapter
  132. hw_iface_adapter_.starting(time_data.uptime);
  133. }
  134. template <class SegmentImpl, class HardwareInterface>
  135. inline void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  136. stopping(const ros::Time& time)
  137. {
  138. preemptActiveGoal();
  139. }
  140. template <class SegmentImpl, class HardwareInterface>
  141. inline void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  142. trajectoryCommandCB(const JointTrajectoryConstPtr& msg)
  143. {
  144. const bool update_ok = updateTrajectoryCommand(msg, RealtimeGoalHandlePtr());
  145. if (update_ok) {preemptActiveGoal();}
  146. }
  147. template <class SegmentImpl, class HardwareInterface>
  148. inline void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  149. preemptActiveGoal()
  150. {
  151. RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
  152. // Cancels the currently active goal
  153. if (current_active_goal)
  154. {
  155. // Marks the current goal as canceled
  156. rt_active_goal_.reset();
  157. current_active_goal->gh_.setCanceled();
  158. }
  159. }
  160. template <class SegmentImpl, class HardwareInterface>
  161. inline void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  162. checkPathTolerances(const typename Segment::State& state_error,
  163. const Segment& segment)
  164. {
  165. const RealtimeGoalHandlePtr rt_segment_goal = segment.getGoalHandle();
  166. const SegmentTolerances<Scalar>& tolerances = segment.getTolerances();
  167. if (!checkStateTolerance(state_error, tolerances.state_tolerance))
  168. {
  169. rt_segment_goal->preallocated_result_->error_code =
  170. control_msgs::FollowJointTrajectoryResult::PATH_TOLERANCE_VIOLATED;
  171. rt_segment_goal->setAborted(rt_segment_goal->preallocated_result_);
  172. rt_active_goal_.reset();
  173. }
  174. }
  175. template <class SegmentImpl, class HardwareInterface>
  176. inline void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  177. checkGoalTolerances(const typename Segment::State& state_error,
  178. const Segment& segment)
  179. {
  180. // Controller uptime
  181. const ros::Time uptime = time_data_.readFromRT()->uptime;
  182. // Checks that we have ended inside the goal tolerances
  183. const RealtimeGoalHandlePtr rt_segment_goal = segment.getGoalHandle();
  184. const SegmentTolerances<Scalar>& tolerances = segment.getTolerances();
  185. const bool inside_goal_tolerances = checkStateTolerance(state_error, tolerances.goal_state_tolerance);
  186. if (inside_goal_tolerances)
  187. {
  188. rt_segment_goal->preallocated_result_->error_code = control_msgs::FollowJointTrajectoryResult::SUCCESSFUL;
  189. rt_segment_goal->setSucceeded(rt_segment_goal->preallocated_result_);
  190. rt_active_goal_.reset();
  191. }
  192. else if (uptime.toSec() < segment.endTime() + tolerances.goal_time_tolerance)
  193. {
  194. // Still have some time left to meet the goal state tolerances
  195. }
  196. else
  197. {
  198. if (verbose_)
  199. {
  200. ROS_ERROR_STREAM_NAMED(name_,"Goal tolerances failed");
  201. // Check the tolerances one more time to output the errors that occures
  202. checkStateTolerance(state_error, tolerances.goal_state_tolerance, true);
  203. }
  204. rt_segment_goal->preallocated_result_->error_code = control_msgs::FollowJointTrajectoryResult::GOAL_TOLERANCE_VIOLATED;
  205. rt_segment_goal->setAborted(rt_segment_goal->preallocated_result_);
  206. rt_active_goal_.reset();
  207. }
  208. }
  209. template <class SegmentImpl, class HardwareInterface>
  210. JointTrajectoryController<SegmentImpl, HardwareInterface>::
  211. JointTrajectoryController()
  212. : verbose_(false), // Set to true during debugging
  213. hold_trajectory_ptr_(new Trajectory)
  214. {}
  215. template <class SegmentImpl, class HardwareInterface>
  216. bool JointTrajectoryController<SegmentImpl, HardwareInterface>::init(HardwareInterface* hw,
  217. ros::NodeHandle& root_nh,
  218. ros::NodeHandle& controller_nh)
  219. {
  220. using namespace internal;
  221. // Cache controller node handle
  222. controller_nh_ = controller_nh;
  223. // Controller name
  224. name_ = getLeafNamespace(controller_nh_);
  225. // State publish rate
  226. double state_publish_rate = 50.0;
  227. controller_nh_.getParam("state_publish_rate", state_publish_rate);
  228. ROS_DEBUG_STREAM_NAMED(name_, "Controller state will be published at " << state_publish_rate << "Hz.");
  229. state_publisher_period_ = ros::Duration(1.0 / state_publish_rate);
  230. // Action status checking update rate
  231. double action_monitor_rate = 20.0;
  232. controller_nh_.getParam("action_monitor_rate", action_monitor_rate);
  233. action_monitor_period_ = ros::Duration(1.0 / action_monitor_rate);
  234. ROS_DEBUG_STREAM_NAMED(name_, "Action status changes will be monitored at " << action_monitor_rate << "Hz.");
  235. // Stop trajectory duration
  236. stop_trajectory_duration_ = 0.0;
  237. if (!controller_nh_.getParam("stop_trajectory_duration", stop_trajectory_duration_))
  238. {
  239. // TODO: Remove this check/warning in Indigo
  240. if (controller_nh_.getParam("hold_trajectory_duration", stop_trajectory_duration_))
  241. {
  242. ROS_WARN("The 'hold_trajectory_duration' has been deprecated in favor of the 'stop_trajectory_duration' parameter. Please update your controller configuration.");
  243. }
  244. }
  245. ROS_DEBUG_STREAM_NAMED(name_, "Stop trajectory has a duration of " << stop_trajectory_duration_ << "s.");
  246. // List of controlled joints
  247. joint_names_ = getStrings(controller_nh_, "joints");
  248. if (joint_names_.empty()) {return false;}
  249. const unsigned int n_joints = joint_names_.size();
  250. // URDF joints
  251. boost::shared_ptr<urdf::Model> urdf = getUrdf(root_nh, "robot_description");
  252. if (!urdf) {return false;}
  253. std::vector<UrdfJointConstPtr> urdf_joints = getUrdfJoints(*urdf, joint_names_);
  254. if (urdf_joints.empty()) {return false;}
  255. assert(n_joints == urdf_joints.size());
  256. // Initialize members
  257. joints_.resize(n_joints);
  258. angle_wraparound_.resize(n_joints);
  259. for (unsigned int i = 0; i < n_joints; ++i)
  260. {
  261. // Joint handle
  262. try {joints_[i] = hw->getHandle(joint_names_[i]);}
  263. catch (...)
  264. {
  265. ROS_ERROR_STREAM_NAMED(name_, "Could not find joint '" << joint_names_[i] << "' in '" <<
  266. this->getHardwareInterfaceType() << "'.");
  267. return false;
  268. }
  269. // Whether a joint is continuous (ie. has angle wraparound)
  270. angle_wraparound_[i] = urdf_joints[i]->type == urdf::Joint::CONTINUOUS;
  271. const std::string not_if = angle_wraparound_[i] ? "" : "non-";
  272. ROS_DEBUG_STREAM_NAMED(name_, "Found " << not_if << "continuous joint '" << joint_names_[i] << "' in '" <<
  273. this->getHardwareInterfaceType() << "'.");
  274. }
  275. assert(joints_.size() == angle_wraparound_.size());
  276. ROS_DEBUG_STREAM_NAMED(name_, "Initialized controller '" << name_ << "' with:" <<
  277. "\n- Number of joints: " << joints_.size() <<
  278. "\n- Hardware interface type: '" << this->getHardwareInterfaceType() << "'" <<
  279. "\n- Trajectory segment type: '" << hardware_interface::internal::demangledTypeName<SegmentImpl>() << "'");
  280. // Default tolerances
  281. ros::NodeHandle tol_nh(controller_nh_, "constraints");
  282. default_tolerances_ = getSegmentTolerances<Scalar>(tol_nh, joint_names_);
  283. // Hardware interface adapter
  284. hw_iface_adapter_.init(joints_, controller_nh_);
  285. // ROS API: Subscribed topics
  286. trajectory_command_sub_ = controller_nh_.subscribe("command", 1, &JointTrajectoryController::trajectoryCommandCB, this);
  287. // ROS API: Published topics
  288. state_publisher_.reset(new StatePublisher(controller_nh_, "state", 1));
  289. // ROS API: Action interface
  290. action_server_.reset(new ActionServer(controller_nh_, "follow_joint_trajectory",
  291. boost::bind(&JointTrajectoryController::goalCB, this, _1),
  292. boost::bind(&JointTrajectoryController::cancelCB, this, _1),
  293. false));
  294. action_server_->start();
  295. // ROS API: Provided services
  296. query_state_service_ = controller_nh_.advertiseService("query_state",
  297. &JointTrajectoryController::queryStateService,
  298. this);
  299. // Preeallocate resources
  300. current_state_ = typename Segment::State(n_joints);
  301. desired_state_ = typename Segment::State(n_joints);
  302. state_error_ = typename Segment::State(n_joints);
  303. hold_start_state_ = typename Segment::State(n_joints);
  304. hold_end_state_ = typename Segment::State(n_joints);
  305. Segment hold_segment(0.0, current_state_, 0.0, current_state_);
  306. hold_trajectory_ptr_->resize(1, hold_segment);
  307. {
  308. state_publisher_->lock();
  309. state_publisher_->msg_.joint_names = joint_names_;
  310. state_publisher_->msg_.desired.positions.resize(n_joints);
  311. state_publisher_->msg_.desired.velocities.resize(n_joints);
  312. state_publisher_->msg_.desired.accelerations.resize(n_joints);
  313. state_publisher_->msg_.actual.positions.resize(n_joints);
  314. state_publisher_->msg_.actual.velocities.resize(n_joints);
  315. state_publisher_->msg_.error.positions.resize(n_joints);
  316. state_publisher_->msg_.error.velocities.resize(n_joints);
  317. state_publisher_->unlock();
  318. }
  319. return true;
  320. }
  321. template <class SegmentImpl, class HardwareInterface>
  322. void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  323. update(const ros::Time& time, const ros::Duration& period)
  324. {
  325. // Get currently followed trajectory
  326. TrajectoryPtr curr_traj_ptr;
  327. curr_trajectory_box_.get(curr_traj_ptr);
  328. Trajectory& curr_traj = *curr_traj_ptr;
  329. // Update time data
  330. TimeData time_data;
  331. time_data.time = time; // Cache current time
  332. time_data.period = period; // Cache current control period
  333. time_data.uptime = time_data_.readFromRT()->uptime + period; // Update controller uptime
  334. time_data_.writeFromNonRT(time_data); // TODO: Grrr, we need a lock-free data structure here!
  335. // NOTE: It is very important to execute the two above code blocks in the specified sequence: first get current
  336. // trajectory, then update time data. Hopefully the following paragraph sheds a bit of light on the rationale.
  337. // The non-rt thread responsible for processing new commands enqueues trajectories that can start at the _next_
  338. // control cycle (eg. zero start time) or later (eg. when we explicitly request a start time in the future).
  339. // If we reverse the order of the two blocks above, and update the time data first; it's possible that by the time we
  340. // fetch the currently followed trajectory, it has been updated by the non-rt thread with something that starts in the
  341. // next control cycle, leaving the current cycle without a valid trajectory.
  342. // Update desired state: sample trajectory at current time
  343. typename Trajectory::const_iterator segment_it = sample(curr_traj, time_data.uptime.toSec(), desired_state_);
  344. if (curr_traj.end() == segment_it)
  345. {
  346. // Non-realtime safe, but should never happen under normal operation
  347. ROS_ERROR_NAMED(name_,
  348. "Unexpected error: No trajectory defined at current time. Please contact the package maintainer.");
  349. return;
  350. }
  351. // Update current state and state error
  352. for (unsigned int i = 0; i < joints_.size(); ++i)
  353. {
  354. current_state_.position[i] = joints_[i].getPosition();
  355. current_state_.velocity[i] = joints_[i].getVelocity();
  356. // There's no acceleration data available in a joint handle
  357. state_error_.position[i] = desired_state_.position[i] - current_state_.position[i];
  358. state_error_.velocity[i] = desired_state_.velocity[i] - current_state_.velocity[i];
  359. state_error_.acceleration[i] = 0.0;
  360. }
  361. // Check tolerances if segment corresponds to currently active action goal
  362. const RealtimeGoalHandlePtr rt_segment_goal = segment_it->getGoalHandle();
  363. if (rt_segment_goal && rt_segment_goal == rt_active_goal_)
  364. {
  365. // Check tolerances
  366. if (time_data.uptime.toSec() < segment_it->endTime())
  367. {
  368. // Currently executing a segment: check path tolerances
  369. checkPathTolerances(state_error_,
  370. *segment_it);
  371. }
  372. else if (segment_it == --curr_traj.end())
  373. {
  374. if (verbose_)
  375. ROS_DEBUG_STREAM_THROTTLE_NAMED(1,name_,"Finished executing last segement, checking goal tolerances");
  376. // Finished executing the LAST segment: check goal tolerances
  377. checkGoalTolerances(state_error_,
  378. *segment_it);
  379. }
  380. }
  381. // Hardware interface adapter: Generate and send commands
  382. hw_iface_adapter_.updateCommand(time_data.uptime, time_data.period,
  383. desired_state_, state_error_);
  384. // Publish state
  385. publishState(time_data.uptime);
  386. }
  387. template <class SegmentImpl, class HardwareInterface>
  388. bool JointTrajectoryController<SegmentImpl, HardwareInterface>::
  389. updateTrajectoryCommand(const JointTrajectoryConstPtr& msg, RealtimeGoalHandlePtr gh)
  390. {
  391. typedef InitJointTrajectoryOptions<Trajectory> Options;
  392. // Preconditions
  393. if (!this->isRunning())
  394. {
  395. ROS_ERROR_NAMED(name_, "Can't accept new commands. Controller is not running.");
  396. return false;
  397. }
  398. if (!msg)
  399. {
  400. ROS_WARN_NAMED(name_, "Received null-pointer trajectory message, skipping.");
  401. return false;
  402. }
  403. // Time data
  404. TimeData* time_data = time_data_.readFromRT(); // TODO: Grrr, we need a lock-free data structure here!
  405. // Time of the next update
  406. const ros::Time next_update_time = time_data->time + time_data->period;
  407. // Uptime of the next update
  408. ros::Time next_update_uptime = time_data->uptime + time_data->period;
  409. // Hold current position if trajectory is empty
  410. if (msg->points.empty())
  411. {
  412. setHoldPosition(time_data->uptime);
  413. ROS_DEBUG_NAMED(name_, "Empty trajectory command, stopping.");
  414. return true;
  415. }
  416. // Trajectory initialization options
  417. TrajectoryPtr curr_traj_ptr;
  418. curr_trajectory_box_.get(curr_traj_ptr);
  419. Options options;
  420. options.other_time_base = &next_update_uptime;
  421. options.current_trajectory = curr_traj_ptr.get();
  422. options.joint_names = &joint_names_;
  423. options.angle_wraparound = &angle_wraparound_;
  424. options.rt_goal_handle = gh;
  425. options.default_tolerances = &default_tolerances_;
  426. // Update currently executing trajectory
  427. try
  428. {
  429. TrajectoryPtr traj_ptr(new Trajectory);
  430. *traj_ptr = initJointTrajectory<Trajectory>(*msg, next_update_time, options);
  431. if (!traj_ptr->empty())
  432. {
  433. curr_trajectory_box_.set(traj_ptr);
  434. }
  435. else
  436. {
  437. // All trajectory points are in the past, nothing new to execute. Keep on executing current trajectory
  438. return false;
  439. }
  440. }
  441. catch(const std::invalid_argument& ex)
  442. {
  443. ROS_ERROR_STREAM_NAMED(name_, ex.what());
  444. return false;
  445. }
  446. catch(...)
  447. {
  448. ROS_ERROR_NAMED(name_, "Unexpected exception caught when initializing trajectory from ROS message data.");
  449. return false;
  450. }
  451. return true;
  452. }
  453. template <class SegmentImpl, class HardwareInterface>
  454. void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  455. goalCB(GoalHandle gh)
  456. {
  457. ROS_DEBUG_STREAM_NAMED(name_,"Recieved new action goal");
  458. // Precondition: Running controller
  459. if (!this->isRunning())
  460. {
  461. ROS_ERROR_NAMED(name_, "Can't accept new action goals. Controller is not running.");
  462. control_msgs::FollowJointTrajectoryResult result;
  463. result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_GOAL; // TODO: Add better error status to msg?
  464. gh.setRejected(result);
  465. return;
  466. }
  467. // Goal should specify all controller joints (they can be ordered differently). Reject if this is not the case
  468. using internal::permutation;
  469. std::vector<unsigned int> permutation_vector = permutation(joint_names_, gh.getGoal()->trajectory.joint_names);
  470. if (permutation_vector.empty())
  471. {
  472. ROS_ERROR_NAMED(name_, "Joints on incoming goal don't match the controller joints.");
  473. control_msgs::FollowJointTrajectoryResult result;
  474. result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_JOINTS;
  475. gh.setRejected(result);
  476. return;
  477. }
  478. // Try to update new trajectory
  479. RealtimeGoalHandlePtr rt_goal(new RealtimeGoalHandle(gh));
  480. const bool update_ok = updateTrajectoryCommand(internal::share_member(gh.getGoal(), gh.getGoal()->trajectory),
  481. rt_goal);
  482. if (update_ok)
  483. {
  484. // Accept new goal
  485. preemptActiveGoal();
  486. gh.setAccepted();
  487. rt_active_goal_ = rt_goal;
  488. // Setup goal status checking timer
  489. goal_handle_timer_ = controller_nh_.createTimer(action_monitor_period_,
  490. &RealtimeGoalHandle::runNonRealtime,
  491. rt_goal);
  492. goal_handle_timer_.start();
  493. }
  494. else
  495. {
  496. // Reject invalid goal
  497. control_msgs::FollowJointTrajectoryResult result;
  498. result.error_code = control_msgs::FollowJointTrajectoryResult::INVALID_GOAL;
  499. gh.setRejected(result);
  500. }
  501. }
  502. template <class SegmentImpl, class HardwareInterface>
  503. void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  504. cancelCB(GoalHandle gh)
  505. {
  506. RealtimeGoalHandlePtr current_active_goal(rt_active_goal_);
  507. // Check that cancel request refers to currently active goal (if any)
  508. if (current_active_goal && current_active_goal->gh_ == gh)
  509. {
  510. // Reset current goal
  511. rt_active_goal_.reset();
  512. // Controller uptime
  513. const ros::Time uptime = time_data_.readFromRT()->uptime;
  514. // Enter hold current position mode
  515. setHoldPosition(uptime);
  516. ROS_DEBUG_NAMED(name_, "Canceling active action goal because cancel callback recieved from actionlib.");
  517. // Mark the current goal as canceled
  518. current_active_goal->gh_.setCanceled();
  519. }
  520. }
  521. template <class SegmentImpl, class HardwareInterface>
  522. bool JointTrajectoryController<SegmentImpl, HardwareInterface>::
  523. queryStateService(control_msgs::QueryTrajectoryState::Request& req,
  524. control_msgs::QueryTrajectoryState::Response& resp)
  525. {
  526. // Preconditions
  527. if (!this->isRunning())
  528. {
  529. ROS_ERROR_NAMED(name_, "Can't sample trajectory. Controller is not running.");
  530. return false;
  531. }
  532. // Convert request time to internal monotonic representation
  533. TimeData* time_data = time_data_.readFromRT();
  534. const ros::Duration time_offset = req.time - time_data->time;
  535. const ros::Time sample_time = time_data->uptime + time_offset;
  536. // Sample trajectory at requested time
  537. TrajectoryPtr curr_traj_ptr;
  538. curr_trajectory_box_.get(curr_traj_ptr);
  539. Trajectory& curr_traj = *curr_traj_ptr;
  540. typename Segment::State state;
  541. typename Trajectory::const_iterator segment_it = sample(curr_traj, sample_time.toSec(), state);
  542. if (curr_traj.end() == segment_it)
  543. {
  544. ROS_ERROR_STREAM_NAMED(name_, "Requested sample time preceeds trajectory start time.");
  545. return false;
  546. }
  547. // Populate response
  548. resp.name = joint_names_;
  549. resp.position = state.position;
  550. resp.velocity = state.velocity;
  551. resp.acceleration = state.acceleration;
  552. return true;
  553. }
  554. template <class SegmentImpl, class HardwareInterface>
  555. void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  556. publishState(const ros::Time& time)
  557. {
  558. // Check if it's time to publish
  559. if (!state_publisher_period_.isZero() && last_state_publish_time_ + state_publisher_period_ < time)
  560. {
  561. if (state_publisher_ && state_publisher_->trylock())
  562. {
  563. last_state_publish_time_ += state_publisher_period_;
  564. state_publisher_->msg_.header.stamp = time_data_.readFromRT()->time;
  565. state_publisher_->msg_.desired.positions = desired_state_.position;
  566. state_publisher_->msg_.desired.velocities = desired_state_.velocity;
  567. state_publisher_->msg_.desired.accelerations = desired_state_.acceleration;
  568. state_publisher_->msg_.actual.positions = current_state_.position;
  569. state_publisher_->msg_.actual.velocities = current_state_.velocity;
  570. state_publisher_->msg_.error.positions = state_error_.position;
  571. state_publisher_->msg_.error.velocities = state_error_.velocity;
  572. state_publisher_->unlockAndPublish();
  573. }
  574. }
  575. }
  576. template <class SegmentImpl, class HardwareInterface>
  577. void JointTrajectoryController<SegmentImpl, HardwareInterface>::
  578. setHoldPosition(const ros::Time& time)
  579. {
  580. // Settle position in a fixed time. We do the following:
  581. // - Create segment that goes from current (pos,vel) to (pos,-vel) in 2x the desired stop time
  582. // - Assuming segment symmetry, sample segment at its midpoint (desired stop time). It should have zero velocity
  583. // - Create segment that goes from current state to above zero velocity state, in the desired time
  584. // NOTE: The symmetry assumption from the second point above might not hold for all possible segment types
  585. assert(1 == hold_trajectory_ptr_->size());
  586. const typename Segment::Time start_time = time.toSec();
  587. const typename Segment::Time end_time = time.toSec() + stop_trajectory_duration_;
  588. const typename Segment::Time end_time_2x = time.toSec() + 2.0 * stop_trajectory_duration_;
  589. // Create segment that goes from current (pos,vel) to (pos,-vel)
  590. const unsigned int n_joints = joints_.size();
  591. for (unsigned int i = 0; i < n_joints; ++i)
  592. {
  593. hold_start_state_.position[i] = joints_[i].getPosition();
  594. hold_start_state_.velocity[i] = joints_[i].getVelocity();
  595. hold_start_state_.acceleration[i] = 0.0;
  596. hold_end_state_.position[i] = joints_[i].getPosition();
  597. hold_end_state_.velocity[i] = -joints_[i].getVelocity();
  598. hold_end_state_.acceleration[i] = 0.0;
  599. }
  600. hold_trajectory_ptr_->front().init(start_time, hold_start_state_,
  601. end_time_2x, hold_end_state_);
  602. // Sample segment at its midpoint, that should have zero velocity
  603. hold_trajectory_ptr_->front().sample(end_time, hold_end_state_);
  604. // Now create segment that goes from current state to one with zero end velocity
  605. hold_trajectory_ptr_->front().init(start_time, hold_start_state_,
  606. end_time, hold_end_state_);
  607. curr_trajectory_box_.set(hold_trajectory_ptr_);
  608. }
  609. } // namespace
  610. #endif // header guard