Arm Motor Problems

Hello,

Our arm motors started having issues since last week. After roughly 20 ~ 30 seconds of running it it’d stop lifting. I thought it was an overheating issue but whenever it stops, as soon as we turn the joystick off and on it’d work again. However when we don’t disconnect and just wait for the motors to cool off the motors never came back “online”. The motors (2 x 393) had been on the robot for 2 / 3 weeks.

The motors do not feel warm at all whenever it stops working. However when I had the joystick connected to the computer and the cortex directly by serial cable and USB cable, the issue doesn’t appear anymore. I proceeded to try different Vexnet keys, joysticks, batteries, motor ports (from 1 & 10 to 5 & 6) and nothing seems to help.

Besides the motors, what else might be the problem? Because we know everything had work before but now the motors are not as reliable anymore (unless we disconnect and connect again). Our last robot ran the same motor setup and ratio with tubing as counterweight so we are not sure what’s causing it. What else can we do to identify and diagnose the problem? If anyone thinks the code might be the issue, I’ll upload the code.

Thanks!

Code is always a possible issue, so go ahead and upload it.

Describe in more detail the circumstances that make it stop working?
Is this a competition Gateway robot?
What is your arm lift gear ratio and arm length?
Does the arm stop in the same place all the time?
Do you have a standby value driving the motors to keep the arm in the lifted position?

Hypothesis: it is the standard reason, PTC overload on Cortex ports 1-5 or 6-10
Test: when arm motors are on ports 2,3 and stop, do ports 4,5 still work?

Hypothesis: your motors have a broken gear tooth.
Test: disassemble the motors and check the gears.

Hypothesis: the motors are overheating, or something else in the motor is electrically bad.
Test: when the motors stop, unplug them and plug in a different unloaded motor to the same port, If the new motor works, then it isn’t the code or the PTC.

Hypothesis: the code is bad
Test: add a print statements to update the screen when the motor is told to change value; If the print statements stop when the motor stops, then it is the code.

Hypothesis: Something weird in the firmware or vexnet link
Test: reflash the joystick, reflash the cortex, reload the cortex, Does it work now?

One issue I have seen with arm control this year is if you are running PID control or using any holding power on the arm, then when the PTCs in the motors trip you cannot get control back until power is removed (the small current flowing through the PTC keeps it tripped). Turning off the joystick would cause the supervisor processor to detect a disconnect situation and remove power from all motors allowing the PTC to reset. Not saying this is your problem but just an observation.

#pragma config(Sensor, in1, Arm_Pot, sensorPotentiometer)
#pragma config(Sensor, dgtl1, Lower_Limit, sensorTouch)
#pragma config(Motor, port1, A_L, tmotorNormal, openLoop)
#pragma config(Motor, port2, D_L_R, tmotorNormal, openLoop)
#pragma config(Motor, port3, D_L_RE, tmotorNormal, openLoop)
#pragma config(Motor, port4, D_L_HS, tmotorNormal, openLoop)
#pragma config(Motor, port5, C_L, tmotorNormal, openLoop)
#pragma config(Motor, port6, C_R, tmotorNormal, openLoop, reversed)
#pragma config(Motor, port7, D_R_HS, tmotorNormal, openLoop, reversed)
#pragma config(Motor, port8, D_R_RE, tmotorNormal, openLoop, reversed)
#pragma config(Motor, port9, D_R_R, tmotorNormal, openLoop, reversed)
#pragma config(Motor, port10, A_R, tmotorNormal, openLoop, reversed)
//!!Code automatically generated by ‘ROBOTC’ configuration wizard !!//

#pragma platform(VEX)

//Competition Control and Duration Settings
#pragma competitionControl(Competition)
#pragma autonomousDuration(20)
#pragma userControlDuration(120)

#include “Vex_Competition_Includes.c” //Main competition background code…do not modify!

const int HIGH_GOAL = 1700;
const int MEDIUM_GOAL = 1200;
const int LOW_GOAL = 900;
const int GROUND = 270;
float ARM_POWER;
float ARM_TARGET;
int MAX_HEIGHT = 1700;
int MIN_HEIGHT = 270;
int ARM_ERROR_CAP = (MAX_HEIGHT - MIN_HEIGHT) / 2;
int ARM_INTEGRAL_CAP = 15000;
int ARM_LAST_TARGET = 0;
float ARM_ERROR = 0;
float ARM_INTEGRAL = 0;
float ARM_LAST_ERROR = 0;
float ARM_DERIVATIVE = 0;
float ARM_KP = ((MAX_HEIGHT - MIN_HEIGHT) / 2) / 127;
float ARM_KI = 4;
float ARM_KD = 0.25;

const int cycle = 25;
//D denotes Drive
//L denotes Left
//R denotes Right
//P denotes Power
//C denotes Constant
//V denotes Value

void pre_auton()
{
}

task autonomous()
{
}

void Assign_Motor(int L, int R)
{
motor[D_L_R] = L;
motor[D_L_RE] = L;
motor[D_L_HS] = L;
motor[D_R_HS] = R;
motor[D_R_RE] = R;
motor[D_R_R] = R;
}

float Ramp(float Last_V, int Current_V)
{
float Dummy;
if (abs(Current_V - Last_V) < Cycle)
Dummy = Current_V;
else
if ((Current_V - Last_V) > 0)
Dummy += Cycle;
else
Dummy -= Cycle;
return Dummy;
}

float Ramp_T(float Last_V, int Current_V)
{
float Dummy;
if (abs(Current_V - Last_V) < Cycle)
Dummy = Current_V;
else
if ((Current_V - Last_V) > 0)
Dummy += Cycle;
else
Dummy -= Cycle;
return Dummy;
}

task Drive()
{
float D_Last_L_P = 0;
float D_Last_R_P = 0;
float D_L_P = 0;
float D_R_P = 0;
int J_L_V = 0;
int J_R_V = 0;
while (true)
{
ClearTimer(T1);
J_L_V = vexRT[Ch3];
J_R_V = vexRT[Ch2];
if (abs(J_L_V) < 15)
D_L_P = 0;
else
D_L_P = Ramp(D_Last_L_P, J_L_V);
if (abs(J_R_V) < 15)
D_R_P = 0;
else
D_R_P = Ramp_T(D_Last_R_P, J_R_V);
Assign_Motor(D_L_P, D_R_P);
D_Last_L_P = D_L_P;
D_Last_R_P = D_R_P;
while (time1[T1] < 13)
{
}
}
}

task Arm()
{
while (true)
{
ClearTimer(T2);
if (vexRT[Btn7DXmtr2] || vexRT[Btn7D])
ARM_TARGET = GROUND;
if (vexRT[Btn7UXmtr2] || vexRT[Btn7U])
ARM_TARGET = LOW_GOAL;
if (vexRT[Btn8DXmtr2] || vexRT[Btn8D])
ARM_TARGET = MEDIUM_GOAL;
if (vexRT[Btn8UXmtr2] || vexRT[Btn8U])
ARM_TARGET = HIGH_GOAL;
if (vexRT[Btn5U])
{
motor[A_L] = 127;
motor[A_R] = 127;
}
else
{
if (vexRT[Btn5D])
{
motor[A_L] = -127;
motor[A_R] = -127;
}
else
{
if (ARM_TARGET == GROUND)
{
if (SensorValue(Lower_Limit) != 1)
{
motor[A_L] = -127;
motor[A_R] = -127;
}
else
{
motor{A_L] = -10;
motor[A_R] = -10;
}
}
else
{
if (abs(ARM_TARGET - SensorValue(Arm_Pot)) > 0)
{
if (abs(ARM_POWER) < 10)
{
motor[A_L] = 0;
motor[A_R] = 0;
}
else
{
if (ARM_POWER < 0)
{
motor[A_L] = ARM_POWER;
motor[A_R] = ARM_POWER;
}
else
{
motor[A_L] = ARM_POWER;
motor[A_R] = ARM_POWER;
}
}
}
else
{
motor[A_L] = 0;
motor[A_R] = 0;
}
}
}
}
if (ARM_TARGET < MAX_HEIGHT)
if (vexRT[Btn5UXmtr2])
ARM_TARGET+=10;
if (ARM_TARGET > MIN_HEIGHT)
if (vexRT[Btn5DXmtr2])
ARM_TARGET-=10;
if (vexRT[Btn6U] || vexRT[Btn6UXmtr2])
{
motor[C_L] = 127;
motor[C_R] = 127;
}
else
{
if (vexRT[Btn6D] || vexRT[Btn6DXmtr2])
{
motor[C_L] = -127;
motor[C_R] = -127;
}
else
{
if (vexRT[Btn8L] || vexRT[Btn8LXmtr2])
{
motor[C_L] = 127;
motor[C_R] = -127;
}
else
{
if (vexRT[Btn8R] || vexRT[Btn8RXmtr2])
{
motor[C_L] = -127;
motor[C_R] = 127;
}
else
{
motor[C_L] = 0;
motor[C_R] = 0;
}
}
}
}
ARM_ERROR = ARM_TARGET - SensorValue(Arm_Pot);
if (ARM_ERROR > ARM_ERROR_CAP)
ARM_ERROR = ARM_ERROR_CAP;
else
if (ARM_ERROR < -ARM_ERROR_CAP)
ARM_ERROR = -ARM_ERROR_CAP;
if (ARM_TARGET != ARM_LAST_TARGET)
ARM_INTEGRAL = 0;
if ((ARM_INTEGRAL > ARM_INTEGRAL_CAP) || (ARM_INTEGRAL < -ARM_INTEGRAL_CAP))
if (ARM_INTEGRAL < 0)
ARM_INTEGRAL = -ARM_INTEGRAL_CAP;
else
ARM_INTEGRAL = ARM_INTEGRAL_CAP;
ARM_INTEGRAL = ARM_INTEGRAL + ARM_ERROR / 100;
ARM_DERIVATIVE = ARM_ERROR - ARM_LAST_ERROR;
if (ARM_ERROR > 0)
ARM_POWER = ARM_ERROR / ARM_KP + ARM_INTEGRAL / ARM_KI;// - ARM_DERIVATIVE / ARM_KD;
else
ARM_POWER = (ARM_ERROR / ARM_KP + ARM_INTEGRAL / ARM_KI) * 0.75;
if (abs(ARM_POWER) > 127)
if (ARM_POWER > 0)
ARM_POWER = 127;
else
ARM_POWER = -127;
while (time1[T2] < 13)
{
}
ARM_LAST_ERROR = ARM_ERROR;
ARM_LAST_TARGET = ARM_TARGET;
}
}

task usercontrol()
{
// User control code here, inside the loop
StartTask(Drive);
StartTask(Arm);
while (true)
{
if (vexRT[Btn7L])
{
StopTask(Arm);
wait1Msec(250);
StartTask(Arm);
}
}
}

Here’s the code.
I’ve tried tweaking a lot of things such as how often the loops goes through (currently it’s at 13 ms) / Stopping the task / Moving all the code into the main task (to take out the possibility of multi-tasking interfering with… whatever).

I have not tried to send the motors a power of 0 (removing it away from PID). I’ll try that to see if it makes any difference. I didn’t put it as one of the first things to try because it didn’t seem like the motors are overheating (it didn’t heat up at all plus in my encounters the motors took longer to reset). Do motors “wear out” and drop in performance significantly?

The motors stop working whenever. It seems random to me however it doesn’t occur nearly often when I used a USB connection between the joystick and the cortex, if that’s an indication for anything. Oh and also, whenever the connection is back on after disconnecting, the motors seem to be running full power without any trouble. I’m not quite sure about the thermal fuse but doesn’t it take a while to get the motor to full power again?

And yes this is a gateway competition robot. Sorry I didn’t clarify. Gear ratio is 7:1 with an arm that’s roughly 17.5 in. long however one thing that we significantly changed was from a round collector to a “chainsaw” collector. Again we’ve copied the arm from our old robot which ran with the exact same settings and it didn’t have issues for 2 / 3 weeks until now.

No the arm does not stop at the same place all the time. However due to the tubing / gravity it usually rests at wherever the “balance point” is and doesn’t move when it tries to go up and only goes down a little bit. I tested it with the manual override buttons (127 / -127).

Everything else (the drivetrain / collector) works fine when the arm motors stop working.

I’ve not consider the possibility of motor gears breaking but I’ll check tomorrow if all else fails, along with swapping out motors.

I have noticed the motors aren’t moving when the arm stopped moving (the motors are chained (1:1) to the same axle with the 12 tooth gear). I took the chain off and noticed that. I’ll perform the test tomorrow.

I used the debugger window in RobotC to check the motor power and there isn’t any abnormalities that I noticed. The pot is working fine along with bottom limit switch. However I did noticed that (I took the chain off after it stopped working once) the power value sent to the motors are not changing when I manual set it at a certain height. It remains at 127 but when I used the override buttons the motors still has no response.

I downloaded all the newest firmware today and that didn’t make a difference.

Thank you all for your help!

Add some type of temporary disable for the PID loop and send 0 to the motors, see what happens.

The PTC devices reset quite quickly, see this thread on PTC performance, it’s dealing with the 4A devices in the cortex but the theory is the same. Just because the motor is not hot does not mean the PTC has not tripped, the PTC device will be hot but there is enough thermal mass in the motor case that you may not notice if it has only tripped once. Any small amount of current flowing through the PTC will keep it tripped, you have to disable power to the motor completely. No idea on the USB thing.

Use the # button to add CODE tags around your code will make it easier to read by preserving the spacing.

I see you are using ports 1,10 for Arm motors, as per jpearman suggestion that these can react faster to PID routines.
These ports are a little different, you might try swapping the arm to other motor ports to see if it is port related.

I see you have a complex PID routine for the Arm position.
Hypothesis: it is a complex PID code problem, not a hw problem.
Test: Swap out the Arm PID code for simple button up/down control,
if that works and doesn’t stop, then you know it is in the PID code somewhere.
if that still fails, keep the simple up/down control to debug the hw issues.
Ah, I see that jpearman has already suggested that above, while I was looking at the code in some detail…

If there is something tricky about working better with USB tether connections, vs wifi, it won’t be obvious in the code.
It seems like I’ve heard that before, but I don’t remember the outcome.
Maybe reinitializing the link will redo an INITIALIZE routine that fixes some variable in the PID code?

The remainder of my comments are just Code style suggestions for your consideration (or not).

Code styling suggestions (based on my preferences, and “Perl Best Practices”)

  • You might consider adding comments here and there about why you are doing things
  • I see at least 3 different methods for clipping a variable to a +/- limit.
    That might be a good place to use a subroutine.
  • I see that your drive code uses variables, and then assigns them to multiple motors at the end, thats nice.
    You could consider doing that for the Arm as well.
    You could consider doing that for the Claw intake motors as well, but I see you have some cases where you want to rotate an object, rather than just intake or outtake it.
  • You might see how you like how it looks if you avoid huge train of nested ELSEs,
    for example, put the IF cases in reverse order so highest priority is last .
Your way:
 if (A) motor=do_a 
   else if(B) motor=do_b 
     else if (C) motor=do_c;
My suggestion:
 if (C) var = do_c;
 if (B) var = do_b;
 if (A) var = do_a;
 motor = var;
  • When you have Arm movement overrides Btn5U/Btn5d, you can put them last, so that they have highest priority, example (A) above.
  • For overheating motor possibilities, having an override for “motors off” would be a good idea too. A previous thread showed that 5 seconds of all motors off would allow the PTC to reset.

Programming style, a whole other topic and a dangerous place to go, however…

Instead of this


task Arm()
{
    while (true)
      {
      ClearTimer(T2);

      // Lots of interesting code    

      while (time1[T2] < 13)
          {
          }
      }
}

Use wait1Msec(13);


task Arm()
{
    while (true)
      {
      // Lots of interesting code

      wait1Msec(13);
      }
}

Try and find a way of differentiating variables from constants, for example.

Instead of this


const int HIGH_GOAL = 1700;
float ARM_TARGET;

if (vexRT[Btn8UXmtr2] || vexRT[Btn8U])
    ARM_TARGET = HIGH_GOAL;

Try this


const int HIGH_GOAL = 1700;
float arm_target;

if (vexRT[Btn8UXmtr2] || vexRT[Btn8U])
    arm_target = HIGH_GOAL;

or this


#define kHighGoal  1700
float arm_target;

if (vexRT[Btn8UXmtr2] || vexRT[Btn8U])
    arm_target = kHighGoal;

It will make very little difference when you are reading your own code, but it really helps others who are not familiar with it and will help you next year when you perhaps want to reuse some of it. There’s lots of good information on this topic and it does depend on what the platform you are programming is as you do tend to fit in with the style of vendor libraries. For example, this document pulled at random from the Atmel web site has quite specific guidelines on the style used in their embedded frameworks, when working with this type of processor I tend to adopt their guidelines so the code is consistent. Apple on the other hand have a very different style, for example, use of “k” before constant definitions as I used above. Good code need to be well documented and look good as well as having the desired functionality.

Somewhat off-topic, but related to RobotC coding:
Even the Drive code looks wonky.

const int cycle = 25; // with lower case c
but all other uses of word Cycle have upper case C.
This generates warnings, but is automatically fixed by RobotC.

Some people use a variable naming convention that tags each name with type, eg: Last_Vf for float, and Current_Vi for int

The following Drive code and uses look like there are a lot of type conversions and unassigned assignments going on.
I’m surprised it works at all.

void Assign_Motor(int  L, int R){
  motor[D_L_R]  = L;  motor[D_L_RE] = L;   motor[D_L_HS] = L;
  motor[D_R_HS] = R;  motor[D_R_RE] = R;   motor[D_R_R]  = R; }

float Ramp(float Last_V, int Current_V) {
  float Dummy;
  if (abs(Current_V - Last_V) < Cycle) Dummy = Current_V;
    else if ((Current_V - Last_V) > 0)     Dummy += Cycle;
    else                                   Dummy -= Cycle;
  return Dummy;
} //Ramp
...
 J_R_V = vexRT[Ch2];
 D_R_P = Ramp_T(D_Last_R_P, J_R_V);
 Assign_Motor(D_L_P, D_R_P);
D_Last_R_P = D_R_P;

Paraphrase:
RAMP takes the difference between a float and an int.
Compares that to a constant Cycle (25),
sometimes recast the int as type float and returns it,
or take an uninitialized float value (Dummy) and add or subtract the constant Cycle, and return that.
The returned float value is passed to Assign_Motor which takes int arguments, and is used as a set motor speed argument.

Maybe it works in RobotC, because in RobotC, the Dummy variable is a global, and keeps its state across calls, and there are separate but identical Ramp/Ramp_T functions for Left/Right wheels to create different persistant Dummy variables… (?)

I also noticed that the deadband test on the joystick causes the wheels to go immediately (not ramp) to zero, which is may be fine for avoiding PTC overloads, but it doesn’t smooth out stopping jerks to the gear train.

I apologize for the messy code with a lot of details ignored. This was meant for testing only until everything works then I’ll finalize the style, fix errors, etc.

As for the int / float mixups / conversions, I’ve “abused” / let it go because it doesn’t effect anything. I put one of them as float (probably should’ve put the other one as float also in the procedure Ramp) in case of fancy algorithms later and those would requrie floats.

The deadband for joystick I ignored because the driveteam never felt they needed it to ramp down.

I have two separate but identical Ramp functions because what happened was when there’s only one and both the left and right joystick is using it, it wouldn’t work. When only one side is powered it works but when both sides are powered they always turn the same direction (the robot can only go forward / backward). I thought it had something to do with accessing the same function (even though each time it’s called it should create a new Ramp to run the code in I believe) so I just copied it and changed it like that and it worked.

The while (time1[Timer] < 13) {} trick I learned from the Vex example codes. Before then I always used wait1Msec(13). I don’t know how long it takes to go through the code in the loop but if it’s less then it will always go through the loop every 13 ms instead of 13 ms + however long it takes to run the code. I used it so PID would be consistent (since I didn’t factor in the T since the loop takes the same amount of time each time). As for using it in the drivecode I heard variables (sensors / motors, etc.) update every 13 ms (I heard from using the NXT version of RobotC and the fact that it’s the same in the Vex sample PID linefollowing code) so I just put it in there.

The PID code was for testing and quick tweaks so I apologize for the fact it was hard to read. I had deadbands for the error / motor power and as well as having a different code for when it’s going up / down. I haven’t take out the unnecessary code because in case I have to reuse to tweak again.

Thanks for all your help (and again I aplogozie for the messy code. Currently I’m testing for consistency and doing quick changes do the code to see if it makes a difference rather than focusing on the code. I will clean it up once the driveteam is practing their driving).

It seems like it’s a motor problem but even after I swapped out the motors the issue is still present. I’ll try rebuilding the code to see if it makes any difference.

No need to apologize, quick code for testing is often less than perfect, perhaps we should not comment on code style as you did not ask us for that.

That makes complete sense, my usual assumption is that the few lines of code here would operate in less than 1mS. There are a few speed tests we did last year here to give you an idea of how fast things run.

So have you tried killing the PID loop and sending 0 power to the motors when you have lost control yet ?

That’s okay. Any comments / concerns are welcomed. Since I have other tasks other than being the programmer on the team I often cannot spend time to perfect the style of the code. And I appreciate all the resources you have found for programming! I’ll take a look at it and adapt to a particular one and use the generic drive code I have for practice.

Yes I have tried that and the motors do seem to reset and work back again. Then I took the PID off completely and just use manual control and even with load (6+elements) the motors didn’t time out at all so I figured out it wasn’t the motors.

I rewrote the control loop and now it’s running fine (a lot simpler and cleaner too). I couldn’t find any logic error or odd behavior for any of the variables but the new code seems to work. Thank you for all your help!