Community project
High-Speed Line-Following Car
This high-speed line-following car uses an 8-channel analog sensor array to detect and track a black line on a white surface, with an Arduino Uno running a PID control algorithm to steer two micro metal gear motors at speeds up to 220 RPM. The TB6612FNG motor driver provides independent speed and direction control for each motor, while dual 18650 cells with protection circuitry deliver stable 5V logic power and motor drive voltage.
This guide provides a complete wiring diagram, detailed parts list, and step-by-step assembly instructions for mounting all components on a chassis frame. You'll also get the full Arduino firmware with PID tuning parameters, calibration routines, and motor control logic ready to upload. The assembly section covers proper capacitor placement for noise filtering, sensor alignment for accurate line detection, and pre-flight checks to ensure reliable operation before your first test run.
Wiring diagram

Gather all the parts
Assemble it in 8 steps
1. Bố trí các phần trên khung
Nhìn từ trên xuống: đặt line_sensor_8 chạy ngang sát mép đầu xe, tám mắt quay xuống mặt đường. Đặt bánh tự lựa ngay sau hoặc ngay dưới vùng giữa của cảm biến nhưng không che mắt. Đặt motor_left ở bên trái và motor_right ở bên phải, ngang trục bánh. Đặt Arduino Nano ở giữa thân xe, đặt tb6612_1 sát phía sau Nano để bảy dây điều khiển ngắn nhất có thể. Đặt cell_1, cell_2 và bms_2s thấp sát sàn ở cuối xe để xe cân bằng.
- Đo chiều dài thật của mảng cảm biến trước khi khoan; loại thẳng thường dài khoảng 85–95 mm.
- Giữ mặt mắt cảm biến cách mặt đường khoảng 2,5–3 mm để nó phân biệt rõ vạch đen và nền sáng.
- Đặt cảm biến quá thấp có thể làm nó cạ mặt đường; đặt quá cao khiến tín hiệu yếu và xe khó bám vạch.
2. Nối hai cell với mạch bảo vệ
Khi công tắc đang tắt, mắc cell_1 và cell_2 nối tiếp: cell_1 GND → bms_2s B- (đầu âm của bộ pin), cell_1 +V → cell_2 GND và bms_2s B1 (điểm giữa hai pin), cell_2 +V → bms_2s B+ (đầu dương của bộ pin). Đây là bộ pin 2S: khoảng 7,4 V danh định và có thể lên 8,4 V khi vừa sạc đầy.
- Dùng dây nguồn đủ to và bọc kín mọi mối nối pin.
- Không được đảo B-, B1 và B+; đấu sai pin Li-ion có thể làm dây hoặc mạch bảo vệ nóng rất nhanh.
3. Tạo đường nguồn sau công tắc
Nối bms_2s P+ → power_switch T1 (nguồn được bảo vệ vào công tắc). Nối power_switch T2 → VIN của Arduino Nano và → VM của tb6612_1; đây là đường đỏ 7,4–8,4 V. Nối bms_2s P- → GND Nano → GND tb6612_1 → GND line_sensor_8; đây là bus GND chung màu đen. Đi dây VM và GND tới tb6612_1 ngắn và dày hơn các dây tín hiệu.
- Tạo một điểm chia ngay sau công tắc rồi đi một nhánh tới VIN Nano và một nhánh tới VM driver để sơ đồ gọn, dễ kiểm tra.
- Chỉ nối nguồn pin sau công tắc vào VIN của Nano, không nối vào chân 5V; 7,4–8,4 V ở chân 5V có thể làm hỏng Nano, cảm biến và driver.
4. Nối nguồn logic 5 V
Nối chân 5V của Arduino Nano → VCC của tb6612_1 (nguồn logic) và → VCC của line_sensor_8 (nguồn cho cảm biến). Vẫn giữ các chân GND của Nano, driver và cảm biến nối chung với P- của bms_2s. Nếu board cảm biến thực tế có chân LEDON hoặc EN, nối LEDON/EN → 5V để bật đèn hồng ngoại.
- Đi cặp dây 5V và GND của cảm biến cùng nhau, sát thân xe và tránh chạy song song dài với dây động cơ.
- Nguồn 5V này lấy từ ổn áp trên Nano. Nếu Nano nóng nhanh khi chạy, tắt công tắc ngay; không tự nối thêm nguồn 5V khác vào đường này khi Nano đang nhận nguồn qua VIN.
5. Đặt đúng bốn tụ lọc sát TB6612
Hàn cap_vm_10u và cap_vm_100n song song giữa VM và GND ngay sát chân nguồn của tb6612_1: cap_vm_10u chân + → VM, chân có sọc - → GND; cap_vm_100n chân 1 → VM, chân 2 → GND. Hàn cap_vcc_10u và cap_vcc_100n song song giữa VCC và GND ngay sát chân logic của tb6612_1: cap_vcc_10u chân + → VCC, chân có sọc - → GND; cap_vcc_100n chân 1 → VCC, chân 2 → GND. Tụ gốm 104 là 0,1 µF và không có chiều.
- Bốn tụ không mắc nối tiếp: mỗi cặp có hai chân cùng chạm vào cùng hai đường nguồn/mass.
- Tụ 10 µF 25 V dùng ở cả VM 7,4–8,4 V và VCC 5 V đều có mức chịu áp dư an toàn.
- Đảo cực tụ điện phân có thể làm tụ rò, nóng hoặc hỏng. Không dùng dây dài để kéo tụ đi xa driver vì sẽ giảm tác dụng chống nhiễu.
6. Nối động cơ và bảy dây điều khiển
Nối tb6612_1 AO1 → motor_left M+ và AO2 → motor_left M- (đầu ra động cơ trái). Nối BO1 → motor_right M+ và BO2 → motor_right M- (đầu ra động cơ phải). Nối Nano D8 → STBY (bật driver), D4 → AIN1 và D5 → AIN2 (đặt chiều động cơ trái), D9 → PWMA (đặt tốc độ trái), D6 → BIN1 và D7 → BIN2 (đặt chiều động cơ phải), D10 → PWMB (đặt tốc độ phải).
- Xoắn nhẹ từng cặp dây ra động cơ để giảm nhiễu; giữ chúng xa các dây OUT1–OUT8 của cảm biến.
- Nếu một bánh quay ngược sau thử nghiệm, tắt nguồn rồi đảo hai dây của riêng động cơ đó.
- Không để AO1/AO2 hoặc BO1/BO2 chạm nhau khi pin đang nối; điều đó có thể làm driver quá dòng.
7. Nối tám tín hiệu cảm biến theo thứ tự
Nối line_sensor_8 VCC → 5V (nguồn), GND → GND (mass), OUT1 → A0 (tín hiệu), OUT2 → A1 (tín hiệu), OUT3 → A2 (tín hiệu), OUT4 → A3 (tín hiệu), OUT5 → A4 (tín hiệu), OUT6 → A5 (tín hiệu), OUT7 → A6 (tín hiệu), OUT8 → A7 (tín hiệu). Đi tám dây tín hiệu thành một bó từ cảm biến về Nano và tránh bó chung với dây động cơ.
- Trước khi cố định dây, xác định OUT1 nằm ở đầu trái hay đầu phải của board khi nhìn từ phía trước xe.
- A6 và A7 trên Nano chỉ dùng để đọc analog; đó chính là chức năng cần cho OUT7 và OUT8.
- Không cắm nhầm VCC vào GND; cấp nguồn ngược có thể làm hỏng board cảm biến.
8. Kiểm tra trước khi cho xe chạy
Khi công tắc tắt, kiểm tra cực tụ hóa, B-/B1/B+, VIN Nano, VM driver và một bus GND chung. Nhấc bánh xe khỏi mặt bàn, cắm Nano qua USB và nhấn Deploy trong Schematik. Khi bật công tắc, đèn Nano sáng khoảng 2,5 giây; trong thời gian đó cầm xe quét tám mắt qua cả nền sáng lẫn vạch đen để hiệu chuẩn.
- Sau vài lần chạy ngắn, tắt nguồn rồi chạm nhanh vùng ổn áp Nano và TB6612: chúng chỉ nên ấm nhẹ.
- Nếu xe bẻ sai phía, kiểm tra thứ tự OUT1–OUT8 trước; sau đó mới xem xét đảo dây động cơ.
- Bộ pin 2S đầy có 8,4 V. Dừng thử ngay nếu Nano, TB6612, dây nguồn hoặc tụ điện nóng bất thường.
Review all connections
1. Connections between "cell_1" and "Arduino"
2. Connections between "cell_2" and "Arduino"
3. Connections between "bms_2s" and "Arduino"
4. Connections between "tb6612_1" and "Arduino"
5. Connections between "motor_left" and "Arduino"
6. Connections between "motor_right" and "Arduino"
7. Connections between "line_sensor_8" and "Arduino"
8. Connections between "cap_vm_10u" and "Arduino"
9. Connections between "cap_vm_100n" and "Arduino"
10. Connections between "cap_vcc_10u" and "Arduino"
11. Connections between "cap_vcc_100n" and "Arduino"
12. Connections between "power_switch" and "Arduino"
Deploy the firmware
#include <Arduino.h>
#include <QTRSensors.h>
constexpr uint8_t SENSOR_COUNT = 8;
constexpr uint16_t LINE_CENTER = 3500;
constexpr uint8_t STBY_PIN = 8;
constexpr uint8_t AIN1_PIN = 4;
constexpr uint8_t AIN2_PIN = 5;
constexpr uint8_t PWMA_PIN = 9;
constexpr uint8_t BIN1_PIN = 6;
constexpr uint8_t BIN2_PIN = 7;
constexpr uint8_t PWMB_PIN = 10;
constexpr int BASE_SPEED = 150;
constexpr int MAX_SPEED = 220;
constexpr int REVERSE_MAX_SPEED = 100;
float kp = 0.07f;
float ki = 0.0f;
float kd = 0.6f;
QTRSensors qtr;
uint16_t sensorValues[SENSOR_COUNT];
int lastError = 0;
float integral = 0.0f;
void driveMotor(int speed, uint8_t in1, uint8_t in2, uint8_t pwmPin) {
if (speed >= 0) {
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
} else {
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
speed = -speed;
}
analogWrite(pwmPin, constrain(speed, 0, 255));
}
void setup() {
const uint8_t sensorPins[SENSOR_COUNT] = {A0, A1, A2, A3, A4, A5, A6, A7};
qtr.setTypeAnalog();
qtr.setSensorPins(sensorPins, SENSOR_COUNT);
pinMode(STBY_PIN, OUTPUT);
pinMode(AIN1_PIN, OUTPUT);
pinMode(AIN2_PIN, OUTPUT);
pinMode(PWMA_PIN, OUTPUT);
pinMode(BIN1_PIN, OUTPUT);
pinMode(BIN2_PIN, OUTPUT);
pinMode(PWMB_PIN, OUTPUT);
digitalWrite(STBY_PIN, HIGH);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
for (uint16_t i = 0; i < 250; ++i) {
qtr.calibrate();
delay(10); // khoảng 2,5 giây để lắc xe qua nền trắng và vạch đen
}
digitalWrite(LED_BUILTIN, LOW);
}
void loop() {
const int position = qtr.readLineBlack(sensorValues);
const int error = position - LINE_CENTER;
integral += error;
integral = constrain(integral, -3000.0f, 3000.0f);
const int derivative = error - lastError;
const float correction = kp * error + ki * integral + kd * derivative;
lastError = error;
const int leftSpeed = constrain(static_cast<int>(BASE_SPEED + correction), -REVERSE_MAX_SPEED, MAX_SPEED);
const int rightSpeed = constrain(static_cast<int>(BASE_SPEED - correction), -REVERSE_MAX_SPEED, MAX_SPEED);
driveMotor(leftSpeed, AIN1_PIN, AIN2_PIN, PWMA_PIN);
driveMotor(rightSpeed, BIN1_PIN, BIN2_PIN, PWMB_PIN);
}Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.




