[ODE] Yet another question about servo emulation
Shamyl Zakariya
zakariya at earthlink.net
Sat May 24 11:32:02 2003
All,
I'm pretty new to ODE, and up till now, I've had a fair amount of
success, thanks entirely to reading this list!
In my project, I need to model servo behavior, such that I can say in
my code, more or less something like this:
someServo->setPosition( angle )
I gather several people have need for similar functionality. Now, to do
this, I wrote a servo class which basically creates a motor and
attaches it to an existing hinge and the two bodies connected by this
hinge. In the interest of simplicity, I've made the motor a dAMotorUser
type, following the axis of the hinge its controlling. I'm using a
straightforward approach of using a feedback loop, e.g., it checks what
the current angle of the hinge is, and corrects the velocity (and
direction) of the motor to bring it closer to the desired position,
every timestep. When it gets to the desired position, it sets the
motor's velocity to 0, and ups the dParamFMax to hold it there.
here's a snippet of the relevant parts of my servo class
ServoMotor::ServoMotor( dWorldID world, dJointID hinge, dBodyID
parentBody, dBodyID childBody )
:_hinge(hinge), _motor(0), _parent(parentBody), _child(childBody),
_min( toRadians( -178 )), _max(toRadians(178)), _current(0),
_desired(0),
_speed(1.0), _force(1000.0), _verbose(false)
{
_motor = dJointCreateAMotor( world, 0 );
dJointAttach( _motor, _parent, _child );
dJointSetAMotorMode( _motor, dAMotorUser );
dJointSetAMotorNumAxes( _motor, 1 );
dVector3 axis;
dJointGetHingeAxis( _hinge, axis );
// set joint zero, axis relative to first body, along axis of hinge
dJointSetAMotorAxis( _motor, 0, 1, axis[0], axis[1], axis[2]);
dJointSetAMotorParam( _motor, dParamFMax, _force );
dJointSetHingeParam( _hinge, dParamLoStop, toRadians( -179.0 ) );
dJointSetHingeParam( _hinge, dParamHiStop, toRadians( 179.0 ) );
dJointSetAMotorParam( _motor, dParamFudgeFactor, 0.5 ); //arbitrary?
dJointSetHingeParam( _hinge, dParamStopERP, 0.5 ); //arbitrary?
}
void ServoMotor::setPosition( dReal radians, dReal speed )
{
if (radians < _min) radians = _min;
else if (radians > _max) radians = _max;
_desired = radians;
if (speed < 0.0) speed = 0.0;
else if (speed > 1.0) speed = 1.0;
_speed = 0.1 + (0.9 * speed); //speed from 0.1 -> 1.0
}
dReal ServoMotor::getCurrentPosition( void )
{
_current = dJointGetHingeAngle( _hinge );
return _current;
}
void ServoMotor::step( void )
{
dReal da = _desired - getCurrentPosition(); //magnitude and direction
to turn
if (fabs(da) > toRadians( 1.0 ))
{
dReal velocity = da * _speed;
if (_verbose)
{
printf("ServoMotor::step()\tda: %.2f, velocity: %.2f\n", da,
velocity);
dumpState();
}
dJointSetAMotorParam( _motor, dParamFMax, _force );
dJointSetAMotorParam( _motor, dParamVel, velocity );
}
else
{
if (_verbose)
{
printf("ServoMotor::step()\tLOCKDOWN\n");
dumpState();
}
//stop motor, will hold via force setting
dJointSetAMotorParam( _motor, dParamFMax, dInfinity );
dJointSetAMotorParam( _motor, dParamVel, 0 );
}
}
Now, by and large, this works beautifully. The servos move to the angle
I request, and hold that position just fine, even when bearing a load.
The trouble is, if something occurs which "knocks" the servo out of
position (like a collision, or the structure falling over) the servo(s)
freak out. It's hard to describe, but the limb just starts "flailing"
about swinging back and forth, and then, if it's really severe, the
simulation explodes. I've tried to track the position of the servo when
its wigging and it seems that it's trying to go to the position it
wants to be at, but it just doesn't succeed. Perhaps excessive torque
force is building up?
I'd really appreciate it if somebody here can point out what's wrong
with my code. Perhaps there's a logical error in the feedback loop?
perhaps I'm not fully understanding the dynamics of the way motors
work, or how to use ERP or CFM adjustments. Note that I read about the
problems with setting stops in powered joints, but my stops are only to
prevent the joint from going past -PI and +PI (which is realistic for a
real servo, and keeps the math cleaner). When I didn't have stops in
place, the system blew up much more frequently.
Please, any help would be greatly appreciated.
Also, one more thing -- if anybody can give me some notion of how to
get the torque force a joint/motor is experiencing while holding
position I'd also appreciate it. I gather the function
dJointGetFeedback() is the way to go, but I haven't tried it yet. And
I'm sufficiently new to this API that I'm uncertain about how to go
about doing this correctly.
Shamyl Zakariya
"this is, after all, one of those movies where people spend a great
deal of time looking at things and pointing."
From a review of _Fantastic Voyage_