845 lines
26 KiB
C++
845 lines
26 KiB
C++
/* -------------------------------------------------------------------------- *
|
|
* OpenSim: SmoothSegmentedFunction.cpp *
|
|
* -------------------------------------------------------------------------- *
|
|
* The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
|
|
* See http://opensim.stanford.edu and the NOTICE file for more information. *
|
|
* OpenSim is developed at Stanford University and supported by the US *
|
|
* National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
|
|
* through the Warrior Web program. *
|
|
* *
|
|
* Copyright (c) 2005-2012 Stanford University and the Authors *
|
|
* Author(s): Matthew Millard *
|
|
* *
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
|
* not use this file except in compliance with the License. You may obtain a *
|
|
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
|
|
* *
|
|
* Unless required by applicable law or agreed to in writing, software *
|
|
* distributed under the License is distributed on an "AS IS" BASIS, *
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
|
* See the License for the specific language governing permissions and *
|
|
* limitations under the License. *
|
|
* -------------------------------------------------------------------------- */
|
|
/*
|
|
Update:
|
|
This is a port of the original code so that it will work with
|
|
the multibody code RBDL written by Martin Felis.
|
|
|
|
Author:
|
|
Matthew Millard
|
|
|
|
Date:
|
|
Nov 2015
|
|
|
|
*/
|
|
|
|
//=============================================================================
|
|
// INCLUDES
|
|
//=============================================================================
|
|
#include "SmoothSegmentedFunction.h"
|
|
#include <fstream>
|
|
#include <ostream>
|
|
|
|
//=============================================================================
|
|
// STATICS
|
|
//=============================================================================
|
|
//using namespace SimTK;
|
|
//using namespace OpenSim;
|
|
using namespace std;
|
|
using namespace RigidBodyDynamics::Addons::Geometry;
|
|
|
|
static bool DEBUG = false;
|
|
static double UTOL = std::numeric_limits<double>::epsilon()*1e6;
|
|
static double INTTOL = std::numeric_limits<double>::epsilon()*1e2;
|
|
static double SQRTEPS = std::sqrt(numeric_limits<double>::epsilon());
|
|
static int MAXITER = 20;
|
|
static int NUM_SAMPLE_PTS = 100;
|
|
//=============================================================================
|
|
// UTILITY FUNCTIONS
|
|
//=============================================================================
|
|
/*
|
|
DETAILED COMPUTATIONAL COSTS:
|
|
=========================================================================
|
|
WITHOUT INTEGRAL
|
|
_________________________________________________________________________
|
|
Function Comp Div Mult Add Assignments
|
|
_________________________________________________________________________
|
|
member assign M:2, 9
|
|
curve gen: m,m*100 m m*100 m m*100*(4)
|
|
+m*100(3) +m*100*(3)
|
|
|
|
Function detail
|
|
Evaluations Function
|
|
m SimTK::SplineFitter<double>::
|
|
fitForSmoothingParameter(3,x,u,0).getSpline();
|
|
Cost: ?
|
|
|
|
m*100 SegmentedQuinticBezierToolkit::
|
|
calcQuinticBezierCurveVal
|
|
Cost: Mult Add Assignments
|
|
21 20 13
|
|
|
|
Total ~typically > 2100*m multiplications, additions,
|
|
> 1000*m assignments
|
|
> 100*m divisions
|
|
_________________________________________________________________________
|
|
Comp Div Mult Add Assignments
|
|
Total: m+m*100(3) m*100 m*100*21 m*100*20 m*100*13
|
|
+m+m*100*3 +m*100*4+9+M:2
|
|
+ m*Cost(SimTK::SplineFitter ...)
|
|
=========================================================================
|
|
ADDITIONAL COST OF COMPUTING THE INTEGRAL CURVE
|
|
|
|
Comp Div Mult Add Assign
|
|
RK45 Fn.Eval m*100*(156 12 618 390 456)
|
|
RK45 Overhead m*100*(? ? ? ? ? )
|
|
Spline cost m*100*(? ? ? ? ? )
|
|
|
|
Total: ~typically > 100,000's mult, additions, assignments
|
|
> 40,000 comparisions
|
|
> 3000 divisions
|
|
|
|
=========================================================================
|
|
M: Matrix
|
|
V: Vector
|
|
|
|
N.B. These costs are dependent on SegmentedQuinticBezierToolkit
|
|
*/
|
|
SmoothSegmentedFunction::
|
|
SmoothSegmentedFunction(
|
|
const RigidBodyDynamics::Math::MatrixNd& mX,
|
|
const RigidBodyDynamics::Math::MatrixNd& mY,
|
|
double x0, double x1,
|
|
double y0, double y1,
|
|
double dydx0, double dydx1,
|
|
const std::string& name):
|
|
_x0(x0),_x1(x1),_y0(y0),_y1(y1),_dydx0(dydx0),_dydx1(dydx1),
|
|
_name(name)
|
|
{
|
|
|
|
|
|
_numBezierSections = mX.cols();
|
|
_mXVec.resize(_numBezierSections);
|
|
_mYVec.resize(_numBezierSections);
|
|
for(int s=0; s < _numBezierSections; s++){
|
|
_mXVec[s] = mX.col(s);
|
|
_mYVec[s] = mY.col(s);
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
SmoothSegmentedFunction::SmoothSegmentedFunction():
|
|
_x0(NAN),_x1(NAN),
|
|
_y0(NAN),_y1(NAN),
|
|
_dydx0(NAN),_dydx1(NAN),_name("NOT_YET_SET")
|
|
{
|
|
//_arraySplineUX.resize(0);
|
|
_mXVec.resize(0);
|
|
_mYVec.resize(0);
|
|
//_splineYintX = SimTK::Spline();
|
|
_numBezierSections = (int)NAN;
|
|
}
|
|
|
|
//==============================================================================
|
|
void SmoothSegmentedFunction::
|
|
updSmoothSegmentedFunction(
|
|
const RigidBodyDynamics::Math::MatrixNd& mX,
|
|
const RigidBodyDynamics::Math::MatrixNd& mY,
|
|
double x0, double x1,
|
|
double y0, double y1,
|
|
double dydx0, double dydx1,
|
|
const std::string& name)
|
|
{
|
|
|
|
if(mX.rows() != 6 || mY.rows() != 6 || mX.cols() != mY.cols() ){
|
|
cerr<<"SmoothSegmentedFunction::updSmoothSegmentedFunction "
|
|
<< _name.c_str()
|
|
<<": matrices mX and mY must have 6 rows, and the same"
|
|
<<" number of columns."
|
|
<< endl;
|
|
assert(0);
|
|
abort();
|
|
}
|
|
|
|
_x0 =x0;
|
|
_x1 =x1;
|
|
_y0 =y0;
|
|
_y1 =y1;
|
|
_dydx0=dydx0;
|
|
_dydx1=dydx1;
|
|
|
|
if(mX.cols() != _mXVec.size()){
|
|
_mXVec.resize(mX.cols());
|
|
_mYVec.resize(mY.cols());
|
|
}
|
|
|
|
_numBezierSections = mX.cols();
|
|
for(int s=0; s < mX.cols(); s++){
|
|
_mXVec[s] = mX.col(s);
|
|
_mYVec[s] = mY.col(s);
|
|
}
|
|
|
|
_name = name;
|
|
}
|
|
//==============================================================================
|
|
void SmoothSegmentedFunction::shift(double xShift, double yShift)
|
|
{
|
|
_x0 += xShift;
|
|
_x1 += xShift;
|
|
_y0 += yShift;
|
|
_y1 += yShift;
|
|
|
|
for(int i=0; i<_mXVec.size();++i){
|
|
for(int j=0; j<_mXVec.at(i).rows();++j){
|
|
_mXVec.at(i)[j] += xShift;
|
|
_mYVec.at(i)[j] += yShift;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void SmoothSegmentedFunction::scale(double xScale, double yScale)
|
|
{
|
|
|
|
if( abs( xScale ) <= SQRTEPS){
|
|
cerr<<"SmoothSegmentedFunction::scale "
|
|
<< _name.c_str()
|
|
<<": xScale must be greater than sqrt(eps). Setting xScale to such"
|
|
<<" a small value will cause the slope of the curve to approach "
|
|
<<" infinity, or become undefined."
|
|
<< endl;
|
|
assert(0);
|
|
abort();
|
|
}
|
|
|
|
_x0 *= xScale;
|
|
_x1 *= xScale;
|
|
_y0 *= yScale;
|
|
_y1 *= yScale;
|
|
_dydx0 *= yScale/xScale;
|
|
_dydx1 *= yScale/xScale;
|
|
|
|
for(int i=0; i<_mXVec.size();++i){
|
|
for(int j=0; j<_mXVec.at(i).rows();++j){
|
|
_mXVec.at(i)[j] *= xScale;
|
|
_mYVec.at(i)[j] *= yScale;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
|
|
|
/*Detailed Computational Costs
|
|
________________________________________________________________________
|
|
If x is in the Bezier Curve
|
|
Name Comp. Div. Mult. Add. Assign.
|
|
_______________________________________________________________________
|
|
SegmentedQuinticBezierToolkit::
|
|
calcIndex 3*m+2 1*m 3
|
|
*calcU 15 2 82 42 60
|
|
calcQuinticBezierCurveVal 21 20 13
|
|
total 15+3*m+2 2 103 62+1*m 76
|
|
|
|
*Approximate. Uses iteration
|
|
________________________________________________________________________
|
|
If x is in the linear region
|
|
|
|
Name Comp. Div. Mult. Add. Assign.
|
|
1 1 2 1
|
|
________________________________________________________________________
|
|
|
|
*/
|
|
|
|
double SmoothSegmentedFunction::calcValue(double x) const
|
|
{
|
|
double yVal = 0;
|
|
if(x >= _x0 && x <= _x1 )
|
|
{
|
|
int idx = SegmentedQuinticBezierToolkit::calcIndex(x,_mXVec);
|
|
double u = SegmentedQuinticBezierToolkit::
|
|
calcU(x,_mXVec[idx], UTOL, MAXITER);
|
|
yVal = SegmentedQuinticBezierToolkit::
|
|
calcQuinticBezierCurveVal(u,_mYVec[idx]);
|
|
}else{
|
|
if(x < _x0){
|
|
yVal = _y0 + _dydx0*(x-_x0);
|
|
}else{
|
|
yVal = _y1 + _dydx1*(x-_x1);
|
|
}
|
|
}
|
|
|
|
return yVal;
|
|
}
|
|
|
|
double SmoothSegmentedFunction::calcValue(
|
|
const RigidBodyDynamics::Math::VectorNd& ax) const
|
|
{
|
|
|
|
if( !(ax.size() == 1) ){
|
|
cerr<<"SmoothSegmentedFunction::calcValue " << _name.c_str()
|
|
<<": Argument x must have only 1 element, as this function is "
|
|
<< "designed only for 1D functions, but a function with "
|
|
<< ax.size() << " elements was entered"
|
|
<< endl;
|
|
assert(0);
|
|
abort();
|
|
|
|
}
|
|
|
|
return calcValue(ax[0]);
|
|
}
|
|
|
|
/*Detailed Computational Costs
|
|
________________________________________________________________________
|
|
If x is in the Bezier Curve, and dy/dx is being evaluated
|
|
Name Comp. Div. Mult. Add. Assign.
|
|
_______________________________________________________________________
|
|
Overhead:
|
|
SegmentedQuinticBezierToolkit::
|
|
calcIndex 3*m+2 1*m 3
|
|
*calcU 15 2 82 42 60
|
|
Derivative Evaluation:
|
|
**calcQuinticBezierCurveDYDX 21 20 13
|
|
dy/du 20 19 11
|
|
dx/du 20 19 11
|
|
dy/dx 1
|
|
|
|
total 17+3*m 3 143 m+100 98
|
|
|
|
*Approximate. Uses iteration
|
|
**Higher order derivatives cost more
|
|
________________________________________________________________________
|
|
If x is in the linear region
|
|
|
|
Name Comp. Div. Mult. Add. Assign.
|
|
1 1
|
|
________________________________________________________________________
|
|
*/
|
|
|
|
double SmoothSegmentedFunction::calcDerivative(double x, int order) const
|
|
{
|
|
//return calcDerivative( SimTK::Array_<int>(order,0),
|
|
// RigidBodyDynamics::Math::VectorNd(1,x));
|
|
double yVal = 0;
|
|
|
|
//QUINTIC SPLINE
|
|
|
|
|
|
if(order==0){
|
|
yVal = calcValue(x);
|
|
}else{
|
|
if(x >= _x0 && x <= _x1){
|
|
int idx = SegmentedQuinticBezierToolkit::calcIndex(x,_mXVec);
|
|
double u = SegmentedQuinticBezierToolkit::
|
|
calcU(x,_mXVec[idx],UTOL,MAXITER);
|
|
yVal = SegmentedQuinticBezierToolkit::
|
|
calcQuinticBezierCurveDerivDYDX(u, _mXVec[idx],
|
|
_mYVec[idx], order);
|
|
/*
|
|
std::cout << _mX(3, idx) << std::endl;
|
|
std::cout << _mX(idx) << std::endl;*/
|
|
}else{
|
|
if(order == 1){
|
|
if(x < _x0){
|
|
yVal = _dydx0;
|
|
}else{
|
|
yVal = _dydx1;}
|
|
}else{
|
|
yVal = 0;}
|
|
}
|
|
}
|
|
|
|
return yVal;
|
|
}
|
|
|
|
|
|
|
|
double SmoothSegmentedFunction::
|
|
calcDerivative( const std::vector<int>& derivComponents,
|
|
const RigidBodyDynamics::Math::VectorNd& ax) const
|
|
{
|
|
/*
|
|
for(int i=0; i < (signed)derivComponents.size(); i++){
|
|
SimTK_ERRCHK2_ALWAYS( derivComponents[i] == 0,
|
|
"SmoothSegmentedFunction::calcDerivative",
|
|
"%s: derivComponents can only be populated with 0's because "
|
|
"SmoothSegmentedFunction is only valid for a 1D function, but "
|
|
"derivComponents had a value of %i in it",
|
|
_name.c_str(), derivComponents[i]);
|
|
}
|
|
SimTK_ERRCHK2_ALWAYS( derivComponents.size() <= 6,
|
|
"SmoothSegmentedFunction::calcDerivative",
|
|
"%s: calcDerivative is only valid up to a 6th order derivative"
|
|
" but derivComponents had a size of %i",
|
|
_name.c_str(), derivComponents.size());
|
|
|
|
SimTK_ERRCHK2_ALWAYS( ax.size() == 1,
|
|
"SmoothSegmentedFunction::calcValue",
|
|
"%s: Argument x must have only 1 element, as this function is "
|
|
"designed only for 1D functions, but ax had a size of %i",
|
|
_name.c_str(), ax.size());
|
|
*/
|
|
|
|
for(int i=0; i < (signed)derivComponents.size(); i++){
|
|
if( !(derivComponents[i] == 0)){
|
|
cerr << "SmoothSegmentedFunction::calcDerivative "
|
|
<< _name.c_str()
|
|
<<": derivComponents can only be populated with 0's because "
|
|
<< "SmoothSegmentedFunction is only valid for a 1D function,"
|
|
<< " but derivComponents had a value of "
|
|
<< derivComponents[i] << " in it"
|
|
<< endl;
|
|
assert(0);
|
|
abort();
|
|
|
|
}
|
|
}
|
|
|
|
if( !(derivComponents.size() <= 6)){
|
|
cerr << "SmoothSegmentedFunction::calcDerivative " << _name.c_str()
|
|
<< ": calcDerivative is only valid up to a 6th order derivative"
|
|
<< " but derivComponents had a size of "
|
|
<< derivComponents.size()
|
|
<< endl;
|
|
assert(0);
|
|
abort();
|
|
}
|
|
|
|
if( !(ax.size() == 1) ){
|
|
cerr << "SmoothSegmentedFunction::calcValue " << _name.c_str()
|
|
<< ": Argument x must have only 1 element, as this function is "
|
|
<< "designed only for 1D functions, but ax had a size of "
|
|
<< ax.size()
|
|
<< endl;
|
|
assert(0);
|
|
abort();
|
|
}
|
|
|
|
|
|
return calcDerivative(ax[0], derivComponents.size());
|
|
}
|
|
|
|
/*Detailed Computational Costs
|
|
________________________________________________________________________
|
|
If x is in the Bezier Curve, and dy/dx is being evaluated
|
|
Name Comp. Div. Mult. Add. Assign.
|
|
_______________________________________________________________________
|
|
*spline.calcValue 7 2 3 1
|
|
|
|
*Approximate cost of evaluating a cubic spline with 100 knots, where
|
|
the bisection method is used to find the correct index
|
|
________________________________________________________________________
|
|
If x is in the linear region
|
|
|
|
Name Comp. Div. Mult. Add. Assign.
|
|
*spline.calcValue 1 2 3 1
|
|
integral eval 2 4 5 1
|
|
total 3 6 8 2
|
|
|
|
*Approximate cost of evaluating a cubic spline at its last knot point
|
|
________________________________________________________________________
|
|
|
|
*/
|
|
/*
|
|
double SmoothSegmentedFunction::calcIntegral(double x) const
|
|
{
|
|
SimTK_ERRCHK1_ALWAYS(_computeIntegral,
|
|
"SmoothSegmentedFunction::calcIntegral",
|
|
"%s: This curve was not constructed with its integral because"
|
|
"computeIntegral was false",_name.c_str());
|
|
|
|
double yVal = 0;
|
|
if(x >= _x0 && x <= _x1){
|
|
yVal = _splineYintX.calcValue(RigidBodyDynamics::Math::VectorNd(1,x));
|
|
}else{
|
|
//LINEAR EXTRAPOLATION
|
|
if(x < _x0){
|
|
RigidBodyDynamics::Math::VectorNd tmp(1);
|
|
tmp(0) = _x0;
|
|
double ic = _splineYintX.calcValue(tmp);
|
|
if(_intx0x1){//Integrating left to right
|
|
yVal = _y0*(x-_x0)
|
|
+ _dydx0*(x-_x0)*(x-_x0)*0.5
|
|
+ ic;
|
|
}else{//Integrating right to left
|
|
yVal = -_y0*(x-_x0)
|
|
- _dydx0*(x-_x0)*(x-_x0)*0.5
|
|
+ ic;
|
|
}
|
|
}else{
|
|
RigidBodyDynamics::Math::VectorNd tmp(1);
|
|
tmp(0) = _x1;
|
|
double ic = _splineYintX.calcValue(tmp);
|
|
if(_intx0x1){
|
|
yVal = _y1*(x-_x1)
|
|
+ _dydx1*(x-_x1)*(x-_x1)*0.5
|
|
+ ic;
|
|
}else{
|
|
yVal = -_y1*(x-_x1)
|
|
- _dydx1*(x-_x1)*(x-_x1)*0.5
|
|
+ ic;
|
|
}
|
|
}
|
|
}
|
|
|
|
return yVal;
|
|
}
|
|
*/
|
|
/*
|
|
bool SmoothSegmentedFunction::isIntegralAvailable() const
|
|
{
|
|
return _computeIntegral;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
bool SmoothSegmentedFunction::isIntegralComputedLeftToRight() const
|
|
{
|
|
return _intx0x1;
|
|
}
|
|
*/
|
|
|
|
int SmoothSegmentedFunction::getArgumentSize() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int SmoothSegmentedFunction::getMaxDerivativeOrder() const
|
|
{
|
|
return 6;
|
|
}
|
|
|
|
std::string SmoothSegmentedFunction::getName() const
|
|
{
|
|
return _name;
|
|
}
|
|
|
|
void SmoothSegmentedFunction::setName(const std::string &name)
|
|
{
|
|
_name = name;
|
|
}
|
|
|
|
RigidBodyDynamics::Math::VectorNd
|
|
SmoothSegmentedFunction::getCurveDomain() const
|
|
{
|
|
RigidBodyDynamics::Math::VectorNd xrange(2);
|
|
|
|
xrange[0] = 0;
|
|
xrange[1] = 0;
|
|
if (!_mXVec.empty()) {
|
|
xrange[0] = _mXVec[0][0];
|
|
xrange[1] = _mXVec[_mXVec.size()-1][_mXVec[0].size()-1];
|
|
}
|
|
return xrange;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Utility functions
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/*Detailed Computational Costs
|
|
|
|
_______________________________________________________________________
|
|
Name Comp. Div. Mult. Add. Assign.
|
|
_______________________________________________________________________
|
|
|
|
*overhead (17+3*m 2 82 42+m 63)*7
|
|
119+21*m 14 574 294+7m 441
|
|
|
|
calcValue 21 20 13
|
|
calcDerivative: dy/dx 1 40 38 22
|
|
: d2y/dx2 2 78 73 23
|
|
: d3y/dx3 4 118 105 58
|
|
: d4y/dx4 5 168 137 71
|
|
: d5y/dx5 7 236 170 88
|
|
: d6y/dx6 9 334 209 106
|
|
|
|
**calcIntegral 7 2 3 1
|
|
|
|
total per point 126+21*m 42 1571 1049 823
|
|
total per elbow 126k+21k*m 42k 1571k 1049k 823k
|
|
|
|
*Approximate. Overhead associated with finding the correct Bezier
|
|
spline section, and evaluating u(x).
|
|
Assumes 2 Newton iterations in calcU
|
|
|
|
**Approximate. Includes estimated cost of evaluating a cubic spline
|
|
with 100 knots
|
|
*/
|
|
RigidBodyDynamics::Math::MatrixNd
|
|
SmoothSegmentedFunction::calcSampledCurve(int maxOrder,
|
|
double domainMin,
|
|
double domainMax) const{
|
|
|
|
int pts = 1; //Number of points between each of the spline points used
|
|
//to fit u(x), and also the integral spline
|
|
if( !(maxOrder <= getMaxDerivativeOrder()) ){
|
|
cerr << "SmoothSegmentedFunction::calcSampledCurve "
|
|
<< "Derivative order past the maximum computed order requested"
|
|
<< endl;
|
|
assert(0);
|
|
abort();
|
|
}
|
|
|
|
double x0,x1,delta;
|
|
//y,dy,d1y,d2y,d3y,d4y,d5y,d6y,iy
|
|
RigidBodyDynamics::Math::VectorNd
|
|
midX(NUM_SAMPLE_PTS*_numBezierSections-(_numBezierSections-1));
|
|
RigidBodyDynamics::Math::VectorNd x(NUM_SAMPLE_PTS);
|
|
|
|
//Generate a sample of X values inside of the curve that is denser where
|
|
//the curve is more curvy.
|
|
double u;
|
|
int idx = 0;
|
|
for(int s=0; s < _numBezierSections; s++){
|
|
//Sample the local set for u and x
|
|
for(int i=0;i<NUM_SAMPLE_PTS;i++){
|
|
u = ( (double)i )/( (double)(NUM_SAMPLE_PTS-1) );
|
|
x[i] = SegmentedQuinticBezierToolkit::
|
|
calcQuinticBezierCurveVal(u,_mXVec[s]);
|
|
if(_numBezierSections > 1){
|
|
//Skip the last point of a set that has another set of points
|
|
//after it. Why? The last point and the starting point of the
|
|
//next set are identical in value.
|
|
if(i<(NUM_SAMPLE_PTS-1) || s == (_numBezierSections-1)){
|
|
midX[idx] = x[i];
|
|
idx++;
|
|
}
|
|
}else{
|
|
midX[idx] = x[i];
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
RigidBodyDynamics::Math::VectorNd xsmpl(pts*(midX.size()-1)+2*10*pts);
|
|
|
|
RigidBodyDynamics::Math::MatrixNd results;
|
|
|
|
/*
|
|
if(_computeIntegral){
|
|
results.resize(pts*(midX.size()-1)+2*10*pts,maxOrder+2+1);
|
|
}else{
|
|
|
|
}
|
|
*/
|
|
results.resize(pts*(midX.size()-1)+2*10*pts,maxOrder+2);
|
|
|
|
//Array initialization is so ugly ...
|
|
std::vector<int> d1y(1),d2y(2),d3y(3),d4y(4),d5y(5),d6y(6);
|
|
d1y[0]=0;
|
|
d2y[0] = 0;
|
|
d2y[1] = 0;
|
|
for(int i=0;i<3;i++)
|
|
d3y[i]=0;
|
|
for(int i=0;i<4;i++)
|
|
d4y[i]=0;
|
|
for(int i=0;i<5;i++)
|
|
d5y[i]=0;
|
|
for(int i=0;i<6;i++)
|
|
d6y[i]=0;
|
|
|
|
//generate some sample points in the extrapolated region
|
|
idx = 0;
|
|
x0 = _x0 - 0.1*(_x1-_x0);
|
|
if(domainMin < x0)
|
|
x0 = domainMin;
|
|
|
|
x1 = _x0;
|
|
delta = (0.1)*(x1-x0)/(pts);
|
|
|
|
for(int j=0; j<pts*10; j++){
|
|
xsmpl[idx] = x0 + delta*j;
|
|
idx++;
|
|
}
|
|
|
|
|
|
//generate some points in the mid region
|
|
for(int i=0; i< midX.size()-1;i++){
|
|
x0 = midX[i];
|
|
x1 = midX[i+1];
|
|
delta = (x1-x0)/pts;
|
|
for(int j=0;j<pts;j++){
|
|
xsmpl[idx] = x0 + delta*j;
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
//generate some points in the extrapolated region
|
|
x0 = _x1;
|
|
x1 = _x1 + 0.1*(_x1-_x0);
|
|
if(domainMax > x1)
|
|
x1 = domainMax;
|
|
|
|
delta = (1.0/9.0)*(x1-x0)/(pts);
|
|
|
|
for(int j=0;j<pts*10;j++){
|
|
xsmpl[idx] = x0 + delta*j;
|
|
idx++;
|
|
}
|
|
|
|
//Populate the results matrix at the sample points
|
|
RigidBodyDynamics::Math::VectorNd ax(1);
|
|
for(int i=0; i < xsmpl.size(); i++){
|
|
ax[0] = xsmpl[i];
|
|
results(i,0) = ax[0];
|
|
if(i==48){
|
|
double here = 1.0;
|
|
}
|
|
results(i,1) = calcValue(ax);
|
|
if(maxOrder>=1)
|
|
results(i,2) = calcDerivative(d1y,ax);
|
|
|
|
if(maxOrder>=2)
|
|
results(i,3) = calcDerivative(d2y,ax);
|
|
|
|
if(maxOrder>=3)
|
|
results(i,4) = calcDerivative(d3y,ax);
|
|
|
|
if(maxOrder>=4)
|
|
results(i,5) = calcDerivative(d4y,ax);
|
|
|
|
if(maxOrder>=5)
|
|
results(i,6) = calcDerivative(d5y,ax);
|
|
|
|
if(maxOrder>=6)
|
|
results(i,7) = calcDerivative(d6y,ax);
|
|
|
|
/*
|
|
if(_computeIntegral){
|
|
results(i,maxOrder+2) = calcIntegral(ax(0));
|
|
}
|
|
*/
|
|
}
|
|
return results;
|
|
}
|
|
|
|
void SmoothSegmentedFunction::getXControlPoints(
|
|
RigidBodyDynamics::Math::MatrixNd& mat) const
|
|
{
|
|
mat.resize(_mXVec.size(), _mXVec.at(0).size());
|
|
|
|
for(int i=0; i<_mXVec.size();++i){
|
|
for(int j=0; j<_mXVec.size();++j){
|
|
mat(i,j) = _mXVec.at(i)[j];
|
|
}
|
|
}
|
|
|
|
}
|
|
void SmoothSegmentedFunction::getYControlPoints(
|
|
RigidBodyDynamics::Math::MatrixNd& mat) const
|
|
{
|
|
mat.resize(_mYVec.size(), _mYVec.at(0).size());
|
|
|
|
for(int i=0; i<_mYVec.size();++i){
|
|
for(int j=0; j<_mYVec.size();++j){
|
|
mat(i,j) = _mYVec.at(i)[j];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*Detailed Computational Costs
|
|
|
|
_______________________________________________________________________
|
|
Name Comp. Div. Mult. Add. Assign.
|
|
_______________________________________________________________________
|
|
|
|
*overhead (17+3*m 2 82 42+m 63)*3
|
|
51+9m 6 246 126+3m 189
|
|
|
|
calcValue 21 20 13
|
|
calcDerivative : dy/dx 1 40 38 22
|
|
: d2y/dx2 2 78 73 23
|
|
|
|
**calcIntegral 7 2 3 1
|
|
|
|
total per point 58+9m 9 387 260+3m 248
|
|
total per elbow 5.8k+900m 900 38.7k 26k+300m 24.8k
|
|
|
|
*Approximate. Overhead associated with finding the correct Bezier
|
|
spline section, and evaluating u(x).
|
|
Assumes 2 Newton iterations in calcU
|
|
|
|
**Approximate. Includes estimated cost of evaluating a cubic spline
|
|
with 100 knots
|
|
*/
|
|
void SmoothSegmentedFunction::printCurveToCSVFile(
|
|
const std::string& path,
|
|
const std::string& fileNameWithoutExtension,
|
|
double domainMin,
|
|
double domainMax) const
|
|
{
|
|
//Only compute up to the 2nd derivative
|
|
RigidBodyDynamics::Math::MatrixNd results =
|
|
calcSampledCurve(2,domainMin,domainMax);
|
|
std::vector<std::string> colNames(results.cols());
|
|
colNames[0] = "x";
|
|
colNames[1] = "y";
|
|
colNames[2] = "dy/dx";
|
|
colNames[3] = "d2y/dx2";
|
|
/*
|
|
if(results.cols() == 5){
|
|
colNames[4] = "int_y(x)";
|
|
}
|
|
*/
|
|
std::string fname = fileNameWithoutExtension;
|
|
fname.append(".csv");
|
|
printMatrixToFile(results,colNames,path,fname);
|
|
}
|
|
/*
|
|
This function will print cvs file of the column vector col0 and the matrix data
|
|
*/
|
|
void SmoothSegmentedFunction::
|
|
printMatrixToFile( RigidBodyDynamics::Math::MatrixNd& data,
|
|
std::vector<std::string>& colNames,
|
|
const std::string& path,
|
|
const std::string& filename) const
|
|
{
|
|
|
|
ofstream datafile;
|
|
std::string fullpath = path;
|
|
|
|
if(fullpath.length() > 0)
|
|
fullpath.append("/");
|
|
|
|
fullpath.append(filename);
|
|
|
|
datafile.open(fullpath.c_str(),std::ios::out);
|
|
|
|
if(!datafile){
|
|
datafile.close();
|
|
cerr << "SmoothSegmentedFunction::printMatrixToFile "
|
|
<< _name.c_str() << ": Failed to open the file path: "
|
|
<< fullpath.c_str()
|
|
<< endl;
|
|
assert(0);
|
|
abort();
|
|
}
|
|
|
|
|
|
for(int i = 0; i < (signed)colNames.size(); i++){
|
|
if(i < (signed)colNames.size()-1)
|
|
datafile << colNames[i] << ",";
|
|
else
|
|
datafile << colNames[i] << "\n";
|
|
}
|
|
|
|
for(int i = 0; i < data.rows(); i++){
|
|
for(int j = 0; j < data.cols(); j++){
|
|
if(j<data.cols()-1)
|
|
datafile << data(i,j) << ",";
|
|
else
|
|
datafile << data(i,j) << "\n";
|
|
}
|
|
}
|
|
datafile.close();
|
|
}
|
|
|
|
|