D.T.A | Elegant Motor Control Code

D.T.A http://www.sherv.net/cm/emo/dancing/penguin.gif
Define
Target
Apply

Its here, the thing you have all been waiting for… well, actually, probably not… Let me explain: DTA is a motor control ‘idea’. I personally have found this idea helpful while programming, I hope it can be of help to someone else. This idea is not designed for use in autonomous code, it CAN be used, but it is not recommended, as there are much better solutions to the problem (like just calling autonomous functions). This system is designed to be used in a driver control loop in conjunction with driver input to create toggles and while (held) buttons for mechanisms. e.g. flywheels, arms, intakes.

It works different to your conventional method by not applying speed changes to a motor until the end of the loop, instead of during a loop. This means that D.T.A is not subject to jitter ( for all reasonable applications :slight_smile: ) and is elegant and compact, not to mention readable. An example is of the advantages of P.T.A is below.

Lets look at a piece of code to control an intake and a flywheel: ( Note: Code written for demonstration, not an actual piece of code ).

	if(vexRT[Btn5U] == 1)
	{
		motor[tlrfwr1] = 127;
		motor[tlrfwr2] = 127;
		motor[tlrfwl1] = 127;
		motor[tlrfwl2] = 127;
	}
	// my new stop button
	else if(vexRT[Btn5D] == 1)
	{
		motor[port2] = 0;
		motor[port3] = 0;
		motor[port4] = 0;
		motor[port5] = 0;
	}
	
	// Hold Switch
	if{vexRT[Btn6U] == 1)
	{
		motor[intake1] = 127;
		motor[intake2] = 127;
	}
	// Low speed Intake for Michael Bays Film
	if(vexRT[Btn6D] == 1)
	{
		motor[intake1] = 69;
		motor[intake2] = 58;
	}
	// Else doesn't	work for some reason. :(
	if(vexRT[Btn6D] == 0 && vexRT[Btn6U] == 0)
	{
		motor[intake1] = 0;
		motor[intake2] = 0;
	}

Lets look at some issues with this code, firstly the intake motors are subject to JITTER :slight_smile: Of course we can fix this by including else:

// Hold Switch
	if{vexRT[Btn6U] == 1)
	{
		motor[intake1] = 127;
		motor[intake2] = 127;
	}
	// Low speed Intake for Michael Bays Film
	**else** if(vexRT[Btn6D] == 1)
	{
		motor[intake1] = 69;
		motor[intake2] = 58;
	}

	**else**
	{
		motor[intake1] = 0;
		motor[intake2] = 0;
	}

Another thing is, it is tedious to write out the motor names every time we go to change a motor value. Also if we want to change say, a speed we have to go through and edit all the motor speeds that we want to change individually, of course we can fix this by including a function/subroutine to control the motors.

if(vexRT[Btn5U] == 1)
	{
		motor[tlrfwr1] = **56**;
		motor[tlrfwr2] = **56**;
		motor[tlrfwl1] = **56**;
		motor[tlrfwl2] = **56**;
        }
	}

#ImFarToLazyToDoThisEverytimeIWantToChangeASpeed

The original programmer has used “tlrfw” to define a flywheel motor, but another programmer has come along and, confused, just used the port names instead of changing the actual names of the motors which just makes the code even harder to read and understand. D.T.A allows a single easy to understand Variable to run your motors. Not to mention now to change a name we have to tediously go through all of the motors, of course again we can fix this by including a function/subroutine to control the motors.

e.g.


fw_setspeed(int speed)
{
    	motor[tlrfwr1] = speed;
	motor[tlrfwr2] = speed;
	motor[tlrfwl1] = speed;
	motor[tlrfwl2] = speed;
}

We don’t even need to update the code more than once every 20 milliseconds anyway? so why do we have code that updates during a loop, why not at the end of it.

D.T.A is based off the idea of simple, readable and elegant code. It uses the system: define, target, apply to allow it to not only look nice, but be extremely effective and practical as well. Its up to you if / how you use it, I have found it useful for some of my projects, and you may too.

TL;DR D.T.A is just a way of making motors move that looks nice and is readable. Its also good for lazy people :smiley:

So how do you apply this to your program?

(will continue on next post)

*P.S. I think I got a bit carried away, this was meant to be a quick info thing. Oh well. You can kill me for wasting 2 minutes of your life if you want * :smiley:

Define

The first step is to define your variables in a structure. Start by creating a typedef structure and defining your two variables: TargetSpeed and CurrentSpeed. ( Note that you can just define the variables mentioned below without a structure, but I like the look and understandability of a structure)

typedef struct{
	float TargetSpeed;
	float CurrentSpeed;
} flywheel, feed;

flywheel fw_Right;
flywheel fw_Left;
feed intaker1;
feed intaker2;

You can also other variables universally relating to your arms, flywheels and intakes if you want. It may also be useful to create individual structs for different objects Mechanisms

TargetSpeed: The speed that you want an motor to move at.
CurrentSpeed: The speed that the motor is currently moving at.

In some applications it might be useful to have:
PrevSpeed: The speed that the motor was moving at on the last iteration of the loop

Target

The second step is to set your target speed in your driver control loop, you first will want to make sure you are working within a while loop just as you would be conventionally.

while(true)
{
}

To set a motor speed simply change the value of TargetSpeed within your Struct. For instance:

fw_Right.TargetSpeed = 127;
fw_Left.TargetSpeed = 54;

You can also use if-else statements to create button toggles and hold ( to activate ) switchs.

Button Toggle:

if(vexRT[Btn5U] == 1)
{
	intaker1.TargetSpeed = 127;
	intaker2.TargetSpeed = 54;
}
// See that rebel non inclusion of else :)
if(vexRT[Btn5D] == 1)
{
	intaker1.TargetSpeed = 0;
	intaker2.TargetSpeed = 0;
}

Hold Switch:

if(vexRT[Btn5U] == 1)
{ 	fw_Right.TargetSpeed = 127;
	fw_Left.TargetSpeed = 54;
}
else if(vexRT[Btn5U == 0)
{
	fw_Right.TargetSpeed = 0;
	fw_Left.TargetSpeed = 0;
}

You can also use ternary operators to create more compact code with hold switches (and I’m sure theres an easy way to make button toggles):

intaker1.TargetSpeed = (vexRT[Btn5U] == 1) ? 127 : 0
intaker2.TargetSpeed = (vexRT[Btn5U] == 1) ? 127 : 0

Apply

Firstly you will have to write a small function that will update your motors speed. You can update motor speed in an actual loop. But a function is preferred because it is a more elegant and usually easier to read.

e.g.

void updateAllMotorSpeed()
{
	// Update Flywheels
	fw_Right.CurrentSpeed = fw_Right.TargetSpeed;
	fw_Left.CurrentSpeed = fw_Left.TargetSpeed;
	motor[tlrfwr1] = fw_Right.CurrentSpeed;
	motor[tlrfwr2] = fw_Right.CurrentSpeed;
	motor[tlrfwl1] = fw_Left.CurrentSpeed;
	motor[tlrfwl2] = fw_Left.CurrentSpeed;
	
	// Update Intake
	intaker1.CurrentSpeed = intake1.TargetSpeed;
	intaker2.CurrentSpeed = intake2.TargetSpeed;
}
void intake_updateMotorSpeed()
{
	intaker1.CurrentSpeed = intake1.TargetSpeed;
	intaker2.CurrentSpeed = intake2.TargetSpeed;
}
void fw_updateMotorSpeed()
{
	fw_Right.CurrentSpeed = fw_Right.TargetSpeed;
	fw_Left.CurrentSpeed = fw_Left.TargetSpeed;
	motor[tlrfwr1] = fw_Right.CurrentSpeed;
	motor[tlrfwr2] = fw_Right.CurrentSpeed;
	motor[tlrfwl1] = fw_Left.CurrentSpeed;
	motor[tlrfwl2] = fw_Left.CurrentSpeed;
}

Now simply call your functions at the end of your loop, followed by a delay.

intake_updateMotorSpeed();
fw_updateMotorSpeed();

// dont hog CPU
wait1Msec(20);

So whats the difference, you might ask?

Heres the original Code:


while(true)
{
         if(vexRT[Btn5U] == 1)
	{
		motor[tlrfwr1] = 127;
		motor[tlrfwr2] = 127;
		motor[tlrfwl1] = 127;
		motor[tlrfwl2] = 127;
	}
	// my new stop button
	else if(vexRT[Btn5D] == 1)
	{
		motor[port2] = 0;
		motor[port3] = 0;
		motor[port4] = 0;
		motor[port5] = 0;
	}
	
	// Hold Switch
	if{vexRT[Btn6U] == 1)
	{
		motor[intake1] = 127;
		motor[intake2] = 127;
	}
	// Low speed Intake for Michael Bays Film
	if(vexRT[Btn6D] == 1)
	{
		motor[intake1] = 69;
		motor[intake2] = 58;
	}
	if(vexRT[Btn6D] == 0 && vexRT[Btn6U] == 0)
	{
		motor[intake1] = 0;
		motor[intake2] = 0;
	}

        wait1Msec(20);
}

And heres the code using D.T.A:

while(true)
	{
		if(vexRT[Btn5U] == 1)
		{
			fw_Right.TargetSpeed = 127;
			fw_left.TargetSpeed = 127;
		}
		if(vexRT[Btn5D] == 1)
		{
			fw_Right.TargetSpeed = 0;
			fw_left.TargetSpeed = 0;
		}

		// High Speed Intake Hold Switch
		intaker1.TargetSpeed = (vexRT[Btn6U] == 1) ? 127 : 0;
		intaker2.TargetSpeed = (vexRT[Btn6U] == 1) ? 127 : 0;

		// Low speed intake Hold Switch
		intaker1.TargetSpeed = (vexRT[Btn6D] == 1) ? 69  : 0;
		intaker2.TargetSpeed = (vexRT[Btn6D] == 1) ? 58  : 0;

                intake_updateMotorSpeed();
                fw_updateMotorSpeed();

                wait1Msec(20);
	}

No only is the latter code more compact, but it is also a lot easier to edit ( more than 2x :slight_smile: ) and somewhat IMO easier to read. It allows for what would be a breading ground for jitter to pass fine and allows the next guy to edit this code to easily see what is being changed and edit it without decrypting the laziness of the previous programmer.

A thing to remember: if you have a lot of if statements that check for different button presses nad set a motor speed depending on which button is pressed and you hold all the buttons down on your controller, the last if statement will be the one that controls the motor speed.

Another step would be to wrap your intake and flywheel values in functions then only access the individual values when needed.

void fw_targetSpeed(int speed)
{
	fw_Right.TargetSpeed = speed;
	fw_left.TargetSpeed = speed;
}

Remember that this does not restrict you to not using conventional code, for instance time based operations can be achieved in two ways, through to usage of an iteration system ( say 50 iterations because the loop runs every 20 milliseconds so around 1000 milliseconds ), and through conventional code:


motor[magic] = 127;
wait1Msec(1000);
motor[magic]

Although the latter option does mean none of the motors can update or receive instruction while waiting, which happens normally anyway. So the former option is preferred.

I though I would also suggest a way you could use the DTA concept in a drive system:

You Define a Struct with X and Y axis in it.

typedef struct{
	float XaxisTarget;
	float XaxisCurrent;
	float YaxisTarget;
	float YaxisCurrent;
} _driveSystem;
_driveSystem driver;

In the main loop you simply collect the joystick values and update the motors.

task main
{
while(true)
{
	driver.XaxisTarget = vexRT[Ch3];
	driver.YaxisTarget = vexRT[Ch2];
	
	updateDriveMotor();
}

And then you have a function, where all the magic happens:

int deadZoneThreshold = 15;
void updateDriveMotor()
{
	driver.XaxisCurrent = driver.XaxisTarget;
	driver.YaxisCurrent = driver.YaxisTarget;

	// Inbuilt Deadzone for ellegance
	driver.XaxisCurrent = (driver.XaxisCurrent < deadZoneThreshold) ? abs(vexRT[Ch3]) : 0;
	driver.YaxisCurrent = (driver.YaxisCurrent < deadZoneThreshold) ? abs(vexRT[Ch3]) : 0;
	
	motor[driveR] = driver.XaxisCurrent;
	motor[driveL] = driver.YaxisCurrent;
}


ConventionalCode.c (1.17 KB)
D.T.A.Code.c (1.67 KB)

Good Job John
I guess the “kiwi ingenuity works” :stuck_out_tongue:

Haha, yeah. Seriously though, number 8 wire is where its at. This programming stuff, who needs that when you have number 8 wire :slight_smile:

Can I suggest David that you --snip-- your quote as its absolutely huge :smiley:

Update:
I will probably make a simpler and clearer explanation for DTA at some point tomorrow, but until then, live with my awful tired grammar. :stuck_out_tongue:

Nice implementation! Honestly, though, I would just put the intake and flywheel in two different tasks. The cortex can support a lot of tasks at a time.

Thanks :slight_smile: Yeah, well I was attempting to do this in one task just for the sake of it, but your right, you would ideally build individual tasks for each mechanism. The intention with this was something really simple, that can be deployed in a pinch, that is easily readable and allows the lazy programmer to live forward without his code reflecting that.

A quote comes to mind,
"Remember:
99% of all ROBOTC programs can be written without multiple tasks if you combine related behaviors!
and
99.9% of all errors in programs that use multitasking are because of improper use of multitasking!!"

I am going to take that with a HUGE grain of salt :wink:

:stuck_out_tongue: No need to take my salt!
http://www.gjsentinel.com/blogs/images/120611%20SaltCrystaUSGOV.jpg

Its not even my quote, its bfeher’s from over on the sticky robotC multitasking thread. No Salt Snatching here :rolleyes:

Wait did you just now correct the spelling of “huge?” (hudge) I honestly thought that was intentional

Ummm, well yeah I didn’t even notice it was spelt incorrectly till just before xD Do you prefer HUDGE? :smiley: I guess I should probably keep it, its become somewhat an icon of my signature :stuck_out_tongue:

EDIT: Changed!

Awesome way to improve on code! I will be looking on using this on my programming system.

But, is there a way you can implement this using rotations the wheel instead of power values of the motor? It will definitely help in accuracy.

Thanks!

Thanks :slight_smile:

In terms of doing this with rotations, I think what your looking for is using a PID/P/PD loop (or TBH :)). This video by 1727B is quite helpful, for PID loops. James (pearman) did the thread earlier on in the year on TBH. :smiley:

D.T.A is just a framework, a way of formatting code. You can expand on this framework with PID control or whatever you decide to do ( like meeeeee ), but in itself its, well, It needs a little love. <3