===========================
- itasc adopted for Eigen3 library.
It compiles well, but need deeper testing for regressions.
- Removed Eigen2 library.
- Added settings to tracker which could be changed from UI.
- Pattern area is now affects on tracker.
Currently libmv supports square patterns which are centered
to marker's position. Maximal pattern dimensions is sending
to libmv as pattern size. Would be changed when libmv would
support non-centered and non-square patterns.
- Fixed bug with syncing pattern's flags when tracking.
- Current frame in cache line became a bit more visible.
It's useful for me to when debugging.
- Changed behaviour of "Add Marekr" operator: not it's non-modal
and places marker on mouse position at click.
- Added macro "Add Marekr and Move" which is used to place
markers from toolbar button.
- Added some utility functions to get image buffer under search
and pattern area which also returns relative position of
marker center for this images.
Generated images are more "correct" from coords rounding
POV, but re-calculation of marker position back to
frame coords is more complicated and not implemented yet,
so old not very accurate logic is still used.
- Added preview widget with content of pattern area.
NOTE: files saved in previous versions of this branch could
easily crash on tracking. Use "Reset To Settings" button
from Tracking Settings before tracking selected markers
for such files.
TODO:
- Implement adjusting marker position from marker's
preview widget.
- We've got an idea of sliding marker after click before
releasing mouse button.
484 lines
14 KiB
C++
484 lines
14 KiB
C++
/** \file itasc/CopyPose.cpp
|
|
* \ingroup itasc
|
|
*/
|
|
/* $Id$
|
|
* CopyPose.cpp
|
|
*
|
|
* Created on: Mar 17, 2009
|
|
* Author: benoit bolsee
|
|
*/
|
|
|
|
#include "CopyPose.hpp"
|
|
#include "kdl/kinfam_io.hpp"
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
namespace iTaSC
|
|
{
|
|
|
|
const unsigned int maxPoseCacheSize = (2*(3+3*2));
|
|
CopyPose::CopyPose(unsigned int control_output, unsigned int dynamic_output, double armlength, double accuracy, unsigned int maximum_iterations):
|
|
ConstraintSet(),
|
|
m_cache(NULL),
|
|
m_poseCCh(-1),m_poseCTs(0)
|
|
{
|
|
m_maxerror = armlength/2.0;
|
|
m_outputControl = (control_output & CTL_ALL);
|
|
unsigned int _nc = nBitsOn(m_outputControl);
|
|
if (!_nc)
|
|
return;
|
|
// reset the constraint set
|
|
reset(_nc, accuracy, maximum_iterations);
|
|
_nc = 0;
|
|
m_nvalues = 0;
|
|
int nrot = 0, npos = 0;
|
|
int nposCache = 0, nrotCache = 0;
|
|
m_outputDynamic = (dynamic_output & m_outputControl);
|
|
memset(m_values, 0, sizeof(m_values));
|
|
memset(m_posData, 0, sizeof(m_posData));
|
|
memset(m_rotData, 0, sizeof(m_rotData));
|
|
memset(&m_rot, 0, sizeof(m_rot));
|
|
memset(&m_pos, 0, sizeof(m_pos));
|
|
if (m_outputControl & CTL_POSITION) {
|
|
m_pos.alpha = 1.0;
|
|
m_pos.K = 20.0;
|
|
m_pos.tolerance = 0.05;
|
|
m_values[m_nvalues].alpha = m_pos.alpha;
|
|
m_values[m_nvalues].feedback = m_pos.K;
|
|
m_values[m_nvalues].tolerance = m_pos.tolerance;
|
|
m_values[m_nvalues].id = ID_POSITION;
|
|
if (m_outputControl & CTL_POSITIONX) {
|
|
m_Wy(_nc) = m_pos.alpha/*/(m_pos.tolerance*m_pos.K)*/;
|
|
m_Cf(_nc++,0)=1.0;
|
|
m_posData[npos++].id = ID_POSITIONX;
|
|
if (m_outputDynamic & CTL_POSITIONX)
|
|
nposCache++;
|
|
}
|
|
if (m_outputControl & CTL_POSITIONY) {
|
|
m_Wy(_nc) = m_pos.alpha/*/(m_pos.tolerance*m_pos.K)*/;
|
|
m_Cf(_nc++,1)=1.0;
|
|
m_posData[npos++].id = ID_POSITIONY;
|
|
if (m_outputDynamic & CTL_POSITIONY)
|
|
nposCache++;
|
|
}
|
|
if (m_outputControl & CTL_POSITIONZ) {
|
|
m_Wy(_nc) = m_pos.alpha/*/(m_pos.tolerance*m_pos.K)*/;
|
|
m_Cf(_nc++,2)=1.0;
|
|
m_posData[npos++].id = ID_POSITIONZ;
|
|
if (m_outputDynamic & CTL_POSITIONZ)
|
|
nposCache++;
|
|
}
|
|
m_values[m_nvalues].number = npos;
|
|
m_values[m_nvalues++].values = m_posData;
|
|
m_pos.firsty = 0;
|
|
m_pos.ny = npos;
|
|
}
|
|
if (m_outputControl & CTL_ROTATION) {
|
|
m_rot.alpha = 1.0;
|
|
m_rot.K = 20.0;
|
|
m_rot.tolerance = 0.05;
|
|
m_values[m_nvalues].alpha = m_rot.alpha;
|
|
m_values[m_nvalues].feedback = m_rot.K;
|
|
m_values[m_nvalues].tolerance = m_rot.tolerance;
|
|
m_values[m_nvalues].id = ID_ROTATION;
|
|
if (m_outputControl & CTL_ROTATIONX) {
|
|
m_Wy(_nc) = m_rot.alpha/*/(m_rot.tolerance*m_rot.K)*/;
|
|
m_Cf(_nc++,3)=1.0;
|
|
m_rotData[nrot++].id = ID_ROTATIONX;
|
|
if (m_outputDynamic & CTL_ROTATIONX)
|
|
nrotCache++;
|
|
}
|
|
if (m_outputControl & CTL_ROTATIONY) {
|
|
m_Wy(_nc) = m_rot.alpha/*/(m_rot.tolerance*m_rot.K)*/;
|
|
m_Cf(_nc++,4)=1.0;
|
|
m_rotData[nrot++].id = ID_ROTATIONY;
|
|
if (m_outputDynamic & CTL_ROTATIONY)
|
|
nrotCache++;
|
|
}
|
|
if (m_outputControl & CTL_ROTATIONZ) {
|
|
m_Wy(_nc) = m_rot.alpha/*/(m_rot.tolerance*m_rot.K)*/;
|
|
m_Cf(_nc++,5)=1.0;
|
|
m_rotData[nrot++].id = ID_ROTATIONZ;
|
|
if (m_outputDynamic & CTL_ROTATIONZ)
|
|
nrotCache++;
|
|
}
|
|
m_values[m_nvalues].number = nrot;
|
|
m_values[m_nvalues++].values = m_rotData;
|
|
m_rot.firsty = npos;
|
|
m_rot.ny = nrot;
|
|
}
|
|
assert(_nc == m_nc);
|
|
m_Jf=e_identity_matrix(6,6);
|
|
m_poseCacheSize = ((nrotCache)?(3+nrotCache*2):0)+((nposCache)?(3+nposCache*2):0);
|
|
}
|
|
|
|
CopyPose::~CopyPose()
|
|
{
|
|
}
|
|
|
|
bool CopyPose::initialise(Frame& init_pose)
|
|
{
|
|
m_externalPose = m_internalPose = init_pose;
|
|
updateJacobian();
|
|
return true;
|
|
}
|
|
|
|
void CopyPose::modelUpdate(Frame& _external_pose,const Timestamp& timestamp)
|
|
{
|
|
m_internalPose = m_externalPose = _external_pose;
|
|
updateJacobian();
|
|
}
|
|
|
|
void CopyPose::initCache(Cache *_cache)
|
|
{
|
|
m_cache = _cache;
|
|
m_poseCCh = -1;
|
|
if (m_cache) {
|
|
// create one channel for the coordinates
|
|
m_poseCCh = m_cache->addChannel(this, "Xf", m_poseCacheSize*sizeof(double));
|
|
// don't save initial value, it will be recomputed from external pose
|
|
//pushPose(0);
|
|
}
|
|
}
|
|
|
|
double* CopyPose::pushValues(double* item, ControlState* _state, unsigned int mask)
|
|
{
|
|
ControlState::ControlValue* _yval;
|
|
int i;
|
|
|
|
*item++ = _state->alpha;
|
|
*item++ = _state->K;
|
|
*item++ = _state->tolerance;
|
|
|
|
for (i=0, _yval=_state->output; i<_state->ny; mask<<=1) {
|
|
if (m_outputControl & mask) {
|
|
if (m_outputDynamic & mask) {
|
|
*item++ = _yval->yd;
|
|
*item++ = _yval->yddot;
|
|
}
|
|
_yval++;
|
|
i++;
|
|
}
|
|
}
|
|
return item;
|
|
}
|
|
|
|
void CopyPose::pushPose(CacheTS timestamp)
|
|
{
|
|
if (m_poseCCh >= 0) {
|
|
if (m_poseCacheSize) {
|
|
double buf[maxPoseCacheSize];
|
|
double *item = buf;
|
|
if (m_outputDynamic & CTL_POSITION)
|
|
item = pushValues(item, &m_pos, CTL_POSITIONX);
|
|
if (m_outputDynamic & CTL_ROTATION)
|
|
item = pushValues(item, &m_rot, CTL_ROTATIONX);
|
|
m_cache->addCacheVectorIfDifferent(this, m_poseCCh, timestamp, buf, m_poseCacheSize, KDL::epsilon);
|
|
} else
|
|
m_cache->addCacheVectorIfDifferent(this, m_poseCCh, timestamp, NULL, 0, KDL::epsilon);
|
|
m_poseCTs = timestamp;
|
|
}
|
|
}
|
|
|
|
double* CopyPose::restoreValues(double* item, ConstraintValues* _values, ControlState* _state, unsigned int mask)
|
|
{
|
|
ConstraintSingleValue* _data;
|
|
ControlState::ControlValue* _yval;
|
|
int i, j;
|
|
|
|
_values->alpha = _state->alpha = *item++;
|
|
_values->feedback = _state->K = *item++;
|
|
_values->tolerance = _state->tolerance = *item++;
|
|
|
|
for (i=_state->firsty, j=i+_state->ny, _yval=_state->output, _data=_values->values; i<j; mask<<=1) {
|
|
if (m_outputControl & mask) {
|
|
m_Wy(i) = _state->alpha/*/(_state->tolerance*_state->K)*/;
|
|
if (m_outputDynamic & mask) {
|
|
_data->yd = _yval->yd = *item++;
|
|
_data->yddot = _yval->yddot = *item++;
|
|
}
|
|
_data++;
|
|
_yval++;
|
|
i++;
|
|
}
|
|
}
|
|
return item;
|
|
}
|
|
|
|
bool CopyPose::popPose(CacheTS timestamp)
|
|
{
|
|
bool found = false;
|
|
if (m_poseCCh >= 0) {
|
|
double *item = (double*)m_cache->getPreviousCacheItem(this, m_poseCCh, ×tamp);
|
|
if (item) {
|
|
found = true;
|
|
if (timestamp != m_poseCTs) {
|
|
int i=0;
|
|
if (m_outputControl & CTL_POSITION) {
|
|
if (m_outputDynamic & CTL_POSITION) {
|
|
item = restoreValues(item, &m_values[i], &m_pos, CTL_POSITIONX);
|
|
}
|
|
i++;
|
|
}
|
|
if (m_outputControl & CTL_ROTATION) {
|
|
if (m_outputDynamic & CTL_ROTATION) {
|
|
item = restoreValues(item, &m_values[i], &m_rot, CTL_ROTATIONX);
|
|
}
|
|
i++;
|
|
}
|
|
m_poseCTs = timestamp;
|
|
item = NULL;
|
|
}
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
void CopyPose::interpolateOutput(ControlState* _state, unsigned int mask, const Timestamp& timestamp)
|
|
{
|
|
ControlState::ControlValue* _yval;
|
|
int i;
|
|
|
|
for (i=0, _yval=_state->output; i<_state->ny; mask <<= 1) {
|
|
if (m_outputControl & mask) {
|
|
if (m_outputDynamic & mask) {
|
|
if (timestamp.substep && timestamp.interpolate) {
|
|
_yval->yd += _yval->yddot*timestamp.realTimestep;
|
|
} else {
|
|
_yval->yd = _yval->nextyd;
|
|
_yval->yddot = _yval->nextyddot;
|
|
}
|
|
}
|
|
i++;
|
|
_yval++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CopyPose::pushCache(const Timestamp& timestamp)
|
|
{
|
|
if (!timestamp.substep && timestamp.cache) {
|
|
pushPose(timestamp.cacheTimestamp);
|
|
}
|
|
}
|
|
|
|
void CopyPose::updateKinematics(const Timestamp& timestamp)
|
|
{
|
|
if (timestamp.interpolate) {
|
|
if (m_outputDynamic & CTL_POSITION)
|
|
interpolateOutput(&m_pos, CTL_POSITIONX, timestamp);
|
|
if (m_outputDynamic & CTL_ROTATION)
|
|
interpolateOutput(&m_rot, CTL_ROTATIONX, timestamp);
|
|
}
|
|
pushCache(timestamp);
|
|
}
|
|
|
|
void CopyPose::updateJacobian()
|
|
{
|
|
//Jacobian is always identity at the start of the constraint chain
|
|
//instead of going through complicated jacobian operation, implemented direct formula
|
|
//m_Jf(1,3) = m_internalPose.p.z();
|
|
//m_Jf(2,3) = -m_internalPose.p.y();
|
|
//m_Jf(0,4) = -m_internalPose.p.z();
|
|
//m_Jf(2,4) = m_internalPose.p.x();
|
|
//m_Jf(0,5) = m_internalPose.p.y();
|
|
//m_Jf(1,5) = -m_internalPose.p.x();
|
|
}
|
|
|
|
void CopyPose::updateState(ConstraintValues* _values, ControlState* _state, unsigned int mask, double timestep)
|
|
{
|
|
unsigned int id = (mask == CTL_ROTATIONX) ? ID_ROTATIONX : ID_POSITIONX;
|
|
ControlState::ControlValue* _yval;
|
|
ConstraintSingleValue* _data;
|
|
int i, j, k;
|
|
int action = 0;
|
|
|
|
if ((_values->action & ACT_ALPHA) && _values->alpha >= 0.0) {
|
|
_state->alpha = _values->alpha;
|
|
action |= ACT_ALPHA;
|
|
}
|
|
if ((_values->action & ACT_TOLERANCE) && _values->tolerance > KDL::epsilon) {
|
|
_state->tolerance = _values->tolerance;
|
|
action |= ACT_TOLERANCE;
|
|
}
|
|
if ((_values->action & ACT_FEEDBACK) && _values->feedback > KDL::epsilon) {
|
|
_state->K = _values->feedback;
|
|
action |= ACT_FEEDBACK;
|
|
}
|
|
for (i=_state->firsty, j=_state->firsty+_state->ny, _yval=_state->output; i<j; mask <<= 1, id++) {
|
|
if (m_outputControl & mask) {
|
|
if (action)
|
|
m_Wy(i) = _state->alpha/*/(_state->tolerance*_state->K)*/;
|
|
// check if this controlled output is provided
|
|
for (k=0, _data=_values->values; k<_values->number; k++, _data++) {
|
|
if (_data->id == id) {
|
|
switch (_data->action & (ACT_VALUE|ACT_VELOCITY)) {
|
|
case 0:
|
|
// no indication, keep current values
|
|
break;
|
|
case ACT_VELOCITY:
|
|
// only the velocity is given estimate the new value by integration
|
|
_data->yd = _yval->yd+_data->yddot*timestep;
|
|
// walkthrough
|
|
case ACT_VALUE:
|
|
_yval->nextyd = _data->yd;
|
|
// if the user sets the value, we assume future velocity is zero
|
|
// (until the user changes the value again)
|
|
_yval->nextyddot = (_data->action & ACT_VALUE) ? 0.0 : _data->yddot;
|
|
if (timestep>0.0) {
|
|
_yval->yddot = (_data->yd-_yval->yd)/timestep;
|
|
} else {
|
|
// allow the user to change target instantenously when this function
|
|
// if called from setControlParameter with timestep = 0
|
|
_yval->yd = _yval->nextyd;
|
|
_yval->yddot = _yval->nextyddot;
|
|
}
|
|
break;
|
|
case (ACT_VALUE|ACT_VELOCITY):
|
|
// the user should not set the value and velocity at the same time.
|
|
// In this case, we will assume that he wants to set the future value
|
|
// and we compute the current value to match the velocity
|
|
_yval->yd = _data->yd - _data->yddot*timestep;
|
|
_yval->nextyd = _data->yd;
|
|
_yval->nextyddot = _data->yddot;
|
|
if (timestep>0.0) {
|
|
_yval->yddot = (_data->yd-_yval->yd)/timestep;
|
|
} else {
|
|
_yval->yd = _yval->nextyd;
|
|
_yval->yddot = _yval->nextyddot;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
_yval++;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool CopyPose::setControlParameters(struct ConstraintValues* _values, unsigned int _nvalues, double timestep)
|
|
{
|
|
while (_nvalues > 0) {
|
|
if (_values->id >= ID_POSITION && _values->id <= ID_POSITIONZ && (m_outputControl & CTL_POSITION)) {
|
|
updateState(_values, &m_pos, CTL_POSITIONX, timestep);
|
|
}
|
|
if (_values->id >= ID_ROTATION && _values->id <= ID_ROTATIONZ && (m_outputControl & CTL_ROTATION)) {
|
|
updateState(_values, &m_rot, CTL_ROTATIONX, timestep);
|
|
}
|
|
_values++;
|
|
_nvalues--;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CopyPose::updateValues(Vector& vel, ConstraintValues* _values, ControlState* _state, unsigned int mask)
|
|
{
|
|
ConstraintSingleValue* _data;
|
|
ControlState::ControlValue* _yval;
|
|
int i, j;
|
|
|
|
_values->action = 0;
|
|
|
|
for (i=_state->firsty, j=0, _yval=_state->output, _data=_values->values; j<3; j++, mask<<=1) {
|
|
if (m_outputControl & mask) {
|
|
*(double*)&_data->y = vel(j);
|
|
*(double*)&_data->ydot = m_ydot(i);
|
|
_data->yd = _yval->yd;
|
|
_data->yddot = _yval->yddot;
|
|
_data->action = 0;
|
|
i++;
|
|
_data++;
|
|
_yval++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CopyPose::updateOutput(Vector& vel, ControlState* _state, unsigned int mask)
|
|
{
|
|
ControlState::ControlValue* _yval;
|
|
int i, j;
|
|
double coef=1.0;
|
|
if (mask & CTL_POSITION) {
|
|
// put a limit on position error
|
|
double len=0.0;
|
|
for (j=0, _yval=_state->output; j<3; j++) {
|
|
if (m_outputControl & (mask<<j)) {
|
|
len += KDL::sqr(_yval->yd-vel(j));
|
|
_yval++;
|
|
}
|
|
}
|
|
len = KDL::sqrt(len);
|
|
if (len > m_maxerror)
|
|
coef = m_maxerror/len;
|
|
}
|
|
for (i=_state->firsty, j=0, _yval=_state->output; j<3; j++) {
|
|
if (m_outputControl & (mask<<j)) {
|
|
m_ydot(i)=_yval->yddot+_state->K*coef*(_yval->yd-vel(j));
|
|
_yval++;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CopyPose::updateControlOutput(const Timestamp& timestamp)
|
|
{
|
|
//IMO this should be done, no idea if it is enough (wrt Distance impl)
|
|
Twist y = diff(F_identity, m_internalPose);
|
|
bool found = true;
|
|
if (!timestamp.substep) {
|
|
if (!timestamp.reiterate) {
|
|
found = popPose(timestamp.cacheTimestamp);
|
|
}
|
|
}
|
|
if (m_constraintCallback && (m_substep || (!timestamp.reiterate && !timestamp.substep))) {
|
|
// initialize first callback the application to get the current values
|
|
int i=0;
|
|
if (m_outputControl & CTL_POSITION) {
|
|
updateValues(y.vel, &m_values[i++], &m_pos, CTL_POSITIONX);
|
|
}
|
|
if (m_outputControl & CTL_ROTATION) {
|
|
updateValues(y.rot, &m_values[i++], &m_rot, CTL_ROTATIONX);
|
|
}
|
|
if ((*m_constraintCallback)(timestamp, m_values, m_nvalues, m_constraintParam)) {
|
|
setControlParameters(m_values, m_nvalues, (found && timestamp.interpolate)?timestamp.realTimestep:0.0);
|
|
}
|
|
}
|
|
if (m_outputControl & CTL_POSITION) {
|
|
updateOutput(y.vel, &m_pos, CTL_POSITIONX);
|
|
}
|
|
if (m_outputControl & CTL_ROTATION) {
|
|
updateOutput(y.rot, &m_rot, CTL_ROTATIONX);
|
|
}
|
|
}
|
|
|
|
const ConstraintValues* CopyPose::getControlParameters(unsigned int* _nvalues)
|
|
{
|
|
Twist y = diff(m_internalPose,F_identity);
|
|
int i=0;
|
|
if (m_outputControl & CTL_POSITION) {
|
|
updateValues(y.vel, &m_values[i++], &m_pos, CTL_POSITIONX);
|
|
}
|
|
if (m_outputControl & CTL_ROTATION) {
|
|
updateValues(y.rot, &m_values[i++], &m_rot, CTL_ROTATIONX);
|
|
}
|
|
if (_nvalues)
|
|
*_nvalues=m_nvalues;
|
|
return m_values;
|
|
}
|
|
|
|
double CopyPose::getMaxTimestep(double& timestep)
|
|
{
|
|
// CopyPose should not have any limit on linear velocity:
|
|
// in case the target is out of reach, this can be very high.
|
|
// We will simply limit on rotation
|
|
e_scalar maxChidot = m_chidot.block(3,0,3,1).array().abs().maxCoeff();
|
|
if (timestep*maxChidot > m_maxDeltaChi) {
|
|
timestep = m_maxDeltaChi/maxChidot;
|
|
}
|
|
return timestep;
|
|
}
|
|
|
|
}
|