RC Drift Car Hardware Part 1

by jmsam139 in Circuits > Robots

22 Views, 1 Favorites, 0 Comments

RC Drift Car Hardware Part 1

car_upside_down.png
RC Hardware Schematic.png

In this project, I and Arturo Paras hacked into an RC car to automatically detect and turn and drift in the direction of an arrow with CV.

Supplies

Here's the list of all the materials we used:


RC car used:

Citroen C3 RC Car


Control Related:

Arduino

Digital Potentiometer x1


CV Related:

raspberry pi 5

Camera x1


(consider buying more than one order of the potentiometers/cameras, as they may come broken)

Collecting Values Before You Break It

First, a brief explanation of our approach: Instead of trying to talk directly to tap into the ESC already hardwired into the motors and other parts of the car, we decided to take a less invasive approach, at least for the car itself. The hand-held controller on the other hand, we took apart quite liberally.

Before we rip everything apart, it is much easier to perform some data collection now that is necessary later. We will still need to open up the controller for this step, just don't remove any pieces yet.

When opening the hand-held controller, we see that the user input actually just boils down to two potentiometers. The first is for the trigger, and controls movement forwards and backwards. The second controls steering.

Note that when you are not touching the controller at all, the potentiometers are in a position that divides the voltage roughly equally. Using a voltmeter, get the value of this "base state" for your controller. You will use this to calculate at what step your digital potentiometers should reset themselves to to start (since it won't trivially be zero). As an example, my potentiometer has a wiper to ground value of 1.23 in its resting state, and goes up to 2.8 when directing the car to go forward, and 0 when directing the car to go backward. The steering potentiometer had 1.63 resting (straight), 2.8 right, and 0 left for it's respective values. Your highest values should be close to the full voltage across the potentiometer (high to ground), which may vary between RC cars you choose.

If you don't want to do all that, honestly, I'm pretty sure you don't need to be this precise with the values, so it should be fine. In retrospect, it seems the steering is just straight if the ratio is about a half, then gets gradually turned more right or left as the ratio goes up or down. The trigger is a little more complicated. Since the motor can't do anything below a certain threshold of voltage, the car uses the values below a certain threshold to tell the car to go backwards, instead of it being just a low value of forwards. For our car, if it was going forwards, then you push the trigger out to tell it to go backwards, it will just break. You need to re-center the trigger, then push it out to go backwards again, and then it will actually go backwards. Note any similar oddities in your own car's controller as you try to automate it.

Repurposing the Controller

(ADD IN VIDEO FROM SLIDE 8 IN THE FINAL PRESENTATION)

Remember, as far as the controller is concerned, there are only the two potentiometers for input. So, all we have to do to insert our own inputs is to swap out the physical potentiometers with our own digital ones that we can control automatically. That is our end goal.

To get there, first test all your connections by breadboarding in a different pair of potentiometers, which should result in something like the above video. I personally made the connections by first fully removing the old potentiometers and soldering on pin headers in their place. Then, I simply used jumper wires from the pins onto a breadboard, where I placed some other 10K potentiometers I had lying around (what resistance value the potentiometers are doesn't really matter, the controller only cares about the resulting voltage ratio that is produced by them)

Going Digital

Now that you have easy access to the controllers input system, we can easily swap out the potentiometers you used for testing with digital potentiometers!

For the ones we used (listed in the materials section), the corresponding data sheet applies.

Detailed in the data sheet is how we need to interface with the chip to get it to do what we want, that being control the car. Note the number of steps whatever digital potentiometer you choose is capable of. You may want to test that it behaves as expected. For our project, we had to use two different digital potentiometers, which is why our variables in code may be a little weird for you looking through them. The potentiometer we used for the steering only had 34 steps (STEERING_MAX), while the motor had a finer number of 100 steps (MOTOR_MAX). Whatever you change those variables to, you should also change their corresponding resting variables: STATIC_MOTOR_STEP should be about 55-60% of the max number of steps. STATIC_STEERING_STEP should be as close to half of STEERING_MAX as possible.

On your end, I don't think any other modifications to the code will be necessary other than those variables. (if you use a digital potentiometer at least within the same family as the ones we did) See the last steps for all the Arduino code.

Raspberry Pi Integration

Will be added by partner

CV Implementation

Will be added by partner

All Arduino Code

Below is all the Arduino code that we used for the final project. It won't let me zip it up for you, so this is the next best I can do:

const int STEERING_POT_CS_N = 11; // Select pin for Potentiometer 1 (servo steering)
const int STEERING_POT_UD = 12; // Up/Down pin for Potentiometer 1
const int STEERING_POT_INC_N = 13; // Increment pin for Potentiometer 1

const int MOTOR_POT_CS_N = 4; // Select pin for Potentiometer 2 (motor power)
const int MOTOR_POT_UD = 5; // Up/Down pin for Potentiometer 2
const int MOTOR_POT_INC_N = 6; // Increment pin for Potentiometer 2

// potentiometer step constants
const int STATIC_MOTOR_STEP = 60;
const int STATIC_STEERING_STEP = 17;
const int STEERING_MAX = 34;
const int MOTOR_MAX = 100;
const int MOTOR_MIN = 0;

const int PI_BAUD_RATE = 9600;

int steering_counter = 0; // value that stores the amount we've incremented by for pot1
int motor_counter = 0; // value that stores the amount we've incremented by for pot2

char incomingByte = '\0';
char prev_char = '\0';

int desired_speed; // dynamic target value for the movement motor
int desired_angle; // dynamic target value for the servo steering motor

int forward_speed_cap;
int backward_speed_cap;

bool hit_motor_target;
bool hit_steering_target;

void setup() {
// declare the Potentiometer pins as OUTPUTs:
pinMode(STEERING_POT_CS_N, OUTPUT);
pinMode(STEERING_POT_UD, OUTPUT);
pinMode(STEERING_POT_INC_N, OUTPUT);

pinMode(MOTOR_POT_CS_N, OUTPUT);
pinMode(MOTOR_POT_UD, OUTPUT);
pinMode(MOTOR_POT_INC_N, OUTPUT);

steering_counter = 0;
motor_counter = 0;

desired_speed = STATIC_MOTOR_STEP;
desired_angle = STATIC_STEERING_STEP;
forward_speed_cap = 100;
backward_speed_cap = 0;

hit_motor_target = true;
hit_steering_target = true;

Serial.begin(PI_BAUD_RATE);
}

// RESET THE POTENTIOMETERS! **************************************************
void reset_potens() {
// start high to reset potentiometer
digitalWrite(STEERING_POT_CS_N, HIGH);
digitalWrite(MOTOR_POT_CS_N, HIGH);
digitalWrite(STEERING_POT_INC_N, HIGH);
digitalWrite(MOTOR_POT_INC_N, HIGH);
// ask_and_wait("set high");
delay(2);

// forcing the counter down to zero:
digitalWrite(STEERING_POT_CS_N, LOW); // activate potentiometer
digitalWrite(MOTOR_POT_CS_N, LOW); // activate potentiometer
digitalWrite(STEERING_POT_UD, HIGH); // counter set to increment down
digitalWrite(MOTOR_POT_UD, HIGH); // counter set to increment down
// ask_and_wait("set low");

// tick the counter down a bunch of times
for (int i = 0; i < 100; i++) {
digitalWrite(STEERING_POT_INC_N, HIGH); // set pos
digitalWrite(MOTOR_POT_INC_N, HIGH); // set pos
delay(2);
digitalWrite(STEERING_POT_INC_N, LOW); // make neg edge to increment
digitalWrite(MOTOR_POT_INC_N, LOW); // make neg edge to increment
// Serial.print("stepping with reset value: ");
// Serial.println(i);
// ask_and_wait("");
delay(2);

}

digitalWrite(STEERING_POT_INC_N, HIGH); // set back to pos
digitalWrite(MOTOR_POT_INC_N, HIGH); // set back to pos
delay(2);
}


/* ***** ***** SET TARGET FUNCTIONS ***** ***** */

void set_motor_target_forward() {
desired_speed = forward_speed_cap;
}

void set_motor_target_backward() {
desired_speed = backward_speed_cap;
}

void set_angle_target_right() {
desired_angle = 0;
}

void set_angle_target_left() {
desired_angle = STEERING_MAX;
}


void reset_motor_target() {
desired_speed = STATIC_MOTOR_STEP;
}

void reset_angle_target() {
desired_angle = STATIC_STEERING_STEP;
}


void reset_targets() {
reset_angle_target();
reset_motor_target();
}

// bool prev_char_valid() {
// return (prev_char == 'w' ||
// prev_char == 'a' ||
// prev_char == 's' ||
// prev_char == 'd' ||
// prev_char == 'r' ||);
// }

void set_targets(char c) {

// base case, if nothing is being pressed
if (c == 'w') {
// Serial.println("Going UP");
set_motor_target_forward();
reset_angle_target();
} else if (c == 'q') {
set_motor_target_forward();
set_angle_target_left();
} else if (c == 'e') {
set_motor_target_forward();
set_angle_target_right();
} else if (c == 's') {
// Serial.println("Going DOWN");
set_motor_target_backward();
reset_angle_target();
} else if (c == 'a') {
// Serial.println("Going LEFT");
set_motor_target_backward();
set_angle_target_left();
} else if (c == 'd') {
// Serial.println("Going RIGHTu");
set_motor_target_backward();
set_angle_target_right();
} else if (c == 'r') {
reset_targets();
} else if (c == ' ') {
reset_motor_target();
} else if (c == '1') {
forward_speed_cap = MOTOR_MAX - 40;
} else if (c == '2') {
forward_speed_cap = MOTOR_MAX - 30;
} else if (c == '3') {
forward_speed_cap = MOTOR_MAX - 20;
} else if (c == '4') {
forward_speed_cap = MOTOR_MAX - 10;
} else if (c == '5') {
forward_speed_cap = MOTOR_MAX - 0;
}
// else {
// // println("not valid: ");
// // Serial.println(c);
// }
prev_char = c;
}

/* ***** ***** REACT/CHASE TARGET FUNCTIONS ***** ***** */

// set UD counter to high or low based on the current value and target
// FOR THE STEERING SERVO
void react_to_angle_target() {
if (steering_counter < desired_angle) {
digitalWrite(STEERING_POT_UD, LOW);
hit_steering_target = false;
} else if (steering_counter > desired_angle) {
digitalWrite(STEERING_POT_UD, HIGH);
hit_steering_target = false;
}
}

// create negative edges for the INCREMENT values as needed
// FOR THE STEERING SERVO
void chase_angle_target() {
if (steering_counter < desired_angle) {
digitalWrite(STEERING_POT_INC_N, LOW);
steering_counter++;
} else if (steering_counter > desired_angle) {
digitalWrite(STEERING_POT_INC_N, LOW);
steering_counter--;
}

if (steering_counter == desired_angle) {
hit_steering_target = true;
}
}

// set UD counter to high or low based on the current value and target
// FOR THE MOTOR
void react_to_motor_target() {
if (motor_counter < desired_speed) {
digitalWrite(MOTOR_POT_UD, LOW);
hit_motor_target = false;
} else if (motor_counter > desired_speed) {
digitalWrite(MOTOR_POT_UD, HIGH);
hit_motor_target = false;
}
}

// create negative edges for the INCREMENT values as needed
// FOR THE MOTOR
void chase_motor_target() {
if (motor_counter < desired_speed) {
digitalWrite(MOTOR_POT_INC_N, LOW);
motor_counter++;
} else if (motor_counter > desired_speed) {
digitalWrite(MOTOR_POT_INC_N, LOW);
motor_counter--;
}

if (motor_counter == desired_speed) {
hit_motor_target = true;
}
}

// first react to new angle/motor target changes by changing U/D pins
// then chase those targets with a INCREMENT Neg edge if needed
void chase_targets() {
react_to_angle_target();
react_to_motor_target();

// make sure that combinational logic can propogate
// in the digital potentiometers with a delay
delay(2);

chase_angle_target();
chase_motor_target();
}




/* ***** ***** RASPI INTERFACE FUNCTIONS ***** ***** */
char get_pi_pressed() {
// only change input if we've hit all targets
if (hit_motor_target && hit_steering_target) {
incomingByte = '\0';
}

// String incomingString = Serial.read();
// char incomingByte = incomingString.charAt(0));
if (Serial.available()) {
incomingByte = Serial.read();
// Serial.println(incomingByte);
}

return incomingByte;
}

void loop() {
// Serial.println("Initializing Potentiometers:");
reset_potens();

while (1) {

// Serial.print("SPEED STEP = ");
// Serial.print(motor_counter);
// Serial.print(" | SERVO STEP = ");
// Serial.println(steering_counter);

char c = get_pi_pressed();
// c = 'a';

set_targets(c);

chase_targets();

// add slight delay to make negedges distinct between loops
delay(2);
// end by pulling all potens high again
digitalWrite(STEERING_POT_INC_N, HIGH);
digitalWrite(MOTOR_POT_INC_N, HIGH);
}
}

Raspberry Pi Code

Will be added by partner.