Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Distance.cpp « itasc « intern - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: eebe9bab4c6c030c4b09c67fc0d746be31e8d23e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/** \file itasc/Distance.cpp
 * \ingroup itasc
 */
/*
 * Distance.cpp
 *
 *  Created on: Jan 30, 2009
 *      Author: rsmits
 */

#include "Distance.hpp"
#include "kdl/kinfam_io.hpp"
#include <math.h>
#include <string.h>

namespace iTaSC
{
// a distance constraint is characterized by 5 values: alpha, tolerance, K, yd, yddot
static const unsigned int distanceCacheSize = sizeof(double)*5 + sizeof(e_scalar)*6;

Distance::Distance(double armlength, double accuracy, unsigned int maximum_iterations):
    ConstraintSet(1,accuracy,maximum_iterations),
    m_chiKdl(6),m_jac(6),m_cache(NULL),
	m_distCCh(-1),m_distCTs(0)
{
    m_chain.addSegment(Segment(Joint(Joint::RotZ)));
    m_chain.addSegment(Segment(Joint(Joint::RotX)));
    m_chain.addSegment(Segment(Joint(Joint::TransY)));
    m_chain.addSegment(Segment(Joint(Joint::RotZ)));
    m_chain.addSegment(Segment(Joint(Joint::RotY)));
    m_chain.addSegment(Segment(Joint(Joint::RotX)));

	m_fksolver = new KDL::ChainFkSolverPos_recursive(m_chain);
	m_jacsolver = new KDL::ChainJntToJacSolver(m_chain);
    m_Cf(0,2)=1.0;
	m_alpha = 1.0;
	m_tolerance = 0.05;
	m_maxerror = armlength/2.0;
	m_K = 20.0;
	m_Wy(0) = m_alpha/*/(m_tolerance*m_K)*/;
	m_yddot = m_nextyddot = 0.0;
	m_yd = m_nextyd = KDL::epsilon;
	memset(&m_data, 0, sizeof(m_data));
	// initialize the data with normally fixed values
	m_data.id = ID_DISTANCE;
	m_values.id = ID_DISTANCE;
	m_values.number = 1;
	m_values.alpha = m_alpha;
	m_values.feedback = m_K;
	m_values.tolerance = m_tolerance;
	m_values.values = &m_data;
}

Distance::~Distance()
{
    delete m_fksolver;
    delete m_jacsolver;
}

bool Distance::computeChi(Frame& pose)
{
	double dist, alpha, beta, gamma;
	dist = pose.p.Norm();
	Rotation basis;
	if (dist < KDL::epsilon) {
		// distance is almost 0, no need for initial rotation
		m_chi(0) = 0.0;
		m_chi(1) = 0.0;
	} else {
		// find the XZ angles that bring the Y axis to point to init_pose.p
		Vector axis(pose.p/dist);
		beta = 0.0;
		if (fabs(axis(2)) > 1-KDL::epsilon) {
			// direction is aligned on Z axis, just rotation on X
			alpha = 0.0;
			gamma = KDL::sign(axis(2))*KDL::PI/2;
		} else {
			alpha = -KDL::atan2(axis(0), axis(1));
			gamma = KDL::atan2(axis(2), KDL::sqrt(KDL::sqr(axis(0))+KDL::sqr(axis(1))));
		}
		// rotation after first 2 joints
		basis = Rotation::EulerZYX(alpha, beta, gamma);
		m_chi(0) = alpha;
		m_chi(1) = gamma;
	}
	m_chi(2) = dist;
	basis = basis.Inverse()*pose.M;
	basis.GetEulerZYX(alpha, beta, gamma);
	// alpha = rotation on Z
	// beta = rotation on Y
	// gamma = rotation on X in that order
	// it corresponds to the joint order, so just assign
	m_chi(3) = alpha;
	m_chi(4) = beta;
	m_chi(5) = gamma;
	return true;
}

bool Distance::initialise(Frame& init_pose)
{
	// we will initialize m_chi to values that match the pose
    m_externalPose=init_pose;
	computeChi(m_externalPose);
	// get current Jf and update internal pose
    updateJacobian();
	return true;
}

bool Distance::closeLoop()
{
	if (!Equal(m_internalPose.Inverse()*m_externalPose,F_identity,m_threshold)){
		computeChi(m_externalPose);
		updateJacobian();
	}
	return true;
}

void Distance::initCache(Cache *_cache)
{
	m_cache = _cache;
	m_distCCh = -1;
	if (m_cache) {
		// create one channel for the coordinates
		m_distCCh = m_cache->addChannel(this, "Xf", distanceCacheSize);
		// save initial constraint in cache position 0
		pushDist(0);
	}
}

void Distance::pushDist(CacheTS timestamp)
{
	if (m_distCCh >= 0) {
		double *item = (double*)m_cache->addCacheItem(this, m_distCCh, timestamp, NULL, distanceCacheSize);
		if (item) {
			*item++ = m_K;
			*item++ = m_tolerance;
			*item++ = m_yd;
			*item++ = m_yddot;
			*item++ = m_alpha;
			memcpy(item, &m_chi[0], 6*sizeof(e_scalar));
		}
		m_distCTs = timestamp;
	}
}

bool Distance::popDist(CacheTS timestamp)
{
	if (m_distCCh >= 0) {
		double *item = (double*)m_cache->getPreviousCacheItem(this, m_distCCh, &timestamp);
		if (item && timestamp != m_distCTs) {
			m_values.feedback = m_K = *item++;
			m_values.tolerance = m_tolerance = *item++;
			m_yd = *item++;
			m_yddot = *item++;
			m_values.alpha = m_alpha = *item++;
			memcpy(&m_chi[0], item, 6*sizeof(e_scalar));
			m_distCTs = timestamp;
			m_Wy(0) = m_alpha/*/(m_tolerance*m_K)*/;
			updateJacobian();
		}
		return (item) ? true : false;
	}
	return true;
}

void Distance::pushCache(const Timestamp& timestamp)
{
	if (!timestamp.substep && timestamp.cache)
		pushDist(timestamp.cacheTimestamp);
}

void Distance::updateKinematics(const Timestamp& timestamp)
{
	if (timestamp.interpolate) {
		//the internal pose and Jf is already up to date (see model_update)
		//update the desired output based on yddot
		if (timestamp.substep) {
			m_yd += m_yddot*timestamp.realTimestep;
			if (m_yd < KDL::epsilon)
				m_yd = KDL::epsilon;
		} else {
			m_yd = m_nextyd;
			m_yddot = m_nextyddot;
		}
	}
	pushCache(timestamp);
}

void Distance::updateJacobian()
{
    for(unsigned int i=0;i<6;i++)
        m_chiKdl[i]=m_chi[i];

    m_fksolver->JntToCart(m_chiKdl,m_internalPose);
    m_jacsolver->JntToJac(m_chiKdl,m_jac);
    changeRefPoint(m_jac,-m_internalPose.p,m_jac);
    for(unsigned int i=0;i<6;i++)
        for(unsigned int j=0;j<6;j++)
            m_Jf(i,j)=m_jac(i,j);
}

bool Distance::setControlParameters(struct ConstraintValues* _values, unsigned int _nvalues, double timestep)
{
	int action = 0;
	int i;
	ConstraintSingleValue* _data;

	while (_nvalues > 0) {
		if (_values->id == ID_DISTANCE) {
			if ((_values->action & ACT_ALPHA) && _values->alpha >= 0.0) {
				m_alpha = _values->alpha;
				action |= ACT_ALPHA;
			}
			if ((_values->action & ACT_TOLERANCE) && _values->tolerance > KDL::epsilon) {
				m_tolerance = _values->tolerance;
				action |= ACT_TOLERANCE;
			}
			if ((_values->action & ACT_FEEDBACK) && _values->feedback > KDL::epsilon) {
				m_K = _values->feedback;
				action |= ACT_FEEDBACK;
			}
			for (_data = _values->values, i=0; i<_values->number; i++, _data++) {
				if (_data->id == ID_DISTANCE) {
					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 = m_yd+_data->yddot*timestep;
						// walkthrough for negative value correction
					case ACT_VALUE:
						// only the value is given, estimate the velocity from previous value
						if (_data->yd < KDL::epsilon)
							_data->yd = KDL::epsilon;
						m_nextyd = _data->yd;
						// if the user sets the value, we assume future velocity is zero
						// (until the user changes the value again)
						m_nextyddot = (_data->action & ACT_VALUE) ? 0.0 : _data->yddot;
						if (timestep>0.0) {
							m_yddot = (_data->yd-m_yd)/timestep;
						} else {
							// allow the user to change target instantenously when this function
							// if called from setControlParameter with timestep = 0
							m_yddot = m_nextyddot;
							m_yd = m_nextyd;
						}
						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 want to set the future value
						// and we compute the current value to match the velocity
						if (_data->yd < KDL::epsilon)
							_data->yd = KDL::epsilon;
						m_yd = _data->yd - _data->yddot*timestep;
						if (m_yd < KDL::epsilon)
							m_yd = KDL::epsilon;
						m_nextyd = _data->yd;
						m_nextyddot = _data->yddot;
						if (timestep>0.0) {
							m_yddot = (_data->yd-m_yd)/timestep;
						} else {
							m_yd = m_nextyd;
							m_yddot = m_nextyddot;
						}
						break;
					}
				}
			}
		}
		_nvalues--;
		_values++;
	}
	if (action & (ACT_TOLERANCE|ACT_FEEDBACK|ACT_ALPHA)) {
		// recompute the weight
		m_Wy(0) = m_alpha/*/(m_tolerance*m_K)*/;
	}
	return true;
}

const ConstraintValues* Distance::getControlParameters(unsigned int* _nvalues)
{
	*(double*)&m_data.y = m_chi(2);
	*(double*)&m_data.ydot = m_ydot(0);
	m_data.yd = m_yd;
	m_data.yddot = m_yddot;
	m_data.action = 0;
	m_values.action = 0;
	if (_nvalues) 
		*_nvalues=1; 
	return &m_values; 
}

void Distance::updateControlOutput(const Timestamp& timestamp)
{
	bool cacheAvail = true;
	if (!timestamp.substep) {
		if (!timestamp.reiterate)
			cacheAvail = popDist(timestamp.cacheTimestamp);
	}
	if (m_constraintCallback && (m_substep || (!timestamp.reiterate && !timestamp.substep))) {
		// initialize first callback the application to get the current values
		*(double*)&m_data.y = m_chi(2);
		*(double*)&m_data.ydot = m_ydot(0);
		m_data.yd = m_yd;
		m_data.yddot = m_yddot;
		m_data.action = 0;
		m_values.action = 0;
		if ((*m_constraintCallback)(timestamp, &m_values, 1, m_constraintParam)) {
			setControlParameters(&m_values, 1, timestamp.realTimestep);
		}
	}
	if (!cacheAvail || !timestamp.interpolate) {
		// first position in cache: set the desired output immediately as we cannot interpolate
		m_yd = m_nextyd;
		m_yddot = m_nextyddot;
	}
	double error = m_yd-m_chi(2);
	if (KDL::Norm(error) > m_maxerror)
		error = KDL::sign(error)*m_maxerror;
    m_ydot(0)=m_yddot+m_K*error;
}

}