// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2002 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #include "kdl_cp/path_line.hpp" #include "kdl_cp/path_roundedcomposite.hpp" #include "kdl_cp/rotational_interpolation_sa.hpp" #include "kdl_cp/trajectory_composite.hpp" #include "kdl_cp/trajectory_segment.hpp" #include "kdl_cp/utilities/error.h" #include "kdl_cp/velocityprofile_trap.hpp" #include #include #include #include "RobotAlgos.h" #include "Trajectory.h" using namespace Robot; using namespace Base; TYPESYSTEM_SOURCE(Robot::Trajectory, Base::Persistence) Trajectory::Trajectory() = default; Trajectory::Trajectory(const Trajectory& Trac) : vpcWaypoints(Trac.vpcWaypoints.size()) , pcTrajectory(nullptr) { operator=(Trac); } Trajectory::~Trajectory() { for (auto it : vpcWaypoints) { delete it; } delete pcTrajectory; } Trajectory& Trajectory::operator=(const Trajectory& Trac) { if (this == &Trac) { return *this; } for (auto it : vpcWaypoints) { delete it; } vpcWaypoints.clear(); vpcWaypoints.resize(Trac.vpcWaypoints.size()); int i = 0; for (std::vector::const_iterator it = Trac.vpcWaypoints.begin(); it != Trac.vpcWaypoints.end(); ++it, i++) { vpcWaypoints[i] = new Waypoint(**it); } generateTrajectory(); return *this; } double Trajectory::getLength(int n) const { if (pcTrajectory) { if (n < 0) { return pcTrajectory->GetPath()->PathLength(); } else { return pcTrajectory->Get(n)->GetPath()->PathLength(); } } else { return 0; } } double Trajectory::getDuration(int n) const { if (pcTrajectory) { if (n < 0) { return pcTrajectory->Duration(); } else { return pcTrajectory->Get(n)->Duration(); } } else { return 0; } } Placement Trajectory::getPosition(double time) const { if (pcTrajectory) { return Placement(toPlacement(pcTrajectory->Pos(time))); } return {}; } double Trajectory::getVelocity(double time) const { if (pcTrajectory) { KDL::Vector vec = pcTrajectory->Vel(time).vel; Base::Vector3d vec2(vec[0], vec[1], vec[2]); return vec2.Length(); } else { return 0; } } void Trajectory::deleteLast(unsigned int n) { for (unsigned int i = 0; i <= n; i++) { delete (*vpcWaypoints.rbegin()); vpcWaypoints.pop_back(); } } void Trajectory::generateTrajectory() { if (vpcWaypoints.empty()) { return; } // delete the old and create a new one if (pcTrajectory) { delete (pcTrajectory); } pcTrajectory = new KDL::Trajectory_Composite(); // pointer to the pieces while iterating std::unique_ptr pcTrak; std::unique_ptr pcVelPrf; std::unique_ptr pcRoundComp; KDL::Frame Last; try { // handle the first waypoint special bool first = true; for (std::vector::const_iterator it = vpcWaypoints.begin(); it != vpcWaypoints.end(); ++it) { if (first) { Last = toFrame((*it)->EndPos); first = false; } else { // destinct the type of movement switch ((*it)->Type) { case Waypoint::LINE: case Waypoint::PTP: { KDL::Frame Next = toFrame((*it)->EndPos); // continues the movement until no continuous waypoint or the end bool Cont = (*it)->Cont && !(it == --vpcWaypoints.end()); // start of a continue block if (Cont && !pcRoundComp) { pcRoundComp = std::make_unique( 3, 3, new KDL::RotationalInterpolation_SingleAxis() ); // the velocity of the first waypoint is used pcVelPrf = std::make_unique( (*it)->Velocity, (*it)->Acceleration ); pcRoundComp->Add(Last); pcRoundComp->Add(Next); // continue a continues block } else if (Cont && pcRoundComp) { pcRoundComp->Add(Next); // end a continuous block } else if (!Cont && pcRoundComp) { // add the last one pcRoundComp->Add(Next); pcRoundComp->Finish(); pcVelPrf->SetProfile(0, pcRoundComp->PathLength()); pcTrak = std::make_unique( pcRoundComp.release(), pcVelPrf.release() ); // normal block } else if (!Cont && !pcRoundComp) { KDL::Path* pcPath; pcPath = new KDL::Path_Line( Last, Next, new KDL::RotationalInterpolation_SingleAxis(), 1.0, true ); pcVelPrf = std::make_unique( (*it)->Velocity, (*it)->Acceleration ); pcVelPrf->SetProfile(0, pcPath->PathLength()); pcTrak = std::make_unique( pcPath, pcVelPrf.release() ); } Last = Next; break; } case Waypoint::WAIT: break; default: break; } // add the segment if no continuous block is running if (!pcRoundComp && pcTrak) { pcTrajectory->Add(pcTrak.release()); } } } } catch (KDL::Error& e) { throw Base::RuntimeError(e.Description()); } } std::string Trajectory::getUniqueWaypointName(const char* Name) const { if (!Name || *Name == '\0') { return {}; } // check for first character whether it's a digit std::string CleanName = Name; if (!CleanName.empty() && CleanName[0] >= 48 && CleanName[0] <= 57) { CleanName[0] = '_'; } // strip illegal chars for (char& it : CleanName) { if (!((it >= 48 && it <= 57) || // number (it >= 65 && it <= 90) || // uppercase letter (it >= 97 && it <= 122))) { // lowercase letter it = '_'; // it's neither number nor letter } } // name in use? std::vector::const_iterator it; for (it = vpcWaypoints.begin(); it != vpcWaypoints.end(); ++it) { if ((*it)->Name == CleanName) { break; } } if (it == vpcWaypoints.end()) { // if not, name is OK return CleanName; } else { // find highest suffix int nSuff = 0; for (it = vpcWaypoints.begin(); it != vpcWaypoints.end(); ++it) { const std::string& ObjName = (*it)->Name; if (ObjName.substr(0, CleanName.length()) == CleanName) { // same prefix std::string clSuffix(ObjName.substr(CleanName.length())); if (!clSuffix.empty()) { std::string::size_type nPos = clSuffix.find_first_not_of("0123456789"); if (nPos == std::string::npos) { nSuff = std::max(nSuff, std::atol(clSuffix.c_str())); } } } } std::stringstream str; str << CleanName << (nSuff + 1); return str.str(); } } void Trajectory::addWaypoint(const Waypoint& WPnt) { std::string UniqueName = getUniqueWaypointName(WPnt.Name.c_str()); Waypoint* tmp = new Waypoint(WPnt); tmp->Name = UniqueName; vpcWaypoints.push_back(tmp); } unsigned int Trajectory::getMemSize() const { return 0; } void Trajectory::Save(Writer& writer) const { writer.Stream() << writer.ind() << "" << std::endl; writer.incInd(); for (unsigned int i = 0; i < getSize(); i++) { vpcWaypoints[i]->Save(writer); } writer.decInd(); writer.Stream() << writer.ind() << "" << std::endl; } void Trajectory::Restore(XMLReader& reader) { vpcWaypoints.clear(); // read my element reader.readElement("Trajectory"); // get the value of my Attribute int count = reader.getAttribute("count"); vpcWaypoints.resize(count); for (int i = 0; i < count; i++) { Waypoint* tmp = new Waypoint(); tmp->Restore(reader); vpcWaypoints[i] = tmp; } generateTrajectory(); }