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 48 – ĐIỀU KHIỂN VỊ TRÍ MOTOR BẰNG ENCODER TRÊN STM32F103

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

BÀI 48 – ĐIỀU KHIỂN VỊ TRÍ MOTOR BẰNG ENCODER TRÊN STM32F103

Đây là bước rất quan trọng sau Bài 45–47.

Ở Bài 45, chúng ta biết encoder đo được vị trí.

Ở Bài 46, chúng ta dùng:

PWM + Encoder

để biết motor đang chạy bao nhiêu RPM.

Ở Bài 47, chúng ta đưa PID vào để điều khiển tốc độ.

Còn Bài 48 sẽ nâng cấp thêm một tầng:

Ra lệnh motor quay đến đúng vị trí, rồi tự dừng tại vị trí đó.


1. Từ điều khiển tốc độ sang điều khiển vị trí

Ví dụ ta muốn motor quay:

1000 counts

Ta không muốn chỉ nói:

PWM = 50%

mà muốn:

Motor → 1000 counts → STOP

Sơ đồ:

Target Position
      │
      ▼
   So sánh
      │
      ▼
 Position Error
      │
      ▼
 Controller
      │
      ▼
     PWM
      │
      ▼
    MOTOR
      │
      ▼
   ENCODER
      │
      ▼
Current Position
      │
      └───────────────► So sánh

Đây chính là:

Position Closed Loop


2. Encoder đang đo cái gì?

Giả sử encoder của motor có:

400 counts/revolution

Khi motor quay:

0
100
200
300
400

ta biết motor đã quay:

0°
90°
180°
270°
360°

Công thức:

[
Angle = \frac{Count}{CPR}\times360^\circ
]


3. Vị trí không nhất thiết phải tính bằng độ

Trong robot, đôi khi thuận tiện hơn nếu dùng:

count

Ví dụ:

Target = 2000 counts

Encoder:

Current = 1500 counts

thì:

[
Error = 2000-1500=500
]

Motor phải tiếp tục quay.


4. Khi gần đến vị trí

Ví dụ:

Target = 2000
Current = 1900

thì:

Error = 100

Ta không nên tiếp tục chạy motor với PWM lớn.

Nếu làm:

PWM = 80%

motor có thể vượt qua:

2000

và bắt đầu:

1900 → 2000 → 2100 → 2200

Đây gọi là:

Overshoot


5. Vì vậy cần giảm tốc khi gần đích

Ví dụ:

Error lớn
    ↓
PWM lớn

nhưng:

Error nhỏ
    ↓
PWM nhỏ

Đây chính là ý tưởng của P – Proportional.


6. Bộ điều khiển P đơn giản

Công thức:

[
PWM = K_p \times Error
]

Ví dụ:

Kp = 0.2
Error = 500

thì:

[
PWM=0.2\times500=100
]

Nếu giới hạn PWM:

0 → 100%

thì:

PWM = 100%

Ví dụ:

Error = 100

thì:

[
PWM=0.2\times100=20
]

→ PWM chỉ còn 20%.


7. Đây là một điểm cực kỳ quan trọng

Ta có:

Target
  ↓
Error lớn
  ↓
Motor chạy nhanh

Khi motor tiến gần:

Error giảm
  ↓
PWM giảm
  ↓
Motor chậm lại

Đến:

Error ≈ 0

thì:

PWM ≈ 0

Motor dừng.


8. Nhưng P đơn thuần có vấn đề

Giả sử:

Target = 1000
Current = 995

thì:

Error = 5

và:

Kp = 0.2

suy ra:

PWM = 1%

Motor có thể không đủ lực để quay.

Khi đó motor đứng ở:

995

thay vì:

1000

Đây là:

Static error / steady-state error


9. Vì vậy điều khiển vị trí thực tế thường phức tạp hơn

Một cách rất phổ biến là:

Position Controller
       ↓
Target Speed
       ↓
Speed Controller
       ↓
PWM
       ↓
Motor
       ↓
Encoder

Tức là có hai vòng điều khiển.


10. Cascade Control

Đây là kiến trúc rất quan trọng.

              POSITION LOOP
Target Position
      │
      ▼
Position Error
      │
      ▼
 Position PID
      │
      ▼
Target RPM
      │
      ▼
              SPEED LOOP
   Actual RPM ◄────────────┐
      │                    │
      ▼                    │
 Speed PID                 │
      │                    │
      ▼                    │
     PWM                   │
      │                    │
      ▼                    │
    MOTOR ───► ENCODER ────┘

Đây gọi là:

Cascade Control – điều khiển vòng tầng.


11. Nhưng trong Bài 48 chúng ta chưa cần phức tạp ngay

Đầu tiên hãy hiểu:

Position P Controller

Sau đó mới nâng cấp thành:

Position PID

hoặc tốt hơn:

Position → Speed → PWM

12. Đọc vị trí từ Encoder

Theo Bài 45:

int32_t position;

Ví dụ:

position = (int16_t)TIM2->CNT;

Nhưng có một vấn đề.


13. Counter 16-bit có giới hạn

TIM2:

0 → 65535

Nếu motor quay quá nhiều:

65535
↓
0

Do đó nếu chúng ta muốn điều khiển vị trí lâu dài:

100000 counts

thì chỉ dùng:

TIM2->CNT

không đủ.


14. Tạo Position Counter 32-bit

Ta có thể duy trì:

volatile int32_t encoder_position;

và cập nhật dựa trên delta.

Ví dụ:

int16_t current;
int16_t delta;

current = (int16_t)TIM2->CNT;

delta = current - last_count;

encoder_position += delta;

last_count = current;

15. Tại sao cách này xử lý overflow?

Ví dụ:

last = 65530
current = 5

Nếu xử lý đúng kiểu signed 16-bit:

delta = +11

Motor thực sự đã quay tiến 11 counts.

Ngược lại:

last = 5
current = 65530

thì:

delta = -11

Đây là kỹ thuật rất quan trọng khi làm encoder.


16. Driver Encoder nâng cấp

Ta có thể viết:

typedef struct
{
    int16_t last_count;
    int32_t position;
} Encoder_t;

Khai báo:

Encoder_t encoder;

17. Hàm cập nhật

void Encoder_Update(void)
{
    int16_t current;
    int16_t delta;

    current =
        (int16_t)TIM2->CNT;

    delta =
        current - encoder.last_count;

    encoder.position += delta;

    encoder.last_count =
        current;
}

18. Khởi tạo

void Encoder_Position_Init(void)
{
    TIM2->CNT = 0;

    encoder.last_count = 0;
    encoder.position = 0;
}

19. Đọc vị trí

int32_t Encoder_GetPosition(void)
{
    return encoder.position;
}

Bây giờ:

position = Encoder_GetPosition();

có thể là:

0
500
1000
5000
10000
...

thậm chí:

-5000

nếu quay ngược.


20. Đặt vị trí mục tiêu

Ta định nghĩa:

int32_t target_position;

Ví dụ:

target_position = 1000;

21. Tính sai số

int32_t error;

error =
    target_position -
    encoder.position;

Ví dụ:

Target = 1000
Current = 700

thì:

Error = +300

→ cần quay thuận.


Nếu:

Target = 1000
Current = 1300

thì:

Error = -300

→ cần quay ngược.


22. Đây chính là lý do Encoder cho biết chiều

Ta không cần tự hỏi:

Motor đang quay trái hay phải?

Chỉ cần:

Error > 0

→ một chiều.

Error < 0

→ chiều còn lại.


23. Hàm điều khiển vị trí P

Ví dụ:

#define KP_POSITION 0.2f
#define PWM_MAX     1000
#define PWM_MIN      100

Hàm:

void Position_Control(void)
{
    int32_t error;
    float output;

    error =
        target_position -
        encoder.position;

    output =
        KP_POSITION *
        error;

    if (output > PWM_MAX)
        output = PWM_MAX;

    if (output < -PWM_MAX)
        output = -PWM_MAX;

    Motor_SetPWM((int16_t)output);
}

24. Vấn đề PWM_MIN

Nếu:

output = 10

thì motor có thể không quay vì ma sát.

Do đó:

0 < PWM < PWM_MIN

ta có thể ép thành:

PWM_MIN

Ví dụ:

if (output > 0 &&
    output < PWM_MIN)
{
    output = PWM_MIN;
}

Tương tự chiều âm.


25. Nhưng có một lỗi nếu làm như vậy

Giả sử:

Error = 1

P controller cho:

PWM = 0.2

Ta ép:

PWM = 100

Motor có thể chạy:

Target = 1000
Current = 999

→ motor chạy qua:

1000

→ 1001

→ 1002

→ …

→ controller đổi chiều.

Robot có thể:

← → ← → ← →

gần vị trí đích.

Đây là:

Hunting / Oscillation


26. Cần vùng chết – Deadband

Ví dụ:

#define POSITION_TOLERANCE 3

Nếu:

abs(error) <= 3

thì coi như đã tới vị trí.

if (abs(error) <=
    POSITION_TOLERANCE)
{
    Motor_Stop();
    return;
}

27. Đây là kỹ thuật cực kỳ hữu ích

Ví dụ:

Target = 1000

Nếu:

Current = 998

thì:

Error = 2

và:

|Error| <= 3

→ STOP.

Không cần cố ép motor đến đúng từng count tuyệt đối.


28. Code Position Controller cơ bản

void Position_Control(void)
{
    int32_t error;
    float output;

    error =
        target_position -
        encoder.position;

    /*
        Deadband
    */

    if (error <= 3 &&
        error >= -3)
    {
        Motor_Stop();
        return;
    }

    /*
        P Controller
    */

    output =
        KP_POSITION *
        error;

    /*
        Limit
    */

    if (output > PWM_MAX)
        output = PWM_MAX;

    if (output < -PWM_MAX)
        output = -PWM_MAX;

    /*
        Minimum PWM
    */

    if (output > 0 &&
        output < PWM_MIN)
    {
        output = PWM_MIN;
    }

    if (output < 0 &&
        output > -PWM_MIN)
    {
        output = -PWM_MIN;
    }

    Motor_SetPWM(
        (int16_t)output);
}

29. Motor_SetPWM phải hiểu dấu

Ta cần một hàm kiểu:

void Motor_SetPWM(int16_t command)
{
    if (command > 0)
    {
        Motor_Forward();

        Motor_SetDuty(
            command);
    }

    else if (command < 0)
    {
        Motor_Reverse();

        Motor_SetDuty(
            -command);
    }

    else
    {
        Motor_Stop();
    }
}

Như vậy:

+500

→ quay thuận.

-500

→ quay ngược.

0

→ dừng.


30. Đây là một kiến trúc rất đẹp

Position Error
      │
      ▼
Position Controller
      │
      ▼
 -1000 ... +1000
      │
      ▼
Motor_SetPWM()
      │
 ┌────┴────┐
 ▼         ▼
DIR       PWM

31. Ví dụ thực tế

Giả sử:

Target = 2000
Current = 0
Kp = 0.5
PWM_MAX = 1000

Ban đầu:

Error = 2000

P:

PWM = 1000

Motor chạy nhanh.


Khi:

Current = 1200

thì:

Error = 800

PWM:

400

Khi:

Current = 1800

thì:

Error = 200

PWM:

100

Khi:

Current = 1998

thì:

Error = 2

nếu tolerance = 3:

STOP

32. Nhìn trực quan

PWM
100% ┤████████████
     │
 80% ┤██████████
     │
 60% ┤████████
     │
 40% ┤████
     │
 20% ┤██
     │
  0% ┼──────────────────
       0             Target
       Position →

Càng gần target:

PWM ↓

33. Nhưng đây vẫn chưa phải giải pháp tốt nhất

Bạn sẽ thấy một vấn đề:

Khi motor chạy nhanh:

Error lớn

P controller cấp PWM lớn.

Khi gần đích:

Error nhỏ

PWM giảm.

Nhưng motor có quán tính.

Ví dụ:

Current = 1950
Target = 2000

PWM vẫn còn 25%.

Motor đang chạy nhanh và có thể vượt:

2000

đến:

2040

sau đó controller đảo chiều.


34. Đây là lúc Bài 47 phát huy tác dụng

Ở Bài 47 ta đã học:

Speed PID

Do đó kiến trúc tốt hơn là:

Target Position
       │
       ▼
Position Controller
       │
       ▼
Target RPM
       │
       ▼
Speed PID
       │
       ▼
PWM
       │
       ▼
Motor
       │
       ▼
Encoder

Đây là kiến trúc cascade.


35. Vòng ngoài

Position Controller:

Position Error
     ↓
Target Speed

Ví dụ:

Error = 1000 counts
→ Target Speed = 100 RPM

Khi gần đích:

Error = 100 counts
→ Target Speed = 20 RPM

36. Vòng trong

Speed Controller:

Target RPM
    ↓
Speed PID
    ↓
PWM

Encoder đo:

Actual RPM

và PID liên tục điều chỉnh PWM.


37. Vì sao cascade tốt hơn?

Vì:

Vòng vị trí

quan tâm:

"Tôi đã tới đâu?"

Vòng tốc độ

quan tâm:

"Tôi đang chạy nhanh bao nhiêu?"

Hai vòng phối hợp:

Position
   ↓
Speed
   ↓
PWM

38. Ví dụ

Target:

1000 counts

Current:

0

Position loop:

Error = 1000

→ yêu cầu:

Target RPM = 100 RPM

Khi:

Current = 800

thì:

Error = 200

Position loop giảm:

Target RPM = 30 RPM

Khi:

Current = 980

thì:

Error = 20

Target RPM:

5 RPM

Khi:

Current ≈ 1000

thì:

Target RPM = 0

→ Motor dừng.


39. Đây là cách robot công nghiệp làm

Không nhất thiết phải điều khiển:

Position → PWM trực tiếp

mà thường:

Position Loop
      ↓
Velocity Loop
      ↓
Current/Torque Loop
      ↓
Motor

Các hệ thống servo chuyên nghiệp thậm chí có:

Position
 ↓
Velocity
 ↓
Current

nhiều vòng điều khiển lồng nhau.


40. Bài tập 1

Giả sử:

CPR = 400
Target = 800 counts

Motor cần quay:

[
800/400=2
]

→ 2 vòng.


41. Bài tập 2

Encoder:

400 CPR

Target:

1000 counts

Góc:

[
1000/400\times360
]

=

900°

Tức:

2.5 vòng

42. Bài tập 3 – quay chính xác 90°

Encoder:

400 CPR

Muốn:

90°

Target:

[
400\times90/360
]

=

100 counts

Do đó:

target_position = 100;

Motor quay tới:

100 counts

→ khoảng 90°.


43. Bài tập 4 – robot

Giả sử bánh xe:

D = 70 mm
CPR = 400

Muốn robot đi:

500 mm

Số vòng:

[
500/(\pi\times70)
]

≈

2.27 vòng

Số count:

[
2.27\times400
]

≈

908 counts

Vậy:

target_position = 908;

44. Nhưng robot hai bánh có một vấn đề

Nếu chỉ có một encoder:

Encoder
   ↓
Motor

ta biết bánh đó đã đi bao xa.

Nhưng robot có:

Left Motor
Right Motor

thì nên có:

Left Encoder
Right Encoder

45. Robot đi thẳng

Ví dụ:

Left = 1000 counts
Right = 1000 counts

→ hai bánh đi cùng khoảng cách.

Robot có xu hướng đi thẳng.

Nhưng nếu:

Left = 1000
Right = 900

thì:

Left bánh đi xa hơn

robot sẽ lệch hướng.


46. Điều này dẫn đến bài học tiếp theo

Ta sẽ cần:

Left Position
Right Position

và so sánh:

Left Error
Right Error

để điều khiển từng motor độc lập.


47. Từ Bài 48 tới robot thực tế

Kiến trúc sẽ dần trở thành:

             TARGET
                │
                ▼
        Position Controller
          ┌─────┴─────┐
          ▼           ▼
       Left Ref     Right Ref
          │           │
          ▼           ▼
       Speed PID    Speed PID
          │           │
          ▼           ▼
       Left PWM     Right PWM
          │           │
          ▼           ▼
       Left Motor   Right Motor
          │           │
          ▼           ▼
       Left ENC     Right ENC
          │           │
          └─────┬─────┘
                ▼
             Feedback

Đây chính là nền móng của robot di chuyển chính xác.


48. Một lưu ý rất quan trọng về Motor_Stop()

Có nhiều kiểu dừng motor:

Coast

PWM = 0

Motor tự giảm tốc.

Brake

Hai đầu motor được điều khiển để hãm.

Active Brake

Driver motor chủ động tạo mô-men hãm.

Đối với điều khiển vị trí, kiểu dừng ảnh hưởng rất lớn đến:

overshoot

Nếu motor có quán tính lớn, chỉ:

PWM = 0;

có thể chưa đủ.


49. Vì vậy khi tuning Position Controller

Bạn phải quan sát:

Target = 1000

Motor thực tế:

0
200
400
600
800
950
1000

là tốt.

Nhưng nếu:

0
300
700
1100
900
1050
980
1020
...

thì hệ thống đang dao động.

Cần điều chỉnh:

Kp
PWM_MAX
PWM_MIN
deadband
speed limit

50. Ba thông số rất quan trọng

Đối với Position Controller đơn giản:

#define POSITION_KP       0.5f
#define POSITION_MAX_PWM 1000
#define POSITION_DEADBAND 3

Đừng bắt đầu với giá trị quá lớn.

Hãy:

Kp nhỏ
 ↓
test
 ↓
tăng dần

51. Quy trình tuning

Bước 1

Cho:

Kp rất nhỏ

Ví dụ:

0.05

Bước 2

Ra lệnh:

1000 counts

Bước 3

Quan sát.

Nếu motor quá chậm:

Kp ↑

Nếu motor vượt đích mạnh:

Kp ↓

52. Đừng vội thêm PID vị trí

Một lỗi người mới thường mắc là:

Encoder
+
PID
+
PWM
+
Position
+
Speed

tất cả đưa vào cùng một công thức ngay từ đầu.

Rất khó debug.

Nên học theo thứ tự:

1. Encoder
      ↓
2. Position
      ↓
3. P Position
      ↓
4. Speed PID
      ↓
5. Position → Speed
      ↓
6. Cascade

53. Bài 48 bạn cần nắm chắc 10 ý

1.

Encoder:

A + B

cho biết vị trí và chiều.

2.

CNT

là bộ đếm phần cứng.

3.

Vị trí có thể biểu diễn bằng:

counts

4.

Góc:

[
Angle=Count/CPR\times360
]

5.

Khoảng cách:

[
Distance=Count/CPR\times\pi D
]

6.

Sai số:

[
Error=Target-Current
]

7.

P Controller:

[
Output=K_p\times Error
]

8.

Deadband:

|Error| <= tolerance
→ STOP

9.

P position có thể bị:

Overshoot
Hunting
Steady-state error

10.

Giải pháp chuyên nghiệp:

Position Loop
      ↓
Speed Loop
      ↓
PWM

54. Toàn bộ kiến thức từ Bài 41 → 48

Bây giờ chuỗi kiến thức của bạn đã rất đẹp:

Bài 41
PWM
 │
 ▼
Bài 42
Servo
 │
 ▼
Bài 43
HC-SR04
 │
 ▼
Bài 44
HC-SR04 State Machine
 │
 ▼
Bài 45
Encoder Mode
 │
 ▼
Bài 46
PWM + Encoder
 │
 ▼
Bài 47
Speed PID
 │
 ▼
Bài 48
Position Control

Và bước tiếp theo rất tự nhiên là:

BÀI 49 – PID ĐIỀU KHIỂN VỊ TRÍ MOTOR + CASCADE POSITION → SPEED

Ở Bài 49, chúng ta sẽ không chỉ dùng:

Position P

mà sẽ xây dựng một cấu trúc hoàn chỉnh:

TARGET POSITION
       │
       ▼
┌──────────────────┐
│ POSITION CONTROL │
└────────┬─────────┘
         │ Target RPM
         ▼
┌──────────────────┐
│    SPEED PID     │
└────────┬─────────┘
         │ PWM
         ▼
       MOTOR
         │
         ▼
      ENCODER
         │
     ┌───┴────┐
     ▼        ▼
 POSITION    RPM
     │        │
     └────────┘

Đây là lúc bạn bắt đầu thực sự hiểu servo motor và hệ thống điều khiển chuyển động, chứ không còn chỉ là lập trình GPIO/Timer đơn thuần nữa.

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