# 3. Arduino Version ## 3.1 Introduction ### 3.1.1 Arduino Introduction to the Required Hardware for Development
### 3.1.2 Install the motor drive module
### 3.1.3 Install the Arduino development board
### 3.1.4 Connection Instructions
### 3.1.5 Battery connection and power supply precautions * **Development Tutorial (Arduino Version)** (1) Forward for 4 seconds. (2) Back for 4 seconds. (3) Turn left for 4 seconds. (4) Return to the initial position. (5) Forward to the right for 4 seconds. (6) Return to the original position. ## 3.2 Hardware Introduction ### 3.2.1 Arduino UNO Controller Arduino is a convenient, flexible, and user-friendly open-source electronic prototyping platform. It features 14 digital input/output pins (with 6 capable of PWM output), 6 analog inputs, a 16 MHz ceramic resonator (CSTCE16M0V53-R0), a USB connection, a power socket, an ICSP header, and a reset button. The following diagram illustrates the physical pin layout of Arduino UNO (please refer to your specific Arduino UNO main control board for accurate details):
### 3.2.2 4-Channel Encoder Motor Driver
This is a motor drive module designed to work with a microcontroller for driving TT motors or magnetic encoder motors. Each channel is equipped with a `YX-4055AM` motor drive chip, and its voltage range is DC 3V-12V. The specific voltage depends on the voltage requirements of the connected motor. The interface distribution is illustrated in the figure below:
The introduction to the interface on the driver is as below:
| Interface type | NO. | Function |
|---|---|---|
| Encoder motor interface | GND | Negative electrode of the Hall power |
| A | A-phase pulse signal output terminal | |
| B | B-phase pulse signal output terminal | |
| VCC | Positive electrode of the Hall power | |
| M+ | Positive electrode of the motor power supply | |
| M- | Positive electrode of the motor power supply | |
| Note: 1. The voltage between VCC and GND is determined based on the power supply voltage of the microcontroller used. Typically, 3.3V or 5V is used. 2. When the spindle rotates clockwise, the output pulse signal of channel A is ahead of channel B; when the spindle rotates counterclockwise, the signal of channel A is behind channel B. 3. The voltage between M+ and M- is determined based on the voltage requirements of the motor used. |
||
| IIC | SCL | Clock line |
| SDA | Bi-directional data line | |
| GND | Power ground line | |
| 5V | 5V DC output | |
| 3Pin power | - | Power negative electrode |
| port | + | Power positive input |
| NC | Empty | |
| Power port | + | Power positive input |
| - | Power negative electrode |
The steering servo in this chassis utilizes the `LD-1501MG PWM` servo model.
The `LD-1501MG` operates on a `PWM` servo mechanism. To control it, a `PWM` signal with a 20ms period is sent to the signal end. The servo angle is adjusted by manipulating the pulse width within the range of 500 to 2500μs. This corresponds to an angle range of 0 to 180°, and the recommended operating voltage is between `6V` and `8.4V`.
The `PWM` waveform signal is employed to regulate the servo position. When the `PWM` signal is fed into the motor drive circuit, the motor rotation is controlled based on the signal level. A high `PWM` duty cycle results in a greater driving force, enabling a larger rotation angle. Conversely, a low duty cycle yields a smaller driving force, resulting in a reduced motor rotation angle.
By continuously fine-tuning the `PWM` duty cycle, the microcontroller can precisely control the motor's rotation angle. This, in turn, drives the mechanical structure to rotate, achieving accurate position control of the steering gear output shaft.
The diagram above illustrates the wiring port distribution for the bus servo, accompanied by the pin distribution table below. **"Please note: When the servo and the microcontroller are powered by different sources, ensure both power supplies are grounded together."**
| **PIN** | **Description** |
|:-------:|:---------------:|
| White | Signal wire |
| Red | Positive power electrode |
| Black | Ground wire |
### 3.2.4 Encoder Geared Motor
The motor model employed in this chassis is `JGB37-520R30-12`. Here's the breakdown: **"J"** signifies a DC motor, **"GB"** denotes an eccentric output shaft, **"37"** indicates the diameter of the reduction box, **"520"** represents the motor model, **"R30"** stands for the reduction ratio of 1:30, and **"12"** signifies the rated voltage of `12V`. Please refer to the interface description illustrated in the figure below:
## 3.3 Wiring
The `Arduino Uno` is outfitted with a 4-channel motor driver. It operates using an `11.1V 6000mAh` lithium battery to power the motor, while a separate `7.4V` battery is utilized to power the steering servo. Refer to the image below for the `Arduino UNO` wiring diagram.
## 3.4 Environment Configuration and Program Download
### 3.4.1 Environment Configuration
Prior to downloading, ensure that the **"Arduino IDE"** is installed on your computer. You can find the software package in the [2. Software Tools->01 Arduino Installation Package](Appendix.md).
### 3.4.2 Program Running
Open the `control.ino` program saved in [3. Programs->control](../_static/source_code/cotrol.zip) using **"Arduino IDE"**.
(1) Choose the Arduino development board type. In this case, select **"Arduino UNO"**.
(2) Select the USB port the Arduino currently connecting to your computer. The IDE will detect it automatically; in this case, choose **"COM56"**.
(3) Connect `Arduino UNO` to the computer. Select **"Arduino UNO"** in the tool bar, and click-on
to download the program.
(4) The software will compile the program automatically. Please wait until the compilation process is successfully completed.
(5) Wait for the program to finish uploading.
### 3.4.3 Program Outcome
Once the program is downloaded, the car chassis executes the following actions in sequence:
(1) Move forward for 4 seconds;
(2) Move backward for 4 seconds;
(3) Turn left for 4 seconds;
(4) Return to the initial position;
(5) Move forward to the right for 4 seconds;
(6) Return to the original position. There is a 1-second interval between each action.
## 3.5 Program Analysis
[Source Code](../_static/source_code/cotrol.zip)
### 3.5.1 Import Necessary Library
{lineno-start=1}
```c
#include ### 3.7.2 Ackermann Chassis Bluetooth Control In this section, the car is controlled to move by receiving data sent from the mobile app via the Bluetooth module. * **Program Logic**
* **Bluetooth Module**
(1) Module Introduction
The Bluetooth module is a hardware component that integrates Bluetooth communication capabilities and is commonly used for wireless data transmission between electronic devices. It fundamentally communicates through a `UART serial interface`.
(2) Module Connection
Connect the Bluetooth module to the Bluetooth port of the expansion board as below:
* **Program Download**
[Source Code](../_static/source_code/Akerman_app.zip)
::: {Note}
* Please remove the Bluetooth module before downloading the program. Otherwise, a serial port conflict may occur, resulting in a failed download.
* When connecting the Type-B download cable, make sure the battery box switch is set to the **"OFF"** position. This helps prevent accidental contact between the cable and the power pins on the expansion board, which could cause a short circuit.
:::
(1) Locate and open the program file **"3.2.3 Programs\Akerman_app\Akerman_app.ino"** in the same directory as this section.
(2) Connect the Arduino to your computer using a Type-B USB data cable. Click on **"Select Board"** — the software will automatically detect the current Arduino port. Click to establish the connection.
(3) Click the **Upload**
button to download the program to the Arduino. Wait for the upload to complete.
* **Program Outcome**
Once the mobile app is connected to the Bluetooth module, you can control the Ackermann chassis movement directly through the app's buttons.
* **Program Analysis**
[Source Code](../_static/source_code/Akerman_app.zip)
(1) Import Library Files
Import the chassis control library required for this activity.
{lineno-start=1}
```c
#include "Akerman_chassis.h"
```
(2) Define Variables and Create Objects
① First, an enumeration type is defined to represent the various control states of the chassis. Then, a key-value pair structure is defined to store the control states. An array of key-value pairs is initialized to map the app's commands to their corresponding control states.
{lineno-start=3}
```c
typedef enum
{
NULL_KEY = 0,
STOP,
GO_FORWARD,
TURN_LEFT,
TURN_RIGHT,
GO_BACK,
SPEED_UP,
SPEED_DOWN,
LEFT_SPIN,
RIGHT_SPIN
}Mode_State;
// Define key-value pair structure
typedef struct {
const char *value;
Mode_State key;
} HashTableEntry;
/* Hash table */
#define TABLE_SIZE 9
HashTableEntry table[TABLE_SIZE] = {
{"A", GO_FORWARD},
{"C", TURN_LEFT},
{"G", TURN_RIGHT},
{"E", GO_BACK},
{"I", STOP},
{"j", SPEED_UP},
{"n", SPEED_DOWN},
{"l", LEFT_SPIN},
{"p", RIGHT_SPIN}
};
```
② An `Ackermann chassis control object` is defined, with the initial control state set to `"none"`. A variable is created to store the `speed`, initialized at 150 mm/s. The `speed_limit` variable is used to define the maximum allowable speed.
{lineno-start=37}
```c
Akerman akerman;
Mode_State key = NULL_KEY;
float speed = 150.0f;
float speed_limit = 300.0f;
```
(3) Initialize Settings
In the `setup()` function, the serial port is first initialized with a baud rate of `9600` for communication. Then, the Ackermann chassis is initialized.
{lineno-start=56}
```c
void setup() {
Serial.begin(9600);
akerman.begin(MINACKER_CHASSIS);
}
```
(4) Main Function
① In the main function, `Serial.available()` is used to check whether there is incoming Bluetooth data in the buffer. If the value is greater than 0, it means Bluetooth data is available, which is then read using `Serial.read()` and stored in the `cmd` variable. The `lookup()` function is then called to determine the corresponding control state based on the received data.
{lineno-start=61}
```c
void loop() {
if(Serial.available() > 0)
{
char cmd;
cmd = Serial.read();
key = lookup(cmd);
```
② Based on the control state, the `akerman.move()` function is called to move the car accordingly. The car can be controlled to stop, move forward, turn left, turn right, or move backward.
{lineno-start=68}
```c
switch(key)
{
case STOP:
akerman.move(0, 0.0f);
break;
case GO_FORWARD:
akerman.move(speed, 0.0f);
break;
case TURN_LEFT:
akerman.move(speed, -400.0f);
break;
case TURN_RIGHT:
akerman.move(speed, 400.0f);
break;
case GO_BACK:
akerman.move(-speed, 0.0f);
break;
```
③ If the parsed data corresponds to speed control, the `speed` variable is increased or decreased by 10 units with each command.
{lineno-start=90}
```c
case SPEED_UP:
if(speed < speed_limit)
{
speed += 10.0f;
}
break;
case SPEED_DOWN:
if(speed > 0)
{
speed -= 10.0f;
}
break;
case LEFT_SPIN:
if(akerman.cal_left_wheel_speed() > 0){
akerman.move(speed, -230.0f);
}else if(akerman.cal_left_wheel_speed() < 0){
akerman.move(-speed, -230.0f);
}
break;
case RIGHT_SPIN:
if(akerman.cal_left_wheel_speed() > 0){
akerman.move(speed, 230.0f);
}else if(akerman.cal_left_wheel_speed() < 0){
akerman.move(-speed, 230.0f);
}
break;
default:
break;
}
```
④ If the data corresponds to a left or right rotation command, the chassis is controlled to move in a circular path with a radius of 230 mm, either counterclockwise or clockwise. After each command is executed, the received data is cleared to prepare for the next operation.
{lineno-start=104}
```c
case LEFT_SPIN:
if(akerman.cal_left_wheel_speed() > 0){
akerman.move(speed, -230.0f);
}else if(akerman.cal_left_wheel_speed() < 0){
akerman.move(-speed, -230.0f);
}
break;
case RIGHT_SPIN:
if(akerman.cal_left_wheel_speed() > 0){
akerman.move(speed, 230.0f);
}else if(akerman.cal_left_wheel_speed() < 0){
akerman.move(-speed, 230.0f);
}
break;
default:
break;
}
key = NULL_KEY;
}
```
(5) Key-Value Lookup Function
This function is primarily used to search the predefined list of key-value pairs based on the received data. It returns the corresponding value if a match is found; otherwise, it returns `NULL_KEY`.
{lineno-start=44}
```c
Mode_State lookup(char val)
{
for (int i = 0; i < TABLE_SIZE && table[i].value != NULL; ++i)
{
if (strcmp(table[i].value, &val) == 0)
{
return table[i].key;
}
}
return NULL_KEY;
}
```
(6) Chassis Control Function
① In the chassis control function, motor and servo control values are calculated based on Ackermann kinematics. The parameter `v` controls the motor speed of the car (positive values indicate forward movement, negative values indicate reverse, with units in mm/s). The parameter `r` controls the turning radius of the car (positive for clockwise rotation, negative for counterclockwise rotation, units in mm). When the linear velocity `v` is not zero, the function calculates the speeds for the left and right wheels during turning. Since the right wheel follows a larger radius during a turn, it moves faster than the left wheel, which follows a smaller radius. The turning radius `r` determines these speeds. After calculating the wheel speeds, the steering angle needed for the turn is also computed.
{lineno-start=39}
```c++
void Akerman::move(float v, float r)
{
float akerman_vr, akerman_vl, akerman_angle;
if(r == 0.0f)
{
akerman_vl = v;
akerman_vr = v;
akerman_angle = 0;
}
else
{
akerman_vl = v / r * (r + akerman.wheelbase / 2);
akerman_vr = v / r * (r - akerman.wheelbase / 2);
akerman_angle = atan(akerman.shaft_length / r);
}
```
② After obtaining the steering angle, the angular velocity is checked. If the angular velocity is zero, it means the car is moving straight and no steering adjustment is needed. The steering angle is then constrained within the range of -π/5 to π/5 radians to ensure safe turning limits.
{lineno-start=56}
```c++
if(akerman_angle >= PI / 5.0f)
{
akerman_angle = PI / 5.0f;
}
else if(akerman_angle <= -PI / 5.0f)
{
akerman_angle = -PI / 5.0f;
}
```
③ Once the steering angle is determined, it is mapped to the servo control value. This value is stored in the `akerman.angle` variable. At the same time, the rotational speeds for the left and right motors are calculated.
{lineno-start=65}
```c++
if(akerman.chassis_type == TIACKER_CHASSIS){
akerman.angle = -2000 / PI * akerman_angle + 1500; //Calculate the steering angle
}else if(akerman.chassis_type == MINACKER_CHASSIS){
akerman.angle = 2000 / PI * akerman_angle + 1500; //Calculate the steering angle
}
akerman.vl = linear_speed_to_rps(&akerman, akerman_vl);
akerman.vr = linear_speed_to_rps(&akerman, akerman_vr);
servos.set_servo(4, (uint16_t)akerman.angle);
motor.set_speed(akerman.motor_type, 1, 0, akerman.vr);
motor.set_speed(akerman.motor_type, 2, 0, akerman.vl);
```
④ Finally, the servo is commanded to rotate to the calculated angle, and the motors are controlled to rotate at the specified speeds, enabling the Ackermann chassis to move as intended.
{lineno-start=65}
```c++
if(akerman.chassis_type == TIACKER_CHASSIS){
akerman.angle = -2000 / PI * akerman_angle + 1500; //Calculate the steering angle
}else if(akerman.chassis_type == MINACKER_CHASSIS){
akerman.angle = 2000 / PI * akerman_angle + 1500; //Calculate the steering angle
}
akerman.vl = linear_speed_to_rps(&akerman, akerman_vl);
akerman.vr = linear_speed_to_rps(&akerman, akerman_vr);
servos.set_servo(4, (uint16_t)akerman.angle);
motor.set_speed(akerman.motor_type, 1, 0, akerman.vr);
motor.set_speed(akerman.motor_type, 2, 0, akerman.vl);
}
```
* **FAQ**
(1) Code upload failed.
A: Please check if the Bluetooth module is connected to the expansion board. If it is, remove the Bluetooth module before attempting to upload the code again.
(2) Unable to find Bluetooth device.
A: Please ensure that the Bluetooth function on your phone is turned on. If it is already on, try restarting the app.