38 #include <bio_ik/goal.h> 39 #include <bio_ik/robot_info.h> 40 #include <kdl/treefksolverpos_recursive.hpp> 41 #include <kdl_parser/kdl_parser.hpp> 43 #if defined(__x86_64__) || defined(__i386__) 45 #include <emmintrin.h> 46 #include <immintrin.h> 47 #include <x86intrin.h> 49 #if(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) 50 #define FUNCTION_MULTIVERSIONING 1 52 #define FUNCTION_MULTIVERSIONING 0 57 #define FUNCTION_MULTIVERSIONING 0 68 std::vector<double> joint_cache_variables;
69 std::vector<Frame> joint_cache_frames;
70 std::vector<Frame> link_frames;
73 std::vector<Vector3> joint_axis_list;
74 moveit::core::RobotModelConstPtr robot_model;
75 std::vector<double> variables;
78 inline void getJointFrame(
const moveit::core::JointModel* joint_model,
const double* variables, Frame& frame)
80 auto joint_type = joint_model->getType();
81 if(joint_type == moveit::core::JointModel::FIXED)
83 frame = Frame::identity();
86 size_t joint_index = joint_model->getJointIndex();
89 case moveit::core::JointModel::REVOLUTE:
91 auto axis = joint_axis_list[joint_index];
93 auto v = variables[joint_model->getFirstVariableIndex()];
99 auto half_angle = v * 0.5;
103 auto fcos = cos(half_angle);
104 auto fsin = sin(half_angle);
109 frame = Frame(Vector3(0.0, 0.0, 0.0), Quaternion(axis.x() * fsin, axis.y() * fsin, axis.z() * fsin, fcos));
113 case moveit::core::JointModel::PRISMATIC:
115 auto axis = joint_axis_list[joint_index];
116 auto v = variables[joint_model->getFirstVariableIndex()];
117 frame = Frame(axis * v, Quaternion(0.0, 0.0, 0.0, 1.0));
120 case moveit::core::JointModel::FLOATING:
122 auto* vv = variables + joint_model->getFirstVariableIndex();
123 frame.pos = Vector3(vv[0], vv[1], vv[2]);
124 frame.rot = Quaternion(vv[3], vv[4], vv[5], vv[6]).normalized();
130 auto* joint_variables = variables + joint_model->getFirstVariableIndex();
131 Eigen::Isometry3d joint_transform;
132 joint_model->computeTransform(joint_variables, joint_transform);
133 frame = Frame(joint_transform);
140 inline void getJointFrame(
const moveit::core::JointModel* joint_model,
const std::vector<double>& variables, Frame& frame) { getJointFrame(joint_model, variables.data(), frame); }
143 inline const Frame& getLinkFrame(
const moveit::core::LinkModel* link_model) {
return link_frames[link_model->getLinkIndex()]; }
145 inline bool checkJointMoved(
const moveit::core::JointModel* joint_model)
147 size_t i0 = joint_model->getFirstVariableIndex();
148 size_t cnt = joint_model->getVariableCount();
149 if(cnt == 0)
return true;
150 if(cnt == 1)
return !(variables[i0] == joint_cache_variables[i0]);
151 for(
size_t i = i0; i < i0 + cnt; i++)
152 if(!(variables[i] == joint_cache_variables[i]))
return true;
155 inline void putJointCache(
const moveit::core::JointModel* joint_model,
const Frame& frame)
157 joint_cache_frames[joint_model->getJointIndex()] = frame;
158 size_t i0 = joint_model->getFirstVariableIndex();
159 size_t cnt = joint_model->getVariableCount();
160 for(
size_t i = i0; i < i0 + cnt; i++)
161 joint_cache_variables[i] = variables[i];
163 inline const Frame& getJointFrame(
const moveit::core::JointModel* joint_model)
165 size_t joint_index = joint_model->getJointIndex();
167 if(!checkJointMoved(joint_model))
return joint_cache_frames[joint_index];
169 getJointFrame(joint_model, variables, joint_cache_frames[joint_index]);
171 size_t cnt = joint_model->getVariableCount();
174 size_t i0 = joint_model->getFirstVariableIndex();
176 joint_cache_variables[i0] = variables[i0];
178 for(
size_t i = i0; i < i0 + cnt; i++)
179 joint_cache_variables[i] = variables[i];
188 return joint_cache_frames[joint_index];
195 joint_cache_variables.clear();
196 joint_cache_variables.resize(model->getVariableCount(), DBL_MAX);
198 joint_cache_frames.clear();
199 joint_cache_frames.resize(model->getJointModelCount());
202 for(
auto* link_model : model->getLinkModels())
203 link_frames.push_back(Frame(link_model->getJointOriginTransform()));
205 joint_axis_list.clear();
206 joint_axis_list.resize(robot_model->getJointModelCount());
207 for(
size_t i = 0; i < joint_axis_list.size(); i++)
209 auto* joint_model = robot_model->getJointModel(i);
210 if(
auto* j = dynamic_cast<const moveit::core::RevoluteJointModel*>(joint_model)) joint_axis_list[i] = Vector3(j->getAxis().x(), j->getAxis().y(), j->getAxis().z());
211 if(
auto* j = dynamic_cast<const moveit::core::PrismaticJointModel*>(joint_model)) joint_axis_list[i] = Vector3(j->getAxis().x(), j->getAxis().y(), j->getAxis().z());
220 std::vector<std::string> tip_names;
221 std::vector<Frame> tip_frames;
222 std::vector<const moveit::core::LinkModel*> tip_links;
223 std::vector<const moveit::core::LinkModel*> link_schedule;
224 std::vector<Frame> global_frames;
225 std::vector<std::vector<const moveit::core::LinkModel*>> link_chains;
226 std::vector<size_t> active_variables;
227 std::vector<std::pair<size_t, size_t>> variable_to_link_map;
228 std::vector<size_t> variable_to_link_map_2;
229 std::vector<std::pair<size_t, size_t>> variable_to_link_chain_map;
230 inline void updateMimic(std::vector<double>& values)
232 for(
auto* joint : robot_model->getMimicJointModels())
234 auto src = joint->getMimic()->getFirstVariableIndex();
235 auto dest = joint->getFirstVariableIndex();
238 values[dest] = values[src] * joint->getMimicFactor() + joint->getMimicOffset();
253 void initialize(
const std::vector<size_t>& tip_link_indices)
256 tip_names.resize(tip_link_indices.size());
257 for(
size_t i = 0; i < tip_link_indices.size(); i++)
258 tip_names[i] = robot_model->getLinkModelNames()[tip_link_indices[i]];
260 tip_frames.resize(tip_names.size());
263 for(
const auto& n : tip_names)
264 tip_links.push_back(robot_model->getLinkModel(n));
266 global_frames.resize(robot_model->getLinkModelCount());
269 link_schedule.clear();
270 for(
auto* tip_link : tip_links)
272 std::vector<const moveit::core::LinkModel*> chain;
273 for(
auto* link = tip_link; link; link = link->getParentLinkModel())
274 chain.push_back(link);
275 reverse(chain.begin(), chain.end());
276 link_chains.push_back(chain);
277 for(
auto* link : chain)
279 if(find(link_schedule.begin(), link_schedule.end(), link) != link_schedule.end())
continue;
280 link_schedule.push_back(link);
284 active_variables.clear();
285 for(
auto* link_model : link_schedule)
287 auto link_index = link_model->getLinkIndex();
288 auto* joint_model = link_model->getParentJointModel();
289 size_t vstart = joint_model->getFirstVariableIndex();
290 size_t vcount = joint_model->getVariableCount();
291 for(
size_t vi = vstart; vi < vstart + vcount; vi++)
293 variable_to_link_map.push_back(std::make_pair(vi, link_index));
295 if(find(active_variables.begin(), active_variables.end(), vi) != active_variables.end())
continue;
296 active_variables.push_back(vi);
300 variable_to_link_map_2.resize(robot_model->getVariableCount());
301 for(
auto* link_model : link_schedule)
303 auto link_index = link_model->getLinkIndex();
304 auto* joint_model = link_model->getParentJointModel();
305 size_t vstart = joint_model->getFirstVariableIndex();
306 size_t vcount = joint_model->getVariableCount();
307 for(
size_t vi = vstart; vi < vstart + vcount; vi++)
309 variable_to_link_map_2[vi] = link_index;
313 variable_to_link_chain_map.resize(robot_model->getVariableCount());
314 for(
size_t chain_index = 0; chain_index < link_chains.size(); chain_index++)
316 auto& link_chain = link_chains[chain_index];
317 for(
size_t ipos = 0; ipos < link_chain.size(); ipos++)
319 auto* link_model = link_chain[ipos];
320 auto* joint_model = link_model->getParentJointModel();
321 size_t vstart = joint_model->getFirstVariableIndex();
322 size_t vcount = joint_model->getVariableCount();
323 for(
size_t vi = vstart; vi < vstart + vcount; vi++)
325 variable_to_link_chain_map[vi] = std::make_pair(chain_index, ipos);
330 void applyConfiguration(
const std::vector<double>& jj0)
334 updateMimic(variables);
335 for(
auto* link_model : link_schedule)
337 auto* joint_model = link_model->getParentJointModel();
338 auto* parent_link_model = joint_model->getParentLinkModel();
339 if(parent_link_model)
341 concat(global_frames[parent_link_model->getLinkIndex()], getLinkFrame(link_model), getJointFrame(joint_model), global_frames[link_model->getLinkIndex()]);
345 concat(getLinkFrame(link_model), getJointFrame(joint_model), global_frames[link_model->getLinkIndex()]);
349 for(
size_t itip = 0; itip < tip_links.size(); itip++)
351 tip_frames[itip] = global_frames[tip_links[itip]->getLinkIndex()];
354 inline const Frame& getTipFrame(
size_t fi)
const {
return tip_frames[fi]; }
355 inline const std::vector<Frame>& getTipFrames()
const {
return tip_frames; }
356 inline void incrementalBegin(
const std::vector<double>& jj) { applyConfiguration(jj); }
357 inline void incrementalEnd() {}
358 inline const Frame& getJointVariableFrame(
size_t variable_index) {
return global_frames[variable_to_link_map_2[variable_index]]; }
365 std::vector<std::vector<Frame>> chain_cache;
366 std::vector<double> vars, variables0;
367 std::vector<Frame> test_tips;
368 std::vector<uint8_t> links_changed;
369 bool use_incremental;
370 bool last_use_incremental;
371 void updateIncremental(
const std::vector<double>& vars0)
380 for(
auto& vi : active_variables)
383 variables0[vi] = variables[vi];
387 for(
auto* link_model : link_schedule)
388 links_changed[link_model->getLinkIndex()] =
false;
389 for(
auto& x : variable_to_link_map)
391 auto variable_index = x.first;
392 auto link_index = x.second;
393 if(vars[variable_index] != variables[variable_index]) links_changed[link_index] =
true;
397 for(
size_t ichain = 0; ichain < link_chains.size(); ichain++)
399 auto& link_chain = link_chains[ichain];
400 auto& cache_chain = chain_cache[ichain];
403 for(
size_t ipos = 0; ipos < link_chain.size(); ipos++)
405 auto* link_model = link_chain[ipos];
406 bool changed = links_changed[link_model->getLinkIndex()];
407 if(changed) iposmax = ipos;
410 for(
size_t ipos = 0; ipos <= iposmax; ipos++)
412 auto* link_model = link_chain[ipos];
413 auto* joint_model = link_model->getParentJointModel();
415 bool changed = links_changed[link_model->getLinkIndex()];
417 if(cache_chain.size() <= ipos || changed)
421 if(ipos < cache_chain.size())
423 before = cache_chain[ipos];
429 concat(cache_chain[ipos - 1], getLinkFrame(link_model), getJointFrame(joint_model), before);
433 concat(getLinkFrame(link_model), getJointFrame(joint_model), before);
437 chain_cache[ichain].resize(ipos + 1, Frame::identity());
441 if(ichain > 0 && ipos < link_chains[ichain - 1].size() && link_chains[ichain][ipos] == link_chains[ichain - 1][ipos])
443 after = chain_cache[ichain - 1][ipos];
447 size_t vstart = joint_model->getFirstVariableIndex();
448 size_t vcount = joint_model->getVariableCount();
451 variables[vstart] = vars[vstart];
453 for(
size_t vi = vstart; vi < vstart + vcount; vi++)
454 variables[vi] = vars[vi];
458 concat(cache_chain[ipos - 1], getLinkFrame(link_model), getJointFrame(joint_model), after);
462 concat(getLinkFrame(link_model), getJointFrame(joint_model), after);
466 variables[vstart] = variables0[vstart];
468 for(
size_t vi = vstart; vi < vstart + vcount; vi++)
469 variables[vi] = variables0[vi];
473 chain_cache[ichain][ipos] = after;
486 change(after, before, tip_frames[ichain], tip_frames[ichain]);
491 chain_cache[ichain][ipos] = after;
499 for(
auto& vi : active_variables)
500 variables[vi] = vars[vi];
505 test_tips = tip_frames;
506 RobotFK_Fast_Base::applyConfiguration(vars);
507 for(
size_t i = 0; i < tip_frames.size(); i++)
509 auto dist = tip_frames[i].pos.distance(test_tips[i].pos);
513 LOG(tip_frames[i].pos.x(), tip_frames[i].pos.y(), tip_frames[i].pos.z());
514 LOG(test_tips[i].pos.x(), test_tips[i].pos.y(), test_tips[i].pos.z());
515 ERROR(
"incremental update error");
524 , use_incremental(
false)
527 inline void incrementalBegin(
const std::vector<double>& jj)
529 applyConfiguration(jj);
531 chain_cache.resize(tip_frames.size());
532 links_changed.resize(robot_model->getLinkModelCount());
533 vars.resize(jj.size());
534 variables0.resize(jj.size());
535 use_incremental =
true;
537 inline void incrementalEnd()
539 use_incremental =
false;
542 inline void applyConfiguration(
const std::vector<double>& jj)
545 updateIncremental(jj);
547 RobotFK_Fast_Base::applyConfiguration(jj);
557 std::vector<std::vector<const moveit::core::JointModel*>> joint_dependencies;
558 std::vector<int> tip_dependencies;
565 void initialize(
const std::vector<size_t>& tip_link_indices)
567 Base::initialize(tip_link_indices);
568 auto tip_count = tip_names.size();
569 joint_dependencies.resize(robot_model->getJointModelCount());
570 for(
auto& x : joint_dependencies)
572 for(
auto* link_model : link_schedule)
574 auto* joint_model = link_model->getParentJointModel();
575 joint_dependencies[joint_model->getJointIndex()].push_back(joint_model);
577 for(
auto* link_model : link_schedule)
579 auto* joint_model = link_model->getParentJointModel();
580 if(
auto* mimic = joint_model->getMimic())
582 while(mimic->getMimic() && mimic->getMimic() != joint_model)
583 mimic = mimic->getMimic();
584 joint_dependencies[mimic->getJointIndex()].push_back(joint_model);
587 tip_dependencies.resize(robot_model->getJointModelCount() * tip_count);
588 for(
auto& x : tip_dependencies)
590 for(
size_t tip_index = 0; tip_index < tip_count; tip_index++)
592 for(
auto* link_model = tip_links[tip_index]; link_model; link_model = link_model->getParentLinkModel())
594 auto* joint_model = link_model->getParentJointModel();
595 tip_dependencies[joint_model->getJointIndex() * tip_count + tip_index] = 1;
599 void computeJacobian(
const std::vector<size_t>& variable_indices, Eigen::MatrixXd& jacobian)
601 double step_size = 0.00001;
602 double half_step_size = step_size * 0.5;
603 double inv_step_size = 1.0 / step_size;
604 auto tip_count = tip_frames.size();
605 jacobian.resize(tip_count * 6, variable_indices.size());
606 for(
size_t icol = 0; icol < variable_indices.size(); icol++)
608 for(
size_t itip = 0; itip < tip_count; itip++)
610 jacobian(itip * 6 + 0, icol) = 0;
611 jacobian(itip * 6 + 1, icol) = 0;
612 jacobian(itip * 6 + 2, icol) = 0;
613 jacobian(itip * 6 + 3, icol) = 0;
614 jacobian(itip * 6 + 4, icol) = 0;
615 jacobian(itip * 6 + 5, icol) = 0;
618 for(
size_t icol = 0; icol < variable_indices.size(); icol++)
620 auto ivar = variable_indices[icol];
621 auto* var_joint_model = robot_model->getJointOfVariable(ivar);
622 if(var_joint_model->getMimic())
continue;
623 for(
auto* joint_model : joint_dependencies[var_joint_model->getJointIndex()])
626 for(
auto* m = joint_model; m->getMimic() && m->getMimic() != joint_model; m = m->getMimic())
628 scale *= m->getMimicFactor();
630 auto* link_model = joint_model->getChildLinkModel();
631 switch(joint_model->getType())
633 case moveit::core::JointModel::FIXED:
638 case moveit::core::JointModel::REVOLUTE:
640 auto& link_frame = global_frames[link_model->getLinkIndex()];
641 for(
size_t itip = 0; itip < tip_count; itip++)
643 if(!tip_dependencies[joint_model->getJointIndex() * tip_count + itip])
continue;
645 auto& tip_frame = tip_frames[itip];
647 auto q = link_frame.rot.inverse() * tip_frame.rot;
650 auto rot = joint_axis_list[joint_model->getJointIndex()];
651 quat_mul_vec(q, rot, rot);
653 auto vel = link_frame.pos - tip_frame.pos;
654 quat_mul_vec(tip_frame.rot.inverse(), vel, vel);
656 vel = vel.cross(rot);
658 jacobian(itip * 6 + 0, icol) += vel.x() * scale;
659 jacobian(itip * 6 + 1, icol) += vel.y() * scale;
660 jacobian(itip * 6 + 2, icol) += vel.z() * scale;
662 jacobian(itip * 6 + 3, icol) += rot.x() * scale;
663 jacobian(itip * 6 + 4, icol) += rot.y() * scale;
664 jacobian(itip * 6 + 5, icol) += rot.z() * scale;
670 case moveit::core::JointModel::PRISMATIC:
672 auto& link_frame = global_frames[link_model->getLinkIndex()];
673 for(
size_t itip = 0; itip < tip_count; itip++)
675 if(!tip_dependencies[joint_model->getJointIndex() * tip_count + itip])
continue;
677 auto& tip_frame = tip_frames[itip];
679 auto q = link_frame.rot.inverse() * tip_frame.rot;
682 auto axis = joint_axis_list[joint_model->getJointIndex()];
684 quat_mul_vec(q, axis, v);
686 jacobian(itip * 6 + 0, icol) += v.x() * scale;
687 jacobian(itip * 6 + 1, icol) += v.y() * scale;
688 jacobian(itip * 6 + 2, icol) += v.z() * scale;
698 if(joint_model->getMimic()) ivar2 = ivar2 - var_joint_model->getFirstVariableIndex() + joint_model->getFirstVariableIndex();
699 auto& link_frame_1 = global_frames[link_model->getLinkIndex()];
700 auto v0 = variables[ivar2];
702 variables[ivar2] = v0 + step_size;
703 auto joint_frame_2 = getJointFrame(joint_model);
704 variables[ivar2] = v0;
706 if(
auto* parent_link_model = joint_model->getParentLinkModel())
707 concat(global_frames[parent_link_model->getLinkIndex()], getLinkFrame(link_model), joint_frame_2, link_frame_2);
709 concat(getLinkFrame(link_model), joint_frame_2, link_frame_2);
710 for(
size_t itip = 0; itip < tip_count; itip++)
712 if(!tip_dependencies[joint_model->getJointIndex() * tip_count + itip])
continue;
713 auto tip_frame_1 = tip_frames[itip];
715 change(link_frame_2, link_frame_1, tip_frame_1, tip_frame_2);
716 auto twist = frameTwist(tip_frame_1, tip_frame_2);
717 jacobian(itip * 6 + 0, icol) += twist.vel.x() * inv_step_size * scale;
718 jacobian(itip * 6 + 1, icol) += twist.vel.y() * inv_step_size * scale;
719 jacobian(itip * 6 + 2, icol) += twist.vel.z() * inv_step_size * scale;
720 jacobian(itip * 6 + 3, icol) += twist.rot.x() * inv_step_size * scale;
721 jacobian(itip * 6 + 4, icol) += twist.rot.y() * inv_step_size * scale;
722 jacobian(itip * 6 + 5, icol) += twist.rot.z() * inv_step_size * scale;
736 std::vector<size_t> mutation_variable_indices;
737 std::vector<double> mutation_initial_variable_positions;
738 std::vector<double> mutation_temp;
739 RobotFK(moveit::core::RobotModelConstPtr model)
743 void initializeMutationApproximator(
const std::vector<size_t>& variable_indices)
745 mutation_variable_indices = variable_indices;
746 mutation_initial_variable_positions = variables;
750 mutation_temp = mutation_initial_variable_positions;
751 mutation_temp[variable_index] += variable_delta;
752 applyConfiguration(mutation_temp);
754 for(
auto& f : tip_frames) output.push_back(f);
756 void computeApproximateMutations(
size_t mutation_count,
const double*
const* mutation_values, std::vector<
aligned_vector<Frame>>& tip_frame_mutations)
758 auto tip_count = tip_names.size();
759 tip_frame_mutations.resize(mutation_count);
760 for(
auto& m : tip_frame_mutations)
762 for(
size_t mutation_index = 0; mutation_index < mutation_count; mutation_index++)
764 mutation_temp = mutation_initial_variable_positions;
765 for(
size_t i = 0; i < mutation_variable_indices.size(); i++)
767 mutation_temp[mutation_variable_indices[i]] = mutation_values[mutation_index][i];
769 applyConfiguration(mutation_temp);
770 for(
size_t tip_index = 0; tip_index < tip_count; tip_index++)
772 tip_frame_mutations[mutation_index][tip_index] = tip_frames[tip_index];
780 #define USE_QUADRATIC_EXTRAPOLATION 0 785 Eigen::MatrixXd mutation_approx_jacobian;
786 std::vector<aligned_vector<Frame>> mutation_approx_frames;
787 #if USE_QUADRATIC_EXTRAPOLATION 788 std::vector<aligned_vector<Frame>> mutation_approx_quadratics;
790 std::vector<size_t> mutation_approx_variable_indices;
791 std::vector<std::vector<int>> mutation_approx_mask;
792 std::vector<std::vector<size_t>> mutation_approx_map;
801 void initializeMutationApproximator(
const std::vector<size_t>& variable_indices)
805 mutation_approx_variable_indices = variable_indices;
807 auto tip_count = tip_names.size();
809 tip_frames_aligned.resize(tip_frames.size());
810 for(
size_t i = 0; i < tip_frames.size(); i++)
811 tip_frames_aligned[i] = tip_frames[i];
815 if(mutation_approx_frames.size() < tip_count) mutation_approx_frames.resize(tip_count);
817 for(
size_t itip = 0; itip < tip_count; itip++)
818 mutation_approx_frames[itip].resize(robot_model->getVariableCount());
820 for(
size_t itip = 0; itip < tip_count; itip++)
821 for(
auto ivar : variable_indices)
822 mutation_approx_frames[itip][ivar] = Frame::identity();
824 computeJacobian(variable_indices, mutation_approx_jacobian);
826 for(
size_t icol = 0; icol < variable_indices.size(); icol++)
828 size_t ivar = variable_indices[icol];
829 for(
size_t itip = 0; itip < tip_count; itip++)
833 t.setX(mutation_approx_jacobian(itip * 6 + 0, icol));
834 t.setY(mutation_approx_jacobian(itip * 6 + 1, icol));
835 t.setZ(mutation_approx_jacobian(itip * 6 + 2, icol));
836 quat_mul_vec(tip_frames[itip].rot, t, t);
837 mutation_approx_frames[itip][ivar].pos = t;
842 q.setX(mutation_approx_jacobian(itip * 6 + 3, icol) * 0.5);
843 q.setY(mutation_approx_jacobian(itip * 6 + 4, icol) * 0.5);
844 q.setZ(mutation_approx_jacobian(itip * 6 + 5, icol) * 0.5);
846 quat_mul_quat(tip_frames[itip].rot, q, q);
847 q -= tip_frames[itip].rot;
848 mutation_approx_frames[itip][ivar].rot = q;
855 #if USE_QUADRATIC_EXTRAPOLATION 857 if(mutation_approx_quadratics.size() < tip_count) mutation_approx_quadratics.resize(tip_count);
858 for(
size_t itip = 0; itip < tip_count; itip++)
859 mutation_approx_quadratics[itip].resize(robot_model->getVariableCount());
860 for(
size_t itip = 0; itip < tip_count; itip++)
861 for(
auto ivar : variable_indices)
862 mutation_approx_quadratics[itip][ivar].pos = Vector3(0, 0, 0);
863 for(
auto ivar : variable_indices)
865 auto* var_joint_model = robot_model->getJointOfVariable(ivar);
866 if(var_joint_model->getMimic())
continue;
867 for(
auto* joint_model : joint_dependencies[var_joint_model->getJointIndex()])
870 for(
auto* m = joint_model; m->getMimic() && m->getMimic() != joint_model; m = m->getMimic())
872 scale *= m->getMimicFactor();
874 auto* link_model = joint_model->getChildLinkModel();
875 switch(joint_model->getType())
877 case moveit::core::JointModel::REVOLUTE:
879 auto& link_frame = global_frames[link_model->getLinkIndex()];
880 for(
size_t itip = 0; itip < tip_count; itip++)
882 if(!tip_dependencies[joint_model->getJointIndex() * tip_count + itip])
continue;
884 auto& tip_frame = tip_frames[itip];
886 auto axis = joint_axis_list[joint_model->getJointIndex()];
887 quat_mul_vec(link_frame.rot, axis, axis);
889 auto v = link_frame.pos - tip_frame.pos;
890 v -= axis * v.dot(axis);
892 mutation_approx_quadratics[itip][ivar].pos = v * 0.5 * (scale * scale);
907 if(mutation_approx_mask.size() < tip_count) mutation_approx_mask.resize(tip_count);
908 if(mutation_approx_map.size() < tip_count) mutation_approx_map.resize(tip_count);
909 for(
size_t itip = 0; itip < tip_count; itip++)
911 if(mutation_approx_mask[itip].size() < robot_model->getVariableCount()) mutation_approx_mask[itip].resize(robot_model->getVariableCount());
912 mutation_approx_map[itip].clear();
913 for(
size_t ii = 0; ii < variable_indices.size(); ii++)
916 auto ivar = variable_indices[ii];
917 auto& frame = mutation_approx_frames[itip][ivar];
919 b |= (frame.pos.x() != 0.0);
920 b |= (frame.pos.y() != 0.0);
921 b |= (frame.pos.z() != 0.0);
922 b |= (frame.rot.x() != 0.0);
923 b |= (frame.rot.y() != 0.0);
924 b |= (frame.rot.z() != 0.0);
925 mutation_approx_mask[itip][ivar] = b;
926 if(b) mutation_approx_map[itip].push_back(ii);
931 #if FUNCTION_MULTIVERSIONING 932 __attribute__((hot)) __attribute__((noinline)) __attribute__((target(
"fma",
"avx")))
void computeApproximateMutation1(
size_t variable_index,
double variable_delta,
const aligned_vector<Frame>& input,
aligned_vector<Frame>& output)
const 935 auto tip_count = tip_names.size();
936 output.resize(tip_count);
937 for(
size_t itip = 0; itip < tip_count; itip++)
939 if(mutation_approx_mask[itip][variable_index] == 0)
continue;
940 auto& joint_delta = mutation_approx_frames[itip][variable_index];
941 const Frame& tip_frame = input[itip];
942 const double* tip_frame_ptr = (
const double*)&tip_frame;
943 __m256d p = _mm256_load_pd(tip_frame_ptr + 0);
944 __m256d r = _mm256_load_pd(tip_frame_ptr + 4);
946 auto joint_delta_ptr = (
const double* __restrict__) & (joint_delta);
947 __m256d ff = _mm256_set1_pd(variable_delta);
948 p = _mm256_fmadd_pd(ff, _mm256_load_pd(joint_delta_ptr + 0), p);
949 r = _mm256_fmadd_pd(ff, _mm256_load_pd(joint_delta_ptr + 4), r);
951 #if USE_QUADRATIC_EXTRAPOLATION 953 auto joint_delta_ptr = (
const double* __restrict__) & (mutation_approx_quadratics[itip][variable_index]);
954 __m256d ff = _mm256_set1_pd(variable_delta * variable_delta);
955 p = _mm256_fmadd_pd(ff, _mm256_load_pd(joint_delta_ptr + 0), p);
958 auto& tip_mutation = output[itip];
959 double* __restrict__ tip_mutation_ptr = (
double*)&tip_mutation;
960 _mm256_store_pd(tip_mutation_ptr + 0, p);
961 _mm256_store_pd(tip_mutation_ptr + 4, r);
965 __attribute__((hot)) __attribute__((noinline)) __attribute__((target(
"sse2")))
void computeApproximateMutation1(
size_t variable_index,
double variable_delta,
const aligned_vector<Frame>& input,
aligned_vector<Frame>& output)
const 968 auto tip_count = tip_names.size();
969 output.resize(tip_count);
970 for(
size_t itip = 0; itip < tip_count; itip++)
972 if(mutation_approx_mask[itip][variable_index] == 0)
continue;
973 auto& joint_delta = mutation_approx_frames[itip][variable_index];
974 const Frame& tip_frame = input[itip];
975 const double* tip_frame_ptr = (
const double*)&tip_frame;
976 __m128d pxy = _mm_load_pd(tip_frame_ptr + 0);
977 __m128d pzw = _mm_load_sd(tip_frame_ptr + 2);
978 __m128d rxy = _mm_load_pd(tip_frame_ptr + 4);
979 __m128d rzw = _mm_load_pd(tip_frame_ptr + 6);
981 auto joint_delta_ptr = (
const double* __restrict__) & (joint_delta);
982 __m128d ff = _mm_set1_pd(variable_delta);
983 pxy = _mm_add_pd(pxy, _mm_mul_pd(ff, _mm_load_pd(joint_delta_ptr + 0)));
984 pzw = _mm_add_sd(pzw, _mm_mul_sd(ff, _mm_load_sd(joint_delta_ptr + 2)));
985 rxy = _mm_add_pd(rxy, _mm_mul_pd(ff, _mm_load_pd(joint_delta_ptr + 4)));
986 rzw = _mm_add_pd(rzw, _mm_mul_pd(ff, _mm_load_pd(joint_delta_ptr + 6)));
988 #if USE_QUADRATIC_EXTRAPOLATION 990 auto joint_delta_ptr = (
const double* __restrict__) & (mutation_approx_quadratics[itip][variable_index]);
991 __m128d ff = _mm_set1_pd(variable_delta * variable_delta);
992 pxy = _mm_add_pd(pxy, _mm_mul_pd(ff, _mm_load_pd(joint_delta_ptr + 0)));
993 pzw = _mm_add_sd(pzw, _mm_mul_sd(ff, _mm_load_sd(joint_delta_ptr + 2)));
996 auto& tip_mutation = output[itip];
997 double* __restrict__ tip_mutation_ptr = (
double*)&tip_mutation;
998 _mm_store_pd(tip_mutation_ptr + 0, pxy);
999 _mm_store_sd(tip_mutation_ptr + 2, pzw);
1000 _mm_store_pd(tip_mutation_ptr + 4, rxy);
1001 _mm_store_pd(tip_mutation_ptr + 6, rzw);
1005 __attribute__((hot)) __attribute__((noinline)) __attribute__((target(
"default")))
1011 auto tip_count = tip_names.size();
1012 output.resize(tip_count);
1013 for(
size_t itip = 0; itip < tip_count; itip++)
1015 if(mutation_approx_mask[itip][variable_index] == 0)
continue;
1017 auto& joint_delta = mutation_approx_frames[itip][variable_index];
1019 const Frame& tip_frame = input[itip];
1021 double px = tip_frame.pos.x();
1022 double py = tip_frame.pos.y();
1023 double pz = tip_frame.pos.z();
1025 double rx = tip_frame.rot.x();
1026 double ry = tip_frame.rot.y();
1027 double rz = tip_frame.rot.z();
1028 double rw = tip_frame.rot.w();
1030 px += joint_delta.pos.x() * variable_delta;
1031 py += joint_delta.pos.y() * variable_delta;
1032 pz += joint_delta.pos.z() * variable_delta;
1034 #if USE_QUADRATIC_EXTRAPOLATION 1035 double variable_delta_sq = variable_delta * variable_delta;
1036 px += mutation_approx_quadratics[itip][variable_index].pos.x() * variable_delta_sq;
1037 py += mutation_approx_quadratics[itip][variable_index].pos.y() * variable_delta_sq;
1038 pz += mutation_approx_quadratics[itip][variable_index].pos.z() * variable_delta_sq;
1041 rx += joint_delta.rot.x() * variable_delta;
1042 ry += joint_delta.rot.y() * variable_delta;
1043 rz += joint_delta.rot.z() * variable_delta;
1044 rw += joint_delta.rot.w() * variable_delta;
1046 auto& tip_mutation = output[itip];
1048 tip_mutation.pos.setX(px);
1049 tip_mutation.pos.setY(py);
1050 tip_mutation.pos.setZ(pz);
1052 tip_mutation.rot.setX(rx);
1053 tip_mutation.rot.setY(ry);
1054 tip_mutation.rot.setZ(rz);
1055 tip_mutation.rot.setW(rw);
1059 #if FUNCTION_MULTIVERSIONING 1060 __attribute__((hot)) __attribute__((noinline)) __attribute__((target(
"fma",
"avx")))
void computeApproximateMutations(
size_t mutation_count,
const double*
const* mutation_values, std::vector<
aligned_vector<Frame>>& tip_frame_mutations)
const 1062 const double* p_variables = variables.data();
1063 auto tip_count = tip_names.size();
1064 tip_frame_mutations.resize(mutation_count);
1065 for(
auto& m : tip_frame_mutations)
1066 m.resize(tip_count);
1067 for(
size_t itip = 0; itip < tip_count; itip++)
1069 auto& joint_deltas = mutation_approx_frames[itip];
1071 const Frame& tip_frame = tip_frames_aligned[itip];
1073 const double* tip_frame_ptr = (
const double*)&tip_frame;
1074 __m256d p0 = _mm256_load_pd(tip_frame_ptr + 0);
1075 __m256d r0 = _mm256_load_pd(tip_frame_ptr + 4);
1077 for(
size_t imutation = 0; imutation < mutation_count; imutation++)
1082 for(
size_t vii : mutation_approx_map[itip])
1084 size_t variable_index = mutation_approx_variable_indices[vii];
1085 double variable_delta = mutation_values[imutation][vii] - p_variables[variable_index];
1088 auto joint_delta_ptr = (
const double* __restrict__) & (joint_deltas[variable_index]);
1089 __m256d ff = _mm256_set1_pd(variable_delta);
1090 p = _mm256_fmadd_pd(ff, _mm256_load_pd(joint_delta_ptr + 0), p);
1091 r = _mm256_fmadd_pd(ff, _mm256_load_pd(joint_delta_ptr + 4), r);
1094 #if USE_QUADRATIC_EXTRAPOLATION 1096 auto joint_delta_ptr = (
const double* __restrict__) & (mutation_approx_quadratics[itip][variable_index]);
1097 __m256d ff = _mm256_set1_pd(variable_delta * variable_delta);
1098 p = _mm256_fmadd_pd(ff, _mm256_load_pd(joint_delta_ptr + 0), p);
1103 auto& tip_mutation = tip_frame_mutations[imutation][itip];
1104 double* __restrict__ tip_mutation_ptr = (
double*)&tip_mutation;
1105 _mm256_store_pd(tip_mutation_ptr + 0, p);
1106 _mm256_store_pd(tip_mutation_ptr + 4, r);
1111 __attribute__((hot)) __attribute__((noinline)) __attribute__((target(
"sse2")))
void computeApproximateMutations(
size_t mutation_count,
const double*
const* mutation_values, std::vector<
aligned_vector<Frame>>& tip_frame_mutations)
const 1113 const double* p_variables = variables.data();
1114 auto tip_count = tip_names.size();
1115 tip_frame_mutations.resize(mutation_count);
1116 for(
auto& m : tip_frame_mutations)
1117 m.resize(tip_count);
1118 for(
size_t itip = 0; itip < tip_count; itip++)
1120 auto& joint_deltas = mutation_approx_frames[itip];
1122 const Frame& tip_frame = tip_frames_aligned[itip];
1124 const double* tip_frame_ptr = (
const double*)&tip_frame;
1125 __m128d pxy0 = _mm_load_pd(tip_frame_ptr + 0);
1126 __m128d pzw0 = _mm_load_sd(tip_frame_ptr + 2);
1127 __m128d rxy0 = _mm_load_pd(tip_frame_ptr + 4);
1128 __m128d rzw0 = _mm_load_pd(tip_frame_ptr + 6);
1130 for(
size_t imutation = 0; imutation < mutation_count; imutation++)
1137 for(
size_t vii : mutation_approx_map[itip])
1139 size_t variable_index = mutation_approx_variable_indices[vii];
1140 double variable_delta = mutation_values[imutation][vii] - p_variables[variable_index];
1143 auto joint_delta_ptr = (
const double* __restrict__) & (joint_deltas[variable_index]);
1144 __m128d ff = _mm_set1_pd(variable_delta);
1145 pxy = _mm_add_pd(_mm_mul_pd(ff, _mm_load_pd(joint_delta_ptr + 0)), pxy);
1146 pzw = _mm_add_sd(_mm_mul_sd(ff, _mm_load_sd(joint_delta_ptr + 2)), pzw);
1147 rxy = _mm_add_pd(_mm_mul_pd(ff, _mm_load_pd(joint_delta_ptr + 4)), rxy);
1148 rzw = _mm_add_pd(_mm_mul_pd(ff, _mm_load_pd(joint_delta_ptr + 6)), rzw);
1151 #if USE_QUADRATIC_EXTRAPOLATION 1153 auto joint_delta_ptr = (
const double* __restrict__) & (mutation_approx_quadratics[itip][variable_index]);
1154 __m128d ff = _mm_set1_pd(variable_delta * variable_delta);
1155 pxy = _mm_add_pd(_mm_mul_pd(ff, _mm_load_pd(joint_delta_ptr + 0)), pxy);
1156 pzw = _mm_add_sd(_mm_mul_sd(ff, _mm_load_sd(joint_delta_ptr + 2)), pzw);
1161 auto& tip_mutation = tip_frame_mutations[imutation][itip];
1162 double* __restrict__ tip_mutation_ptr = (
double*)&tip_mutation;
1163 _mm_store_pd(tip_mutation_ptr + 0, pxy);
1164 _mm_store_sd(tip_mutation_ptr + 2, pzw);
1165 _mm_store_pd(tip_mutation_ptr + 4, rxy);
1166 _mm_store_pd(tip_mutation_ptr + 6, rzw);
1171 __attribute__((hot)) __attribute__((noinline)) __attribute__((target(
"default")))
1174 computeApproximateMutations(
size_t mutation_count,
const double*
const* mutation_values, std::vector<
aligned_vector<Frame>>& tip_frame_mutations)
const 1176 const double* p_variables = variables.data();
1177 auto tip_count = tip_names.size();
1178 tip_frame_mutations.resize(mutation_count);
1179 for(
auto& m : tip_frame_mutations)
1180 m.resize(tip_count);
1181 for(
size_t itip = 0; itip < tip_count; itip++)
1183 auto& joint_deltas = mutation_approx_frames[itip];
1185 const Frame& tip_frame = tip_frames[itip];
1186 for(
size_t imutation = 0; imutation < mutation_count; imutation++)
1188 double px = tip_frame.pos.x();
1189 double py = tip_frame.pos.y();
1190 double pz = tip_frame.pos.z();
1192 double rx = tip_frame.rot.x();
1193 double ry = tip_frame.rot.y();
1194 double rz = tip_frame.rot.z();
1195 double rw = tip_frame.rot.w();
1197 for(
size_t vii : mutation_approx_map[itip])
1199 size_t variable_index = mutation_approx_variable_indices[vii];
1201 double variable_delta = mutation_values[imutation][vii] - p_variables[variable_index];
1203 px += joint_deltas[variable_index].pos.x() * variable_delta;
1204 py += joint_deltas[variable_index].pos.y() * variable_delta;
1205 pz += joint_deltas[variable_index].pos.z() * variable_delta;
1207 #if USE_QUADRATIC_EXTRAPOLATION 1208 double variable_delta_sq = variable_delta * variable_delta;
1209 px += mutation_approx_quadratics[itip][variable_index].pos.x() * variable_delta_sq;
1210 py += mutation_approx_quadratics[itip][variable_index].pos.y() * variable_delta_sq;
1211 pz += mutation_approx_quadratics[itip][variable_index].pos.z() * variable_delta_sq;
1214 rx += joint_deltas[variable_index].rot.x() * variable_delta;
1215 ry += joint_deltas[variable_index].rot.y() * variable_delta;
1216 rz += joint_deltas[variable_index].rot.z() * variable_delta;
1217 rw += joint_deltas[variable_index].rot.w() * variable_delta;
1220 auto& tip_mutation = tip_frame_mutations[imutation][itip];
1222 tip_mutation.pos.setX(px);
1223 tip_mutation.pos.setY(py);
1224 tip_mutation.pos.setZ(pz);
1226 tip_mutation.rot.setX(rx);
1227 tip_mutation.rot.setY(ry);
1228 tip_mutation.rot.setZ(rz);
1229 tip_mutation.rot.setW(rw);
1240 ssize_t mutation_index;
1241 ssize_t variable_index;
1242 Vector3 delta_position;
1243 Vector3 delta_rotation;
1245 size_t parent_link_index;
1246 Vector3 link_position;
1248 std::vector<JointApprox> joint_approximators;
1255 std::vector<LinkApprox> link_approximators;
1257 std::vector<int> link_approx_mask;
1259 std::vector<std::vector<size_t>> variable_to_approximator_index_map;
1262 RobotFK_Mutator_2(moveit::core::RobotModelConstPtr model)
1267 void initializeMutationApproximator(
const std::vector<size_t>& variable_indices)
1269 RobotFK_Mutator::initializeMutationApproximator(variable_indices);
1271 BLOCKPROFILER(
"initializeMutationApproximator tree");
1273 auto tip_count = tip_names.size();
1275 link_approximators.resize(robot_model->getLinkModelCount() + 1);
1276 for(
auto& l : link_approximators)
1278 l.position = Vector3(0, 0, 0);
1279 l.rotation = Vector3(0, 0, 0);
1282 link_approx_mask.clear();
1283 link_approx_mask.resize(robot_model->getLinkModelCount(), 0);
1285 joint_approximators.clear();
1288 for(
size_t imut = 0; imut < variable_indices.size(); imut++)
1290 size_t ivar = variable_indices[imut];
1291 auto* joint_model = robot_model->getJointOfVariable(ivar);
1293 auto* link_model = joint_model->getChildLinkModel();
1295 joint_approximators.emplace_back();
1296 auto& joint = joint_approximators.back();
1297 joint.mutation_index = imut;
1298 joint.variable_index = ivar;
1299 joint.link_index = link_model->getLinkIndex();
1300 link_approx_mask[link_model->getLinkIndex()] = 1;
1302 for(
auto* mimic_joint_model : joint_model->getMimicRequests())
1305 auto* joint_model = mimic_joint_model;
1306 auto* link_model = joint_model->getChildLinkModel();
1308 joint_approximators.emplace_back();
1309 auto& joint = joint_approximators.back();
1310 joint.mutation_index = imut;
1311 joint.variable_index = ivar;
1312 joint.link_index = link_model->getLinkIndex();
1313 link_approx_mask[link_model->getLinkIndex()] = 1;
1317 for(
auto& joint : joint_approximators)
1321 auto* link_model = robot_model->getLinkModel(joint.link_index);
1322 auto* joint_model = link_model->getParentJointModel();
1323 joint.delta_position = Vector3(0, 0, 0);
1324 joint.delta_rotation = Vector3(0, 0, 0);
1325 auto& link_frame = global_frames[link_model->getLinkIndex()];
1326 switch(joint_model->getType())
1328 case moveit::core::JointModel::REVOLUTE:
1329 quat_mul_vec(link_frame.rot, joint_axis_list[joint_model->getJointIndex()], joint.delta_rotation);
1331 case moveit::core::JointModel::PRISMATIC:
1332 quat_mul_vec(link_frame.rot, joint_axis_list[joint_model->getJointIndex()], joint.delta_position);
1335 for(
auto* j = joint_model; j->getMimic(); j = j->getMimic())
1337 joint.delta_rotation *= j->getMimicFactor();
1338 joint.delta_position *= j->getMimicFactor();
1344 for(
auto* link_model : tip_links)
1346 if(link_approx_mask[link_model->getLinkIndex()])
continue;
1347 joint_approximators.emplace_back();
1348 auto& joint = joint_approximators.back();
1349 joint.mutation_index = 0;
1350 joint.variable_index = 0;
1351 joint.link_index = link_model->getLinkIndex();
1352 joint.delta_position = Vector3(0, 0, 0);
1353 joint.delta_rotation = Vector3(0, 0, 0);
1354 link_approx_mask[link_model->getLinkIndex()] = 1;
1357 std::sort(joint_approximators.begin(), joint_approximators.end(), [](
const JointApprox& a,
const JointApprox& b) {
return a.link_index < b.link_index; });
1359 for(
auto& joint : joint_approximators)
1361 for(
auto* link_model = robot_model->getLinkModel(joint.link_index); link_model;)
1363 if(link_approx_mask[link_model->getLinkIndex()] >= 2)
1365 joint.parent_link_index = link_model->getLinkIndex();
1366 joint.link_position = global_frames[joint.link_index].pos - global_frames[joint.parent_link_index].pos;
1367 link_approx_mask[joint.link_index] = 2;
1370 link_model = link_model->getParentLinkModel();
1373 joint.parent_link_index = robot_model->getLinkModelCount();
1374 joint.link_position = global_frames[joint.link_index].pos;
1375 link_approx_mask[joint.link_index] = 2;
1381 variable_to_approximator_index_map.resize(robot_model->getVariableCount());
1382 for(
auto& l : variable_to_approximator_index_map)
1384 for(
size_t approximator_index = 0; approximator_index < joint_approximators.size(); approximator_index++)
1386 if(joint_approximators[approximator_index].variable_index < 0)
continue;
1387 variable_to_approximator_index_map[joint_approximators[approximator_index].variable_index].push_back(approximator_index);
1391 void computeApproximateMutations(
size_t mutation_count,
const double*
const* mutation_values, std::vector<
aligned_vector<Frame>>& tip_frame_mutations)
1393 auto tip_count = tip_names.size();
1394 tip_frame_mutations.resize(mutation_count);
1395 for(
auto& m : tip_frame_mutations)
1396 m.resize(tip_count);
1397 for(
size_t imutation = 0; imutation < mutation_count; imutation++)
1399 for(
auto& joint : joint_approximators)
1405 dvar = mutation_values[imutation][joint.mutation_index] - variables[joint.variable_index];
1407 Vector3 dpos = joint.delta_position * dvar + joint.link_position;
1409 auto& parent = link_approximators[joint.parent_link_index];
1413 Vector3 dpos1 = Vector3(parent.rotation.y() * dpos.z() - parent.rotation.z() * dpos.y(), parent.rotation.z() * dpos.x() - parent.rotation.x() * dpos.z(), parent.rotation.x() * dpos.y() - parent.rotation.y() * dpos.x());
1430 auto& link = link_approximators[joint.link_index];
1431 link.position = parent.position + dpos;
1432 link.rotation = parent.rotation + joint.delta_rotation * dvar;
1435 for(
size_t itip = 0; itip < tip_count; itip++)
1437 const Frame& tip_frame = tip_frames[itip];
1438 auto& tip_mutation = tip_frame_mutations[imutation][itip];
1439 auto& link = link_approximators[tip_links[itip]->getLinkIndex()];
1440 tip_mutation.pos = link.position;
1442 tip_mutation.rot = tf2::Quaternion(link.rotation.x() * 0.5, link.rotation.y() * 0.5, link.rotation.z() * 0.5, 1.0) * tip_frame.rot;
1456 typedef RobotFK_Mutator_2
RobotFK;
1469 moveit::core::RobotModelConstPtr robot_model;
1470 moveit::core::RobotState robot_state;
1471 std::vector<Frame> tipFrames;
1472 std::vector<const moveit::core::LinkModel*> tipLinks;
1473 std::vector<double> jj;
1477 : robot_model(model)
1478 , robot_state(model)
1481 void initialize(
const std::vector<size_t>& tip_link_indices)
1483 tipFrames.resize(tip_link_indices.size());
1484 tipLinks.resize(tip_link_indices.size());
1485 for(
size_t i = 0; i < tip_link_indices.size(); i++)
1486 tipLinks[i] = robot_model->getLinkModel(tip_link_indices[i]);
1488 void applyConfiguration(
const std::vector<double>& jj0)
1490 BLOCKPROFILER(
"RobotFK_MoveIt applyConfiguration");
1492 robot_model->interpolate(jj0.data(), jj0.data(), 0.5, jj.data());
1493 robot_state.setVariablePositions(jj);
1494 robot_state.update();
1495 for(
size_t i = 0; i < tipFrames.size(); i++)
1496 tipFrames[i] = Frame(robot_state.getGlobalLinkTransform(tipLinks[i]));
1498 inline void incrementalBegin(
const std::vector<double>& jj) {}
1499 inline void incrementalEnd() {}
1500 const Frame& getTipFrame(
size_t fi)
const {
return tipFrames[fi]; }
1501 const std::vector<Frame>& getTipFrames()
const {
return tipFrames; }