@@ -5,14 +5,6 @@ sfeQwiicOtos::sfeQwiicOtos()
55 // Set communication bus to nullptr
66 _commBus = nullptr ;
77
8- // Reset offset pose and its inverse to zero
9- _offsetPose.x = 0 ;
10- _offsetPose.y = 0 ;
11- _offsetPose.h = 0 ;
12- _offsetPoseInv.x = 0 ;
13- _offsetPoseInv.y = 0 ;
14- _offsetPoseInv.h = 0 ;
15-
168 // Set default units to inches and degrees
179 _linearUnit = kOtosLinearUnitInches ;
1810 _angularUnit = kOtosAngularUnitDegrees ;
@@ -196,76 +188,36 @@ sfeTkError_t sfeQwiicOtos::setAngularScalar(float scalar)
196188 return kSTkErrFail ;
197189
198190 // Convert to raw integer, multiples of 0.1%
199- int8_t rawScalar = (scalar - 1 .0f ) * 1000 ;
191+ uint8_t rawScalar = (int8_t )(( scalar - 1 .0f ) * 1000 ) ;
200192
201193 // Write the scalar to the device
202- return _commBus->writeRegisterByte (kOtosRegScalarH , ( uint8_t ) rawScalar);
194+ return _commBus->writeRegisterByte (kOtosRegScalarH , rawScalar);
203195}
204196
205- void sfeQwiicOtos::setOffset ( otos_pose2d_t &pose )
197+ sfeTkError_t sfeQwiicOtos::resetTracking ( )
206198{
207- // Copy the pose to the offset
208- _offsetPose = pose;
209-
210- // Convert to meters and radians
211- _offsetPose.x /= _meterToUnit;
212- _offsetPose.y /= _meterToUnit;
213- _offsetPose.h /= _radToUnit;
214-
215- // Compute the inverse of the offset
216- _offsetPoseInv = invertPose (_offsetPose);
199+ // Set tracking reset bit
200+ return _commBus->writeRegisterByte (kOtosRegReset , 0x01 );
217201}
218202
219- void sfeQwiicOtos::getOffset (otos_pose2d_t &pose)
203+ sfeTkError_t sfeQwiicOtos::getOffset (otos_pose2d_t &pose)
220204{
221- // Copy the offset to the pose
222- pose = _offsetPose;
223-
224- // Convert to current units
225- pose.x *= _meterToUnit;
226- pose.y *= _meterToUnit;
227- pose.h *= _radToUnit;
205+ return readPoseRegs (kOtosRegOffXL , pose, kInt16ToMeter , kInt16ToRad );
228206}
229207
230- sfeTkError_t sfeQwiicOtos::resetTracking ( )
208+ sfeTkError_t sfeQwiicOtos::setOffset ( otos_pose2d_t &pose )
231209{
232- // Set tracking reset bit
233- return _commBus->writeRegisterByte (kOtosRegReset , 0x01 );
210+ return writePoseRegs (kOtosRegOffXL , pose, kMeterToInt16 , kRadToInt16 );
234211}
235212
236213sfeTkError_t sfeQwiicOtos::getPosition (otos_pose2d_t &pose)
237214{
238- // Read the raw pose data
239- otos_pose2d_t sensorPose;
240- sfeTkError_t err = readPoseRegs (kOtosRegPosXL , sensorPose, kInt16ToMeter , kInt16ToRad );
241- if (err != kSTkErrOk )
242- return err;
243-
244- // Apply the offset
245- pose = transformPose (sensorPose, _offsetPoseInv);
246-
247- // Convert to current units
248- pose.x *= _meterToUnit;
249- pose.y *= _meterToUnit;
250- pose.h *= _radToUnit;
251-
252- // Done!
253- return kSTkErrOk ;
215+ return readPoseRegs (kOtosRegPosXL , pose, kInt16ToMeter , kInt16ToRad );
254216}
255217
256218sfeTkError_t sfeQwiicOtos::setPosition (otos_pose2d_t &pose)
257219{
258- otos_pose2d_t sensorPose;
259-
260- // Convert to meters and radians
261- sensorPose.x = pose.x / _meterToUnit;
262- sensorPose.y = pose.y / _meterToUnit;
263- sensorPose.h = pose.h / _radToUnit;
264-
265- // Apply the offset
266- sensorPose = transformPose (sensorPose, _offsetPose);
267-
268- return writePoseRegs (kOtosRegPosXL , sensorPose, kMeterToInt16 , kRadToInt16 );
220+ return writePoseRegs (kOtosRegPosXL , pose, kMeterToInt16 , kRadToInt16 );
269221}
270222
271223sfeTkError_t sfeQwiicOtos::getVelocity (otos_pose2d_t &pose)
@@ -288,156 +240,83 @@ sfeTkError_t sfeQwiicOtos::setAccerlation(otos_pose2d_t &pose)
288240 return writePoseRegs (kOtosRegAccXL , pose, kMeterToInt16 , kRpssToInt16 );
289241}
290242
243+ sfeTkError_t sfeQwiicOtos::getPosVelAcc (otos_pose2d_t &pos, otos_pose2d_t &vel, otos_pose2d_t &acc)
244+ {
245+ // Read all three pose registers
246+ uint8_t rawData[18 ];
247+ size_t bytesRead = 0 ;
248+ sfeTkError_t err = _commBus->readRegisterRegion (kOtosRegPosXL , rawData, 18 , bytesRead);
249+ if (err != kSTkErrOk )
250+ return err;
251+
252+ // Check if we read the correct number of bytes
253+ if (bytesRead != 18 )
254+ return kSTkErrFail ;
255+
256+ // Convert raw data to pose units
257+ regsToPose (rawData, pos, kInt16ToMeter , kInt16ToRad );
258+ regsToPose (rawData + 6 , vel, kInt16ToMeter , kInt16ToRps );
259+ regsToPose (rawData + 12 , acc, kInt16ToMeter , kInt16ToRpss );
260+
261+ // Done!
262+ return kSTkErrOk ;
263+ }
264+
291265sfeTkError_t sfeQwiicOtos::readPoseRegs (uint8_t reg, otos_pose2d_t &pose, float rawToXY, float rawToH)
292266{
293267 size_t bytesRead = 0 ;
294268 uint8_t rawData[6 ];
295269
296270 // Attempt to read the raw pose data
297271 sfeTkError_t err = _commBus->readRegisterRegion (reg, rawData, 6 , bytesRead);
298-
299- // Check whether the read was successful
300272 if (err != kSTkErrOk )
301273 return err;
302274
303- // Store raw data
304- int16_t rawX = (rawData[1 ] << 8 ) | rawData[0 ];
305- int16_t rawY = (rawData[3 ] << 8 ) | rawData[2 ];
306- int16_t rawH = (rawData[5 ] << 8 ) | rawData[4 ];
275+ // Check if we read the correct number of bytes
276+ if (bytesRead != 6 )
277+ return kSTkErrFail ;
307278
308- // Store in pose
309- pose.x = rawX * rawToXY;
310- pose.y = rawY * rawToXY;
311- pose.h = rawH * rawToH;
279+ regsToPose (rawData, pose, rawToXY, rawToH);
312280
313281 // Done!
314282 return kSTkErrOk ;
315283}
316284
317285sfeTkError_t sfeQwiicOtos::writePoseRegs (uint8_t reg, otos_pose2d_t &pose, float xyToRaw, float hToRaw)
318286{
319- // Convert pose units to raw data
320- int16_t rawX = pose.x * xyToRaw;
321- int16_t rawY = pose.y * xyToRaw;
322- int16_t rawH = pose.h * hToRaw;
323-
324287 // Store raw data in a temporary buffer
325- uint8_t rawData[8 ];
326- rawData[0 ] = rawX & 0xFF ;
327- rawData[1 ] = (rawX >> 8 ) & 0xFF ;
328- rawData[2 ] = rawY & 0xFF ;
329- rawData[3 ] = (rawY >> 8 ) & 0xFF ;
330- rawData[4 ] = rawH & 0xFF ;
331- rawData[5 ] = (rawH >> 8 ) & 0xFF ;
288+ uint8_t rawData[6 ];
289+ poseToRegs (rawData, pose, xyToRaw, hToRaw);
332290
333291 // Write the raw data to the device
334292 return _commBus->writeRegisterRegion (reg, rawData, 6 );
335293}
336294
337- otos_pose2d_t sfeQwiicOtos::transformPose (otos_pose2d_t &pose12, otos_pose2d_t &pose23)
338- {
339- // We're going to compute this transformation as if the provided poses are
340- // homogeneous transformation matrices. For the 2D case, the transformation
341- // matrix is a 3x3 matrix T that looks like this:
342- //
343- // [R11 R12 x] [cos(h) -sin(h) x]
344- // T = [R d] = [R21 R22 y] = [sin(h) cos(h) y]
345- // [0 1] [0 0 1] [0 0 1]
346- //
347- // Where h is the heading and (x, y) is the translation from one frame to
348- // another. If we know the transformation from frame 1 to frame 2 (T12), and
349- // from frame 2 to frame 3 (T23), we can compute the transformation from
350- // frame 1 to frame 3 (T13) by multiplying the transformation matrices:
351- //
352- // T13 = T12 * T23
353- //
354- // Which expands to:
355- //
356- // [cos(h13) -sin(h13) x13] [cos(h12) -sin(h12) x12] [cos(h23) -sin(h23) x23]
357- // [sin(h13) cos(h13) y13] = [sin(h12) cos(h12) y12] * [sin(h23) cos(h23) y23]
358- // [0 0 1 ] [0 0 1 ] [0 0 1 ]
359- //
360- // For the x13 and y13 components, this expands to:
361- //
362- // x13 = x23 * cos(h12) - y23 * sin(h12) + x12
363- // y13 = x23 * sin(h12) + y23 * cos(h12) + y12
364- //
365- // The upper left 2x2 corner is the resulting rotation matrix, where each
366- // component depends only on h13. The expansion for each element is one of:
367- //
368- // cos(h13) = cos(h12) * cos(h23) - sin(h12) * sin(h23)
369- // sin(h13) = sin(h12) * cos(h23) + cos(h12) * sin(h23)
370- //
371- // Instead of computing the inverse trigonometric functions, we can notice
372- // that these are just the angle addition identities, so we can simplify by
373- // just adding the angles:
374- //
375- // h1 = h2 + h3
376-
377- // Create struct to hold output
378- otos_pose2d_t pose13;
379-
380- // Pre-compute sin and cos
381- float cosh12 = cosf (pose12.h );
382- float sinh12 = sinf (pose12.h );
383-
384- // Compute transformation following equations above
385- pose13.x = pose23.x * cosh12 - pose23.y * sinh12 + pose12.x ;
386- pose13.y = pose23.x * sinh12 + pose23.y * cosh12 + pose12.y ;
387- pose13.h = pose23.h + pose12.h ;
388-
389- // Wrap heading to +/- pi with fmod
390- pose13.h = wrapAngle (pose13.h );
391-
392- // Done!
393- return pose13;
394- }
395-
396- otos_pose2d_t sfeQwiicOtos::invertPose (otos_pose2d_t &pose)
295+ void sfeQwiicOtos::regsToPose (uint8_t *rawData, otos_pose2d_t &pose, float rawToXY, float rawToH)
397296{
398- // Similar to transformPose(), we're going to compute this transformation as
399- // if the provided pose is a homogeneous transformation matrix T. The
400- // inverse of T is given by:
401- //
402- // [ cos(h) sin(h) -x*cos(h)-y*sin(h)]
403- // T^-1 = [R' d'] = [RT -RT*d] = [-sin(h) cos(h) x*sin(h)-y*cos(h)]
404- // [0 1 ] [0 1 ] [0 0 1 ]
405- //
406- // Where R' and d' are the inverse rotation matrix and position vector, and
407- // RT is the transpose of the rotation matrix. Solving for the inverse
408- // position vector, we get:
409- //
410- // x' = -x * cos(h) - y * sin(h)
411- // y' = x * sin(h) - y * cos(h)
412- //
413- // The expansion for the inverse rotation matrix elements are one of:
414- //
415- // cos(h') = cos(h)
416- // sin(h') = -sin(h)
417- //
418- // Instead of computing the inverse trigonometric functions, we can notice
419- // that h' and h must be opposite angles, so we can simplify:
420- //
421- // h' = -h
422-
423- // Create struct to hold output
424- otos_pose2d_t poseInv;
425-
426- // Pre-compute sin and cos
427- float cosh = cosf (pose.h );
428- float sinh = sinf (pose.h );
429-
430- // Compute inverse following equations above
431- poseInv.x = -pose.x * cosh - pose.y * sinh;
432- poseInv.y = pose.x * sinh - pose.y * cosh;
433- poseInv.h = -pose.h ;
297+ // Store raw data
298+ int16_t rawX = (rawData[1 ] << 8 ) | rawData[0 ];
299+ int16_t rawY = (rawData[3 ] << 8 ) | rawData[2 ];
300+ int16_t rawH = (rawData[5 ] << 8 ) | rawData[4 ];
434301
435- // Done!
436- return poseInv;
302+ // Store in pose and convert to units
303+ pose.x = rawX * rawToXY * _meterToUnit;
304+ pose.y = rawY * rawToXY * _meterToUnit;
305+ pose.h = rawH * rawToH * _radToUnit;
437306}
438307
439- float sfeQwiicOtos::wrapAngle ( float angle, otos_angular_unit_t unit )
308+ void sfeQwiicOtos::poseToRegs ( uint8_t *rawData, otos_pose2d_t &pose, float xyToRaw, float hToRaw )
440309{
441- float bound = (unit == kOtosAngularUnitRadians ) ? M_PI : 180.0 ;
442- return fmodf (angle + bound, 2 * bound) - bound;
310+ // Convert pose units to raw data
311+ int16_t rawX = pose.x * xyToRaw / _meterToUnit;
312+ int16_t rawY = pose.y * xyToRaw / _meterToUnit;
313+ int16_t rawH = pose.h * hToRaw / _radToUnit;
314+
315+ // Store raw data in buffer
316+ rawData[0 ] = rawX & 0xFF ;
317+ rawData[1 ] = (rawX >> 8 ) & 0xFF ;
318+ rawData[2 ] = rawY & 0xFF ;
319+ rawData[3 ] = (rawY >> 8 ) & 0xFF ;
320+ rawData[4 ] = rawH & 0xFF ;
321+ rawData[5 ] = (rawH >> 8 ) & 0xFF ;
443322}
0 commit comments