22 #include <robottestingframework/TestAssert.h>
23 #include <robottestingframework/dll/Plugin.h>
24 #include <yarp/os/Time.h>
25 #include <yarp/os/LogStream.h>
26 #include <yarp/math/Math.h>
27 #include <yarp/os/Property.h>
28 #include <yarp/os/ResourceFinder.h>
32 #include "motorEncodersConsistency.h"
34 #include <yarp/dev/IRemoteVariables.h>
41 using namespace robottestingframework;
42 using namespace yarp::os;
43 using namespace yarp::dev;
44 using namespace yarp::math;
49 OpticalEncodersConsistency::OpticalEncodersConsistency() : yarp::robottestingframework::TestCase(
"OpticalEncodersConsistency") {
73 OpticalEncodersConsistency::~OpticalEncodersConsistency() { }
75 bool OpticalEncodersConsistency::setup(yarp::os::Property& property) {
77 if(property.check(
"name"))
78 setName(property.find(
"name").asString());
81 strcpy (b,property.toString().c_str());
82 ROBOTTESTINGFRAMEWORK_TEST_REPORT(
"on setup()");
84 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"robot"),
"The robot name must be given as the test parameter!");
85 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"part"),
"The part name must be given as the test parameter!");
86 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"joints"),
"The joints list must be given as the test parameter!");
87 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"home"),
"The home position must be given as the test parameter!");
88 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"max"),
"The max position must be given as the test parameter!");
89 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"min"),
"The min position must be given as the test parameter!");
90 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"speed"),
"The positionMove reference speed must be given as the test parameter!");
91 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"tolerance"),
"The tolerance of the control signal must be given as the test parameter!");
92 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(property.check(
"matrix_size"),
"The matrix size must be given!");
94 robotName =
property.find(
"robot").asString();
95 partName =
property.find(
"part").asString();
96 if(property.check(
"plot_enabled"))
97 plot_enabled =
property.find(
"plot_enabled").asBool();
105 Bottle* jointsBottle =
property.find(
"joints").asList();
106 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(jointsBottle!=0,
"unable to parse joints parameter");
108 Bottle* homeBottle =
property.find(
"home").asList();
109 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(homeBottle!=0,
"unable to parse home parameter");
111 Bottle* maxBottle =
property.find(
"max").asList();
112 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(maxBottle!=0,
"unable to parse max parameter");
114 Bottle* minBottle =
property.find(
"min").asList();
115 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(minBottle!=0,
"unable to parse min parameter");
117 Bottle* speedBottle =
property.find(
"speed").asList();
118 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(speedBottle!=0,
"unable to parse speed parameter");
120 tolerance =
property.find(
"tolerance").asFloat64();
121 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(tolerance>=0,
"invalid tolerance");
123 int matrix_size=
property.find(
"matrix_size").asInt32();
126 matrix.resize(matrix_size,matrix_size);
149 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR(
"invalid matrix_size: must be >0");
153 if (property.check(
"cycles"))
154 {cycles =
property.find(
"cycles").asInt32();}
157 options.put(
"device",
"remote_controlboard");
158 options.put(
"remote",
"/"+robotName+
"/"+partName);
159 options.put(
"local",
"/positionDirectTest/"+robotName+
"/"+partName);
161 dd =
new PolyDriver(options);
162 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->isValid(),
"Unable to open device driver");
163 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ienc),
"Unable to open encoders interface");
164 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ipos),
"Unable to open position interface");
165 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(icmd),
"Unable to open control mode interface");
166 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(iimd),
"Unable to open interaction mode interface");
167 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(imotenc),
"Unable to open motor encoders interface");
168 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(imot),
"Unable to open motor interface");
169 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(dd->view(ivar),
"Unable to open remote variables interface");
172 if (!ienc->getAxes(&n_part_joints))
174 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR(
"unable to get the number of joints of the part");
177 int n_cmd_joints = jointsBottle->size();
178 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(n_cmd_joints>0 && n_cmd_joints<=n_part_joints,"invalid number of joints, it must be >0 & <= number of part joints
");
180 for (int i=0; i <n_cmd_joints; i++) jointsList.push_back(jointsBottle->get(i).asInt32());
182 enc_jnt.resize(n_cmd_joints); enc_jnt.zero();
183 enc_mot.resize(n_cmd_joints); enc_mot.zero();
184 vel_jnt.resize(n_cmd_joints); vel_jnt.zero();
185 vel_mot.resize(n_cmd_joints); vel_mot.zero();
186 acc_jnt.resize(n_cmd_joints); acc_jnt.zero();
187 acc_mot.resize(n_cmd_joints); acc_mot.zero();
188 prev_enc_jnt.resize(n_cmd_joints); prev_enc_jnt.zero();
189 prev_enc_mot.resize(n_cmd_joints); prev_enc_mot.zero();
190 prev_enc_jnt2mot.resize(n_cmd_joints); prev_enc_jnt2mot.zero();
191 prev_vel_jnt.resize(n_cmd_joints); prev_vel_jnt.zero();
192 prev_vel_mot.resize(n_cmd_joints); prev_vel_mot.zero();
193 prev_vel_jnt2mot.resize(n_cmd_joints); prev_vel_jnt2mot.zero();
194 prev_acc_jnt.resize(n_cmd_joints); prev_acc_jnt.zero();
195 prev_acc_mot.resize(n_cmd_joints); prev_acc_mot.zero();
196 prev_acc_jnt2mot.resize(n_cmd_joints); prev_acc_jnt2mot.zero();
197 zero_vector.resize(n_cmd_joints);
200 max.resize(n_cmd_joints); for (int i=0; i< n_cmd_joints; i++) max[i]=maxBottle->get(i).asFloat64();
201 min.resize(n_cmd_joints); for (int i=0; i< n_cmd_joints; i++) min[i]=minBottle->get(i).asFloat64();
202 home.resize(n_cmd_joints); for (int i=0; i< n_cmd_joints; i++) home[i]=homeBottle->get(i).asFloat64();
203 speed.resize(n_cmd_joints); for (int i=0; i< n_cmd_joints; i++) speed[i]=speedBottle->get(i).asFloat64();
204 gearbox.resize(n_cmd_joints);
205 for (int i=0; i< n_cmd_joints; i++)
208 int b=imot->getGearboxRatio(jointsList[i],&t);
216 void OpticalEncodersConsistency::tearDown()
219 sprintf(buff,"Closing test module
");ROBOTTESTINGFRAMEWORK_TEST_REPORT(buff);
220 setMode(VOCAB_CM_POSITION);
222 if (dd) {delete dd; dd =0;}
225 void OpticalEncodersConsistency::setMode(int desired_mode)
227 if (icmd == 0) ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Invalid control mode interface
");
228 if (iimd == 0) ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Invalid interaction mode interface
");
230 for (unsigned int i=0; i<jointsList.size(); i++)
232 icmd->setControlMode((int)jointsList[i],desired_mode);
233 iimd->setInteractionMode((int)jointsList[i],VOCAB_IM_STIFF);
234 yarp::os::Time::delay(0.010);
238 yarp::dev::InteractionModeEnum imode;
244 for (unsigned int i=0; i<jointsList.size(); i++)
246 icmd->getControlMode ((int)jointsList[i],&cmode);
247 iimd->getInteractionMode((int)jointsList[i],&imode);
248 if (cmode==desired_mode && imode==VOCAB_IM_STIFF) ok++;
250 if (ok==jointsList.size()) break;
253 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Unable to set control mode/interaction mode
");
255 yarp::os::Time::delay(0.2);
260 void OpticalEncodersConsistency::goHome()
262 if (ipos == 0) ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Invalid position control interface
");
263 if (ienc == 0) ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Invalid encoders interface
");
267 sprintf(buff,"Homing the whole part
");ROBOTTESTINGFRAMEWORK_TEST_REPORT(buff);
269 for (unsigned int i=0; i<jointsList.size(); i++)
271 ret = ipos->setRefSpeed((int)jointsList[i],speed[i]);
272 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ret, "ipos->setRefSpeed returned
false");
273 ret = ipos->positionMove((int)jointsList[i],home[i]);
274 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ret, "ipos->positionMove returned
false");
281 for (unsigned int i=0; i<jointsList.size(); i++)
284 ienc->getEncoder((int)jointsList[i],&tmp);
285 if (fabs(tmp-home[i])<tolerance) in_position++;
287 if (in_position==jointsList.size()) break;
290 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Timeout
while reaching home position
");
292 yarp::os::Time::delay(0.2);
297 void OpticalEncodersConsistency::saveToFile(std::string filename, yarp::os::Bottle &b)
300 fs.open (filename.c_str(), std::fstream::out);
302 for (int i=0; i<b.size(); i++)
304 std::string s = b.get(i).toString();
305 std::replace(s.begin(), s.end(), '(', ' ');
306 std::replace(s.begin(), s.end(), ')', ' ');
313 void OpticalEncodersConsistency::run()
316 setMode(VOCAB_CM_POSITION);
319 bool go_to_max=false;
320 for (unsigned int i=0; i<jointsList.size(); i++)
322 ipos->positionMove((int)jointsList[i], min[i]);
326 double start_time = yarp::os::Time::now();
328 //****************************************************************************************
329 //Retrieving coupling matrix using IRemoteVariable
330 //****************************************************************************************
334 ivar->getRemoteVariable("kinematic_mj
", b);
336 int matrix_size = matrix.cols();
342 for(int i=0 ; i< b.size() ; i++)
345 bv.fromString(b.get(i).toString());
346 njoints[i] = sqrt(bv.size());
350 for (int r=0; r < njoints[i]; r++)
352 for (int c=0; c < njoints[i]; c++)
354 matrix(r,c) = bv.get(ele).asFloat64();
361 for (int r=0; r < njoints[i]; r++)
363 for (int c=0; c < njoints[i]; c++)
366 for (int j=0; j < i; j++) jntprev += njoints[j];
367 if(!jntprev > matrix_size) matrix(r+jntprev,c+jntprev) = bv.get(ele).asFloat64();
375 // yDebug() << "MATRIX J2M : \n
" << matrix.toString();
377 // **************************************************************************************
379 trasp_matrix = matrix.transposed();
380 inv_matrix = yarp::math::luinv(matrix);
381 inv_trasp_matrix = inv_matrix.transposed();
383 sprintf(buff,"Matrix:\n %s \n
", matrix.toString().c_str());
384 ROBOTTESTINGFRAMEWORK_TEST_REPORT(buff);
385 sprintf(buff,"Inv matrix:\n %s \n
", inv_matrix.toString().c_str());
386 ROBOTTESTINGFRAMEWORK_TEST_REPORT(buff);
388 Bottle dataToPlot_test1;
389 Bottle dataToPlot_test2;
390 Bottle dataToPlot_test3;
391 Bottle dataToPlot_test4;
392 Bottle dataToPlot_test1rev;
394 bool test_data_is_valid = false;
395 bool first_time = true;
396 yarp::sig::Vector off_enc_mot; off_enc_mot.resize(jointsList.size());
397 yarp::sig::Vector off_enc_jnt; off_enc_jnt.resize(jointsList.size());
398 yarp::sig::Vector off_enc_jnt2mot; off_enc_jnt2mot.resize(jointsList.size());
399 yarp::sig::Vector off_enc_mot2jnt; off_enc_mot2jnt.resize(jointsList.size());
400 yarp::sig::Vector tmp_vector;
401 tmp_vector.resize(n_part_joints);
407 double curr_time = yarp::os::Time::now();
408 double elapsed = curr_time - start_time;
411 ret = ienc->getEncoders(tmp_vector.data());
412 for (unsigned int i = 0; i < jointsList.size(); i++)
413 enc_jnt[i] = tmp_vector[jointsList[i]];
416 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ret, "ienc->getEncoders returned
false");
417 ret = imotenc->getMotorEncoders(tmp_vector.data()); for (unsigned int i = 0; i < jointsList.size(); i++) enc_mot[i] = tmp_vector[jointsList(i)];
418 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ret, "imotenc->getMotorEncoder returned
false");
419 ret = ienc->getEncoderSpeeds(tmp_vector.data()); for (unsigned int i = 0; i < jointsList.size(); i++) vel_jnt[i] = tmp_vector[jointsList(i)];
420 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ret, "ienc->getEncoderSpeeds returned
false");
421 ret = imotenc->getMotorEncoderSpeeds(tmp_vector.data()); for (unsigned int i = 0; i < jointsList.size(); i++) vel_mot[i] = tmp_vector[jointsList(i)];
422 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ret, "imotenc->getMotorEncoderSpeeds returned
false");
423 ret = ienc->getEncoderAccelerations(tmp_vector.data()); for (unsigned int i = 0; i < jointsList.size(); i++) acc_jnt[i] = tmp_vector[jointsList(i)];
424 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ret, "ienc->getEncoderAccelerations returned
false");
425 ret = imotenc->getMotorEncoderAccelerations(tmp_vector.data()); for (unsigned int i = 0; i < jointsList.size(); i++) acc_mot[i] = tmp_vector[jointsList(i)];
426 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR_IF_FALSE(ret, "imotenc->getMotorEncoderAccelerations returned
false");
428 //if (enc_jnt == zero_vector) { ROBOTTESTINGFRAMEWORK_TEST_REPORT("Invalid getEncoders data
"); test_data_is_valid = true; }
429 //if (enc_mot == zero_vector) { ROBOTTESTINGFRAMEWORK_TEST_REPORT("Invalid getMotorEncoders data
"); test_data_is_valid = true; }
430 //if (vel_jnt == zero_vector) { ROBOTTESTINGFRAMEWORK_TEST_REPORT("Invalid getEncoderSpeeds data
"); test_data_is_valid = true; }
431 //if (vel_mot == zero_vector) { ROBOTTESTINGFRAMEWORK_TEST_REPORT("Invalid getMotorEncoderSpeeds data
"); test_data_is_valid = true; }
432 //if (acc_jnt == zero_vector) { ROBOTTESTINGFRAMEWORK_TEST_REPORT("Invalid getEncoderAccelerations data
"); test_data_is_valid = true; }
433 //if (acc_mot == zero_vector) { ROBOTTESTINGFRAMEWORK_TEST_REPORT("Invalid getMotorEncoderAccelerations data
"); test_data_is_valid = true; }
437 off_enc_jnt = enc_jnt;
438 off_enc_mot2jnt = enc_mot2jnt;
442 enc_jnt2mot = matrix * enc_jnt;
443 enc_mot2jnt = inv_matrix * (enc_mot - off_enc_mot);
444 vel_jnt2mot = matrix * vel_jnt;
445 //acc_jnt2mot = matrix * acc_jnt;
448 for (unsigned int i = 0; i < jointsList.size(); i++) enc_jnt2mot[i] = enc_jnt2mot[i] * gearbox[i];;
449 for (unsigned int i = 0; i < jointsList.size(); i++) vel_jnt2mot[i] = vel_jnt2mot[i] * gearbox[i];
450 //for (unsigned int i = 0; i < jointsList.size(); i++) acc_jnt2mot[i] = acc_jnt2mot[i] * gearbox[i];
451 for (unsigned int i = 0; i < jointsList.size(); i++) enc_mot2jnt[i] = enc_mot2jnt[i] / gearbox[i];
453 bool reached = false;
455 for (unsigned int i = 0; i < jointsList.size(); i++)
458 if (go_to_max == false) curr_val = min[i];
459 else curr_val = max[i];
460 if (fabs(enc_jnt[i] - curr_val) < tolerance) in_position++;
462 if (in_position == jointsList.size()) reached = true;
466 ROBOTTESTINGFRAMEWORK_ASSERT_ERROR("Timeout
while moving joint
");
471 sprintf(buff, "Test cycle %d/%d
", cycle, cycles); ROBOTTESTINGFRAMEWORK_TEST_REPORT(buff);
472 if (go_to_max == false)
474 for (unsigned int i = 0; i < jointsList.size(); i++)
475 ipos->positionMove(jointsList[i], max[i]);
478 start_time = yarp::os::Time::now();
482 for (unsigned int i = 0; i < jointsList.size(); i++)
483 ipos->positionMove(jointsList[i], min[i]);
486 start_time = yarp::os::Time::now();
490 //update previous and computes diff
491 diff_enc_jnt = (enc_jnt - prev_enc_jnt) / 0.010;
492 diff_enc_mot = (enc_mot - prev_enc_mot) / 0.010;
493 diff_enc_jnt2mot = (enc_jnt2mot - prev_enc_jnt2mot) / 0.010;
494 diff_vel_jnt = (vel_jnt - prev_vel_jnt) / 0.010;
495 diff_vel_mot = (vel_mot - prev_vel_mot) / 0.010;
496 diff_vel_jnt2mot = (vel_jnt2mot - prev_vel_jnt2mot) / 0.010;
497 diff_acc_jnt = (acc_jnt - prev_acc_jnt) / 0.010;
498 diff_acc_mot = (acc_mot - prev_acc_mot) / 0.010;
499 //diff_acc_jnt2mot = (acc_jnt2mot - prev_acc_jnt2mot) / 0.010;
500 prev_enc_jnt = enc_jnt;
501 prev_enc_mot = enc_mot;
502 prev_enc_jnt2mot = enc_jnt2mot;
503 prev_vel_jnt = vel_jnt;
504 prev_vel_mot = vel_mot;
505 prev_vel_jnt2mot = vel_jnt2mot;
506 // prev_acc_jnt = acc_jnt;
507 //prev_acc_mot = acc_mot;
508 // prev_acc_jnt2mot = acc_jnt2mot;
512 off_enc_mot = enc_mot;
513 off_enc_jnt2mot = enc_jnt2mot;
517 //prepare data to plot
518 //JOINT POSITIONS vs MOTOR POSITIONS
519 Bottle& row_test1 = dataToPlot_test1.addList();
520 Bottle& v1_test1 = row_test1.addList();
521 Bottle& v2_test1 = row_test1.addList();
522 yarp::sig::Vector v1 = enc_mot - off_enc_mot;
523 yarp::sig::Vector v2 = enc_jnt2mot - off_enc_jnt2mot;
529 //JOINT VELOCITES vs MOTOR VELOCITIES
530 Bottle& row_test2 = dataToPlot_test2.addList();
531 Bottle& v1_test2 = row_test2.addList();
532 Bottle& v2_test2 = row_test2.addList();
533 v1_test2.read(vel_mot);
534 v2_test2.read(vel_jnt2mot);
538 //JOINT POSITIONS(DERIVED) vs JOINT SPEED
539 if (first_time == false)
541 Bottle& row_test3 = dataToPlot_test3.addList();
542 Bottle& v1_test3 = row_test3.addList();
543 Bottle& v2_test3 = row_test3.addList();
544 v1_test3.read(vel_jnt);
545 v2_test3.read(diff_enc_jnt);
550 //MOTOR POSITIONS(DERIVED) vs MOTOR SPEED
551 if (first_time == false)
553 Bottle& row_test4 = dataToPlot_test4.addList();
554 Bottle& v1_test4 = row_test4.addList();
555 Bottle& v2_test4 = row_test4.addList();
556 v1_test4.read(vel_mot);
557 v2_test4.read(diff_enc_mot);
562 //JOINT POSITIONS vs MOTOR POSITIONS REVERSED
563 Bottle& row_test1 = dataToPlot_test1rev.addList();
564 Bottle& v1_test1 = row_test1.addList();
565 Bottle& v2_test1 = row_test1.addList();
566 yarp::sig::Vector v1 = enc_jnt;
567 yarp::sig::Vector v2 = enc_mot2jnt + off_enc_jnt;
576 if (cycle>=cycles) break;
581 yarp::os::ResourceFinder rf;
582 rf.setDefaultContext("scripts
");
584 string partfilename = partName+".txt
";
585 string testfilename = "encConsis_
";
586 string filename1 = testfilename + "jointPos_MotorPos_
" + partfilename;
587 saveToFile(filename1,dataToPlot_test1);
588 string filename2 = testfilename + "jointVel_motorVel_
" + partfilename;
589 saveToFile(filename2,dataToPlot_test2);
590 string filename3 = testfilename + "joint_derivedVel_vel_
" + partfilename;
591 saveToFile(filename3,dataToPlot_test3);
592 string filename4 = testfilename + "motor_derivedVel_vel_
" + partfilename;
593 saveToFile(filename4,dataToPlot_test4);
595 string filename1rev = testfilename + "jointPos_MotorPos_reversed_
" + partfilename;
596 saveToFile(filename1rev,dataToPlot_test1rev);
598 //find octave scripts
599 std::string octaveFile = rf.findFile("encoderConsistencyPlotAll.m
");
600 if(octaveFile.size() == 0)
602 yError()<<"Cannot find file encoderConsistencyPlotAll.m
";
606 //prepare octave command
607 std::string octaveCommand= "octave --path
"+ getPath(octaveFile);
609 ss << jointsList.size();
610 string str = ss.str();
611 octaveCommand+= " -q --eval \
"encoderConsistencyPlotAll('" +partName +
"'," + str +
")\" --persist";
615 int ret = system (octaveCommand.c_str());
619 yInfo() <<
"Test has collected all data. You need to plot data to check is test is passed";
620 yInfo() <<
"Please run following command to plot data.";
621 yInfo() << octaveCommand;
622 yInfo() <<
"To exit from Octave application please type 'exit' command.";
628 std::string OpticalEncodersConsistency::getPath(
const std::string& str)
631 found=str.find_last_of(
"/\\");
632 return(str.substr(0,found));
This tests checks if the motor encoder reading are consistent with the joint encoder readings.