View unanswered posts | View active topics It is currently Thu Jul 31, 2014 7:40 am






Reply to topic  [ 10 posts ] 
runStateIdle problem 
Author Message
Rookie

Joined: Sun Sep 18, 2011 7:58 pm
Posts: 12
Location: Porto, Portugal
Post runStateIdle problem
Hello,

I’m new in RobotC and never programed in C so I’m probably missing something here.
I had some code working fine but after the last update (3.08) I believe something changed.

Mainly I need to set the direction wheel of my robot to look straight.
The direction motor has two symmetrical limiters to stop turning.
In simple words, the code tells the motor to run left until the limit, then right until the limit, then it calculates the middle and it uses the nMotorEncoderTarget to put it straight.
This code always worked fine but now the motor does not get in idle state. When the motor reaches the target it start trembling and keeps waiting for the idle state.
Before, when the motor reached the target the nMotorRunState get idle and everything works fine.
I also noticed that now I need to set the motor power much higher then before when looking for the limits.
Can someone help me?
Thank you.

The motor configuration is this:
Code:
#pragma config(Motor,  motorB,          STEERING,      tmotorNormal, openLoop, encoder)

and this is the code:
Code:
//////////////////////////////////////////////////////////////////////////////////////////////////////
// 9 Reset Direction Motor at MidPoint function
//////////////////////////////////////////////////////////////////////////////////////////////////////
void PIDResetMidPointWHL(int dir_Speed)
{
  int r1Encoder_B, r2Encoder_B = 1;                           // Declare local variables to store Encoders Readings
  int lim_R, lim_E;                                           // Declare local variables to store Limits
  int wait_t = (100-abs(dir_Speed))/20+1;                     // Calculate Wait time in function of direction motor speed
  int midPoint;                                               // Declare variable to store Direction MidPoint
  nMotorEncoder[STEERING]=0;                                  // Resets encoder
  //First cicle
  motor[STEERING] = dir_Speed;                                // Start moving motor
  while (r2Encoder_B != r1Encoder_B)                          // Until motor is moving
  {
    r1Encoder_B = nMotorEncoder[STEERING];                    // Read encoder
    wait10Msec(wait_t);                                       // Wait to get diferent encoder readings
    r2Encoder_B = nMotorEncoder[STEERING];                    // Read encoder again after some time
  }
  PlayTone(200,5);                                            // Beep when reachs the limit
  lim_R = r2Encoder_B;                                        // Store limit value
  //Second cicle
  dir_Speed = dir_Speed*-1;                                   // Invert speed variable value
  motor[STEERING] = dir_Speed;                                // Invert motor direction
  wait10Msec(20); r1Encoder_B = nMotorEncoder[STEERING];      // Wait to get diferent encoder readings and read encoder
  wait10Msec(wait_t); r2Encoder_B = nMotorEncoder[STEERING];  // Wait to get diferent encoder readings and read encoder
  while (r2Encoder_B!=r1Encoder_B)                            // Until motor is moving
  {
    r1Encoder_B = nMotorEncoder[STEERING];                    // Read encoder
    wait10Msec(wait_t);                                       // Wait to get diferent encoder readings
    r2Encoder_B = nMotorEncoder[STEERING];                    // Read encoder again after some time
  }
  PlayTone(200,5);                                            // Beep when reachs the limit
  lim_E = r2Encoder_B;                                        // Store limit value
  // Calculation and set to middle
  midPoint = (lim_R + lim_E)/2;                               // Calculate midpoint
  if (midPoint == 0) midPoint = 1;                            // Changes the value 0 because it is not allowed
    midPoint = midPoint - lim_E;                              // Calculate MidPoint
  nMotorEncoderTarget[STEERING] = midPoint;                   // Set target value for motor direction
  motor[STEERING] = 80;                                       // Start moving motor to reach target fast
  while(nMotorRunState[STEERING] != runStateIdle)             // Waint until motor reach target PROBLEMS HERE MOTOR TREMBLE AND PROGRAM STOP
  {
  }
  // Setup motor for control
  nMotorPIDSpeedCtrl[STEERING]= mtrSpeedReg;                  // Activate PID
  nMotorEncoder[STEERING]=0;                                  // Reset encoder at correct position
  WHL_Dir_tick = 0;                                           // Reset wheel Direction
  PlayTone(250,10);                                           // Beep at the end
}


Tue Mar 20, 2012 7:29 pm
Profile
Moderator
Moderator
User avatar

Joined: Wed Mar 05, 2008 8:14 am
Posts: 3165
Location: Rotterdam, The Netherlands
Post Re: runStateIdle problem
What's the usual difference between the two encoders? If it is very small, the PID controller may not power the motors enough to really have enough "space" to ramp up and ramp down to the required encoder count.

- Xander

_________________
| Professional Conduit of Reasonableness
| (Title bestowed upon on the 8th day of November, 2013)
| My Blog: I'd Rather Be Building Robots
| ROBOTC 3rd Party Driver Suite: [Project Page]


Wed Mar 21, 2012 2:30 am
Profile WWW
Rookie

Joined: Sun Sep 18, 2011 7:58 pm
Posts: 12
Location: Porto, Portugal
Post Re: runStateIdle problem
Thank you Xander,
The difference between the encoders readings is big, if it starts near the middle position the motor turn left 140º and then right the double distance. The value of the midPoint variable used to set the nMotorEncoderTarget is usually near -145.
Also, the PID controller is off at this point. The PID controller is turned on with the nMotorPIDSpeedCtr command only after finding the middle position and after reset the encoder to 0.
Anyway I tried also with PID on and the result is the same.
I’m also sure that this code worked normally before.
Thanks,
rbtx


Wed Mar 21, 2012 12:51 pm
Profile
Moderator
Moderator
User avatar

Joined: Wed Mar 05, 2008 8:14 am
Posts: 3165
Location: Rotterdam, The Netherlands
Post Re: runStateIdle problem
Well, I know one of the changes they made in 3.08 has to do with the run to encoder code. Perhaps it would be good to send a mail to support@robotc.net with a description of your problem, the shortest amount of code that can reproduce it and a link to this thread. That will create a ticket.
Be sure to mention the current version of ROBOTC you're using and that it worked fine with the previous version.

- Xander

_________________
| Professional Conduit of Reasonableness
| (Title bestowed upon on the 8th day of November, 2013)
| My Blog: I'd Rather Be Building Robots
| ROBOTC 3rd Party Driver Suite: [Project Page]


Wed Mar 21, 2012 1:02 pm
Profile WWW
Rookie

Joined: Sun Sep 18, 2011 7:58 pm
Posts: 12
Location: Porto, Portugal
Post Re: runStateIdle problem
Ok,

I just did it.
The simplified code is also on the bottom.
You just need to plug a motor to port A with something that does not let it do a full revolution.
The motor will turn to one side then the other and then runs to the middle and tremble, but should stop instead.
I'm using Robot C 3.08 and NXT_0912.rfw firmware.

Thanks,
rbtx


Code:
#pragma config(Motor,  motorA,          DIRSONAR,      tmotorNormal, openLoop, encoder)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//




//////////////////////////////////////////////////////////////////////////////////////////////////////
// 2 Reset Sonar Motor at MidPoint function
//////////////////////////////////////////////////////////////////////////////////////////////////////
void PIDResetMidPointSNR(int snr_Speed)
{
  int r1Encoder_B, r2Encoder_B = 1;                           // Declare local variables to store Encoders Readings
  int lim_R, lim_E;                                           // Declare local variables to store Limits
  int wait_t = ((100-abs(snr_Speed))+1)/20+1;                 // Calculate Wait time in function of direction motor speed
  int midPoint;                                               // Declare variable to store Direction MidPoint
  nMotorEncoder[DIRSONAR]=0;                                  // Resets encoder
  //First cicle
  motor[DIRSONAR] = snr_Speed;                                // Start moving motor
  while (r2Encoder_B != r1Encoder_B)                          // Until motor is moving
  {
    r1Encoder_B = nMotorEncoder[DIRSONAR];                    // Read encoder
    wait10Msec(wait_t);                                       // Wait to get diferent encoder readings
    r2Encoder_B = nMotorEncoder[DIRSONAR];                    // Read encoder again after some time
  }
  PlayTone(200,5);                                            // Beep when reachs the limit
  lim_R = r2Encoder_B;                                        // Store limit value
  //Second cicle
  snr_Speed = snr_Speed*-1;                                   // Invert speed variable value
  motor[DIRSONAR] = snr_Speed;                                // Invert motor direction
  wait10Msec(20); r1Encoder_B = nMotorEncoder[DIRSONAR];      // Wait to get diferent encoder readings and read encoder
  wait10Msec(wait_t); r2Encoder_B = nMotorEncoder[DIRSONAR];  // Wait to get diferent encoder readings and read encoder
  while (r2Encoder_B!=r1Encoder_B)                            // Until motor is moving
  {
    r1Encoder_B = nMotorEncoder[DIRSONAR];                    // Read encoder
    wait10Msec(wait_t);                                       // Wait to get diferent encoder readings
    r2Encoder_B = nMotorEncoder[DIRSONAR];                    // Read encoder again after some time
  }
  PlayTone(200,5);                                            // Beep when reachs the limit
  lim_E = r2Encoder_B;                                        // Store limit value

  // Set motor at midPoint
  midPoint = ((lim_R + lim_E)/2)- lim_E;                      // Calculate distance to midpoint
  if (midPoint == 0) midPoint = 1;                            // Changes the value 0 because it is not allowed
  nMotorEncoderTarget[DIRSONAR] = midPoint;                   // Set target value for motor direction
  motor[DIRSONAR] = 100;                                      // Start moving motor at maximum to reach target
  while(nMotorRunState[DIRSONAR] != runStateIdle)             // Waint until motor reach target
  {
  }
  motor[DIRSONAR] = 0;
  // Setup motor for control
  nMotorPIDSpeedCtrl[DIRSONAR]= mtrSpeedReg;                  // Activate PID
  nMotorEncoder[DIRSONAR]=0;                                  // Reset encoder at correct position and with PID on
  bFloatDuringInactiveMotorPWM = false;                       // Motors will not float when off NOT TO EXECUTE BEFORE THE PID FUNCTION
  PlayTone(250,10);                                           // Beep at the end
}

task main()
{
  PIDResetMidPointSNR(60);

  while (true)
  {
  }
}


Wed Mar 21, 2012 1:55 pm
Profile
Rookie

Joined: Sun Sep 18, 2011 7:58 pm
Posts: 12
Location: Porto, Portugal
Post Re: runStateIdle problem
I have some new information here.
I have been working in my portable computer with RobotC 3.08 but I also have a desktop with RobotC and in that one I still have the 3.05 version.
So I downgraded the firmware on the NXT and downloaded the code from there and it works fine.
Definitely it's something in this new version.
Hope it helps.
rbtx


Wed Mar 21, 2012 6:39 pm
Profile
Creator
Creator

Joined: Fri Feb 09, 2007 9:21 am
Posts: 614
Post Re: runStateIdle problem
If the run to encoder function does not precisely stop on the target encoder position, then the algorithm tries to move it to the target position. This is what causes the 'trembling'. Say it overshoots by 1 encoder tick. And then moving it back to the target position ends up stopping at a one tick undershoot. And then it repeats and overshoots by one tick. And so on.... So the motor trembles and never reaches steady / idle state.

Prior to 3.08, the "has motor reached target positiion" criteria was within two (or three) encoder ticks of the actual position.

The "close enough" criteria was tightened in 3.08 where I recall it is now 1 or 2 ticks. This was in response to a trouble ticket complaining that it wasn't accurate enough. In hindsight, it appears that this was a mistake.

I recall that there is also a "tremble stop" check which stops after 'NN' very small oscillations. It won't work if one of the oscillations exceeds the "nearly there" limit which is slightly larger than the "close enough" limit.

The root cause is that we're trying to get more accuracy out of a motor than the encoder provides. If you're trying to get accuracy to within a single degree then the encoder should probably be accurate to 0.1 degrees.

Complicating the problem are the following factors:
  • There's a 1 to 4 millisecond lag from when the NXT CPU wants to change motor speed until a message is sent to the small 8-bit slave AtMega CPU that actually controls the motor H-bridges.
  • The motor itself will take some time to adjust speed because of its momentum. For example, if it is moving and the new speed is zero, it may still move a few encoder ticks before it stops.

I'll look at the implementation and changes in 3.08 to see if there is a reasonable change that can be done and post of this later.


Fri Mar 23, 2012 9:39 am
Profile
Rookie

Joined: Sun Sep 18, 2011 7:58 pm
Posts: 12
Location: Porto, Portugal
Post Re: runStateIdle problem
Ok,
Thanks for the description.
Now it’s easier to find a workaround.


Fri Mar 23, 2012 9:59 am
Profile
Creator
Creator

Joined: Fri Feb 09, 2007 9:21 am
Posts: 614
Post Re: runStateIdle problem
I tried to duplicate the problem using an unloaded motor. And I didn't have physical stops so I just stopped it by hand twice. I could not duplicate the problem.

I suspect your configuration has motor in direct drive mode to a relatively "heavy" SONAR sensor configuration that generates a lot of momentum. So it is really hard / impossible to make small movements of just a few encoder ticks. The situation is as follows:
  • ROBOTC provides power to the motor to get it to start turning.
  • if the provided power level is inadequate to get motor moving, firmware will gradually increase power level to get the motor to turn.
  • Once motor is turning, firmware can adjust the power to try to stop it at the target positiion.
The challenge is that if the target movement is very small (as in the tremble situation) and the power level to get it to start moving is high, to get it to stop at the target position does not simply require stopping power but actually requires negative power to rapidly stop the motor. Firmware will do this.

From http://www.philohome.com/nxtmotor/nxtmotor.htm you'll see the gear ratio of the NXT motors is 1:48. So 1 encoder tick represents about 1/8 of a revolution of the internal DC motor. As I think about it, getting this resolution of position control on a small DC brushed motor is pretty amazing.

You'd get more precise control over your "arm" by adding a level of additional mechanical gearing between the motor and your arm.


Fri Mar 23, 2012 1:41 pm
Profile
Rookie

Joined: Sun Sep 18, 2011 7:58 pm
Posts: 12
Location: Porto, Portugal
Post Re: runStateIdle problem
Ok,
Thank you for the suggestion,
I just need a simple mechanical solution and attach the sonar sensor directly to the motor is the cleanest and it has enough precision for my requirements.
Sure I will workaround.
It's only strange you couldn't replicate it.
I attached a small video with the NXT, a motor and the code I submitted.


Attachments:

[ Play Quicktime file ] IMG_1897.MOV [ 1.09 MiB | Viewed 2608 times ]
Fri Mar 23, 2012 2:01 pm
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 10 posts ] 

Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  



Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.