What do you use to break out of a control loop?

We were doing our class and implementing P controllers and was wondering the best method to break out of a control loop when it is not a background task and you ant to move on to the next step? This would work for a variety of control loop types I think.

  1. We’ve used within an absolute value of an error with a timer kick out but if you overshoot, then you are not guaranteed to still be in that range because the first time you see it, you kick out and the robot could still be carried by momentum a bit further.

  2. Timers for how long you normally take that step and settle down only as it allows for the correction. This seems like it wastes time and could be dependent upon battery voltage.

The timer seems to allow for settling but you could still be oscillating or have overshot. Has anyone done anything else trickier?

a) look for the rate of settling and change in error to be sufficiently low over a period of time?
b) look for being within an absolute value range of error for a minimum period of time?
c) wait until you are pretty much exactly where you want to be and kick out with a timer when you are not there in time? (like #1 above but a really narrow range)

If you use a superloop state machine then you never really need to “break out”. You just execute the P loop as a one of many tasks in a round-robin.

Yes, this is the typical way to have the background tasks do the work, but the main auton task needs to know when to move on to the next thing. First occurrence of the limit range may just be on the way to overshoot, and not settled into your target.

We used #1 this last year, and #2 the year before. Just looking at error seemed to work fine; your position controller just needs to be tuned so that you don’t get any overshoot (which admittedly does lead to erring on the slow side). We used (a) for our flywheels in auton last year; using just a couple readings rather than one (so only several more milliseconds) seemed to make a big difference.

Theoretically waiting for velocity to be 0 and absolute value of error to be less than some threshold should work the best perhaps, but deriving the velocity of your drive at low speeds requires a slower update time.

Technically, if you are tuned well your P only loop should not overshoot. The big issue with this is deadzone in the motors. If you are using a P only loop you will be dead from -20 to 20, so fixing this will cause oscillation.

I used “Happy Points”


line 638

Thanks Cody, but with the 100 delay wouldn;t that take a long time to settle in?

You look for 50 times being in happy for each (with no reset to 0 if you go back outside the range)

how well did this work out?

Lines 725-741

		// === PREFORM CHECKS =========================================================

			if(within(targetX - x, -.1, .1))	{ samplesX++; happyX = true; } else happyX = false;
			if(within(targetY - y, -.1, .1))	{ samplesY++; happyY = true; } else happyY = false;
			if(within(targetHeading - heading, -1, 1)) { samplesR++; happyR = true; } else happyR = false;

			// Exit condition
			if(samplesX >= 50 && samplesY >= 50 &&  samplesR >= 50 && happyX && happyY && happyR) {

				goGroup(a->left,		0);				goGroup(a->right,	0);
				goGroup(b->left,		0);				goGroup(b->right,	0);

				print("ENDING GO-HOLO-X-CL with a return of true\r\n");

				return true;

			}

However you wait 100 ms between loops??? So 50 times would be 5 seconds to settle?

Line 799 - delay 100ms?


			delay(100);

It DEFINITELY was slow. But we had half decent success.

I haven’t written significant robot code since. I’ll likely take a better stab at this once I get parts / a reason to build another robot.

I generally use #2 (time-based). It might waste a small amount of time, but I’ve found it tends to be more accurate. The other advantage is that if your robot never makes it to a low error state, it won’t be stuck forever trying to get there. I believe the other BNS programmers tend to use the other method however. I might experiment with something more complicated this year.

My team uses a combination of both. We check the error within a threshold over a series of timesteps (I think we repeat 5 times if the error is in the threshold). However, we also use a timeout to help prevent issues with stalling motors and bad IME readings (we still get a lot of both, however). This is made much easier by RobotC’s multitasking.
Our control loops go in a separate task. No our autonomous function, we have a custom blockLim function to block the main program from progressing under certain conditions. It looks like this:


void blockLim(bool cond, int timeOut) {
  int time = nSysTime;

  while(!cond && (nSysTime - time < timeOut))
    wait1Msec(20);
}

As a parameter for it, you can pass in multiple conditions, such as multiple drive PIDs on target. That looks like this:


blockLim(!(lPid.isOnTarg && rPid.isOnTarg), 3000);