Question

I have an Arduino Mega connected to a 6 axis robotic arm. All 6 interrupts are attached to encoders (one encoder pin on an interrupt, the other on a vanilla digital input). The interrupts are handled with this code:

void readEncoder1(){
//encoders is a 2d array, where the first d is the axis, and the two pin numbers
//first pin is on an interrupt (CHANGE), and second is a standard digital in
  if (digitalRead(encoders[0][0]) == digitalRead(encoders[0][1])) {
   positions[0]++;
  } else {
   positions[0]--;
  }
 if(servoEnable){
  updatePositions(); //// compares positions[] to targets[] and adjusts motor speed accordingly
 }
}

This is designed to keep the arm locked at a certain position- if the arduino detects that the position of the motor is off by a certain threshold, it updates the power going to the motor to keep the arm in position.

The problem is this, then -- if two or three (or more) axis are under load (requiring constant updating to stay in position) or they are moving, the Arduino will stop receiving intact commands on Serial input, several characters will be dropped. The interrupts are obviously running quite quickly, and for some reason this is causing commands to become corrupted. Is there any way around this? Architecturally, am I doing this right? My main instinct is to call updatePositions() in the main run loop at, say, 100 ms intervals, will this significantly reduce interrupt overhead? I guess what my question boils down to is how do I get reliable serial commands into the Arduino even if all 6 encoders are pulsing away?

Was it helpful?

Solution

Quadrature encoders were designed to be read by hardware counters. Pulse rates are generally high with the motor running at full speed. One megahertz is not unusual. The higher the number of pulses, the better the servo loop works and the more accurate you can position the motor.

Doing this is in software with a low-power cpu is, well, challenging. It will fall apart when the ISR takes longer than the interval between pulses. You'll lose pulses and thus position. Especially bad because there is no way you can detect this error condition. And that this loss happens when the robot is moving fast, the worst case condition to lose control.

You absolutely cannot afford to update the servo loop in the interrupt handler so get rid of that first. Keep the ISR to the bare minimum, only count the position and nothing else. The servo loop should be separate, driven by a timer interrupt or tick. You cannot properly control a robot with a 100 msec servo update unless it is big an sluggish, this needs to be a handful of milliseconds at most to get smooth acceleration and stable feedback.

There's a limited amount of wisdom in spending forty bucks to control thousands of dollars worth of robot hardware. Not being able to keep up in the servo loop is something you can detect, shut it down when the position error builds up too much. There's nothing you can do about losing pulses, that's a wreck. Get the hardware counters.

OTHER TIPS

First rule of embedded systems:

Do as little as possible in interrupts.

In your case, just update the positions in the interrupt and run your position/speed control loop in the background or at a lower priority.

Aside: I assume you are aware that you are "losing" encoder pulses as you don't have an interrupt on one of the channels?

Also, interrupt-driven encoder-analysis is very noise-prone. If you get a noise pulse, you'll likely only see an interrupt for one of the edges as they'll be too close together to process both.

A more robust way is to use a state machine which watches all 4 transitions, but that requires either interrupts on both edges of both channels, or polling fast enough to not miss anything up the to rate you are expecting to see.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top