Command 46: doesn't complete full move.

Questions about using encoders with the Roboclaw product line
Post Reply
billy
Posts: 5
Joined: Wed Mar 16, 2016 10:57 pm
Command 46: doesn't complete full move.

Post by billy »

Following the directions provided in this forum, I am trying to use command 46.
For example, to move 100K counts at V = 20k/sec and A = 20k/sec.sec, I issue first command to move 90k counts at V = 20k/sec and second follow on command to move 10k counts at V = 0k/sec.
What I get is the motors moving about 99700 counts (as reported by roboclaw (2x 15A).
I have plenty of integral in the tuning so this isn't a case where P fails to get motors to target alone.

Continued repeats of the move leave the motors about 300 counts further behind each time. The errors of the motors are different and the error magnitude varies each move but it roughly 0.3% error.
If I manually pull the motors away from finish position, they move back when I release, but move back to ~99700, not 100K.
Using roboclaw V4.1.16.

Here is the first command (as viewed by logic analyzer on the UART pins):
roboclaw46.gif
roboclaw46.gif (71.54 KiB) Viewed 5917 times
Same message viwed as HEX if that is easier for you to read.
roboclaw46_hex.gif
roboclaw46_hex.gif (70.55 KiB) Viewed 5917 times
Here is the second command: It is shown here sent with 20mS delay after first command, but delay of virtually 0 gives same results.
roboclaw46_2.gif
roboclaw46_2.gif (64.54 KiB) Viewed 5917 times

Here is the code used to setup and send the move(part should look familiar to you):

Code: Select all

int driveForward (int iDistance, int iSpeed, int iAccel){
	char data [23];
	int i, j, k;
	float fD0 = ((float)(iSpeed * iSpeed)) / (2 * iAccel);
	float fD1 = fD0;
	float fDt = fD0 + fD1;

	if( fDt > iDistance ) {
		fD0 = fD0/fDt * iDistance;
		fD1 = fD1/fDt * iDistance;
	}
	else{
		fD0 = iDistance - fD1;
	}

	for (k = 0; k < 2; k++){
		// k == 0 : send accel, top speed, and distance for ramp up and steady state 
		// k == 1 : send accel, final speed (zero), distance for ramp down and stop
		if (k == 1){
			fD0 = fD1;
			iSpeed = 0;
		}
		j = 0;
		// Send: [Address, 46, Accel(4 Bytes), SpeedM1(4 Bytes),
		//	DistanceM1(4 Bytes),SpeedM2(4 bytes) , DistanceM2(4 Bytes), Buffer, CRC(2 bytes)]
		// load address and command
		data[j++] = (char) 128 ;
		data[j++] = (char) 46 ;
		// load accel for both motors
		for (i = 0 ; i < 4 ; i++){
			data[j++] = ((unsigned int) iAccel) >> (24 - (i*8));
		}
		// load speed for motor 0
		for (i = 0 ; i < 4 ; i++){
			data[j++] = ((signed int) iSpeed) >> (24 - (i*8));
		}
		// load distance for motor 0
		for (i = 0 ; i < 4 ; i++){
			data[j++] = ((unsigned int) fD0) >> (24 - (i*8));
		}
		// load speed for motor 1
		for (i = 0 ; i < 4 ; i++){
			data[j++] = ((signed int) iSpeed) >> (24 - (i*8));
		}
		// load distance for motor 1
		for (i = 0 ; i < 4 ; i++){
			data[j++] = ((unsigned int) fD0) >> (24 - (i*8));
		}
		data [j] = 0; // send 0 to put command at rear of buffer

		sendCommand (23, &data);
        vTaskDelay( (TickType_t) 20 );
	}

}

int sendCommand (int bytes, char * data){
	int i;
	uint16_t crc = 0;

	for (i = 0; i < bytes; i++){
		uart1_putc((char) *(data + i) );
		crc = crc16( data + i , crc);
	}

	uart1_putc((char) (crc >> 8) );
	uart1_putc((char) (crc) );
}
Can you please let me know what you think needs to be done to get the move to be correct? The error should be 0.0% every single time if the part is usable. I haven't yet counted the actual encoder edges manually to see if the edges match the numbers being provided by roboclaw.
User avatar
Basicmicro Support
Posts: 1594
Joined: Thu Feb 26, 2015 9:45 pm
Re: Command 46: doesn't complete full move.

Post by Basicmicro Support »

1. I do recommend you make sure the encoder isnt getting bad counts. I've seen several motors with integrated encoders that were designed wrong(snubber circuit is wrong), causing a large amount of noise on the encoders 5v rail and signal lines. The noise was bad enough to cause false counts.

I use a scope to look at the quadrature signals and power pin on the encoders to check for this. The only fix I found has been to remove the snubber cap(s) that are integrated on these motors encoder boards.

Note, if this is the problem you are having, an external snubber will need to be added to the motor(not connected to the encoder board in any way) since without the snubber the brushes will wear down faster.

2. Assuming you have check 1, how accurate is the speed holding when you run a speed only command(without a distance)? It needs to be accurate to go the correct distances. This is because during the decceleration phase the Roboclaw will end the command when the actual speed reaches 0 even if you still have some distance to go and even if the target speed is not 0. This is to prevent an infinite loop waiting for the distance to be reached which will take infinitely long if the actual speed is 0.

Note you shouldnt ever over shoot the distance (when you have accurate encoder counts) becuase the roboclaw will stop the motor when it reaches the set distance even when the target speed is 0.

3. If you need a system that moves to a garunteed position with no error(within the margins the encoder/motor can provide) then you may need to use position commands(and tune the Position PID values) instead of using the velocity commands.

4. Prinout the results of your calculations to confirm they are what you think.

5. Simplify your code. Dont use for loops to process the bytes or commands. I doubt this is saving any significant code and makes it very hard to see if a mistake has been made.

6. Check the size of your int variables. On some processors these are only 16bits so your shifts will not be processed correctly.
billy
Posts: 5
Joined: Wed Mar 16, 2016 10:57 pm
Re: Command 46: doesn't complete full move.

Post by billy »

Thank you for the very thorough response. I can tell you put some serious time into it. Appreciated.
To your points:
1. Checked on the scope. Signals are clean, 5V rail from roboclaw is clean(as measured on encoder power line). Used logic analyzer to count pulses and found no bad (out of order) transitions and got same count as roboclaw. None of this is surprising. These are high quality encoders. Motor leads are routed separate from encoder.
2. There is some variation in speed but unless you can provide me an example of what is excessive it's hard to tell if that is related. I ran the motors both with no load and with heavy load. They ran move evenly with heavy load but that made no difference in the error magnitude. That data point makes it seem unlikely that speed variation is the cause.
3. If you're referring to Command 67, can you let me know how I can stitch sequences of commands together without stopping the motors between commands. I need to be able to keep the motors synchronized (commands starting at same(or known) time) but the motors will not have the same motion profiles. Command 46 would work well for that if the distances were accurate. Example: M1 and M2 ramp together and travel some distance with identical profiles, then M1 slows to a lower speed for some distance then speeds back up to match speed of M2. Then both motors stop at the same time having moved different distances.
4. I provided output so you could see the code functions as expected. Calculations confirmed.
5. I provided output so you could see the code functions as expected. I fail to see how this is relative to the discussion though. There could be no end to an argument over coding styles.
6. I provided output so you could see the code functions as expected. It's a good point though. Cortex M3 - 32bit INT

Out of curiosity, do you have a 2x15A roboclaw with 4.1.16 firmware that gets you an exact move using Command 46 when used as in the example I provided? V = 20k cps, A = 20k cpss, D = 100k counts? Does it work for you?
User avatar
Basicmicro Support
Posts: 1594
Joined: Thu Feb 26, 2015 9:45 pm
Re: Command 46: doesn't complete full move.

Post by Basicmicro Support »

1. Good
2. Once it has reached its constant speed it should be within +-2 or 3 of that speed in IonMotion(eg reading the average speed not the ispeed).
3. Yes I was. And no it wont work for chaining multiple movements together. with different speeds and accelerations without stopping in between.
4. I guess I wasnt clear, I dont want the bytes sent to the Roboclaw. I want the values(eg floating point or ints) that the code is calculating.
5. Rule one of debugging a program is to remove all unneccessary extra code. Using for loops to calculate the bytes to send instead of writing it out long form is unneccessary and may hide mistakes.
6. Use a long or int32_t if you have the option. Never trust a int is what you think it is.

Yes I have tested this. My code is below. My error is 0 to +3 counts on each movement. I wrote this to test the error in both directions over many cycles.

Note, my motor QPPS is only 17000 so I set my velocity to 16k instead of 20k but I tested at other speeds and the error was always within the same margins. My motor was tested without any load, but has a gearhead on it and I tuned it manually without trying to be super accurate.

Code: Select all

//Includes required to use Roboclaw library
#include "BMSerial.h"
#include "RoboClaw.h"

//Roboclaw Address
#define address 0x80

//Definte terminal for display. Use hardware serial pins 0 and 1
BMSerial terminal(0,1);

//Setup communcaitions with roboclaw. Use pins 10 and 11 with 10ms timeout
RoboClaw roboclaw(19,18,10000);

float fD0;
float iSpeed = 15000;
float iAccel = 20000;

void CalcDistance(int32_t pos)
{
  uint8_t stat;
  bool valid;
  int32_t curpos = roboclaw.ReadEncM1(address,&stat,&valid); //Im assuming command is valid, ignore stat and valid
  int32_t distance;
  if(pos<curpos)
    distance = curpos-pos;
  else
    distance = pos-curpos;
  
  int i, j, k;
  fD0 = ((iSpeed * iSpeed)) / (2 * iAccel);
  float fD1 = fD0;
  float fDt = fD0 + fD1;

  if( fDt > distance ) {
     fD0 = fD0/fDt * distance;
     fD1 = fD1/fDt * distance;
  }
  else{
     fD0 = distance - fD1;
  }
}

void setup() {
  //Open terminal and roboclaw serial ports
  terminal.begin(57600);
  roboclaw.begin(38400);
}

uint8_t buf1,buf2;
void loop() {
  CalcDistance(100000);
  roboclaw.SpeedAccelDistanceM1(address,iAccel,iSpeed,fD0,1);
  roboclaw.SpeedAccelDistanceM1(address,iAccel,0,0,0);
  do{ roboclaw.ReadBuffers(address,buf1,buf2); }while(buf1!=0x80);

  delay(2000);

  CalcDistance(0);
  roboclaw.SpeedAccelDistanceM1(address,iAccel,-iSpeed,fD0,1);
  roboclaw.SpeedAccelDistanceM1(address,iAccel,0,0,0);
  do{ roboclaw.ReadBuffers(address,buf1,buf2); }while(buf1!=0x80);

  delay(2000);
}

Post Reply