SMACC2
Loading...
Searching...
No Matches
cb_circular_pivot_motion.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#pragma once
21
22#include <tf2/transform_datatypes.h>
23#include <tf2_ros/transform_listener.h>
25#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
27
28using namespace std::chrono_literals;
29
30namespace cl_moveit2z
31{
32// executes a circular trajectory of the tipLink around one axis defined
33// by the z-axis of the plaenePivotPose
35{
36public:
37 std::optional<double> angularSpeed_rad_s_;
38 std::optional<double> linearSpeed_m_s_;
39
40 // if not specified it would be used the current robot pose
41 std::optional<geometry_msgs::msg::Pose> relativeInitialPose_;
42
43 CbCircularPivotMotion(std::optional<std::string> tipLink = std::nullopt)
45 {
46 }
47
49 const geometry_msgs::msg::PoseStamped & planePivotPose, double deltaRadians,
50 std::optional<std::string> tipLink = std::nullopt)
52 planePivotPose_(planePivotPose),
53 deltaRadians_(deltaRadians)
54 {
55 if (tipLink_) planePivotPose_.header.frame_id = *tipLink;
56 }
57
59 const geometry_msgs::msg::PoseStamped & planePivotPose,
60 const geometry_msgs::msg::Pose & relativeInitialPose, double deltaRadians,
61 std::optional<std::string> tipLink = std::nullopt)
63 relativeInitialPose_(relativeInitialPose),
64 planePivotPose_(planePivotPose),
65 deltaRadians_(deltaRadians)
66 {
67 }
68
69 virtual void generateTrajectory() override
70 {
72 {
74 }
75
76 // project offset into the xy-plane
77 // get the radius
78 double radius = sqrt(
79 relativeInitialPose_->position.z * relativeInitialPose_->position.z +
80 relativeInitialPose_->position.y * relativeInitialPose_->position.y);
81 double initialAngle = atan2(relativeInitialPose_->position.z, relativeInitialPose_->position.y);
82
83 double totallineardist = fabs(radius * deltaRadians_);
84 double totalangulardist = fabs(deltaRadians_);
85
86 // at least 1 sample per centimeter (average)
87 // at least 1 sample per ~1.1 degrees (average)
88
89 const double RADS_PER_SAMPLE = 0.02;
90 const double METERS_PER_SAMPLE = 0.01;
91
92 int totalSamplesCount =
93 std::max(totallineardist / METERS_PER_SAMPLE, totalangulardist / RADS_PER_SAMPLE);
94
95 double linearSecondsPerSample;
96 double angularSecondsPerSamples;
97 double secondsPerSample;
98
100 {
101 linearSecondsPerSample = METERS_PER_SAMPLE / (*linearSpeed_m_s_);
102 }
103 else
104 {
105 linearSecondsPerSample = std::numeric_limits<double>::max();
106 }
107
109 {
110 angularSecondsPerSamples = RADS_PER_SAMPLE / (*angularSpeed_rad_s_);
111 }
112 else
113 {
114 angularSecondsPerSamples = std::numeric_limits<double>::max();
115 }
116
118 {
119 secondsPerSample = 0.5;
120 }
121 else
122 {
123 secondsPerSample = std::min(linearSecondsPerSample, angularSecondsPerSamples);
124 }
125
126 double currentAngle = initialAngle;
127
128 double angleStep = deltaRadians_ / (double)totalSamplesCount;
129
130 tf2::Transform tfBasePose;
131 tf2::fromMsg(planePivotPose_.pose, tfBasePose);
132
133 RCLCPP_INFO_STREAM(
134 getLogger(),
135 "[" << getName() << "] generated trajectory, total samples: " << totalSamplesCount);
136 for (int i = 0; i < totalSamplesCount; i++)
137 {
138 // relativePose i
139 double y = radius * cos(currentAngle);
140 double z = radius * sin(currentAngle);
141
142 geometry_msgs::msg::Pose relativeCurrentPose;
143
144 relativeCurrentPose.position.x = relativeInitialPose_->position.x;
145 relativeCurrentPose.position.y = y;
146 relativeCurrentPose.position.z = z;
147
148 tf2::Quaternion localquat;
149 localquat.setEuler(currentAngle, 0, 0);
150
151 //relativeCurrentPose.orientation = relativeInitialPose_.orientation;
152 //tf2::toMsg(localquat, relativeCurrentPose.orientation);
153 relativeCurrentPose.orientation.w = 1;
154
155 tf2::Transform tfRelativeCurrentPose;
156 tf2::fromMsg(relativeCurrentPose, tfRelativeCurrentPose);
157
158 tf2::Transform tfGlobalPose = tfRelativeCurrentPose * tfBasePose;
159
160 tfGlobalPose.setRotation(tfGlobalPose.getRotation() * localquat);
161
162 geometry_msgs::msg::PoseStamped globalPose;
163 tf2::toMsg(tfGlobalPose, globalPose.pose);
164 globalPose.header.frame_id = planePivotPose_.header.frame_id;
165 globalPose.header.stamp = rclcpp::Time(planePivotPose_.header.stamp) +
166 rclcpp::Duration::from_seconds(i * secondsPerSample);
167 RCLCPP_INFO_STREAM(
168 getLogger(),
169 "[" << getName() << "]" << rclcpp::Time(globalPose.header.stamp).nanoseconds());
170
171 this->endEffectorTrajectory_.push_back(globalPose);
172 currentAngle += angleStep;
173 }
174 }
175
176 virtual void createMarkers() override
177 {
179
180 tf2::Transform localdirection;
181 localdirection.setIdentity();
182 localdirection.setOrigin(tf2::Vector3(0.12, 0, 0));
183 auto frameid = planePivotPose_.header.frame_id;
184
185 visualization_msgs::msg::Marker marker;
186 marker.header.frame_id = frameid;
187 marker.header.stamp = getNode()->now();
188 marker.ns = "trajectory";
189 marker.id = beahiorMarkers_.markers.size();
190 marker.type = visualization_msgs::msg::Marker::ARROW;
191 marker.action = visualization_msgs::msg::Marker::ADD;
192 marker.scale.x = 0.01;
193 marker.scale.y = 0.02;
194 marker.scale.z = 0.02;
195 marker.color.a = 1.0;
196 marker.color.r = 0.0;
197 marker.color.g = 0;
198 marker.color.b = 1.0;
199
200 geometry_msgs::msg::Point start, end;
201 start.x = planePivotPose_.pose.position.x;
202 start.y = planePivotPose_.pose.position.y;
203 start.z = planePivotPose_.pose.position.z;
204
205 tf2::Transform basetransform;
206 tf2::fromMsg(planePivotPose_.pose, basetransform);
207 tf2::Transform endarrow = localdirection * basetransform;
208
209 end.x = endarrow.getOrigin().x();
210 end.y = endarrow.getOrigin().y();
211 end.z = endarrow.getOrigin().z();
212
213 marker.pose.orientation.w = 1;
214 marker.points.push_back(start);
215 marker.points.push_back(end);
216
217 beahiorMarkers_.markers.push_back(marker);
218 }
219
220protected:
221 // the rotation axis (z-axis) and the center of rotation in the space
222 geometry_msgs::msg::PoseStamped planePivotPose_;
223
225
226private:
228 {
229 // Use CpTfListener component for transform lookups
230 CpTfListener * tfListener = nullptr;
231 this->requiresComponent(tfListener, smacc2::ComponentRequirement::SOFT); // Optional component
232
233 tf2::Stamped<tf2::Transform> endEffectorInPivotFrame;
234
235 try
236 {
237 if (!tipLink_ || *tipLink_ == "")
238 {
239 tipLink_ = this->movegroupClient_->moveGroupClientInterface->getEndEffectorLink();
240 }
241
242 RCLCPP_INFO_STREAM(
243 getLogger(), "[" << getName() << "] waiting transform, pivot: '"
244 << planePivotPose_.header.frame_id << "' tipLink: '" << *tipLink_ << "'");
245
246 if (tfListener != nullptr)
247 {
248 // Use component-based TF listener (preferred)
249 auto transformOpt =
250 tfListener->lookupTransform(planePivotPose_.header.frame_id, *tipLink_, rclcpp::Time());
251
252 if (transformOpt)
253 {
254 tf2::fromMsg(transformOpt.value(), endEffectorInPivotFrame);
255 }
256 else
257 {
258 RCLCPP_ERROR_STREAM(
259 getLogger(), "[" << getName() << "] Failed to lookup transform from " << *tipLink_
260 << " to " << planePivotPose_.header.frame_id);
261 return;
262 }
263 }
264 else
265 {
266 // Fallback to legacy TF2 usage if component not available
267 RCLCPP_WARN_STREAM(
268 getLogger(), "[" << getName()
269 << "] CpTfListener component not available, using legacy TF2 (consider "
270 "adding CpTfListener component)");
271 tf2_ros::Buffer tfBuffer(getNode()->get_clock());
272 tf2_ros::TransformListener tfListenerLegacy(tfBuffer);
273
274 tf2::fromMsg(
275 tfBuffer.lookupTransform(
276 planePivotPose_.header.frame_id, *tipLink_, rclcpp::Time(), rclcpp::Duration(10s)),
277 endEffectorInPivotFrame);
278 }
279 }
280 catch (const std::exception & e)
281 {
282 RCLCPP_ERROR_STREAM(
283 getLogger(),
284 "[" << getName()
285 << "] Exception in computeCurrentEndEffectorPoseRelativeToPivot: " << e.what());
286 return;
287 }
288
289 // tf2::Transform endEffectorInBaseLinkFrame;
290 // tf2::fromMsg(currentRobotEndEffectorPose.pose, endEffectorInBaseLinkFrame);
291
292 // tf2::Transform endEffectorInPivotFrame = globalBaseLink * endEffectorInBaseLinkFrame; // pose composition
293
294 // now pivot and EndEffector share a common reference frame (let say map)
295 // now get the current pose from the pivot reference frame with inverse composition
296 tf2::Transform pivotTransform;
297 tf2::fromMsg(planePivotPose_.pose, pivotTransform);
298 tf2::Transform invertedNewReferenceFrame = pivotTransform.inverse();
299
300 tf2::Transform currentPoseRelativeToPivot = invertedNewReferenceFrame * endEffectorInPivotFrame;
301
302 geometry_msgs::msg::Pose finalEndEffectorRelativePose;
303 tf2::toMsg(currentPoseRelativeToPivot, finalEndEffectorRelativePose);
304 relativeInitialPose_ = finalEndEffectorRelativePose;
305 }
306};
307
308} // namespace cl_moveit2z
CbCircularPivotMotion(std::optional< std::string > tipLink=std::nullopt)
std::optional< geometry_msgs::msg::Pose > relativeInitialPose_
CbCircularPivotMotion(const geometry_msgs::msg::PoseStamped &planePivotPose, const geometry_msgs::msg::Pose &relativeInitialPose, double deltaRadians, std::optional< std::string > tipLink=std::nullopt)
geometry_msgs::msg::PoseStamped planePivotPose_
CbCircularPivotMotion(const geometry_msgs::msg::PoseStamped &planePivotPose, double deltaRadians, std::optional< std::string > tipLink=std::nullopt)
std::vector< geometry_msgs::msg::PoseStamped > endEffectorTrajectory_
std::shared_ptr< moveit::planning_interface::MoveGroupInterface > moveGroupClientInterface
Component for shared TF2 transform management across all behaviors.
std::optional< geometry_msgs::msg::TransformStamped > lookupTransform(const std::string &target_frame, const std::string &source_frame, const rclcpp::Time &time=rclcpp::Time(0))
Thread-safe transform lookup.
virtual rclcpp::Logger getLogger() const
virtual rclcpp::Node::SharedPtr getNode() const
void requiresComponent(SmaccComponentType *&storage, ComponentRequirement requirementType=ComponentRequirement::SOFT)