00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "BeoSub/BeoSubOneBal.H"
00039 #include "Devices/HMR3300.H"
00040 #include "BeoSub/BeoSubBallast.H"
00041 #include "BeoSub/BeoSubIMU.H"
00042 #include "Util/MathFunctions.H"
00043 #include "Devices/FrameGrabberFactory.H"
00044
00045
00046
00047
00048
00049
00050
00051 class BeoSubListener : public BeoChipListener
00052 {
00053 public:
00054
00055 BeoSubListener(BeoSubOneBal *sub) : itsBeoSub(sub) { }
00056
00057
00058 virtual ~BeoSubListener() { }
00059
00060
00061 virtual void event(const BeoChipEventType t, const int valint,
00062 const float valfloat)
00063 {
00064
00065 itsBeoSub->dispatchBeoChipEvent(t, valint, valfloat);
00066 }
00067
00068 private:
00069 BeoSubOneBal *itsBeoSub;
00070 };
00071
00072
00073
00074 class HMRlistener : public HMR3300Listener {
00075 public:
00076
00077 HMRlistener(BeoSubOneBal *sub) : itsBeoSub(sub) { }
00078
00079
00080 virtual ~HMRlistener() {};
00081
00082
00083 virtual void newData(const Angle heading, const Angle pitch,
00084 const Angle roll)
00085 {
00086
00087
00088 itsBeoSub->updateCompass(heading, pitch, roll);
00089 }
00090
00091 private:
00092 BeoSubOneBal *itsBeoSub;
00093 };
00094
00095
00096 class IMUListener : public BeoSubIMUListener {
00097 public:
00098
00099 IMUListener(BeoSubOneBal *sub) : itsBeoSub(sub) { }
00100
00101 virtual ~IMUListener() {};
00102
00103
00104 virtual void newData(const float xa, const float ya, const float za,
00105 const Angle xv, const Angle yv, const Angle zv)
00106 {
00107 itsBeoSub->updateIMU(xa, ya, za, xv, yv, zv);
00108 }
00109 private:
00110 BeoSubOneBal *itsBeoSub;
00111 };
00112
00113
00114
00115
00116
00117
00118
00119
00120 virtual ~HMRlistener() {};
00121
00122
00123 virtual void newData(const float xa, const float ya, const float za,
00124 const Angle xv, const Angle yv, const Angle zv)
00125 {
00126 }
00127
00128 private:
00129 BeoSubOneBal *itsBeoSub;
00130 };
00131 */
00132
00133
00134
00135
00136 BeoSubOneBal::BeoSubOneBal(OptionManager& mgr) :
00137 BeoSub(mgr),
00138 itsIMU(new BeoSubIMU(mgr)),
00139 itsLeftThrusterServoNum("BeoSubLeftThrusterServoNum", this, 3),
00140 itsRightThrusterServoNum("BeoSubRightThrusterServoNum", this, 2),
00141 itsHMR3300(new HMR3300(mgr)),
00142 itsBeo(new BeoChip(mgr, "BeoChip", "BeoChip")),
00143 itsFballast(new BeoSubBallast(mgr, "Front Ballast", "FrontBallast")),
00144 itsRballast(new BeoSubBallast(mgr, "Rear Ballast", "RearBallast")),
00145 itsCameraFront(makeIEEE1394grabber(mgr, "Front Camera", "FrontCamera")),
00146 itsCameraDown(makeIEEE1394grabber(mgr, "Down Camera", "DownCamera")),
00147 itsCameraUp(makeIEEE1394grabber(mgr, "Up Camera", "UpCamera")),
00148 itsDepthSensor(30, 0.99999F),
00149 itsHeadingSensor(3, 0.99999F),
00150 itsPitchSensor(3, 0.99999F),
00151 itsDepthPID(0.001F, 0.0F, 0.1F, 3.0F, 3.0F),
00152 itsHeadingPID(0.002F, 0.0F, 0.03F, Angle(170.0), Angle(170.0)),
00153 itsPitchPID(0.001F, 0.0F, 0.1F, Angle(170.0), Angle(170.0)),
00154 itsRotVelPID(0.3, 0.0001, 0, -500, 500),
00155 itsDepthPIDon(false),
00156 itsHeadingPIDon(false),
00157 itsPitchPIDon(false),
00158 itsRotVelPIDon(false),
00159 itsKillSwitchUsed(false),
00160 itsDiveSetting(0.0F),
00161 itsPitchSetting(0.0F),
00162 itsLastHeadingTime(0.0),
00163 itsLastPitchTime(0.0),
00164 itsLastDepthTime(0.0),
00165 #ifdef HAVE_LINUX_PARPORT_H
00166 lp0(new ParPort(mgr)),
00167 markerdropper(lp0),
00168 #endif
00169 killSwitchDebounceCounter(0)
00170
00171 {
00172
00173 addSubComponent(itsHMR3300);
00174 addSubComponent(itsIMU);
00175 addSubComponent(itsBeo);
00176 addSubComponent(itsCameraDown);
00177 addSubComponent(itsCameraFront);
00178 addSubComponent(itsCameraUp);
00179 addSubComponent(itsFballast);
00180 addSubComponent(itsRballast);
00181 #ifdef HAVE_LINUX_PARPORT_H
00182 addSubComponent(lp0);
00183 #endif
00184
00185
00186 LINFO("Resetting the BeoChip...");
00187 itsBeo->reset(MC_RECURSE);
00188 usleep(200000);
00189
00190
00191 rutz::shared_ptr<BeoChipListener> b(new BeoSubListener(this));
00192 itsBeo->setListener(b);
00193
00194
00195 rutz::shared_ptr<HMR3300Listener> bl(new HMRlistener(this));
00196 itsHMR3300->setListener(bl);
00197
00198 rutz::shared_ptr<BeoSubIMUListener> imu(new IMUListener(this));
00199 itsIMU->setListener(imu);
00200
00201
00202 itsFballast->setBeoChip(itsBeo);
00203 itsRballast->setBeoChip(itsBeo);
00204
00205
00206 itsBeo->setModelParamVal("BeoChipUseRTSCTS", false);
00207
00208
00209 itsHMR3300->setModelParamString("HMR3300SerialPortDevName",
00210 "/dev/ttyS0", MC_RECURSE);
00211 itsBeo->setModelParamString("BeoChipDeviceName", "/dev/ttyS2");
00212 itsIMU->setModelParamString("IMUSerialPortDevName", "/dev/ttyS3",
00213 MC_RECURSE);
00214
00215
00216 itsRballast->setModelParamVal("RearBallastOutRed", 1);
00217 itsRballast->setModelParamVal("RearBallastOutWhite", 0);
00218 itsRballast->setModelParamVal("RearBallastInYellow", 1);
00219 itsRballast->setModelParamVal("RearBallastInWhite", 0);
00220
00221 itsFballast->setModelParamVal("FrontBallastOutRed", 3);
00222 itsFballast->setModelParamVal("FrontBallastOutWhite", 2);
00223 itsFballast->setModelParamVal("FrontBallastInYellow", 3);
00224 itsFballast->setModelParamVal("FrontBallastInWhite", 2);
00225
00226 }
00227
00228
00229 BeoSubOneBal::~BeoSubOneBal()
00230 { }
00231
00232
00233 void BeoSubOneBal::start1()
00234 {
00235
00236 itsCameraDown->setModelParamVal("FrameGrabberSubChan", int(BEOSUBCAMDOWN));
00237 itsCameraFront->setModelParamVal("FrameGrabberSubChan", int(BEOSUBCAMFRONT));
00238 itsCameraUp->setModelParamVal("FrameGrabberSubChan", int(BEOSUBCAMUP));
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264 BeoSub::start1();
00265 }
00266
00267
00268 void BeoSubOneBal::start2()
00269 {
00270
00271
00272 itsBeo->captureAnalog(0, true);
00273 itsBeo->captureAnalog(1, false);
00274 itsBeo->capturePulse(0, false);
00275 itsBeo->capturePulse(1, false);
00276
00277
00278 for (int i = 0; i < 8; i ++) itsBeo->setServo(i, 0.0F);
00279
00280
00281 itsBeo->captureKeyboard(true);
00282 itsBeo->debounceKeyboard(false);
00283
00284
00285 CLINFO("filling ballasts...");
00286 setBallasts(1.0F, 1.0F, false); sleep(4);
00287 CLINFO("emptying ballasts...");
00288 setBallasts(0.0F, 0.0F, true);
00289
00290
00291
00292
00293
00294 itsDiveSetting = 0.5F;
00295
00296
00297 itsBeo->lcdClear();
00298 itsBeo->lcdPrintf(3, 0, " iLab and USC rock! ");
00299
00300
00301
00302 }
00303
00304 void BeoSubOneBal::stop1() {
00305
00306 useDepthPID(false);
00307 useHeadingPID(false);
00308 usePitchPID(false);
00309 useRotVelPID(false);
00310 setRotVel(0.0);
00311 setTransVel(0.0);
00312
00313
00314 setBallasts(0.0F, 0.0F, true);
00315
00316 }
00317
00318
00319 void BeoSubOneBal::advanceRel(const float relDist, const bool stop)
00320 {
00321 float d = relDist * 2.0F;
00322
00323
00324 bool hpid = itsHeadingPIDon;
00325 useHeadingPID(false);
00326
00327
00328 if (fabsf(d) < 1.0F)
00329 {
00330 setTransVel(d);
00331 sleep(1);
00332 if (stop)
00333 {
00334 setTransVel(-d);
00335 usleep(350000);
00336 }
00337 }
00338 else
00339 {
00340 float forward = 1.0F, reverse = -1.0F;
00341 if (d < 0.0F) { forward = -forward; reverse = -reverse; }
00342
00343
00344 setTransVel(forward);
00345
00346
00347
00348 usleep(int(fabsf(d) * 1000000));
00349
00350 if (stop)
00351 {
00352
00353 setTransVel(reverse);
00354
00355
00356 sleep(1);
00357 }
00358 }
00359
00360
00361 setTransVel(0);
00362
00363 itsGlobalPosition.x += relDist * cos(itsGlobalHeading.getRadians());
00364 itsGlobalPosition.y += relDist * sin(itsGlobalHeading.getRadians());
00365
00366 useHeadingPID(hpid);
00367 }
00368
00369
00370 void BeoSubOneBal::turnOpen(const Angle openHeading, const bool stop){
00371 bool hpid = itsHeadingPIDon;
00372 useHeadingPID(false);
00373
00374
00375 float value = openHeading.getVal();
00376 float turnrate;
00377 if(value > 0) {
00378 turnrate = 15;
00379 } else {
00380 turnrate = -15;
00381 }
00382
00383 setRotVel(turnrate);
00384 usleep((long)((value/turnrate)*1e6));
00385 setRotVel(0.0);
00386 itsGlobalHeading += openHeading;
00387 useHeadingPID(hpid);
00388 }
00389
00390
00391 void BeoSubOneBal::strafeRel(const float relDist)
00392 {
00393
00394
00395
00396
00397 turnRel(90);
00398 advanceRel(relDist);
00399 turnRel(-90);
00400 }
00401
00402 void BeoSubOneBal::updateThrusters() {
00403 float left = TransVelCommand + RotVelCommand;
00404 float right = TransVelCommand - RotVelCommand;
00405
00406 if (left > 1) left = 1;
00407 else if (left < -1) left = -1;
00408
00409 if (right > 1) right = 1;
00410 else if (right < -1) right = -1;
00411
00412 thrust(left, right);
00413 }
00414
00415 void BeoSubOneBal::updateRotVelPID(const Angle current) {
00416 if(itsRotVelPIDon) {
00417
00418 RotVelCommand = itsRotVelPID.update(PIDRotVel, current).getVal();
00419 updateThrusters();
00420 }
00421 }
00422
00423 void BeoSubOneBal::setRotVel(const Angle desired) {
00424 if(itsRotVelPIDon) {
00425 PIDRotVel = desired;
00426 } else {
00427 RotVelCommand = desired.getVal();
00428 updateThrusters();
00429 }
00430 }
00431
00432 void BeoSubOneBal::setTransVel(const float desired) {
00433
00434 TransVelCommand = desired;
00435 updateThrusters();
00436 }
00437
00438 void BeoSubOneBal::updateIMU(const float xa, const float ya, const float za,
00439 const Angle xv, const Angle yv, const Angle zv)
00440 {
00441
00442 updateRotVelPID(zv);
00443 }
00444
00445
00446 void BeoSubOneBal::updateCompass(const Angle heading, const Angle pitch,
00447 const Angle roll)
00448 {
00449
00450 double t = itsMasterClock.getSecs();
00451
00452 pthread_mutex_lock(&itsLock);
00453
00454
00455 itsHeadingSensor.newMeasurement(heading);
00456 itsPitchSensor.newMeasurement(pitch);
00457
00458
00459 itsCurrentAttitude.heading = itsHeadingSensor.getValue();
00460 itsCurrentAttitude.pitch = itsPitchSensor.getValue();
00461 itsCurrentAttitude.roll = roll;
00462 itsCurrentAttitude.compassTime = t;
00463
00464
00465
00466
00467 if (itsHeadingPIDon && t >= itsLastHeadingTime + 0.5)
00468 {
00469
00470 const float hcmd =
00471 itsHeadingPID.update(itsTargetAttitude.heading,
00472 itsHeadingSensor.getValue()).getVal();
00473 setRotVel(hcmd);
00474
00475
00476 itsLastHeadingTime = t;
00477
00478 LINFO("hcmd = %f at t = %f", hcmd, t);
00479 }
00480
00481
00482 if (itsPitchPIDon && t >= itsLastPitchTime + 0.5)
00483 {
00484
00485 const float pcmd =
00486 itsPitchPID.update(itsTargetAttitude.pitch,
00487 itsPitchSensor.getValue()).getVal();
00488
00489
00490
00491
00492 itsPitchSetting += pcmd;
00493 if (itsPitchSetting > 0.25F) itsPitchSetting = 0.25F;
00494 if (itsPitchSetting < -0.25F) itsPitchSetting = -0.25F;
00495
00496
00497
00498 float f = itsDiveSetting - itsPitchSetting;
00499 float r = itsDiveSetting + itsPitchSetting;
00500 if (f < 0.0F) f = 0.0F; else if (f > 1.0F) f = 1.0F;
00501 if (r < 0.0F) r = 0.0F; else if (r > 1.0F) r = 1.0F;
00502 setBallasts(f, r);
00503
00504
00505 itsLastPitchTime = t;
00506
00507 LINFO("pcmd = %f at t = %f", pcmd, t);
00508 }
00509
00510 pthread_mutex_unlock(&itsLock);
00511 }
00512
00513
00514 void BeoSubOneBal::updateDepth(const float depth)
00515 {
00516
00517 double t = itsMasterClock.getSecs();
00518
00519 pthread_mutex_lock(&itsLock);
00520
00521
00522 itsDepthSensor.newMeasurement(depth);
00523
00524
00525 itsCurrentAttitude.depth = itsDepthSensor.getValue();
00526 itsCurrentAttitude.pressureTime = t;
00527 const float tdepth = itsTargetAttitude.depth;
00528
00529
00530 if (itsDepthPIDon && t >= itsLastDepthTime + 0.5)
00531 {
00532
00533 const float dcmd = itsDepthPID.update(tdepth, itsDepthSensor.getValue());
00534
00535
00536 itsDiveSetting += dcmd;
00537 if (itsDiveSetting < 0.0F) itsDiveSetting = 0.0F;
00538 else if (itsDiveSetting > 1.0F) itsDiveSetting = 1.0F;
00539
00540
00541
00542 float f = itsDiveSetting - itsPitchSetting;
00543 float r = itsDiveSetting + itsPitchSetting;
00544 if (f < 0.0F) f = 0.0F; else if (f > 1.0F) f = 1.0F;
00545 if (r < 0.0F) r = 0.0F; else if (r > 1.0F) r = 1.0F;
00546 setBallasts(f, r);
00547
00548
00549 itsLastDepthTime = t;
00550
00551 LINFO("dcmd = %f at t=%f", dcmd, t);
00552 }
00553
00554 pthread_mutex_unlock(&itsLock);
00555 }
00556
00557
00558 void BeoSubOneBal::thrust(const float leftval, const float rightval)
00559 {
00560
00561
00562 itsBeo->setServo(itsLeftThrusterServoNum.getVal(), leftval);
00563 itsThrustLeft = itsBeo->getServo(itsLeftThrusterServoNum.getVal());
00564
00565 itsBeo->setServo(itsRightThrusterServoNum.getVal(), rightval);
00566 itsThrustRight = itsBeo->getServo(itsRightThrusterServoNum.getVal());
00567
00568
00569 }
00570
00571
00572 void BeoSubOneBal::getThrusters(float& leftval, float& rightval) const
00573 { leftval = itsThrustLeft; rightval = itsThrustRight; }
00574
00575
00576 void BeoSubOneBal::setFrontBallast(const float val, const bool blocking)
00577 { itsFballast->set(val, blocking); }
00578
00579
00580 void BeoSubOneBal::setRearBallast(const float val, const bool blocking)
00581 { itsRballast->set(val, blocking); }
00582
00583
00584 void BeoSubOneBal::setBallasts(const float f, const float r,
00585 const bool blocking)
00586 {
00587
00588 itsFballast->set(f, false);
00589 itsRballast->set(r, false);
00590
00591 if (blocking)
00592 {
00593 const double timeout = itsMasterClock.getSecs() + 20.0;
00594 while(itsMasterClock.getSecs() < timeout)
00595 {
00596 if (itsFballast->moving() == false &&
00597 itsRballast->moving() == false) break;
00598 usleep(100000);
00599 }
00600 if (itsMasterClock.getSecs() >= timeout)
00601 LERROR("Timeout on blocking setBallasts -- IGNORED");
00602 }
00603 }
00604
00605
00606 float BeoSubOneBal::getFrontBallast() const
00607 { return itsFballast->get(); }
00608
00609
00610 float BeoSubOneBal::getRearBallast() const
00611 { return itsRballast->get(); }
00612
00613
00614 void BeoSubOneBal::getBallasts(float& f, float& r) const
00615 { f = itsFballast->get(); r = itsRballast->get(); }
00616
00617
00618 void BeoSubOneBal::dropMarker(const bool blocking)
00619 {
00620 #ifndef HAVE_LINUX_PARPORT_H
00621 LFATAL("<linux/parport.h> must be installed to use this function");
00622 #else
00623 markerdropper.Step(150, 65000);
00624 #endif
00625 }
00626
00627
00628 void BeoSubOneBal::killSwitch()
00629 {
00630
00631 useDepthPID(false);
00632 useHeadingPID(false);
00633 usePitchPID(false);
00634 useRotVelPID(false);
00635 setRotVel(0.0);
00636 setTransVel(0.0);
00637
00638
00639 setBallasts(0.0F, 0.0F, true);
00640
00641 LFATAL("Kill switch pulled!");
00642 }
00643
00644
00645 void BeoSubOneBal::dispatchBeoChipEvent(const BeoChipEventType t,
00646 const int valint,
00647 const float valfloat)
00648 {
00649
00650 switch(t)
00651 {
00652 case NONE:
00653 LERROR("Unexpected BeoChip NONE event!");
00654 break;
00655
00656 case PWM0:
00657 LERROR("Unexpected BeoChip PWM0 event!");
00658 break;
00659
00660 case PWM1:
00661 LERROR("Unexpected BeoChip PWM1 event!");
00662 break;
00663
00664 case KBD:
00665 LDEBUG("Keyboard: new value %d", valint);
00666
00667
00668
00669
00670
00671
00672
00673
00674 itsFballast->input(valint);
00675 itsRballast->input(valint);
00676
00677
00678
00679
00680 break;
00681
00682 case ADC0:
00683 {
00684
00685
00686
00687
00688
00689
00690
00691 float depth = (float(valint) * 2.5F) / 255.0F;
00692
00693
00694
00695
00696
00697
00698 depth -= 0.5F;
00699
00700
00701 depth /= 0.040F;
00702
00703
00704
00705
00706 depth = (depth - 14.275F) / 1.443F;
00707
00708
00709
00710 updateDepth(depth);
00711 }
00712 break;
00713
00714 case ADC1:
00715 LERROR("Unexpected BeoChip ADC1 event!");
00716 break;
00717
00718 case RESET:
00719 LERROR("BeoChip RESET!");
00720 break;
00721
00722 case ECHOREP:
00723 LINFO("BeoChip Echo reply.");
00724 break;
00725
00726 case INOVERFLOW:
00727 LERROR("BeoChip input overflow!");
00728 break;
00729
00730 case SERIALERROR:
00731 LERROR("BeoChip serial error!");
00732 break;
00733
00734 case OUTOVERFLOW:
00735 LERROR("BeoChip output overflow!");
00736 break;
00737
00738 default:
00739 LERROR("BeoChip unknown event %d!", int(t));
00740 break;
00741 }
00742
00743
00744 if(isKilled()) {
00745 killSwitchDebounceCounter++;
00746 if(killSwitchDebounceCounter > 50) {
00747 killSwitch();
00748 }
00749
00750 } else {
00751 if(killSwitchDebounceCounter > 0) {
00752 LINFO("killSwitchDebounce reset\n");
00753 }
00754 killSwitchDebounceCounter = 0;
00755 }
00756 }
00757
00758
00759 Image< PixRGB<byte> >
00760 BeoSubOneBal::grabImage(const enum BeoSubCamera cam) const
00761 {
00762 if (cam == BEOSUBCAMFRONT) return itsCameraFront->readRGB();
00763 if (cam == BEOSUBCAMDOWN) return itsCameraDown->readRGB();
00764 if (cam == BEOSUBCAMUP) return itsCameraUp->readRGB();
00765 LERROR("Wrong camera %d -- RETURNING EMPTY IMAGE", int(cam));
00766 return Image< PixRGB<byte> >();
00767 }
00768
00769
00770 void BeoSubOneBal::useDepthPID(const bool useit)
00771 { itsDepthPIDon = useit; }
00772
00773
00774 void BeoSubOneBal::useHeadingPID(const bool useit)
00775 { itsHeadingPIDon = useit; }
00776
00777
00778 void BeoSubOneBal::usePitchPID(const bool useit)
00779 { itsPitchPIDon = useit; }
00780
00781 void BeoSubOneBal::useRotVelPID(const bool useit)
00782 { itsRotVelPIDon = useit; }
00783
00784
00785 void BeoSubOneBal::useKillSwitch(const bool useit)
00786 { itsKillSwitchUsed = useit; }
00787
00788
00789 bool BeoSubOneBal::isKilled()
00790 {
00791 #ifndef HAVE_LINUX_PARPORT_H
00792 LFATAL("<linux/parport.h> must be installed to use this function");
00793 return true;
00794 #else
00795 unsigned char status;
00796 status = lp0->ReadStatus();
00797 return (status & PARPORT_STATUS_BUSY) || !(status & PARPORT_STATUS_ACK);
00798 #endif
00799 }
00800
00801
00802 void BeoSubOneBal::setDepthPgain(float p)
00803 { itsDepthPID.setPIDPgain(p); }
00804
00805
00806 void BeoSubOneBal::setDepthIgain(float i)
00807 { itsDepthPID.setPIDIgain(i); }
00808
00809
00810 void BeoSubOneBal::setDepthDgain(float d)
00811 { itsDepthPID.setPIDDgain(d); }
00812
00813
00814 void BeoSubOneBal::setHeadingPgain(float p)
00815 { itsHeadingPID.setPIDPgain(p); }
00816
00817
00818 void BeoSubOneBal::setHeadingIgain(float i)
00819 { itsHeadingPID.setPIDIgain(i); }
00820
00821
00822 void BeoSubOneBal::setHeadingDgain(float d)
00823 { itsHeadingPID.setPIDDgain(d); }
00824
00825
00826 void BeoSubOneBal::setPitchPgain(float p)
00827 { itsPitchPID.setPIDPgain(p); }
00828
00829
00830 void BeoSubOneBal::setPitchIgain(float i)
00831 { itsPitchPID.setPIDIgain(i); }
00832
00833
00834 void BeoSubOneBal::setPitchDgain(float d)
00835 { itsPitchPID.setPIDDgain(d); }
00836
00837
00838 void BeoSubOneBal::setRotVelPgain(float p)
00839 { itsRotVelPID.setPIDPgain(p); }
00840
00841
00842 void BeoSubOneBal::setRotVelIgain(float i)
00843 { itsRotVelPID.setPIDIgain(i); }
00844
00845
00846 void BeoSubOneBal::setRotVelDgain(float d)
00847 { itsRotVelPID.setPIDDgain(d); }
00848
00849
00850
00851
00852