Ambassador Moment: Building a Hardware Module for Electric Vehicle
2021-05-07 | By Robby Huang
License: See Original Project
This project was designed by one of Digi-Key’s own university ambassador students. Check out how it works in the video below!
Electric vehicles (EVs) are becoming increasingly common worldwide as the demand for safer, more reliable (and eco-friendly) cars grows. With the latest technology in EVs comes the need for intricate electrical designs capable of carrying out various tasks simultaneously.
This project will focus on the automation module for an electric vehicle, starting with an Arduino prototype and ending with a PCB that will allow the computer in an autonomous vehicle to steer or brake the car.
System Description
The system takes signals from the Jetson GPU for steering, throttling, and braking to the respective controlling system. All signals will first be inputted into the microcontroller. The steering signal tells us the steering angle. We will convert that to steps of the stepper motor through a quadruple half-h bridge driver in which the stepper motor interfaces with the steering column by gears. A feedback system is installed in place to make up the difference between the input steps and the actual steps. The throttling signal will be forwarded to a motor controller. The braking signal will control the other stepper motor, which interfaces with one of the master cylinders of the front brake.
Terminology
Stepper Motor: A brushless DC electric motor that divides a full rotation into a number of equal steps.
Rotary Encoder: Also called a shaft encoder, an electro-mechanical device that converts the angular position or motion of a shaft or axle to analog or digital output signals.
PID controller: A proportional–integral–derivative controller (PID controller or three-term controller) is a control loop mechanism employing feedback widely used in industrial control systems and a variety of other applications requiring continuously modulated control.
Closed-Loop Control System: A closed-loop control system is a set of mechanical or electronic devices that automatically regulates a process variable to the desired state or setpoint without human interaction.
Research and Requirements
This system’s main aspects are using a microcontroller, stepper motor, quadruple half-h bridge driver, and rotary encoder. Essentially, the heart of the system lies within the microcontroller, an integrated circuit chip dedicated to running a specific process. This system will use a microcontroller to communicate with the stepper motor and encoder to ensure the system works properly.
A stepper motor is a specific type of DC motor that moves in particular increments called "steps." This is achieved by having four separate groups of coils energized one at a time, moving the entire motor by one small step (shown below). More information on stepper motors can be found here.
The quadruple half-h bridge driver is an integrated circuit that consists of four separate switches that control the flow of current and switch the polarity of an input voltage (shown below). This allows for the motor’s direction to change from clockwise to counter-clockwise almost instantaneously when connected to a stepper motor. More information on h-bridge drivers can be found here.
A rotary encoder is a device that can take an angular position and convert it into an analog or digital signal to provide feedback. A microcontroller can then use this data to provide correction to the stepper motor.
Design Requirements
This system aims to receive signals from the motor controller to perform braking, steering, and throttling, creating a fully automated driving system. The main requirement is that the braking and steering mechanisms have a manual steering override, which the driver can engage/disengage completely. With this, the system’s speed must also be incredibly fast so that the time delay between the controlling signals is near-negligible; otherwise, the system will not work effectively. The background noise signals created by the motor must also be sent through a low-pass filter to prevent the system from operating when it shouldn't be. The overall design must also be condensed enough to be mounted on the bulkhead with the other equipment.
Key Parameters
The main parameters of this design include the steering angle, torque, speed, and resolution.
Steering Angle: The ideal steering angle values are ±21.5˚. This was determined by the safety parameters of the car to make sure it does not overturn while driving. The chosen range is comfortably within the anticipated steering limits and should not pose any safety concerns.
Torque: The ideal final torque value is 1 Nm. This parameter was calculated and provided by the Front Assembly Team. The primary constraint on the torque is the speed at which the stepper motor is running. This value is entirely dependent on the speed, which is discussed below.
Speed: The final chosen RPM for the stepper motor is around 5 RPM (30˚/sec). After the gear ratio, the final RPM should be approximately 15 RPM. This value was chosen based on the documentation given by the manufacturer (see below). The attached chart illustrates that lower RPMs will show higher torques, and 15 RPM provides a near-optimal torque while working with the gear ratio.
Resolution: The resolution of the encoder was determined to be around 1-2˚. This value was chosen because it accounts for errors in the stepper motor at the speed it is being run. While the resolution can be made more precise, it would lead to round-off errors while processing and make the correction factor invalid.
System Diagram
Potential Designs
Two main potential designs were looked at when first designing the system. The first used an Arduino to control the stepper motors, while the other used a Raspberry Pi:
Arduino
Microcontroller
Ideal for repetitive tasks
Easier to Program
Raspberry Pi
Single-board Computer
Ideal for multi-tasking
Much Harder to Program
The first design is to utilize the Arduino as a microcontroller that will provide a signal to tell the stepper motor to move a certain number of steps to throttle and brake the car. It will be run using a loop that will continuously provide signals when the system is engaged.
The second design will use the Raspberry Pi to communicate with the sensors in the car continuously. This will provide commands to move the car a certain way and essentially need a control hub that can be accessed remotely.
Both design options are relatively comparable in size and weight, so this is not a primary concern when determining the final design. However, the Raspberry Pi is slightly more expensive than the Arduino, which has been considered. The real difference is the feasibility and manufacturability between the two designs. The Arduino is much more realistic to design and implement while still providing an engaging experience. On the other hand, the Raspberry Pi requires much more time and attention and is much more complex than the Arduino. The Raspberry Pi will likely be better in later years when cars are entirely automated.
Selected Design and Justification
Ultimately, an Arduino was chosen for this design as it is open-source and, therefore, easier to implement on a PCB and is more cost-effective (at the time this was written, $20 for an Arduino vs. $35 for a Raspberry Pi). Although the Raspberry Pi is more powerful, its complexity is a significant drawback for implementing the system. Designing a custom PCB also makes it easier to communicate with the other systems on the car, such as the BMS and Motor Controller.
There are also two potential methods for implementing a feedback system: using a PID controller to provide a correction factor or a simple feedback algorithm. The feedback algorithm was chosen because the encoder can be easily implemented, and the code on the Arduino is relatively straightforward. The encoder chosen is also much cheaper than any PID controller that is suitable for this system.
If the desired implementation does not work, the open-source nature of the Arduino provides flexibility to debug and optimize the design.
Detailed Design (Schematic & Layout)
This system takes the analog signal sent from the Jetson computers and converts them to braking, steering, and throttling. A custom PCB schematic and layout were designed to accomplish this. The circuit is essentially a combination of two stepper motor control circuits. With this, the ATmega328p chip (with 32KB flash memory) was programmed to control two SN754410 Quadruple Half-H Bridge drivers. Each of these drivers is connected to one Geared Stepper Motor. One is for steering and the other for braking. An encoder was also connected to the ATmega chip to create a feedback system for the steering motor (which requires more accuracy).
In terms of my PCB layout, it is designed to be a two-layer PCB. The top layer is poured with a polygon pour connected with the 5 V net. The bottom layer is poured with GND. The top layer has all the connectors including two Molex connectors for the motor connections, one Molex connector for input power, a six-pin connector for the shaft encoder, and a four-pin header for the analog inputs. This is to be kept on the top so that they can be easily accessed. All the connections with the ATmega328p remain on the bottom layer because it is quite messy.
Schematic
Layout(Front)
Layout(Back)
Layout(3D)
Component Selection
The final components selected are as follows (prices were accurate at the time this was written):
BOM
Analysis
Load Cases
According to the chips’ spec sheet, the maximum voltage is 5 V, and the current draw is 1 A. Mechanical load cases are included in the design parameter section.
Hand Calculations
Torque Calculation: Max Gear Ratio: 3 Min Torque Supplied From Motor: 3 Nm/3 = 1 Nm
Speed Calculation: 30 degrees/sec = 1800 degrees/min = 5 RPM: 5rpm * 3 (gear ratio) = 15 RPM
Testing Plan
Initial testing for this system was done mostly with a breadboard and prototyping board using the ATmega, SN754410, and stepper motors. A program was created using documentation provided by Arduino. Then, variable voltage inputs were placed throughout the system, and the resulting torque was measured at the required RPM. All of the primary data collection included input voltage, output torque, and RPM.
PCB Testing:
- Before soldering the board, test essential connections.
- During soldering, complete conductivity tests for all connections and possible shorts. Also, perform a resistance test for the resistors.
- Once populated, a sheet was created for a series of tasks that must be passed incrementally. Testing scripts are included below.
Testing Script
Blink.ino
/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.
This example code is in the public domain.
*/
// Pin 13 has an LED connected on most Arduino boards.
// Pin 11 has the LED on Teensy 2.0
// Pin 6 has the LED on Teensy++ 2.0
// Pin 13 has the LED on Teensy 3.0
// give it a name:
int led = 19;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
}
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
Encoder.ino
#define encoder0PinA 19
#define encoder0PinB 20
int encoder0Pos = 0;
int led = 21;
void setup() {
Serial.begin(9600);
pinMode(encoder0PinA, INPUT_PULLUP);
pinMode(encoder0PinB, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(5), doEncoder, CHANGE);
pinMode(led, OUTPUT);
}
int valRotary,lastValRotary;
void loop() {
Serial.println(valRotary);
delay(100);
if (encoder0Pos>500){
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000);
}
}
void doEncoder()
{
if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB))
{
encoder0Pos++;
}
else
{
encoder0Pos--;
}
StepperMotorTest.ino
#include <Stepper.h>
const int stepsPerRevolution = 1036; // change this to fit the number of steps per revolution
// for your motor
// initialize the stepper library on pins 8 through 11:
Stepper steeringStepper(stepsPerRevolution, 2, 3, 5, 4);
Stepper brakingStepper(stepsPerRevolution, 12, 13, 15, 14);
int stepCount = 0; // number of steps the motor has taken
void setup() {
// put your setup code here, to run once:
steeringStepper.setSpeed(7);
brakingStepper.setSpeed(7);
}
void loop() {
steeringStepper.step(stepsPerRevolution);
//steeringStepper.step(-stepsPerRevolution);
brakingStepper.step(stepsPerRevolution);
//brakingStepper.step(-stepsPerRevolution);
}
TwoMotorSpiningCode.ino
#include <Stepper.h>
#define brakeInterrupt 6
const int stepsPerRevolution = 1036.3636; // change this to fit the number of steps per revolution
// for your motor
const int MAX_SPEED = 7;
// initialize the stepper library on pins 8 through 11:
Stepper steeringStepper(stepsPerRevolution, 2, 3, 5, 4);
Stepper brakingStepper(stepsPerRevolution, 12, 13, 15, 14);
void setup() {
// put your setup code here, to run once:
steeringStepper.setSpeed(MAX_SPEED);
brakingStepper.setSpeed(MAX_SPEED);
attachInterrupt(digitalPinToInterrupt(brakeInterrupt), doBrake, RISING);
}
void loop() {
steeringStepper.step(stepsPerRevolution);
//steeringStepper.step(-stepsPerRevolution);
}
void doBrake()
{
//braking motor steps
brakingStepper.step(stepsPerRevolution);
}
OneMotorWithEncoder.ino
#include <Stepper.h>
#define encoderPinA 2
#define encoderPinB 3
const int stepsPerRevolution = 1036; // change this to fit the number of steps per revolution
// for your motor
int steeringPin = 2;
int encoder; //encoder pin
int encoderPos = 0;
int lastPos;
// initialize the stepper library on pins 8 through 11:
Stepper steeringStepper(stepsPerRevolution, 2, 3, 5, 4);
Stepper brakingStepper(stepsPerRevolution, 12, 13, 15, 14);
int stepCount = 0; // number of steps the motor has taken
void setup() {
// put your setup code here, to run once:
steeringStepper.setSpeed(7);
brakingStepper.setSpeed(7);
pinMode(encoderPinA, INPUT_PULLUP);
pinMode(encoderPinB, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), doEncoder, RISING);
}
void loop() {
int sMotorStep = 1036;
steeringStepper.step(sMotorStep);
steeringStepper.setSpeed(7);
int actualSMotorStep = encoderPos*1036/2048;
int difference = sMotorStep - actualSMotorStep;
if (difference != 0) {
steeringStepper.step(difference);
}
}
void doEncoder()
{
if(encoderPos>lastPos) {
}
if(encoderPos>lastPos) {
}
lastPos = encoderPos;
if (digitalRead(encoderPinA) == digitalRead(encoderPinB)) {
encoderPos++;
}
else {
encoderPos--;
}
}
TwoMotorWithEncoder.ino
#include <Stepper.h>
#define encoderPinA 2
#define encoderPinB 3
#define brakeInterrupt 6
int encoder; //encoder pin
int encoderPos = 0;
int steeringPin = 2;
int brakingPin = 12;
int const MAX_SPEED = 7;
const int stepsPerRevolution = 1036; // change this to fit the number of steps per revolution
// for your motor
// initialize the stepper library on pins 8 through 11 for steering motor:
Stepper steeringStepper(stepsPerRevolution, 2, 3, 4, 5);
// initialize the stepper library on pins w through z for braking motor:
Stepper brakingStepper(stepsPerRevolution, 12, 13, 14, 15);
void setup() {
Serial.begin(9600);
pinMode(encoderPinA, INPUT_PULLUP);
pinMode(encoderPinB, INPUT_PULLUP);
pinMode(steeringPin, INPUT);
pinMode(brakingPin, INPUT);
attachInterrupt(digitalPinToInterrupt(2), doEncoder, RISING);
attachInterrupt(digitalPinToInterrupt(brakeInterrupt), doBraking, RISING );
}
void loop() {
//steering motor steps
int sMotorStep = 1036;
steeringStepper.step(sMotorStep);
steeringStepper.setSpeed(MAX_SPEED);
int actualSMotorStep = encoderPos*1036/2048;
//steering motor correction
int difference = sMotorStep - actualSMotorStep;
if (difference != 0) {
steeringStepper.step(difference);
}
}
void doBraking(){
//braking motor steps
int bMotorStep = analogRead(brakingPin);
brakingStepper.step(bMotorStep);
brakingStepper.setSpeed(MAX_SPEED);
}
int lastPos;
//position checking
void doEncoder()
{
if(encoderPos>lastPos) {
}
if(encoderPos>lastPos) {
}
lastPos = encoderPos;
if (digitalRead(encoderPinA) == digitalRead(encoderPinB)) {
encoderPos++;
}
else {
encoderPos--;
}
}
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum