Project Name: Titan VI - A Six-Wheel Off-Road Robotic Arm Remote Control Vehicle Based on a High-Performance Integrated Control Board
I. Project Introduction and Core Advantages
1. Project Overview
"Titan VI" is a mobile robot platform based on a high-performance integrated control board . This control board , centered on the ATmega32U4 , integrates dual 5A H-bridge motor drives , high-current servo power supply , power management , and Bluetooth communication , greatly simplifying external wiring and improving system reliability and power. It perfectly drives a six-wheeled off-road chassis and a six-degree-of-freedom robotic arm, providing an intuitive wireless remote control experience via a PS2 controller. Users can achieve omnidirectional movement of the vehicle and complex movements of the robotic arm, making it suitable for various scenarios such as education and scientific research, disaster relief simulation, and outdoor exploration.
2. Core Components and Features
Powerful core (MCU: ATmega32U4)
Native USB : No additional conversion chip is required; it can be recognized as a game controller, making PS2 remote programming and future communication with PC extremely simple and stable.
Abundant I/O : All I/O ports are brought out, providing ample interfaces for controlling 6 servos, the PS2 receiver, and other sensors.
Exceptional drive capability ( onboard dual 5A H-bridge drivers )
Powerful performance : The continuous current of 5A per circuit is more than enough to drive the six-wheel chassis composed of six geared motors, providing strong off-road and hill-climbing capabilities.
Safe and reliable : The high-temperature automatic protection function effectively prevents the motor from stalling or overload and burning out the driver. It automatically recovers after the fault is cleared, which greatly improves the robustness of the system.
Professional power management system (input 6V-12.6V, LM2675)
Wide voltage input : Lithium batteries ( 7.4V or 11.1V ) can be used directly without the need for an additional voltage regulator module.
High-current power supply : The LM2675 switching regulator chip provides up to 3.6A of current , which is sufficient to stably power multiple servos (especially robotic arm servos which have a large current at startup) and the entire system at the same time, fundamentally solving the jitter and reset problems caused by insufficient power supply to the servos .
Ultimate integration and convenience
Plug-in interface : The motor and servo interfaces are directly laid out on the board, eliminating the need for external drive boards or breadboards, achieving "plug-and-play", resulting in neat wiring and high reliability.
Comprehensive protection : The MOSFET reverse connection and overload protection circuit effectively prevents hardware damage caused by wiring errors or short circuits, making it very user-friendly for beginners and quick deployment.
Communication expansion : Onboard Bluetooth module interface, providing convenient upgrade space for subsequent mobile APP control or wireless data transmission.
II. Hardware Assembly Tutorial
1. List of Required Materials
Core component : Leonardo motor driver development board x1
Power source : Geared motor (with tires) x6
Execution : 6-DOF robotic arm kit (including 6 servos) x1
Controls : PS2 wireless controller and receiver module x1
Power supply : 7.4V lithium battery (2200mAh or higher recommended) x1
Structure : Six-wheeled vehicle chassis structural components, robotic arm mounting bracket
Connections : Servo extension cable, motor cable, DuPont wire (for PS2 receiver)
2. Wiring steps
a. Power supply section:
Connect the 7.4V lithium battery directly to the power input interface of the control board . Ensure proper reverse connection.
b. Motor section:
Connect the three motors on the left side in parallel and connect them to the interface marked ML .
Connect the three motors on the right side in parallel and connect them to the interface marked MR .
c. Robotic arm servo motor component:
Connect the six servo motors (VCC, GND, Signal) directly to the 3-pin servo motor connectors labeled SERVO 1 through SERVO 6 on the control board, one by one. The following layout is recommended:
SERVO 1: Base rotation -> d0
SERVO 2: Upper arm -> d1
SERVO 3: Forearm -> d4
SERVO 4: Wrist Pitch -> d5
SERVO 5: Wrist rotation -> d2
SERVO 6: Claw- >d3
d. PS2 receiver section:
Leonardo's SPI communication is implemented only through the ICSP interface and does not occupy digital I/O pins . Therefore, the PS2 receiver is connected using the pins on the ICSP on the Leonardo motor driver development board .
|
VCC
|
M OSI
|
GND
|
|
MISO
|
SCK
|
RST
|
Functions of the pins used:
MISI : Signal lines used by the master device to send data to the slave device .
MISO : A data transfer line from slave device to master device.
SCK : Clock signal, provided by the master device. Data transmission occurs on the edge (rising or falling edge) of the clock signal.
III. Core of Software Programming
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include < SPI.h >
const int slaveSelectPin = 9;
unsigned char PS2buf[10];
unsigned char i ;
unsigned long t1, t2;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial){
}
Serial.println("Inization...");
pinMode(slaveSelectPin, OUTPUT);
digitalWrite(slaveSelectPin, HIGH);
SPI.begin();
SPI.setBitOrder(LSBFIRST); //LSBFIRST or MSBFIRST先发
SPI.setDataMode(SPI_MODE3); //CPOL=1 CPHA=1
SPI.setClockDivider(SPI_CLOCK_DIV64); //
delay(20);
Serial.println("Inizatin finished.");
}
void loop() {
// put your main code here, to run repeatedly:
t1 = millis();
if(millis() - t2 > 50){
t2 = millis();
Serial.print( Get_PS2Dat(PS2buf));
Serial.print (", ");
for( i = 0; i < 9; i++){
Serial.print (PS2buf[ i ]);
Serial.print (", ");
}
Serial.println ();
}
}
//-----SPI, one-byte read/write
unsigned char PS2_RWByte(unsigned char dat )
{
SPI.transfer ( dat ); //command SPDR= dat ;
// while( !(SPSR&0x80)); // Read SPSR to determine if sending/receiving is complete!
return (SPDR); // SPI.transfer (0x00); //value
}
/*
It requires an interval of at least 20ms to access once.
The buf requires at least 9 spaces.
Returns 0, failure.
1: Success, Normal Mode
2: Success, joystick extended mode
/
unsigned char Get_PS2Dat(unsigned char * buf )
{
unsigned char i ;
digitalWrite(slaveSelectPin, LOW);
delayMicroseconds(15);
buf[0] = PS2_RWByte(0x01); delayMicroseconds(15); //延时再读取
buf[1] = PS2_RWByte(0x42); delayMicroseconds(15);
buf[2] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[3] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[4] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[5] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[6] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[7] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[8] = PS2_RWByte(0x00); delayMicroseconds(15);
digitalWrite(slaveSelectPin, HIGH);
if (( buf [0] == 0xff) && ( buf [1] == 0x41) && ( buf [2] == 0x5a)) // Success, normal mode
return 1;
if (( buf [0] == 0xff) && ( buf [1] == 0x73) && ( buf [2] == 0x5a)) // Success, joystick extended mode
return 2;
return 0;
}
|
First, we need to define the program pin settings and parameters, the SPI one-byte read/write function, and the PS2 nine-byte data read function.
In setup(), we need to set the pin mode and initialize the SPI.
In loop(), we read the PS2 controller's data every 50 milliseconds.
A servo motor is a type of electric motor used to control the precise movement of a device. It typically consists of a DC motor, control circuitry, and a feedback mechanism. The feedback mechanism provides information about the motor shaft's position to the control circuitry, which then adjusts the motor's power accordingly. This feedback loop ensures that the motor shaft moves to the precise position specified by the control signal. Servos are used in a variety of applications requiring precise positioning control, such as robotics, CNC machines, and 3D printers. They are also used in aircraft to control surfaces such as elevators and ailerons.
in Arduino . One is to use the <Servo> library, which is straightforward. This case uses another method: PWM (Pulse Width Modulation).
Let's take a look at this picture:
From this, we can see that there are several points we need to pay attention to:
1. The control frequency determines the pulse period; taking 50Hz as an example, the pulse period is 20ms.
2. Pulse width range of 0.5ms-2.5ms corresponds to an angle of 0°-0180°;
3. Within the pulse width range, each duty cycle corresponds to one angle;
We previously mentioned the resolution of PWM. Taking a resolution of 2 to the power of 10 as an example , simply put, the pulse period is divided into 1024 parts (0~1023). The higher the resolution, the smaller the angle corresponding to each part within the pulse width range, and the more precise the control.
Therefore, to control the output servo to rotate to the corresponding angle, simply output the corresponding duty cycle.
To control the servo motor to rotate to 0°, with a resolution of 2^ 10 , the output duty cycle should be:
|
0 °= 0.5 ms ÷ 20 ms × 1024 − 1 = 24.6
|
Here, 0.5ms refers to the pulse width for the servo to turn to 0° (the pulse width range is 0.5ms-2.5ms, corresponding to an angle of 0°-180°), 20ms refers to the control frequency, which is the pulse period; 1024 means that the pulse period is divided into 1024 parts (0~1023, so 1 must be subtracted at the end); so the principle is actually how many parts are needed to turn to a specified angle.
For instructions on PWM control, please refer to the following:
A rduino code
|
void setup() {
// put your setup code here, to run once:
pinMode (A0, OUTPUT);
for ( int) i = 0; i <= 50; i ++) {
servopulse (A0, 0);
}
}
void loop() {
// put your main code here, to run repeatedly:
for(int i = 0;i<180;i+5){
servopulse(sv[1], i);
delay(10);
}
}
int pulsewidth;
void servopulse(int pin, int dat) {
pulsewidth = (dat * 11) + 500;
digitalWrite (pin, HIGH);
delayMicroseconds ( pulsewidth );
digitalWrite (pin, LOW);
delay(20 - pulsewidth / 1000);
}
|
First, we need to define the PWM function for the program's servo motion.
In setup(), we need to set the pin mode and set the servo to the 0-degree position.
In loop(), we try to rotate the servo from 0 degrees to 180 degrees.
motor
Switch status
Forward
Open Q1 and Q4;
Shut down Q2 and Q3;
Reversal
Speed control (PWM)
If you want to adjust the speed of a DC motor, one solution is:
Turn on Q1 and input a PWM waveform with a 50% duty cycle to Q4. This will reduce the speed. If you need to increase the speed, set the duty cycle of the input PWM to 100%.
Stopped state
The Leonardo motor driver development board features two parallel motor drivers, offering two motor connection methods: one is a DuPont male connector, and the other is a KF-350-2P 3.5MM connector.
A rduino code
|
int Ldirpin = 6; // Left motor direction control pin
int Rdirpin = 5; // Right motor direction control pin
int Lpwmpin = 11; // Left motor pulse width modulation pin
int Rpwmpin = 10; // Right motor pulse width modulation pin
int carSp = 40;
int carGOSp = 40;
int carBACKSp = 150;
void setup() {
// put your setup code here, to run once:
pinMode(Ldirpin, OUTPUT);
pinMode(Rdirpin, OUTPUT);
pinMode(Lpwmpin, OUTPUT);
pinMode(Rpwmpin, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
orientation3();
delay(1000);
stopcar();
delay(1000);
orientation4();
delay(2000);
stopcar();
delay(2000);
}
void orientation1()//左转
{
digitalWrite(Ldirpin, LOW);
analogWrite(Lpwmpin, carBACKSp);
digitalWrite(Rdirpin, HIGH);
analogWrite(Rpwmpin, carSp);
}
void orientation2()//右转
{
digitalWrite(Ldirpin, HIGH);
analogWrite(Lpwmpin, carSp);
digitalWrite(Rdirpin, LOW);
analogWrite(Rpwmpin, carBACKSp);
}
void orientation3()//前进
{
digitalWrite(Ldirpin, HIGH);
analogWrite(Lpwmpin, carGOSp);
digitalWrite(Rdirpin, HIGH);
analogWrite(Rpwmpin, carGOSp);
}
void orientation4()//后退
{
digitalWrite(Ldirpin, LOW);
analogWrite(Lpwmpin, carBACKSp);
digitalWrite(Rdirpin, LOW);
analogWrite(Rpwmpin, carBACKSp);
}
void stopcar()//停车
{
digitalWrite(Ldirpin, LOW);
digitalWrite(Lpwmpin, LOW);
digitalWrite ( Rdirpin , LOW);
digitalWrite ( Rpwmpin , LOW);
}
|
motion speed variables required by the program , as well as the five motion modes composed of two motors .
In setup(), we need to set the pin mode.
In loop(), we'll try out the three motion modes created by the two motors. You can also directly call up two other motion modes. Give it a try!
Complete Arduino IDE code
|
/************For Minidriver Board******************************
attention - to digital pin 6 (simulate SS pin)
PB2 - PWM_R
command - to digital pin 11 (MOSI pin)
data - to digital pin 12 (MISO pin)
clock - to digital pin 13 (SCK pin)
__MISO___MOSI_____________________SS________SCK_______
\ data command X | ground 3.3V attention | clock X X /
--12-----11------------------------6--------13------
********************************************************/
#include <SPI.h>
//#include <Servo.h> //引入lib
int sv[6] = {
A0, A1, A2, A3, A4, A5
};
int D0 = 80, D1 = 60, D2 = 45, D3 = 130, D4 = 115, D5 = 105;
int Ldirpin = 6; // Left motor direction control pin
int Rdirpin = 5; // Right motor direction control pin
int Lpwmpin = 11; // Left motor pulse width modulation pin
int Rpwmpin = 10; // Right motor pulse width modulation pin
int carSp = 40;
int carGOSp = 40;
int carBACKSp = 150;
const int slaveSelectPin = 9;
unsigned char PS2buf[10];
unsigned char i;
unsigned long t1, t2;
void setup()
{
pinMode(Ldirpin, OUTPUT);
pinMode(Rdirpin, OUTPUT);
pinMode(Lpwmpin, OUTPUT);
pinMode(Rpwmpin, OUTPUT);
pinMode(sv[0], OUTPUT);
pinMode(sv[1], OUTPUT);
pinMode(sv[2], OUTPUT);
pinMode(sv[3], OUTPUT);
pinMode(sv[4], OUTPUT);
pinMode(sv[5], OUTPUT);
for (int i = 0; i <= 50; i++) {
servopulse(sv[0], D0);
}
for (int i = 0; i <= 50; i++) {
servopulse(sv[1], D1);
}
for (int i = 0; i <= 50; i++) {
servopulse(sv[2], D2);
}
for (int i = 0; i <= 50; i++) {
servopulse(sv[3], D3);
}
for (int i = 0; i <= 50; i++) {
servopulse(sv[4], D4);
}
for (int i = 0; i <= 50; i++) {
servopulse(sv[5], D5);
}
Serial.begin(9600);
while(!Serial){
}
Serial.println("Inization...");
pinMode(slaveSelectPin, OUTPUT);
digitalWrite(slaveSelectPin, HIGH);
SPI.begin();
SPI.setBitOrder(LSBFIRST); //LSBFIRST or MSBFIRST先发
SPI.setDataMode(SPI_MODE3); //CPOL=1 CPHA=1
SPI.setClockDivider(SPI_CLOCK_DIV64); //
delay(20);
Serial.println("Inizatin finished.");
}
void loop()
{
t1 = millis();
if(millis() - t2 > 50){
t2 = millis();
Serial.print( Get_PS2Dat(PS2buf));
Serial.print(", ");
for(i=0;i<9;i++){
Serial.print(PS2buf[i]);
Serial.print(", ");
}
Serial.println();
if(Get_PS2Dat(PS2buf)==1){
switch (PS2buf[3]){
// Jump to the result of menu()
case 127: orientation1(); break; // Turn left
case 223: orientation2(); break; // Turn right
case 239: orientation3(); break; // Forward
case 191 : orientation4(); break; // Back
default: stopcar (); break;
}
}
switch (PS2buf[4]){
// Jump to the result of menu()
case 239: orientation6(); break; //d1
case 191: orientation7(); break;
case 223: orientation8(); break; //d0
case 127: orientation9(); break;
case 111 : orientation10(); break; //d2
case 63 : orientation11(); break;
case 207 : orientation12(); break; //d3
case 159 : orientation13(); break;
case 247 : orientation14(); break; //d4
case 253 : orientation15(); break;
case 251 : orientation16(); break; //d5
case 254 : orientation17(); break;
default: ; break;
}
}
}
int pulsewidth;
void servopulse ( int pin, int dat ) {
pulsewidth = ( dat * 11) + 500;
digitalWrite (pin, HIGH);
delayMicroseconds ( pulsewidth );
digitalWrite (pin, LOW);
delay(20 - pulsewidth / 1000);
}
//-----SPI, one-byte read/write
unsigned char PS2_RWByte(unsigned char dat )
{
SPI.transfer ( dat ); //command SPDR= dat ;
// while( !(SPSR&0x80)); // Read SPSR to determine if sending/receiving is complete!
return (SPDR); // SPI.transfer (0x00); //value
}
/*
It requires an interval of at least 20ms to access once.
The buf requires at least 9 spaces.
Returns 0, failure.
1: Success, Normal Mode
2: Success, joystick extended mode
/
unsigned char Get_PS2Dat(unsigned char * buf )
{
unsigned char i;
digitalWrite(slaveSelectPin, LOW);
delayMicroseconds(15);
buf[0] = PS2_RWByte(0x01); delayMicroseconds(15); //延时再读取
buf[1] = PS2_RWByte(0x42); delayMicroseconds(15);
buf[2] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[3] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[4] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[5] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[6] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[7] = PS2_RWByte(0x00); delayMicroseconds(15);
buf[8] = PS2_RWByte(0x00); delayMicroseconds(15);
digitalWrite(slaveSelectPin, HIGH);
if (( buf [0] == 0xff) && ( buf [1] == 0x41) && ( buf [2] == 0x5a)) // Success, normal mode
return 1;
if (( buf [0] == 0xff) && ( buf [1] == 0x73) && ( buf [2] == 0x5a)) // Success, joystick extended mode
return 2;
return 0;
}
void orientation1() // Turn left
{
digitalWrite ( Ldirpin , LOW);
analogWrite(Lpwmpin, carBACKSp);
digitalWrite(Rdirpin, HIGH);
analogWrite(Rpwmpin, carSp);
}
void orientation2()//右转
{
digitalWrite(Ldirpin, HIGH);
analogWrite(Lpwmpin, carSp);
digitalWrite(Rdirpin, LOW);
analogWrite(Rpwmpin, carBACKSp);
}
void orientation3()//前进
{
digitalWrite(Ldirpin, HIGH);
analogWrite(Lpwmpin, carGOSp);
digitalWrite(Rdirpin, HIGH);
analogWrite(Rpwmpin, carGOSp);
}
void orientation4()//后退
{
digitalWrite(Ldirpin, LOW);
analogWrite(Lpwmpin, carBACKSp);
digitalWrite(Rdirpin, LOW);
analogWrite(Rpwmpin, carBACKSp);
}
void stopcar()//停车
{
digitalWrite(Ldirpin, LOW);
digitalWrite(Lpwmpin, LOW);
digitalWrite(Rdirpin, LOW);
digitalWrite(Rpwmpin, LOW);
}
void orientation6() //d1
{
D1++;
if (D1 > 160) {
D1 = 160;
}
servopulse(sv[1], D1);
}
void orientation7() //d1
{
D1--;
if (D1 < 1) {
D1 = 1;
}
servopulse(sv[1], D1);
}
void orientation8() //d0
{
D0++;
if (D0 > 170) {
D0 = 170;
}
servopulse(sv[0], D0);
}
void orientation9() //d0
{
D0--;
if (D0 < 20) {
D0 = 20;
}
servopulse ( sv [0], D0);
}
void orientation10() //d4
{
D4++;
if (D4 > 140) {
D4 = 140;
}
servopulse ( sv [4], D4);
}
void orientation11() //d4
{
D4--;
if (D4 < 30) {
D4 = 30;
}
servopulse(sv[4], D4);
}
void orientation12() //d5
{
D5++;
if (D5 > 105) {
D5 = 105;
}
servopulse(sv[5], D5);
}
void orientation13() //d5
{
D5--;
if (D5 < 15) {
D5 = 15;
}
servopulse(sv[5], D5);
}
void orientation14() //d2
{
D2++;
if (D2 > 160) {
D2 = 160;
}
servopulse(sv[2], D2);
}
void orientation15() //d2
{
D2--;
if (D2 < 20) {
D2 = 20;
}
servopulse(sv[2], D2);
}
void orientation16() //d3
{
D3++;
if (D3 > 180) {
D3 = 180;
}
servopulse ( sv [3], D3);
}
void orientation17() //d3
{
D3--;
if (D3 < 1) {
D3 = 1;
}
servopulse ( sv [3], D3);
}
|
IV. User Guide and Advanced Tips
Power-on and testing :
Connect the battery and turn on the switch. The onboard power indicator light should illuminate.
Initial testing recommendation : Test the motors and servos separately first. Upload a simple program to ensure that the left and right motors can rotate forward and backward according to commands , and that each servo can move to the specified angle sequentially.
PS2 Pairing : Test if the PS2 controller data output is normal .
Remote control operation : The logic remains the same, but due to the powerful performance of the board, the operation response will be faster and more powerful . Please pay attention to safety .
Safety and Maintenance :
Make full use of the circuit board's protection functions . If the motor stops, it may be due to over-temperature or overload protection being triggered. Please check if the mechanical structure is stuck; it should recover after a short while.
Although the board has a powerful power supply, continuously operating all servos at full load will still consume a lot of power. Please use batteries of appropriate capacity.
Advanced Development :
Bluetooth Functionality : After inserting the Bluetooth module , you can write Arduino code to communicate with a mobile app (such as the Arduino Bluetooth Controller) via serial port, enabling manual button control and even preset action groups. Note that the module should not be plugged into the interface when uploading the program. The serial port functions (RX, TX) used by this interface can also be replaced with other serial port interface modules or sensors .
Sensor fusion : By utilizing the brought-out I/O ports, ultrasonic obstacle avoidance, line following modules, or IMUs can be easily added, giving the vehicle semi-autonomous capabilities.
Replacing Development Boards : Utilizing the porous structural components, it can be adapted to other development boards, enabling more development activities.