Copy Cat

I haven’t seen any threads about copycat in a while, so I figure I’ll share what I know. My team used a simplified version of copycat for the past two years as a backup to the code we couldn’t get working. It’s quite simple.
The idea is that you push a button in user control mode, then it writes to EEPROM the values you send to your motors. In autonomous mode, you play back the recording and that’s your autonomous mode.

put this in your user_routines.c()


void storemotors(void)
{
	static char lr = 0;

	if (lr==0)						//alternate writing lmotor & rmotor
	{
		writeEE(address, lmotor);		//writes the motor value to EEPROM
		lr=1;
	}else
	{
		writeEE(address, rmotor);
		lr=0;
	}//endif
	address++;					//increments address

}//end storemotors





char readEE(unsigned short address) {

// Load address into address register
EEADRH = ((address>>8)&0x03);   //Bits 2-7 are masked off since EEPROM is only 1KB
EEADR =(address&0xFF);

//Configuration as per manual
EECON1bits.EEPGD =0;
EECON1bits.CFGS =0;
EECON1bits.RD =1;


return EEDATA;
}

void writeEE(unsigned short address, char data)
{
EEADRH = ((address>>8)&0x03);
EEADR =(address&0xFF);
EEDATA = data;

//Configuration as per manual
EECON1bits.EEPGD =0;
EECON1bits.CFGS =0;
EECON1bits.WREN =1; 
INTCONbits.GIE = 0;
EECON2 = 0x55;
EECON2 = 0xAA; 
EECON1bits.WR = 1;
INTCONbits.GIE = 1;
EECON1bits.WREN = 0;
}

put this in your user_routines_fast()


void readmotors(void)
{
	static char lr = 0;

	if (lr == 0)
	{
		lmotor = readEE(address2);
		lr = 1;
	}else
	{
		rmotor = readEE(address2);
		lr=0;
	}//endif

	address2++;

}//end readmotors

address and address2 are unsigned shorts defined in user_routines.c and _fast.c respectively. lmotor is the pwm you used for your left motor and rmotor is the address you used for your right motor.
you can alias lmotor and rmotor like this:


#define lmotor pwm01
#define rmotor pwm02

that should save you some cutting and pasting.

you call storemotors() so long as a certain button, like p1_sw_top, is pushed. That way, as long as the button is pushed, you’re recording the data you send to the motors. call storemotors() only once you’re done processing data and are ready to send it to the pwms.
like this:


	if (writebutton && (address <= 1023)) {storemotors();}		
//stores data in EEPROM for recall in auto mode

you call readmotors() in user_autonomous_code(). It reads out data from the EEPROM directly into the motors.
like this:


if (address2 <= 1023) {readmotors();}		//reads data from EEPROM to motors
else {lmotor = rmotor = 127;}

that’s it. simple. The only problem you might run into is that it takes a while to write to the EEPROM. If you try to write two bits of data right after the other, it won’t write the second bit. This is why I only write one motor per cycle. It definitely took me a while to figure that one out.

This is a really simplified version. It can only record one autonomous mode. The 1023 bytes of space should definitely allow you fifteen seconds of recording time. If not, you can always call storemotors and readmotors every other loop. My goal here is to help you understand copycat, not to provide you with the most function.

this code has not been tested on the new compiler, so if there’s a big problem please tell me.

Please post if you have questions! If you have better versions of copycat please share, and explain too!

Heh, its interesting to see where my CopyCat project has gone over the years :stuck_out_tongue:
I’m glad that people are still trying to keep it alive!
Combined with the PID this year it could be pretty effective… :rolleyes:

Do storemotors() and readmotors() execute once every long long loop (26.2 ms) or every short loop? I’m still a bit confused to those and it would seem like and awful waste of space to do it more than every 26.2ms.

But either way, does this ever have ideas brewing in my head. :smiley:

EDIT: misunderstanding about the loops, got it all sorted out now.

Either way. so long as it’s the same for both. I do it every long loop because that’s when you have new data. readmotors is in _fast because that’s where use_autonomous_mode is.

It works really well as a backup because it’s so simple. I programmed this in an hour while watching Austin Powers. If you have a fairly simple (and not precise) autonomous strategy in mind.

Hmmm. I just tried to compile it and I got a bunch of errors…

what does that mean?

Please show your variable declaration for “address” and I’ll be able to tell you what the problem is.

AHA! You found the problem! I left adress as adress. I take it I am supposed to either change it or declare it as a variable?

It looks like you didn’t follow ALL the instructions:

Try reading the whole post carefully. :rolleyes:

FWIW, the lines should probably be something like:

unsigned short address; // In user_routines.c
	and
unsigned short address2; // In user_routines_fast.c

Actually, since I didn’t see anywhere in the code snippets where the address variables are initialized, that should be:

unsigned short address = 0; // In user_routines.c
	and
unsigned short address2 = 0; // In user_routines_fast.c

And assuming that these variables don’t need to be used outside of these modules, I would make them:

static unsigned short address = 0; // In user_routines.c
	and
static unsigned short address2 = 0; // In user_routines_fast.c

Further, it looks to me like these address variables aren’t needed outside of the storemotors and readmotors functions so, IMHO, these variable declarations should be moved inside their respective functions. E.g.:


void storemotors(void)
{
	static unsigned short address = 0;
	static char lr = 0;

	if (lr==0)			//alternate writing lmotor & rmotor
	etc.
}//end storemotors

Our programmer figured the out copy cat program last year, but due to his hard work he doesn’t want me to share. From what I have seen some of you pretty close. Good luck to everyone trying and happy programming!

GO 1403!!!

**THIS IS NOT MEANT TO BE COMPLETE CODE!!**

of course you have to define these variables. you also have to prototype these functions, but I’m not your team’s programmer, so I’m going to let you figure that part out.

Thanks, though, for pointing out where my code has holes. I didn’t actually use copycat this year, so I did not compile an actual working version.

Hey,

After spending months on a PID control without a lot of success, i believe we will have to develop Copy Cat as a backup. Since we are a newbie team we have no experience with this at all so I would request to know the ins and outs of these controls, and the things to keep in mind before using it. Is it controlled using a joystick when its getting recorded? and How many motors does it record, meaning will it record the movements of the arm or only the mobility?

Having less than a week to get this to work we are in a state of urgency, any assistance is highly appreciated.

Thanks.

while recording, it’s controlled in the same way you control it while driving. The way you record is to call the function storemotors while you are actually driving the bot. you don’t want to call it all the time, though, because every time you call it it messes up whatever you’ve already got recorded.

You can record as many motors as you want. You just have to watch out because the more motor values you want to record simultaneously, the less accurate your playback will be. There’s a simple way to get around this. Let me illustrate:

Suppose I want to control the motion of an arm moving up and down, and suppose I am controlling said arm with a PWM, however the only values I ever send to this arm are 0,127, and 254. I can do this two ways:

  1. every third loop I record the value I’m sending to the arm. This is interspersed with the recording of the left and right motors, so that the left motor is stored every third loop and the right motor is stored every third loop.

void storemotors(void)
{
	static char lr = 0;

	switch (lr)						//alternate writing lmotor & rmotor
	case 0:
             {
		writeEE(address, lmotor);		//writes the motor value to EEPROM
		lr=1; break;
	}
             case 1:
	{
		writeEE(address, rmotor);
		lr=2; break;
	}
             case 2:
             {
                         writeEE(address, armmotor);           //writes arm motor value to EEPROM
                         lr = 0; break;
             }
	address++;					//increments address

}//end storemotors

and you do the same sort of thing in Readmotors to get the values out.

2)since the PWM value 255 is never sent to the motors, you can use 255 as a marker to tell the autonomous mode, “Hey! Arm going up now!” you would also need another unused pwm value to tell the autonomous that the arm is going down. When my team wrote our drive code, we had a little “dead zone” in the middle of the joystick’s range, so that if the joystick was in between 117 and 137 the motor was set to 127. this gave us a whole bunch of unused pwm values.


void storemotors(void)
{
	static char lr = 0;
	static char armupordown = 0;
            

	if(armmotor== 254 && armupordown != 1)  //if the arm is going up and the arm wasn't going up last loop
	{
		writeEE(address, unused pwm value 1);  //stores motor going up marker
		armupordown = 1; //the arm just started going up
	}else if(armmotor == 127 && armupordown != 0) //if the arm has stopped moving and has just this loop stopped moving
	{
		writeEE(address, unused pwm value 2);  //motor stopped marker
		armupordown = 0;
	}else if (armmotor == 0 && armupordown != -1) //if arm just this loop started going down
	{
		writeEE(address, unused pwm value 3);  // motor going down marker
		armupordown= -1;
	}else if (lr==0)						//alternate writing lmotor & rmotor
	{
		writeEE(address, lmotor);		//writes the motor value to EEPROM
		lr=1;
	}else
	{
		writeEE(address, rmotor);
		lr=0;
	}//endif
	address++;					//increments address

}//end storemotors

and then of course somthing similar in readmotors to recognize the markers.

Hope this helps. Ask as many questions as you like.

Thank you very much for this valuable information, this makes it clearer.

Another, question, do the recorded values remain on the memory after the robot has been switched ON/OFF or Robot Reset is used, meaning is the same record of joystick control on the EEPROM repeatable from game to game or do we have to record every time we want to run autonomous?

Thanks again for sharing your expertise on the topic,
Vick.

The recorded memory remains in EEPROM even after you switch the robot off. It wouldn’t be very useful if it was erased every time you turned the robot on, would it? Watch out, though, because every time you download new code to the RC it erases any stored EEPROM memory, including your recorded autonomous!!!
Be very careful about making changes to your code if you don’t have time to re-record your autonomous.

Hi, I was wondering, can u fire a bimba using copy cat?, or is it only for motor control?

Thanks,

I am not sure, but you can using copy cat. But the way i do it is by timing the movements and firing the pneumatics up at a fixed desired time. So it is independant of the EEPROM itself and gives the same accuracy for movement.