Skip to content

Công nghệ 9 giây

“Website hướng dẫn, thử nghiệm và giải thích công nghệ theo cách dễ hiểu cho mọi người.”

Menu
  • Home
  • Công nghệ
  • STM32
  • ESP32
  • Altium Desinger
  • Kiến thức điện máy
Menu

bài tiếp theo hợp lý là: BÀI 63 – SENSOR FUSION THỰC CHIẾN: ENCODER + IMU MPU6050 + EKF + ODOMETRY X,Y,θ TRÊN STM32F103

Posted on August 23, 2026August 23, 2026 by congnghe9s

BÀI 63 – SENSOR FUSION THỰC CHIẾN

ENCODER + MPU6050 + EKF + ODOMETRY X, Y, θ TRÊN STM32F103

Đây là bài ghép toàn bộ những gì chúng ta đã học từ Bài 45 đến Bài 62 thành một hệ thống robot 2 bánh tương đối hoàn chỉnh.

Mục tiêu cuối bài:

ENCODER LEFT ──┐
               │
ENCODER RIGHT ─┤
               ├──→ EKF ──→ X,Y,θ ──→ ROBOT CONTROLLER
               │
MPU6050 GYRO ──┘

Và STM32F103 sẽ liên tục biết:

X      → robot đang ở đâu theo trục X
Y      → robot đang ở đâu theo trục Y
θ      → robot đang quay về hướng nào
V      → robot đang chạy nhanh bao nhiêu
ω      → robot đang quay nhanh bao nhiêu

1. Bài 63 khác Bài 62 ở đâu?

Bài 62 chủ yếu tập trung vào:

Toán học của EKF.

Bài 63 chuyển sang:

Triển khai thực tế trên STM32F103.

Chúng ta sẽ ghép:

TIM Encoder
      ↓
Wheel Speed
      ↓
V, ω
      ↓
       ┌───────────┐
MPU6050│           │
Gyro Z ┤   EKF     ├──→ X,Y,θ,V,ω
       │           │
Encoder│           │
       └───────────┘
             ↓
         Odometry

2. Phần cứng

Một hệ thống tối thiểu:

STM32

STM32F103C8T6

Motor

Motor trái
Motor phải

Encoder

Encoder trái
Encoder phải

IMU

MPU6050

Motor driver

Ví dụ:

TB6612FNG
L298N
MX1508

3. Kết nối MPU6050

MPU6050 sử dụng I²C.

Trên STM32F103:

PB6 → SCL
PB7 → SDA

Thông thường:

MPU6050
    │
    ├── VCC
    ├── GND
    ├── SCL → PB6
    └── SDA → PB7

Địa chỉ thường gặp:

0x68

nếu chân AD0 ở LOW.

Nếu AD0 HIGH:

0x69

4. Đừng nhầm địa chỉ I²C

Một lỗi rất thường gặp.

Datasheet có thể ghi:

0xD0

hoặc:

0x68

Trong HAL STM32 thường sử dụng địa chỉ dạng:

0x68 << 1

tức:

#define MPU6050_ADDR \
    (0x68 << 1)

5. Kiểm tra MPU6050 trước

Trước khi làm EKF, hãy chắc chắn MPU6050 hoạt động.

Đọc:

WHO_AM_I

Thanh ghi:

0x75

Giá trị thường:

0x68

Code:

uint8_t whoami;

HAL_I2C_Mem_Read(
    &hi2c1,
    MPU6050_ADDR,
    0x75,
    I2C_MEMADD_SIZE_8BIT,
    &whoami,
    1,
    100
);

Nếu:

whoami == 0x68

→ MPU6050 phản hồi.


6. Đánh thức MPU6050

Sau reset, MPU6050 thường ở trạng thái sleep.

Thanh ghi:

PWR_MGMT_1
0x6B

ghi:

uint8_t data = 0x00;

HAL_I2C_Mem_Write(
    &hi2c1,
    MPU6050_ADDR,
    0x6B,
    I2C_MEMADD_SIZE_8BIT,
    &data,
    1,
    100
);

7. Cấu hình Gyroscope

Ví dụ:

±250 °/s

FS_SEL:

0

Thanh ghi:

0x1B
uint8_t data = 0x00;

HAL_I2C_Mem_Write(
    &hi2c1,
    MPU6050_ADDR,
    0x1B,
    I2C_MEMADD_SIZE_8BIT,
    &data,
    1,
    100
);

Sensitivity:

[
131\ LSB/(°/s)
]


8. Đọc Gyro

Các thanh ghi:

0x43 → GYRO_XOUT_H
0x45 → GYRO_YOUT_H
0x47 → GYRO_ZOUT_H

Đọc 6 byte:

uint8_t buffer[6];

HAL_I2C_Mem_Read(
    &hi2c1,
    MPU6050_ADDR,
    0x43,
    I2C_MEMADD_SIZE_8BIT,
    buffer,
    6,
    100
);

Ghép:

int16_t gyro_z_raw;

gyro_z_raw =
    (int16_t)(
        ((uint16_t)buffer[4] << 8)
        | buffer[5]
    );

9. Đổi sang °/s

Với ±250°/s:

[
gyro_{dps}

\frac{raw}{131}
]

Code:

float gyro_z_dps =
    gyro_z_raw / 131.0f;

10. Đổi sang rad/s

EKF của chúng ta nên dùng radian.

[
1°=\frac{\pi}{180}
]

Do đó:

float gyro_z_rad =
    gyro_z_dps *
    0.01745329252f;

Hoặc:

float gyro_z_rad =
    gyro_z_dps *
    (M_PI / 180.0f);

11. Tại sao tôi khuyên dùng rad?

Vì:

sinf()
cosf()

của C sử dụng:

radian

Không phải độ.

Do đó:

θ = 90°

phải đổi:

[
90°=\frac{\pi}{2}
]


12. Calibration Gyro

Đây là một bước bắt buộc.

Đặt robot:

ĐỨNG YÊN

không rung.

Lấy:

N = 1000

mẫu.

float gyro_bias = 0.0f;

for (int i = 0; i < 1000; i++)
{
    float gz = MPU6050_ReadGyroZ();

    gyro_bias += gz;

    HAL_Delay(2);
}

gyro_bias /= 1000.0f;

Sau đó:

gyro_z =
    gyro_z -
    gyro_bias;

13. Tại sao calibration quan trọng?

Ví dụ gyro thực tế:

0.02 rad/s

khi robot đứng yên.

Sau 60 giây:

[
\theta=0.02\times60
]

[
=1.2rad
]

≈ 69°.

Robot đứng yên nhưng hệ thống tưởng nó quay gần 70°.


14. Encoder

Giả sử:

Wheel diameter = 65 mm
Wheel base     = 150 mm
Encoder        = 390 pulse/rev

Đây chỉ là ví dụ.

Bạn phải thay bằng thông số thực tế của robot.


15. Chu vi bánh

[
C=\pi D
]

Với:

[
D=65mm
]

thì:

[
C≈204.2mm
]


16. Khoảng cách mỗi pulse

[
d_{pulse}

\frac{\pi D}{PPR}
]

Ví dụ:

[
d_{pulse}

\frac{204.2}{390}
]

≈

[
0.524mm
]


17. Encoder → vận tốc bánh

Nếu trong dt có:

Δcount

thì:

[
distance=
\Delta count
\times d_{pulse}
]

và:

[
V_L=
\frac{distance_L}{dt}
]

[
V_R=
\frac{distance_R}{dt}
]


18. Từ hai bánh → V

[
\boxed{
V=
\frac{V_R+V_L}{2}
}
]


19. Từ hai bánh → ω

Với:

W = wheel base

ta có:

[
\boxed{
\omega=
\frac{V_R-V_L}{W}
}
]

Đơn vị:

rad/s

20. Ví dụ

Giả sử:

VL = 300 mm/s
VR = 360 mm/s
W  = 150 mm

Ta có:

[
V=
\frac{300+360}{2}

330mm/s
]

và:

[
\omega=
\frac{360-300}{150}

0.4rad/s
]


21. Đây là dữ liệu đầu vào EKF

Bây giờ chúng ta có:

Encoder
   ↓
VL
VR
 ↓
Vencoder
ωencoder

và:

MPU6050
   ↓
gyro_z
 ↓
ωgyro

22. EKF state

Ta dùng:

typedef struct
{
    float x[5];

    float P[5][5];

} EKF_t;

Quy ước:

x[0] = X
x[1] = Y
x[2] = theta
x[3] = V
x[4] = omega

23. Khởi tạo

void EKF_Init(EKF_t *ekf)
{
    memset(ekf, 0, sizeof(EKF_t));

    ekf->P[0][0] = 1.0f;
    ekf->P[1][1] = 1.0f;
    ekf->P[2][2] = 0.1f;

    ekf->P[3][3] = 10.0f;
    ekf->P[4][4] = 1.0f;
}

24. Prediction state

void EKF_Predict(
    EKF_t *ekf,
    float dt)
{
    float X =
        ekf->x[0];

    float Y =
        ekf->x[1];

    float theta =
        ekf->x[2];

    float V =
        ekf->x[3];

    float omega =
        ekf->x[4];

    ekf->x[0] =
        X +
        V * cosf(theta) * dt;

    ekf->x[1] =
        Y +
        V * sinf(theta) * dt;

    ekf->x[2] =
        theta +
        omega * dt;

    ekf->x[2] =
        NormalizeAngle(
            ekf->x[2]);
}

25. Đây chính là Odometry

Nếu chỉ dùng:

Encoder

ta đã có:

Odometry

Nhưng ở đây:

Encoder
+
Gyro
↓
EKF
↓
θ
↓
X,Y

ta có một hệ thống state estimation tốt hơn.


26. Prediction Jacobian

Tạo:

float F[5][5] = {0};

Sau đó:

F[0][0] = 1.0f;
F[0][2] =
    -V * sinf(theta) * dt;
F[0][3] =
     cosf(theta) * dt;

F[1][1] = 1.0f;
F[1][2] =
     V * cosf(theta) * dt;
F[1][3] =
     sinf(theta) * dt;

F[2][2] = 1.0f;
F[2][4] = dt;

F[3][3] = 1.0f;

F[4][4] = 1.0f;

Đây chính là ma trận:

[
F=\frac{\partial f}{\partial x}
]


27. Q

Ta bắt đầu với:

float Q[5] =
{
    0.001f,
    0.001f,
    0.001f,
    0.1f,
    0.01f
};

Sau đó:

for (int i = 0; i < 5; i++)
{
    ekf->P[i][i] += Q[i];
}

Đây là phiên bản đơn giản hóa để học.

Trong implementation chuyên nghiệp, ta sẽ thực hiện đầy đủ:

[
P=FPF^T+Q
]


28. Vì sao tôi cố tình chưa đưa ngay ma trận 5×5 đầy đủ?

Vì nếu viết toàn bộ:

F × P × Fᵀ

bằng tay ngay lúc này, code sẽ rất dài và dễ làm bạn mất bản chất.

Ở Bài 63 chúng ta ưu tiên hiểu:

Sensor
 ↓
Measurement
 ↓
EKF
 ↓
State

Sau đó mới tối ưu matrix operations.


29. Encoder Measurement

Ta có:

z_encoder =
[
    Vencoder,
    ωencoder
]

Prediction:

h(x) =
[
    V,
    ω
]

Innovation:

y =
[
    Vencoder - V,
    ωencoder - ω
]

30. Gyro Measurement

Gyro:

zgyro = gyro_z

Prediction:

h(x) = omega

Innovation:

[
y=
\omega_{gyro}

\omega_{EKF}
]


31. H của Encoder

V = x[3]
ω = x[4]

nên:

H =
[0 0 0 1 0]
[0 0 0 0 1]

32. H của Gyro

H =
[0 0 0 0 1]

Đây là một ví dụ rất hay để hiểu bản chất của measurement Jacobian.


33. Fusion thực tế

Ta có:

          Encoder
             │
       ┌─────┴─────┐
       ↓           ↓
       V          ωenc
                   │
                   │
Gyro ───────────→ ωgyro
                   │
                   ▼
                  EKF
                   │
           ┌───────┴────────┐
           ↓       ↓        ↓
           X       Y        θ

34. Nhưng EKF còn có thể làm tốt hơn

Một vấn đề của hệ thống:

Encoder → V
Gyro    → ω

là X,Y vẫn phụ thuộc vào tích phân.

Nếu:

θ

sai một chút:

[
X=V\cos\theta
]

[
Y=V\sin\theta
]

sẽ bắt đầu sai.

Ví dụ:

θ = 0°

thực tế.

Nhưng EKF nghĩ:

θ = 5°

Robot chạy:

1 m

sẽ xuất hiện sai số ngang đáng kể.

Đây là lý do ước lượng θ rất quan trọng.


35. MPU6050 Accelerometer có giúp sửa θ không?

Nếu θ ở đây là yaw:

Không trực tiếp.

Accelerometer chỉ nhìn trọng lực.

Nó rất tốt cho:

roll
pitch

nhưng không cung cấp yaw tuyệt đối khi robot chỉ quay quanh trục Z trên mặt phẳng.


36. Muốn sửa yaw lâu dài

Bước sau có thể thêm:

MPU6050
+
Magnetometer

hoặc:

Encoder
+
IMU
+
GPS

hoặc:

Encoder
+
IMU
+
LiDAR

hoặc:

Encoder
+
IMU
+
Camera

Khi đó EKF mới thực sự trở thành:

Multi-Sensor Fusion

37. Timer 1 ms

Chúng ta đã học ở Bài 36.

Giả sử:

volatile uint32_t tick_ms = 0;

Timer interrupt:

void HAL_TIM_PeriodElapsedCallback(
    TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM2)
    {
        tick_ms++;
    }
}

38. Scheduler

Ta muốn EKF:

100 Hz

tức:

10 ms

Code:

static uint32_t last_ekf = 0;

if (tick_ms - last_ekf >= 10)
{
    last_ekf += 10;

    Robot_EKF_Update(0.01f);
}

39. Một chu kỳ EKF

Mỗi 10 ms:

1. Read encoder
2. Calculate VL/VR
3. Calculate V/ω
4. Read gyro
5. Remove gyro bias
6. EKF Predict
7. Encoder Update
8. Gyro Update
9. Normalize θ
10. Output X,Y,θ

40. Pseudocode hoàn chỉnh

void Robot_EKF_Update(float dt)
{
    float vl;
    float vr;

    Encoder_GetSpeed(
        &vl,
        &vr
    );

    float v =
        (vl + vr) * 0.5f;

    float omega_enc =
        (vr - vl)
        / WHEEL_BASE;

    float gyro =
        MPU6050_GetGyroZ();

    gyro -= gyro_bias;

    EKF_Predict(
        &ekf,
        dt
    );

    EKF_UpdateEncoder(
        &ekf,
        v,
        omega_enc
    );

    EKF_UpdateGyro(
        &ekf,
        gyro
    );
}

41. Kết quả

Sau mỗi 10 ms:

float X =
    ekf.x[0];

float Y =
    ekf.x[1];

float theta =
    ekf.x[2];

float V =
    ekf.x[3];

float omega =
    ekf.x[4];

Đây chính là trạng thái robot.


42. UART Debug

Tôi rất khuyên bạn gửi:

X
Y
theta
V
omega
gyro
omega_encoder
omega_ekf

Ví dụ:

X=125.3
Y=2.1
TH=0.015
V=301.4
W=0.012
GYRO=0.011
ENC_W=0.014

Sau đó có thể mở Serial Plotter để xem đường cong.


43. Test robot chạy thẳng

Ra lệnh:

VL = VR

Kỳ vọng:

V > 0
ω ≈ 0
θ ≈ constant
Y ≈ constant
X tăng

Nếu:

Y

tăng rất nhanh trong khi robot chạy thẳng:

→ kiểm tra:

gyro bias
encoder calibration
wheel diameter
wheel base

44. Test quay tại chỗ

Ra lệnh:

VL = -200
VR = +200

Kỳ vọng:

V ≈ 0
ω ≠ 0
X ≈ constant
Y ≈ constant
θ thay đổi

Nếu:

X/Y

trôi mạnh:

→ có thể do:

encoder mismatch
gyro bias
wheel slip

45. Test vòng tròn

Ví dụ:

VL = 200
VR = 300

Ta có:

V > 0
ω > 0

Robot đi vòng tròn.

EKF phải tạo:

X(t)
Y(t)
θ(t)

liên tục.


46. Từ X,Y,θ đến Controller

Đây là phần cực kỳ quan trọng.

Giả sử trajectory yêu cầu:

Xd
Yd
θd

EKF cho:

X
Y
θ

Sai số:

[
e_x=X_d-X
]

[
e_y=Y_d-Y
]

[
e_\theta=\theta_d-\theta
]

Nhưng không nên trực tiếp đưa ex, ey vào motor.

Ta phải chuyển sai số về hệ tọa độ robot.


47. Sai số trong robot frame

[
e_x^r=
\cos\theta e_x+
\sin\theta e_y
]

[
e_y^r=
-\sin\theta e_x+
\cos\theta e_y
]

và:

[
e_\theta=
normalize(\theta_d-\theta)
]

Sau đó controller:

ex_robot
ey_robot
etheta
      ↓
   Controller
      ↓
     V,ω

48. Đây là nơi Bài 55–58 quay trở lại

Bạn đã học:

Trajectory Tracking

bây giờ:

Trajectory
    ↓
Desired X,Y,θ
    ↓
      ← EKF ← Encoder + IMU
    ↓
Error
    ↓
Controller
    ↓
V,ω
    ↓
Inverse Kinematics
    ↓
VL,VR

Vòng kín hoàn chỉnh.


49. Đây chính là kiến trúc robot mà bạn đang hướng tới

                 TRAJECTORY
                      │
                      ▼
               Xd,Yd,θd,Vd,ωd
                      │
                      ▼
              TRAJECTORY CONTROL
                      │
                      ▼
                    V,ω
                      │
                      ▼
              INVERSE KINEMATICS
                 ↙          ↘
               VLd          VRd
                │             │
                ▼             ▼
             PID LEFT      PID RIGHT
                │             │
                ▼             ▼
              PWM L         PWM R
                │             │
                ▼             ▼
             MOTOR L       MOTOR R
                │             │
                ▼             ▼
           ENCODER L      ENCODER R
                 \           /
                  \         /
                   ▼       ▼
                  ENCODER
                     │
                     │
              ┌──────┴──────┐
              │             │
              ▼             ▼
               V             ω
              │              │
              └──────┬───────┘
                     │
                     ▼
                   EKF
                     ▲
                     │
                  MPU6050
                     │
                     ▼
                   GYRO Z
                     │
                     ▼
                  X,Y,θ,V,ω
                     │
                     └───────────────┐
                                     │
                                     ▼
                              TRAJECTORY CONTROL

50. Một lưu ý quan trọng về implementation

Ở Bài 63 này, chưa nên cố nhét tất cả vào một file main.c.

Hãy bắt đầu tổ chức firmware:

Core/
└── Src/
    ├── main.c
    ├── encoder.c
    ├── motor.c
    ├── pid.c
    ├── mpu6050.c
    ├── ekf.c
    ├── odometry.c
    └── robot.c

Core/
└── Inc/
    ├── encoder.h
    ├── motor.h
    ├── pid.h
    ├── mpu6050.h
    ├── ekf.h
    ├── odometry.h
    └── robot.h

Đây là lúc bạn bắt đầu chuyển từ code học tập sang firmware có kiến trúc.


51. Bài tập thực hành quan trọng nhất

Đừng vội làm toàn bộ robot.

Hãy chia thành 5 bước.

Bước 1

Kiểm tra MPU6050:

WHO_AM_I = 0x68

Bước 2

In:

gyro_z

khi robot đứng yên.

Bước 3

Calibration:

gyro_bias

Bước 4

Đọc encoder:

VL
VR
V
ω

Bước 5

Cuối cùng mới:

Encoder + Gyro
       ↓
      EKF
       ↓
    X,Y,θ

Đừng làm ngược thứ tự.

Nếu EKF cho kết quả sai, bạn sẽ không biết lỗi nằm ở:

I2C?
MPU6050?
Gyro?
Encoder?
Units?
dt?
Jacobian?
Q?
R?
EKF?

52. Bài test vàng

Sau khi hoàn thành, hãy đặt robot ở:

X=0
Y=0
θ=0

Cho robot chạy:

1 m

thẳng.

Kỳ vọng gần:

X ≈ 1000 mm
Y ≈ 0
θ ≈ 0

Sau đó quay:

90°

và chạy:

1 m

Kỳ vọng:

X ≈ 1000 mm
Y ≈ 1000 mm
θ ≈ 90°

Sai số thực tế chắc chắn tồn tại. Chính việc đo sai số rồi hiệu chỉnh D, W, encoder scale, gyro bias, Q và R mới là phần kỹ thuật quan trọng.


53. Một điều cần sửa so với cách hiểu đơn giản

Có một điểm tôi muốn nhấn mạnh để bạn không xây sai hệ thống:

MPU6050 + Encoder không đủ để EKF biết vị trí tuyệt đối X,Y trong thế giới.

Nó chỉ giúp ước lượng tốt hơn:

relative pose

từ vị trí ban đầu.

Nếu robot chạy càng lâu:

t → lớn

thì drift vẫn có thể tăng.

Muốn robot tự định vị lâu dài, bước tiếp theo sẽ phải đưa thêm:

LiDAR / GPS / camera / landmark / UWB

vào bộ lọc.


54. Tóm tắt Bài 63

Bạn cần nắm được chuỗi này:

[
\boxed{
Encoder
\rightarrow
V,\omega
}
]

[
\boxed{
MPU6050
\rightarrow
\omega_{gyro}
}
]

sau đó:

[
\boxed{
Encoder+Gyro
\rightarrow EKF
}
]

và:

[
\boxed{
EKF
\rightarrow
X,Y,\theta,V,\omega
}
]

cuối cùng:

[
\boxed{
X,Y,\theta
\rightarrow
Trajectory\ Controller
}
]

Đây là bước chuyển rất lớn:

Bài 45–52
   ↓
Điều khiển motor

Bài 53–60
   ↓
Điều khiển chuyển động

Bài 61–63
   ↓
Ước lượng trạng thái robot

BÀI 64

Bài tiếp theo tôi đề xuất là:

BÀI 64 – EKF NÂNG CAO: FUSION ENCODER + MPU6050 + GPS/LiDAR, GIẢM DRIFT X,Y,θ VÀ ĐỊNH VỊ ROBOT TRONG THẾ GIỚI

Ở đó chúng ta sẽ giải quyết đúng vấn đề còn lại của Bài 63:

Encoder + IMU
      ↓
    EKF
      ↓
X,Y,θ
      ↓
     DRIFT

và thêm một nguồn tham chiếu bên ngoài:

                 Encoder
                    │
                    ▼
                 ┌─────┐
MPU6050 ───────→ │ EKF │ ←──── GPS / LiDAR
                 └──┬──┘
                    ↓
              X,Y,θ ổn định hơn

Đó mới là nền tảng để tiến tới SLAM, Navigation và Autonomous Robot ở các bài sau.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Bạn có thể hướng dẫn tôi quy trình sửa chữa một mainboard laptop cũng như mainboard của máy tính để bàn không?
  • Có những mức nguồn DC nào trên một bo mạch mainboard laptop?Kỹ thuật đốt dòng để kiểm tra nguồn laptop được thực hiện như thế nào?
  • bài tiếp theo hợp lý là: BÀI 63 – SENSOR FUSION THỰC CHIẾN: ENCODER + IMU MPU6050 + EKF + ODOMETRY X,Y,θ TRÊN STM32F103
  • BÀI 62 – EKF (EXTENDED KALMAN FILTER) CHO ROBOT 2 BÁNH: FUSION ENCODER + IMU + ƯỚC LƯỢNG X, Y, θ, V, ω
  • BÀI 61 – KALMAN FILTER CHO ENCODER + IMU: ƯỚC LƯỢNG TỐC ĐỘ, GÓC θ VÀ SENSOR FUSION

Recent Comments

No comments to show.

Archives

  • September 2026
  • August 2026
  • July 2026
  • June 2026
  • May 2026
  • February 2026

Categories

  • Altium Desinger
  • Công nghệ
  • ESP32
  • Khác
  • Kiến thức điện máy
  • STM32
Live 3D Globes Visitor
©2026 Công nghệ 9 giây | Design: Newspaperly WordPress Theme