SMACC2
Loading...
Searching...
No Matches
cp_joint_space_trajectory_planner.hpp
Go to the documentation of this file.
1// Copyright 2021 RobosoftAI Inc.
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
15/*****************************************************************************************************************
16 *
17 * Authors: Pablo Inigo Blasco, Brett Aldrich
18 *
19 *****************************************************************************************************************/
20
21#pragma once
22
23#include <smacc2/component.hpp>
24
26
27#include <geometry_msgs/msg/pose_stamped.hpp>
28#include <moveit_msgs/msg/robot_trajectory.hpp>
29#include <moveit_msgs/srv/get_position_ik.hpp>
30
31#include <chrono>
32#include <optional>
33#include <string>
34#include <vector>
35
36using namespace std::chrono_literals;
37
38namespace cl_moveit2z
39{
51
56{
57 std::optional<std::string> tipLink;
58 std::optional<std::string> groupName;
60 double maxJointDiscontinuity = 0.3; // radians (~17 degrees)
61 int ikAttempts = 4;
62 bool avoidCollisions = true;
63 std::chrono::seconds ikTimeout = 3s;
64};
65
82
95{
96public:
98 virtual ~CpJointSpaceTrajectoryPlanner() = default;
99
103 inline void onInitialize() override
104 {
106
107 // Create IK service client
108 ikClient_ = getNode()->create_client<moveit_msgs::srv::GetPositionIK>("/compute_ik");
109
110 RCLCPP_INFO(getLogger(), "[CpJointSpaceTrajectoryPlanner] Component initialized");
111 }
112
121 const std::vector<geometry_msgs::msg::PoseStamped> & waypoints,
122 const JointTrajectoryOptions & options = {})
123 {
125
126 if (waypoints.empty())
127 {
129 result.errorMessage = "No waypoints provided";
130 RCLCPP_WARN(getLogger(), "[CpJointSpaceTrajectoryPlanner] %s", result.errorMessage.c_str());
131 return result;
132 }
133
134 if (!moveit2zClient_)
135 {
136 result.errorMessage = "ClMoveit2z client not available";
137 RCLCPP_ERROR(getLogger(), "[CpJointSpaceTrajectoryPlanner] %s", result.errorMessage.c_str());
138 return result;
139 }
140
141 try
142 {
143 auto & moveGroup = moveit2zClient_->moveGroupClientInterface;
144
145 // Get current robot state
146 RCLCPP_INFO(
147 getLogger(),
148 "[CpJointSpaceTrajectoryPlanner] Getting current state for trajectory planning");
149 auto currentState = moveGroup->getCurrentState(100);
150 if (!currentState)
151 {
152 result.errorMessage = "Failed to get current robot state";
153 RCLCPP_ERROR(
154 getLogger(), "[CpJointSpaceTrajectoryPlanner] %s", result.errorMessage.c_str());
155 return result;
156 }
157
158 // Determine group name and tip link
159 std::string groupName = options.groupName.value_or(moveGroup->getName());
160 std::string tipLink = options.tipLink.value_or(moveGroup->getEndEffectorLink());
161
162 RCLCPP_INFO(
163 getLogger(), "[CpJointSpaceTrajectoryPlanner] Planning for group '%s' with tip link '%s'",
164 groupName.c_str(), tipLink.c_str());
165
166 // Get joint names
167 auto jointNames = currentState->getJointModelGroup(groupName)->getActiveJointModelNames();
168
169 // Initialize trajectory with current state
170 std::vector<double> jointPositions;
171 currentState->copyJointGroupPositions(groupName, jointPositions);
172
173 std::vector<std::vector<double>> trajectory;
174 std::vector<rclcpp::Duration> trajectoryTimeStamps;
175
176 trajectory.push_back(jointPositions);
177 trajectoryTimeStamps.push_back(rclcpp::Duration(0s));
178
179 // Reference time for trajectory timestamps
180 auto & first = waypoints.front();
181 rclcpp::Time referenceTime(first.header.stamp);
182
183 // Compute IK for each waypoint
184 int ikAttemptsRemaining = options.ikAttempts;
185 for (size_t k = 0; k < waypoints.size(); k++)
186 {
187 auto & pose = waypoints[k];
188
189 // Create IK request
190 auto req = std::make_shared<moveit_msgs::srv::GetPositionIK::Request>();
191 req->ik_request.ik_link_name = tipLink;
192 req->ik_request.robot_state.joint_state.name = jointNames;
193 req->ik_request.robot_state.joint_state.position = jointPositions;
194 req->ik_request.group_name = groupName;
195 req->ik_request.avoid_collisions = options.avoidCollisions;
196 req->ik_request.pose_stamped = pose;
197
198 RCLCPP_DEBUG(
199 getLogger(), "[CpJointSpaceTrajectoryPlanner] Computing IK for waypoint %lu/%lu", k + 1,
200 waypoints.size());
201
202 // Call IK service
203 auto resFuture = ikClient_->async_send_request(req);
204 auto status = resFuture.wait_for(options.ikTimeout);
205
206 if (status != std::future_status::ready)
207 {
208 result.errorMessage = "IK service timeout at waypoint " + std::to_string(k);
209 RCLCPP_ERROR(
210 getLogger(), "[CpJointSpaceTrajectoryPlanner] %s", result.errorMessage.c_str());
212 return result;
213 }
214
215 auto res = resFuture.get();
216 auto & prevTrajPoint = trajectory.back();
217
218 // Extract joint positions from IK solution
219 for (size_t j = 0; j < res->solution.joint_state.position.size(); j++)
220 {
221 auto & jointName = res->solution.joint_state.name[j];
222 auto it = std::find(jointNames.begin(), jointNames.end(), jointName);
223 if (it != jointNames.end())
224 {
225 int index = std::distance(jointNames.begin(), it);
226 jointPositions[index] = res->solution.joint_state.position[j];
227 }
228 }
229
230 // Continuity check
231 bool shouldCheck = k > 0 || !options.allowInitialDiscontinuity;
232 int discontinuityJointIndex = -1;
233 double discontinuityDelta = -1;
234
235 if (shouldCheck)
236 {
237 for (size_t jointIndex = 0; jointIndex < jointPositions.size(); jointIndex++)
238 {
239 double deltaJoint = jointPositions[jointIndex] - prevTrajPoint[jointIndex];
240 if (fabs(deltaJoint) > options.maxJointDiscontinuity)
241 {
242 discontinuityDelta = deltaJoint;
243 discontinuityJointIndex = jointIndex;
244 break;
245 }
246 }
247 }
248
249 // Handle discontinuity
250 if (ikAttemptsRemaining > 0 && discontinuityJointIndex != -1)
251 {
252 // Retry IK
253 k--;
254 ikAttemptsRemaining--;
255 RCLCPP_WARN(
256 getLogger(),
257 "[CpJointSpaceTrajectoryPlanner] IK discontinuity detected, retrying (%d attempts "
258 "remaining)",
259 ikAttemptsRemaining);
260 continue;
261 }
262 else
263 {
264 // Record discontinuity or accept trajectory point
265 if (ikAttemptsRemaining == 0 && discontinuityJointIndex != -1)
266 {
267 result.discontinuityIndexes.push_back(k);
268
269 RCLCPP_ERROR(
270 getLogger(),
271 "[CpJointSpaceTrajectoryPlanner] Joint discontinuity at waypoint %lu: joint '%s' "
272 "delta=%.3f rad",
273 k, jointNames[discontinuityJointIndex].c_str(), discontinuityDelta);
274
275 if (k == 0)
276 {
277 RCLCPP_ERROR(
278 getLogger(),
279 "[CpJointSpaceTrajectoryPlanner] Initial posture discontinuity detected");
280 }
281 }
282
283 // Reset retry counter
284 ikAttemptsRemaining = options.ikAttempts;
285
286 // Add point to trajectory
287 trajectory.push_back(jointPositions);
288 rclcpp::Duration durationFromStart = rclcpp::Time(pose.header.stamp) - referenceTime;
289 trajectoryTimeStamps.push_back(durationFromStart);
290 }
291 }
292
293 // Build RobotTrajectory message
294 result.trajectory.joint_trajectory.joint_names = jointNames;
295
296 // Skip first point (current state) to avoid discontinuity in some behaviors
297 for (size_t i = 1; i < trajectory.size(); i++)
298 {
299 trajectory_msgs::msg::JointTrajectoryPoint jp;
300 jp.positions = trajectory[i];
301 jp.time_from_start = trajectoryTimeStamps[i];
302 result.trajectory.joint_trajectory.points.push_back(jp);
303 }
304
305 // Determine success
306 if (!result.discontinuityIndexes.empty())
307 {
308 if (result.discontinuityIndexes[0] == 0)
309 {
311 result.errorMessage = "Incorrect initial state";
312 }
313 else
314 {
316 result.errorMessage = "Joint trajectory discontinuity detected";
317 }
318 result.success = false;
319 RCLCPP_WARN(getLogger(), "[CpJointSpaceTrajectoryPlanner] %s", result.errorMessage.c_str());
320 }
321 else
322 {
323 result.success = true;
325 RCLCPP_INFO(
326 getLogger(),
327 "[CpJointSpaceTrajectoryPlanner] Successfully computed trajectory with %lu points",
328 result.trajectory.joint_trajectory.points.size());
329 }
330 }
331 catch (const std::exception & e)
332 {
333 result.success = false;
334 result.errorMessage = std::string("Exception during trajectory planning: ") + e.what();
335 RCLCPP_ERROR(getLogger(), "[CpJointSpaceTrajectoryPlanner] %s", result.errorMessage.c_str());
336 }
337
338 return result;
339 }
340
341private:
343 rclcpp::Client<moveit_msgs::srv::GetPositionIK>::SharedPtr ikClient_;
344};
345
346} // namespace cl_moveit2z
std::shared_ptr< moveit::planning_interface::MoveGroupInterface > moveGroupClientInterface
Component for joint space trajectory generation from Cartesian waypoints.
void onInitialize() override
Initialize the component and create IK service client.
rclcpp::Client< moveit_msgs::srv::GetPositionIK >::SharedPtr ikClient_
JointTrajectoryResult planFromWaypoints(const std::vector< geometry_msgs::msg::PoseStamped > &waypoints, const JointTrajectoryOptions &options={})
Compute joint space trajectory from Cartesian waypoints.
void requiresClient(TClient *&requiredClientStorage)
rclcpp::Logger getLogger() const
rclcpp::Node::SharedPtr getNode()
JointTrajectoryErrorCode
Error codes for joint space trajectory computation.
Configuration options for joint space trajectory planning.
Result of a joint space trajectory planning operation.