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 #include <hardware/lias/CRSnAxesVelocityController.hpp>
00029
00030
00031
00032 #include <rtt/Event.hpp>
00033 #include <rtt/Logger.hpp>
00034
00035 #include <rtt/Command.hpp>
00036 #include <rtt/DataPort.hpp>
00037
00038 #include <dev/IncrementalEncoderSensor.hpp>
00039 #include <rtt/dev/AnalogOutput.hpp>
00040 #include <rtt/dev/DigitalOutput.hpp>
00041 #include <rtt/dev/DigitalInput.hpp>
00042 #include <dev/AnalogDrive.hpp>
00043 #include <dev/Axis.hpp>
00044 #include <rtt/dev/AxisInterface.hpp>
00045
00046
00047 #if defined (OROPKG_OS_LXRT)
00048
00049 #include "IP_Digital_24_DOutInterface.hpp"
00050 #include "IP_Encoder_6_EncInterface.hpp"
00051 #include "IP_FastDAC_AOutInterface.hpp"
00052 #include "IP_OptoInput_DInInterface.hpp"
00053 #include "CombinedDigitalOutInterface.hpp"
00054
00055 #include "LiASConstants.hpp"
00056
00057 #else
00058
00059 #include <dev/SimulationAxis.hpp>
00060
00061 #endif
00062 namespace OCL {
00063
00064 using namespace RTT;
00065 using namespace std;
00066
00067 #define NUM_AXES 6
00068
00069 #define DBG \
00070 log(Info) << endlog(); \
00071 log(Info) << __PRETTY_FUNCTION__ << endlog();
00072
00073
00074 #define TRACE(x) \
00075 log(Info) << "TRACE " << #x << endlog(); \
00076 x;
00077
00078
00079 using namespace Orocos;
00080
00081
00082 CRSnAxesVelocityController::CRSnAxesVelocityController(const std::string& name,const std::string& propertyfilename)
00083 : TaskContext(name),
00084 driveValue(NUM_AXES),
00085 reference(NUM_AXES),
00086 positionValue(NUM_AXES),
00087 output(NUM_AXES),
00088 _propertyfile(propertyfilename),
00089 driveLimits("driveLimits","velocity limits of the axes, (rad/s)"),
00090 lowerPositionLimits("LowerPositionLimits","Lower position limits (rad)"),
00091 upperPositionLimits("UpperPositionLimits","Upper position limits (rad)"),
00092 initialPosition("initialPosition","Initial position (rad) for simulation or hardware"),
00093 signAxes("signAxes","Indicates the sign of each of the axes"),
00094 offset ("offset", "offset to compensate for friction. Should only partially compensate friction"),
00095 driveOutOfRange("driveOutOfRange"),
00096 positionOutOfRange("positionOutOfRange"),
00097 _num_axes("NUM_AXES",NUM_AXES),
00098 _homed(NUM_AXES),
00099 servoGain("servoGain","gain of the servoloop (no units)"),
00100 _servoGain(NUM_AXES),
00101 servoIntegrationFactor("servoIntegrationFactor","INVERSE of the integration time for the servoloop (s)"),
00102 _servoIntegrationFactor(NUM_AXES),
00103 servoFFScale( "servoFFScale","scale factor on the feedforward signal of the servo loop "),
00104 _servoFFScale(NUM_AXES),
00105 servoDerivTime( "servoDerivTime","Derivative time for the servo loop "),
00106 servoIntVel(NUM_AXES),
00107 servoIntError(NUM_AXES),
00108 servoInitialized(false),
00109 previousPos(NUM_AXES),
00110 _axes(NUM_AXES),
00111 _axesInterface(NUM_AXES)
00112 {
00116 properties()->addProperty( &driveLimits );
00117 properties()->addProperty( &lowerPositionLimits );
00118 properties()->addProperty( &upperPositionLimits );
00119 properties()->addProperty( &initialPosition );
00120 properties()->addProperty( &signAxes );
00121 properties()->addProperty( &offset );
00122 properties()->addProperty( &servoGain );
00123 properties()->addProperty( &servoIntegrationFactor );
00124 properties()->addProperty( &servoFFScale );
00125 properties()->addProperty( &servoDerivTime );
00126 attributes()->addConstant( &_num_axes);
00127
00128 if (!marshalling()->readProperties(propertyfilename)) {
00129 log(Error) << "Failed to read the property file, continueing with default values." << endlog();
00130 throw 0;
00131 }
00132 #if defined (OROPKG_OS_LXRT)
00133 log(Info) << "LXRT version of CRSnAxesVelocityController has started" << endlog();
00134
00135 _IP_Digital_24_DOut = new IP_Digital_24_DOutInterface("IP_Digital_24_DOut");
00136
00137 _IP_Encoder_6_task = new IP_Encoder_6_Task(LiAS_ENCODER_REFRESH_PERIOD);
00138 _IP_FastDac_AOut = new IP_FastDAC_AOutInterface("IP_FastDac_AOut");
00139 _IP_OptoInput_DIn = new IP_OptoInput_DInInterface("IP_OptoInput_DIn");
00140
00141 _IP_Encoder_6_task->start();
00142
00143
00144 double driveOffsets[6] = LiAS_OFFSETSinVOLTS;
00145 double radpsec2volt[6] = LiAS_RADproSEC2VOLT;
00146 int encoderOffsets[6] = LiAS_ENCODEROFFSETS;
00147 double ticks2rad[6] = LiAS_TICKS2RAD;
00148 double jointspeedlimits[6] = LiAS_JOINTSPEEDLIMITS;
00149
00150
00151 _enable = new DigitalOutput( _IP_Digital_24_DOut, LiAS_ENABLE_CHANNEL, true);
00152 _enable->switchOff();
00153
00154 _combined_enable_DOutInterface = new CombinedDigitalOutInterface( "enableDOutInterface", _enable, 6, OR );
00155
00156
00157 _brake = new DigitalOutput( _IP_Digital_24_DOut, LiAS_BRAKE_CHANNEL, false);
00158 _brake->switchOn();
00159
00160 _combined_brake_DOutInterface = new CombinedDigitalOutInterface( "brakeDOutInterface", _brake, 2, OR);
00161
00162
00163 for (unsigned int i = 0; i < LiAS_NUM_AXIS; i++)
00164 {
00165 _homed[i] = false;
00166
00167 _encoderInterface[i] = new IP_Encoder_6_EncInterface( *_IP_Encoder_6_task, i );
00168 _encoder[i] = new IncrementalEncoderSensor( _encoderInterface[i], 1.0 / ticks2rad[i], encoderOffsets[i], -180, 180, LiAS_ENC_RES );
00169
00170 _vref[i] = new AnalogOutput( _IP_FastDac_AOut, i + 1 );
00171 _combined_enable[i] = new DigitalOutput( _combined_enable_DOutInterface, i );
00172 _drive[i] = new AnalogDrive( _vref[i], _combined_enable[i], 1.0 / radpsec2volt[i], driveOffsets[i] / radpsec2volt[i]);
00173
00174 _reference[i] = new DigitalInput( _IP_OptoInput_DIn, i + 1 );
00175
00176
00177 _axes[i] = new Axis( _drive[i] );
00178 log(Error) << "_axes[i]->limitDrive( jointspeedlimits[i] ) has been disabled." << endlog();
00179
00180
00181 _axes[i]->setSensor( "Position", _encoder[i] );
00182
00183
00184
00185 if ((i == 1)||(i == 2))
00186 {
00187 _combined_brake[i-1] = new DigitalOutput( _combined_brake_DOutInterface, i-1);
00188 _axes[i]->setBrake( _combined_brake[i-1] );
00189 }
00190
00191 _axesInterface[i] = _axes[i];
00192 }
00193 #else // ifndef OROPKG_OS_LXRT
00194 log(Info) << "GNU-Linux simulation version of CRSnAxesVelocityController has started" << endlog();
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204 for (unsigned int i = 0; i <NUM_AXES; i++) {
00205 _homed[i] = true;
00206 _axes[i] = new SimulationAxis(
00207 0.0,
00208 lowerPositionLimits.value()[i],
00209 upperPositionLimits.value()[i]);
00210 _axes[i]->setMaxDriveValue( driveLimits.value()[i] );
00211 _axesInterface[i] = _axes[i];
00212 }
00213 #endif
00214
00215 _deactivate_axis3 = false;
00216 _deactivate_axis2 = false;
00217 _activate_axis2 = false;
00218 _activate_axis3 = false;
00219
00220
00221
00222
00223 typedef CRSnAxesVelocityController MyType;
00224
00225 this->commands()->addCommand(
00226 command( "startAxis", &MyType::startAxis, &MyType::startAxisCompleted, this),
00227 "start axis, initializes drive value to zero and starts updating the drive-value \
00228 with the drive-port (only possible if axis is unlocked","axis","axis to start" );
00229 this->commands()->addCommand( command( "stopAxis", &MyType::stopAxis, &MyType::stopAxisCompleted, this),
00230 "stop axis, sets drive value to zero and disables the update of the drive-port, \
00231 (only possible if axis is started","axis","axis to stop" );
00232 this->commands()->addCommand( command( "lockAxis", &MyType::lockAxis, &MyType::lockAxisCompleted, this),
00233 "lock axis, enables the brakes (only possible if axis is stopped","axis","axis to lock" );
00234 this->commands()->addCommand( command( "unlockAxis", &MyType::unlockAxis, &MyType::unlockAxisCompleted, this),
00235 "unlock axis, disables the brakes and enables the drive (only possible if \
00236 axis is locked","axis","axis to unlock" );
00237 this->commands()->addCommand( command( "startAllAxes", &MyType::startAllAxes, &MyType::startAllAxesCompleted, this), "start all axes" );
00238 this->commands()->addCommand( command( "stopAllAxes", &MyType::stopAllAxes, &MyType::stopAllAxesCompleted, this), "stops all axes" );
00239 this->commands()->addCommand( command( "lockAllAxes", &MyType::lockAllAxes, &MyType::lockAllAxesCompleted, this), "locks all axes" );
00240 this->commands()->addCommand( command( "unlockAllAxes", &MyType::unlockAllAxes, &MyType::unlockAllAxesCompleted, this), "unlock all axes" );
00241 this->commands()->addCommand( command( "prepareForUse", &MyType::prepareForUse, &MyType::prepareForUseCompleted, this), "prepares the robot for use" );
00242 this->commands()->addCommand( command( "prepareForShutdown", &MyType::prepareForShutdown,&MyType::prepareForShutdownCompleted, this), "prepares the robot for shutdown" );
00243 this->commands()->addCommand( command( "addDriveOffset" , &MyType::addDriveOffset, &MyType::addDriveOffsetCompleted, this), "adds an offset to the drive value of axis","axis","axis to add offset to","offset","offset value in rad/s" );
00244 this->commands()->addCommand( command( "initPosition" , &MyType::initPosition, &MyType::initPositionCompleted, this), "changes position value to the initial position","axis","axis to initialize" );
00245 this->commands()->addCommand( command( "changeServo" , &MyType::changeServo, &MyType::changeServoCompleted, this), "Apply the changed properties of the servo loop" );
00246
00247 this->methods()->addMethod( method( "isDriven", &MyType::isDriven, this), "checks wether axis is driven","axis","axis to check" );
00248
00252 for (int i=0;i<NUM_AXES;++i) {
00253 char buf[80];
00254 sprintf(buf,"driveValue%d",i);
00255 driveValue[i] = new ReadDataPort<double>(buf);
00256 ports()->addPort(driveValue[i]);
00257 sprintf(buf,"reference%d",i);
00258 reference[i] = new WriteDataPort<bool>(buf);
00259 ports()->addPort(reference[i]);
00260 sprintf(buf,"positionValue%d",i);
00261 positionValue[i] = new WriteDataPort<double>(buf);
00262 ports()->addPort(positionValue[i]);
00263 sprintf(buf,"output%d",i);
00264 output[i] = new WriteDataPort<double>(buf);
00265 ports()->addPort(output[i]);
00266 }
00267
00271 events()->addEvent( &driveOutOfRange, "Each axis that is out of range throws a seperate event.", "A", "Axis", "V", "Value" );
00272 events()->addEvent( &positionOutOfRange, "Each axis that is out of range throws a seperate event.", "A", "Axis", "P", "Position" );
00273
00277 for (int axis=0;axis<NUM_AXES;++axis) {
00278
00279 servoIntVel[axis] = initialPosition.value()[axis];
00280 previousPos[axis] = initialPosition.value()[axis];
00281 servoIntError[axis] = 0;
00282
00283 _servoGain[axis] = servoGain.value()[axis];
00284 _servoIntegrationFactor[axis] = servoIntegrationFactor.value()[axis];
00285 _servoFFScale[axis] = servoFFScale.value()[axis];
00286 }
00287 }
00288
00289 CRSnAxesVelocityController::~CRSnAxesVelocityController()
00290 {
00291 DBG;
00292
00293 prepareForShutdown();
00294
00295 for (unsigned int i = 0; i < LiAS_NUM_AXIS; i++)
00296 {
00297
00298 delete _axes[i];
00299 #if defined (OROPKG_OS_LXRT)
00300 delete _reference[i];
00301 delete _encoderInterface[i];
00302 #endif
00303 }
00304 #if defined (OROPKG_OS_LXRT)
00305 delete _combined_enable_DOutInterface;
00306 delete _enable;
00307 delete _combined_brake_DOutInterface;
00308 delete _brake;
00309 if (_IP_Encoder_6_task!=0) _IP_Encoder_6_task->stop();
00310
00311 delete _IP_Digital_24_DOut;
00312 delete _IP_Encoder_6_task;
00313 delete _IP_FastDac_AOut;
00314 delete _IP_OptoInput_DIn;
00315 #endif
00316 }
00317
00318 bool
00319 CRSnAxesVelocityController::isDriven(int axis)
00320 {
00321 DBG;
00322 if (!(axis<0 || axis>NUM_AXES-1))
00323 return _axes[axis]->isDriven();
00324 else{
00325 log(Error) <<"Axis "<< axis <<"doesn't exist!!"<<endlog();
00326 return false;
00327 }
00328 }
00329
00330 bool
00331 CRSnAxesVelocityController::startAxis(int axis)
00332 {
00333 DBG;
00334 if (!(axis<0 || axis>NUM_AXES-1))
00335 return _axes[axis]->drive(0.0);
00336 else{
00337 log(Error) <<"Axis "<< axis <<"doesn't exist!!"<<endlog();
00338 return false;
00339 }
00340 }
00341
00342 bool
00343 CRSnAxesVelocityController::startAxisCompleted(int axis) const {
00344 DBG;
00345 return _axes[axis]->isDriven();
00346 return true;
00347 }
00348
00349
00350 bool
00351 CRSnAxesVelocityController::startAllAxes()
00352 {
00353 DBG;
00354 bool result = true;
00355 for (int axis=0;axis<NUM_AXES;++axis) {
00356 result &= _axes[axis]->drive(0.0);
00357 }
00358 return result;
00359 }
00360
00361
00362 bool CRSnAxesVelocityController::startAllAxesCompleted()const
00363 {
00364 bool _return = true;
00365 for(unsigned int axis = 0;axis<NUM_AXES;axis++)
00366 _return &= _axes[axis]->isDriven();
00367 return _return;
00368 }
00369
00370
00371 bool
00372 CRSnAxesVelocityController::stopAxis(int axis)
00373 {
00374 DBG;
00375 if (!(axis<0 || axis>NUM_AXES-1))
00376 return _axes[axis]->stop();
00377 else{
00378 log(Error) <<"Axis "<< axis <<" doesn't exist!!"<<endlog();
00379 return false;
00380 }
00381 }
00382
00383 bool
00384 CRSnAxesVelocityController::stopAxisCompleted(int axis) const {
00385 DBG;
00386 return _axes[axis]->isStopped();
00387 }
00388
00389
00390 bool
00391 CRSnAxesVelocityController::stopAllAxes()
00392 {
00393 DBG;
00394 bool result = true;
00395 for (int axis=0;axis<NUM_AXES;++axis) {
00396 result &= _axes[axis]->stop();
00397 }
00398 return result;
00399 }
00400
00401 bool
00402 CRSnAxesVelocityController::stopAllAxesCompleted() const
00403 {
00404 return true;
00405 }
00406
00407
00408
00409
00410 bool
00411 CRSnAxesVelocityController::unlockAxis(int axis)
00412 {
00413 DBG;
00414 if (!(axis<0 || axis>LiAS_NUM_AXIS-1))
00415 {
00416 _axes[axis]->unlock();
00417 return true;
00418 } else{
00419 log(Error) <<"Axis "<< axis <<"doesn't exist!!"<<endlog();
00420 return false;
00421 }
00422 }
00423
00424 bool
00425 CRSnAxesVelocityController::unlockAxisCompleted(int axis) const {
00426 DBG;
00427 return true;
00428 }
00429
00430
00431 bool
00432 CRSnAxesVelocityController::lockAxis(int axis)
00433 {
00434 DBG;
00435 if (!(axis<0 || axis>LiAS_NUM_AXIS-1))
00436 {
00437 _axes[axis]->lock();
00438 return true;
00439 } else {
00440 log(Error) <<"Axis "<< axis <<"doesn't exist!!"<<endlog();
00441 return false;
00442 }
00443 }
00444
00445 bool
00446 CRSnAxesVelocityController::lockAxisCompleted(int axis) const {
00447 DBG;
00448 return true;
00449 }
00450
00451
00452 bool
00453 CRSnAxesVelocityController::lockAllAxes() {
00454 DBG;
00455 bool result=true;
00456 for (int axis=0;axis<NUM_AXES;axis++) {
00457 _axes[axis]->lock();
00458 }
00459 return result;
00460 }
00461
00462 bool
00463 CRSnAxesVelocityController::lockAllAxesCompleted() const {
00464 DBG;
00465 return true;
00466 }
00467
00468
00469 bool
00470 CRSnAxesVelocityController::unlockAllAxes() {
00471 DBG;
00472 for (int axis=0;axis<NUM_AXES;axis++) {
00473 _axes[axis]->unlock();
00474 }
00475 return true;
00476 }
00477
00478
00479 bool
00480 CRSnAxesVelocityController::unlockAllAxesCompleted() const {
00481 DBG;
00482 return true;
00483 }
00484
00485 bool
00486 CRSnAxesVelocityController::addDriveOffset(int axis, double offset)
00487 {
00488 DBG;
00489 if (!(axis<0 || axis>NUM_AXES-1)) {
00490 #if defined (OROPKG_OS_LXRT)
00491 _axes[axis]->getDrive()->addOffset(offset);
00492 #endif
00493 return true;
00494 } else {
00495 log(Error) <<"Axis "<< axis <<"doesn't exist!!"<<endlog();
00496 return false;
00497 }
00498 }
00499
00500 bool
00501 CRSnAxesVelocityController::addDriveOffsetCompleted(int axis, double ) const {
00502 DBG;
00503 return true;
00504 }
00505
00506
00507 bool
00508 CRSnAxesVelocityController::initPosition(int axis)
00509 {
00510 DBG;
00511 if (!(axis<0 || axis>NUM_AXES-1)) {
00512 _homed[axis] = true;
00513 #if defined (OROPKG_OS_LXRT)
00514 _encoder[axis]->writeSensor(initialPosition.value()[axis]);
00515 servoIntVel[axis] = initialPosition.value()[axis];
00516 previousPos[axis] = servoIntVel[axis];
00517 #else
00518 #endif
00519 return true;
00520 } else {
00521 log(Error) <<"Axis "<< axis <<"doesn't exist!!"<<endlog();
00522 return false;
00523 }
00524 }
00525
00526 bool
00527 CRSnAxesVelocityController::initPositionCompleted(int) const {
00528 DBG;
00529 return true;
00530 }
00531
00532
00533 bool CRSnAxesVelocityController::changeServo() {
00534 for (int axis=0;axis<NUM_AXES;++axis) {
00535 servoIntVel[axis] *= _servoGain[axis] / servoGain.value()[axis];
00536 servoIntVel[axis] *= _servoIntegrationFactor[axis] / servoIntegrationFactor.value()[axis];
00537 _servoGain[axis] = servoGain.value()[axis];
00538 _servoIntegrationFactor[axis] = servoIntegrationFactor.value()[axis];
00539 }
00540 return true;
00541 }
00542
00543 bool CRSnAxesVelocityController::changeServoCompleted() const {
00544 DBG;
00545 return true;
00546 }
00547
00548
00553 bool CRSnAxesVelocityController::startup() {
00554 DBG;
00555
00556 for (int axis=0;axis<NUM_AXES;++axis) {
00557 servoIntVel[axis] = _axes[axis]->getSensor("Position")->readSensor();
00558 previousPos[axis] = servoIntVel[axis];
00559 }
00560 return true;
00561 }
00562
00566 void CRSnAxesVelocityController::update() {
00567 #if !defined (OROPKG_OS_LXRT)
00568 for (int axis=0;axis<NUM_AXES;axis++) {
00569 double measpos = signAxes.value()[axis]*_axes[axis]->getSensor("Position")->readSensor();
00570
00571
00572
00573
00574
00575
00576
00577 double setpoint = signAxes.value()[axis]*driveValue[axis]->Get();
00578 _axes[axis]->drive(setpoint);
00579 positionValue[axis] ->Set(measpos);
00580 output[axis]->Set(setpoint);
00581 bool ref = measpos > initialPosition.value()[axis];
00582 if (axis==3) ref = !ref;
00583 reference[axis]->Set(ref);
00584 }
00585 #else
00586 double dt;
00587
00588 if (servoInitialized) {
00589 dt = TimeService::Instance()->secondsSince(previousTime);
00590 previousTime = TimeService::Instance()->getTicks();
00591 } else {
00592 dt = 0.0;
00593 }
00594 previousTime = TimeService::Instance()->getTicks();
00595 servoInitialized = true;
00596
00597 double outputvel[NUM_AXES];
00598
00599 for (int axis=0;axis<NUM_AXES;axis++) {
00600 double measpos;
00601 double setpoint;
00602
00603 measpos = signAxes.value()[axis]*_encoder[axis]->readSensor();
00604 positionValue[axis] ->Set( measpos );
00605 if((
00606 (measpos < lowerPositionLimits.value()[axis])
00607 ||(measpos > upperPositionLimits.value()[axis])
00608 ) && _homed[axis]) {
00609
00610 positionOutOfRange(axis, measpos);
00611 }
00612 if (_axes[axis]->isDriven()) {
00613 setpoint = driveValue[axis]->Get();
00614
00615 servoIntVel[axis] += dt*setpoint;
00616 double error = servoIntVel[axis] - measpos;
00617 servoIntError[axis] += dt*error;
00618 double deriv;
00619 if (dt < 1E-4) {
00620 deriv = 0.0;
00621 } else {
00622 deriv = servoDerivTime.value()[axis]*(previousPos[axis]-measpos)/dt;
00623 }
00624 outputvel[axis] = _servoGain[axis]*
00625 (error + _servoIntegrationFactor[axis]*servoIntError[axis] + deriv)
00626 + _servoFFScale[axis]*setpoint;
00627
00628 double offsetsign;
00629 if (previousPos[axis] < measpos) {
00630 offsetsign = 1.0;
00631 } else if ( previousPos[axis] > measpos ) {
00632 offsetsign = -1.0;
00633 } else {
00634 offsetsign = outputvel[axis] < 0.0 ? -1.0 : 1.0;
00635 }
00636 outputvel[axis] += offsetsign*offset.value()[axis];
00637 } else {
00638 outputvel[axis] = 0.0;
00639 }
00640 previousPos[axis] = measpos;
00641 }
00642 for (int axis=0;axis<NUM_AXES;axis++) {
00643
00644 if (outputvel[axis] < -driveLimits.value()[axis]) {
00645
00646
00647
00648 outputvel[axis] = -driveLimits.value()[axis];
00649 }
00650 if (outputvel[axis] > driveLimits.value()[axis]) {
00651
00652
00653
00654 outputvel[axis] = driveLimits.value()[axis];
00655 }
00656 output[axis]->Set(outputvel[axis]);
00657 _axes[axis]->drive(signAxes.value()[axis]*outputvel[axis]);
00658
00659 reference[axis]->Set( _reference[axis]->isOn());
00660 }
00661 #endif
00662 }
00663
00664 bool CRSnAxesVelocityController::prepareForUse() {
00665 DBG;
00666 return true;
00667 }
00668
00669 bool
00670 CRSnAxesVelocityController::prepareForUseCompleted() const {
00671 DBG;
00672 return true;
00673 }
00674
00675
00676 bool CRSnAxesVelocityController::prepareForShutdown() {
00677 DBG;
00678 return true;
00679 }
00680
00681 bool
00682 CRSnAxesVelocityController::prepareForShutdownCompleted() const {
00683 DBG;
00684 return true;
00685 }
00686
00687
00691 void CRSnAxesVelocityController::shutdown() {
00692 DBG;
00693 prepareForShutdown();
00694
00695 }
00696
00697
00698 }
00699
00700