Motor Control - IF Statement Help Needed

Hello,

After many hours and days trying to sort out what doesn't work with my code, I'm seeking help.

The code below is a small part of a larger project and it is the last remaining part of a sketch to make things work. In summary, I am comparing two int variables to determine if a motor should turn clockwise or counter clockwise. The variables are cHeading which is the current Heading of the motor as derived from an external voltage, and aBearing which is an Analog bearing taken from a reference in code. When these values are compared to each other, the motor turns clockwise or counter clockwise by switching on a related relay and when the values are equal, the relays open and the motor stops. Each relay has a corresponding "brake" relay which must be on at the same time (sort of a safety mechanism).

Either of these IF statements work, but I cannot find a way to make them work together. For example, the CCW code will go to the proper position and when the values are equal and the motor stops because both relays open. But using both IF statements does not work because they cycle the relays for both CW and CCW.

I have tried nested IF statements, IF Else statements, and arranging the various actions in a different order, all to no avail.

Thanks for any and all advice.

////////////////////COUNTER CLOCKWISE RELAY/////////////////////////////
  // High = Relay Off - Low = Relay On
  //if cHeading is greater than aBearing then turn on the ccw and brake relay until bearing and heading are the same
  if (cHeading > aBearing) {
    digitalWrite(relay2, LOW);  //BRAKE RELAY ON
    digitalWrite(relay4, LOW);  //CCW RELAY ON
    Serial.println("cHeading is GREATER THAN aBearing SO TURN CCW ");
  } else {
    if (cHeading <= cHeading)      //
    digitalWrite(relay4, HIGH);  //CCW RELAY OFF
    digitalWrite(relay2, HIGH);    //BRAKE RELAY OFF
  }
  ////////////////////////CLOCKWISE RELAY/////////////////////////////////
  //if cHeading is less than aBearing then turn on the cw relay until bearing and heading are the same
  if (cHeading < aBearing) {
    digitalWrite(relay1, LOW);  //BRAKE RELAY ON
    digitalWrite(relay3, LOW);  //CW RELAY ON
    Serial.println("cHeading is LESS THAN aHeading SO TURN CW ");
  } else {
    if (cHeading >= aBearing)      //
    digitalWrite(relay3, HIGH);  //CW RELAY OFF
    digitalWrite(relay1, HIGH);    //BRAKE RELAY OFF
  }


This is a common issue, I think, and could be solved by introducing some hysteresis.

Google it, but it's seen in your home thermostat.

The heat does not go on until it is colder by some margin than the setting.

The heat does not go off until it is warmer by some margin than the setting.

Here, by analogy, you should be happy after arriving at the desired angle, or bearing, or whatever, and then then stay there until the error is large enough to warrant further motor activity.

It may be that the desired angle requires more motion in the same direction, so in this sense it is not like the home heating sitch, where the temp starts to go down as soon as you stop heating, but I think the concept will work.

Go until the angle is satisfactory. Stop, wait for the angle to become large enough (that will require tuning and your own idea of how much error is tolerable) and go once again in the direction which will correct for the observed error.

HTH

a7

Well you're telling it two different things for each direction. Try something like this pseudo:


if (cHeading > aBearing) {
    // turn one way
}
else if(cHeading < aBearing) {
  // turn the other way
} 
else {
  // if it's not greater or less then it must be equal
  // so stop turning
}

See with this, if cHeading is less than aBearing then both the first else and the second if both apply. less than is not greater than, so the first else part applies. And then less than IS less than so the if part applies in the second case. And then it's all vice versa if cHeading is greater than aBearing.

It's frightening, it makes sense, THX

So

  if (cHeading > aBearing) {

    // turn one way

      Serial.println("cHeading is GREATER THAN aBearing SO TURN CCW ");

      digitalWrite(relay2, LOW);   //BRAKE RELAY ON
      digitalWrite(relay4, LOW);   //CCW RELAY ON

      digitalWrite(relay3, HIGH);  //CW RELAY OFF
      digitalWrite(relay1, HIGH);  //BRAKE RELAY OFF
  }

And the other block would turn on and off the opposite relays set.

The do nothing part might be a place to should turn off both relay sets.

This still might benefit from not reacting until the error is more than 0.0001…

@shawninpaso since you posted a snippet, we would have to read carefully to see if you ever said what type the variables are that enter into your decisions.

a7

I am most grateful for the advice from both of you. The code examples provided work as expected but the motor doesn't stop when it is supposed to, even though the sequence of actions with the relays are occurring as they should. Hence, I suspect Alto is on to something with the hysteresis issue.

The values used are 3 digit integers rounded to whole values, e.g. 001 to 185. DC voltage is taken from the motor controller and converted to these values and compared against a static list of values. I'll have to read up on the most effective means to add hysteresis, otherwise I could likely alter the static values which should provide a poor man's version of hysteresis (hope that makes sense).

Sure wish I could post a video here so you could see and hear all of the noise with the motor going back and forth. :slight_smile:

I truly appreciate your help and knowledge !

I don't know that the source of the numbers is important. But comparing integers means a small difference would make the motor move and it seems might mean frequent reversal.

This would spend on the mechanical properties.

If you wrote

if ((cHeading - aBearing) > someTheshhold)  {

someThreshold could be set to inhibit motoring until the error was larger. And a symmetrical expression for motoring the other way.

On the same hand, or is it the other, you may have a control problem that would benefit from a more sophisticated algorithm. This is only something I can name one type of, the PID control loop. PID loops are employed in devices I use but design and tuning of them is outside my ability and experience.

I can't help much anymore, and it seems anyone who could would have to know more about the system you are controlling and your own goals.

a7

You might want to re-think that...as it reads, one relay "brake relay" would short out the corresponding direction.

is this a typo? won't the condition always be true due to ==

is the following

intended to be

  } 
  else  if (cHeading <= cHeading)   {
      digitalWrite(relay4, HIGH);  //CCW RELAY OFF
      digitalWrite(relay2, HIGH);    //BRAKE RELAY OFF
  }

should the logic be

  if (cHeading > aBearing) {
    digitalWrite(relay2, LOW);  //BRAKE RELAY ON
    digitalWrite(relay4, LOW);  //CCW RELAY ON
    Serial.println("cHeading is GREATER THAN aBearing SO TURN CCW ");
  }
  else {
    digitalWrite(relay4, HIGH);  //CCW RELAY OFF
    digitalWrite(relay2, HIGH);    //BRAKE RELAY OFF
  }
 
  if (cHeading < aBearing) {
    digitalWrite(relay1, LOW);  //BRAKE RELAY ON
    digitalWrite(relay3, LOW);  //CW RELAY ON
    Serial.println("cHeading is LESS THAN aHeading SO TURN CW ");
  } 
  else {
    digitalWrite(relay3, HIGH);  //CW RELAY OFF
    digitalWrite(relay1, HIGH);    //BRAKE RELAY OFF
  }

Thanks for your time and help, Alto. I will explore the threshold idea later today. It certainly seems spot on as the current code actuates the relays correctly but still "slips by" when the motor should come to a full stop.

Much appreciated.

Hey Blue -

Thanks, my wording wasn't exactly the best. My failure was trying to abbreviate a longer description of what's going on. The "motor" an antenna rotator and it's associated control box. The control box has three large buttons. The left button must be held down to turn the rotator counter clockwise, the right button make it go clockwise but the middle button must be held down before and with either the left or right button. The middle button releases a brake which keeps the rotator in position and thus won't move until the brake is released. I have hard wired four relays to the control box and found that I needed to use two relays for the brake button to emulate what is done within the control box.

Hope that makes sense, thanks for your help.

Hi gc, I sure appreciate your help.

Yes, it's a colossal cut and paste mistake. Thanks for pointing it out as I have caused others to deal with it. Reminds me of a lecture in school a very long time ago about one of the first rocket launches in the USA that failed because a programmer used a comma where he should have used a semi-colon.

Thanks for the sharp observation, my code had already been corrected so it wasn't part of the original issue.

So gc, I just tried the code you so kindly provided.

The logic performs better, but the motor still slips by the intended stopping point, and then goes back and forth seeking it while the relays actuate accordingly. The thing is, this is the first time where the motor has stopped more than once when cHeading is equal to aBearing (which seems huge to me).

Looking at the serial monitor for the data and the voltage values coming off the motor controller, it appears that the cHeading values are lagging behind a bit (slow to "catch up"), which may be the source of the issue, but I'm not completely sure just yet. As you've seen, alto suggested the 'someThreshold' value which may correct for the lagging values but I have not attempted that yet.

I'm grateful for your super help!

someone suggested hysterisis.
maybe something like following, Margin set to 1 deg(?) would be more stable but perhaps not stop the motor precisely

    if (cHeading > (aBearing * Margin)) {
    }
    else  if (cHeading == aBearing) {
    }

i don't see how PID can be used with a binary output, but perhaps the control needs to engage the brake before reaching the target. when to apply the brake would depend on the rate of change and knowing the effectiveness (rate) of the brake

why would there be a CW and CCW brake? not just a single brake?

One issue that you're not accounting for is inertia. The motor won't stop moving instantly when you apply the brakes.

Wow, that's great - I'll give it a try this evening.

There is only one brake, but the manner in which it is wired in the control box requires two relays, basically doing the same thing, but it just doesn't work correctly with one relay.

Thanks again for the help!

Nor do I, but either something that can learn, or something that can be programmed to account for inertia and other mechanical realities might be the ticket.

Probably the latter easiest first, and it may just be a matter of tuning with a threshold concept but applied also or only during motion

when moving, stop when you are within X of the target.

Thrusters to stationkeeping. All velocity zero. Our inertia should do the job now.

Like that.

a7

Ah, that's a good point. I think I'll take some measurements and try to account for that difference.
The actual voltages from the controller are in the range of 1.5vdc to 12.5vdc. I'm using a voltage divider to bring that down to something around .5vdc to 3.8vdc .

Pardon the sloppy video, I just wanted to put things into perspective. I'm using an old rotator controller so that I don't destroy an expensive unit.

NP. Hadda go off-site to see it.

If you did, point to the code you posted on this thread that was running when the video was taken.

If you didn't, do.

TIA

a7