BÀI 53 – ODOMETRY NÂNG CAO: TÍNH X, Y, θ VÀ ĐIỀU KHIỂN ROBOT ĐẾN MỘT TỌA ĐỘ
Ở Bài 52, chúng ta đã làm được điều rất quan trọng:
Encoder trái + Encoder phải
↓
Quãng đường bánh
↓
Δθ
↓
X, Y, θ
Nhưng robot mới chỉ biết mình đang ở đâu.
Bài 53 sẽ tiến thêm một bước:
Robot biết vị trí hiện tại và tự chạy đến tọa độ mà chúng ta yêu cầu.
Ví dụ:
Robot_GoTo(1000, 500);
nghĩa là:
Hãy đi đến điểm
(X = 1000 mm, Y = 500 mm).
1. Bức tranh tổng thể
Ta sẽ xây dựng hệ thống:
TARGET
X = 1000 mm
Y = 500 mm
│
▼
┌─────────────────┐
│ POSITION │
│ CONTROLLER │
└────────┬────────┘
│
┌────────┴────────┐
▼ ▼
Target Distance Target Heading
│ │
└────────┬────────┘
▼
MOTION CONTROL
│
┌────────┴────────┐
▼ ▼
LEFT RPM RIGHT RPM
│ │
▼ ▼
PID LEFT PID RIGHT
│ │
▼ ▼
PWM LEFT PWM RIGHT
│ │
▼ ▼
MOTOR LEFT MOTOR RIGHT
│ │
▼ ▼
ENCODER LEFT ENCODER RIGHT
└────────┬────────┘
▼
ODOMETRY
X, Y, θ
│
└──────► feedback
Đây chính là cấu trúc cơ bản của một robot differential-drive có điều khiển vị trí.
2. Robot phải biết 3 thứ
Ta gọi trạng thái robot:
typedef struct
{
float x;
float y;
float theta;
} Pose_t;
Trong đó:
x → tọa độ X
y → tọa độ Y
theta → hướng robot
Ví dụ:
X = 500 mm
Y = 300 mm
θ = 90°
có nghĩa robot đang ở:
Y
↑
│ ROBOT ↑
│
│
└────────────────→ X
vị trí:
(500, 300)
và đang hướng lên.
3. Hệ tọa độ
Ta thống nhất:
+Y
↑
│
│
│
-X ◄──────┼──────► +X
│
│
│
↓
-Y
Góc:
θ = 0°
robot hướng:
→
và:
θ = +90°
robot hướng:
↑
4. Tại sao phải dùng X, Y, θ?
Ở Bài 51 ta có:
Move 1000 mm
Ở Bài 52:
Turn 90°
Nhưng bây giờ ta có thể nói:
GoTo(1000, 500)
Robot không cần biết trước phải:
đi 700 mm
quay 30°
đi 400 mm
...
Controller tự tính.
5. Tính vị trí bằng encoder
Ta quay lại công thức differential drive.
Gọi:
ΔL = quãng đường bánh trái
ΔR = quãng đường bánh phải
Khoảng cách hai bánh:
W = Wheel Base
Ta có:
[
\Delta S=\frac{\Delta L+\Delta R}{2}
]
và:
[
\Delta\theta=\frac{\Delta L-\Delta R}{W}
]
Đây là hai công thức cốt lõi của odometry.
6. Cập nhật X, Y
Nếu robot đang có góc:
θ
thì:
[
\Delta X=\Delta S\cos(\theta)
]
[
\Delta Y=\Delta S\sin(\theta)
]
Sau đó:
X = X + ΔX
Y = Y + ΔY
θ = θ + Δθ
Đây là cách đơn giản nhất để hiểu odometry.
7. Nhưng có một cải tiến quan trọng
Trong Bài 52, chúng ta dùng:
cos(theta)
sin(theta)
với theta hiện tại.
Khi robot vừa đi vừa quay, chính xác hơn là dùng góc trung bình trong bước đó:
[
\theta_{mid}
\theta+\frac{\Delta\theta}{2}
]
Sau đó:
[
\Delta X=
\Delta S\cos(\theta_{mid})
]
[
\Delta Y=
\Delta S\sin(\theta_{mid})
]
Đây là cải tiến rất đáng dùng trong Bài 53.
8. Tại sao phải dùng góc giữa?
Giả sử trong một chu kỳ:
θ đầu = 20°
θ cuối = 30°
Robot không chạy toàn bộ quãng đường ở:
20°
mà nó liên tục quay:
20 → 21 → 22 → ... → 30°
Góc trung bình:
25°
sẽ đại diện tốt hơn.
9. Hàm Odometry_Update nâng cao
void Odometry_Update(void)
{
int32_t left_now;
int32_t right_now;
int32_t delta_left;
int32_t delta_right;
float left_mm;
float right_mm;
float delta_s;
float delta_theta;
float theta_mid;
left_now =
left_encoder.position;
right_now =
right_encoder.position;
delta_left =
left_now -
odom.last_left;
delta_right =
right_now -
odom.last_right;
odom.last_left = left_now;
odom.last_right = right_now;
left_mm =
(float)delta_left /
LEFT_COUNTS_PER_MM;
right_mm =
(float)delta_right /
RIGHT_COUNTS_PER_MM;
delta_s =
(left_mm + right_mm)
/ 2.0f;
delta_theta =
(left_mm - right_mm)
/ WHEEL_BASE_MM;
theta_mid =
odom.theta +
delta_theta / 2.0f;
odom.x +=
delta_s * cosf(theta_mid);
odom.y +=
delta_s * sinf(theta_mid);
odom.theta +=
delta_theta;
}
Đây là một phiên bản odometry tốt hơn phiên bản ở Bài 52.
10. Chuẩn hóa góc
Sau nhiều vòng quay:
θ = 370°
hoặc:
θ = -450°
không có gì sai về mặt toán học, nhưng điều khiển sẽ dễ hơn nếu chuẩn hóa.
Ta có:
float NormalizeAngleRad(float angle)
{
while (angle > M_PI)
angle -= 2.0f * M_PI;
while (angle < -M_PI)
angle += 2.0f * M_PI;
return angle;
}
Sau:
odom.theta =
NormalizeAngleRad(odom.theta);
ta giữ:
-180° ≤ θ ≤ +180°
11. Tọa độ hiện tại
Ta có thể viết:
Pose_t Robot_GetPose(void)
{
return odom.pose;
}
hoặc đơn giản:
float Robot_GetX(void)
{
return odom.x;
}
float Robot_GetY(void)
{
return odom.y;
}
float Robot_GetTheta(void)
{
return odom.theta;
}
12. Bây giờ đến phần quan trọng nhất: GoTo
Giả sử:
Robot hiện tại:
X = 200
Y = 100
θ = 0°
Mục tiêu:
X = 1000
Y = 500
Ta cần tính:
ΔX = 1000 - 200 = 800
ΔY = 500 - 100 = 400
13. Khoảng cách đến mục tiêu
Dùng định lý Pythagore:
[
d=\sqrt{\Delta X^2+\Delta Y^2}
]
Trong ví dụ:
[
d=\sqrt{800^2+400^2}
]
≈
894.4 mm
Trong C:
distance =
sqrtf(dx * dx +
dy * dy);
14. Robot cần quay về hướng nào?
Ta cần tính góc của vector:
(dx, dy)
Công thức:
target_heading =
atan2f(dy, dx);
Đây là một hàm cực kỳ quan trọng trong robotics.
15. atan2f() là gì?
Nếu:
dx = 800
dy = 400
thì:
atan2f(400, 800)
≈
26.565°
Nghĩa là robot phải hướng khoảng:
26.6°
để chạy thẳng về điểm đích.
16. Đây là lúc atan2() trở thành vũ khí quan trọng
Trong robotics bạn sẽ gặp rất nhiều:
atan2f(y, x)
Nó giúp tính:
Vector này đang hướng theo góc bao nhiêu?
Ví dụ:
TARGET
●
/
/
/
ROBOT ●────→
Vector:
(dx, dy)
và:
angle = atan2(dy, dx)
17. Tính sai số góc
Ta có:
target_heading
và:
odom.theta
Do đó:
heading_error =
target_heading -
odom.theta;
Nhưng phải normalize:
heading_error =
NormalizeAngleRad(
heading_error);
18. Ví dụ
Robot:
θ = 0°
Target heading:
26.6°
thì:
heading error = +26.6°
Robot phải quay trái.
19. Nếu robot đang quay 170°
và target:
-170°
tính bình thường:
-170 - 170
=
-340°
nhưng robot thực tế chỉ cần:
+20°
Đây chính là lý do:
NormalizeAngleRad()
cực kỳ quan trọng.
20. Điều khiển góc
Ta tạo:
#define GO_TO_ANGLE_KP 2.0f
và:
turn_command =
GO_TO_ANGLE_KP *
heading_error;
Sau đó giới hạn:
turn_command =
Clamp(
turn_command,
-MAX_TURN_SPEED,
MAX_TURN_SPEED);
21. Bây giờ có hai thành phần điều khiển
Robot cần:
Thành phần tiến
distance_error
và:
Thành phần quay
heading_error
Ta có:
Distance → Forward Speed
Angle → Turn Speed
Đây là một ý tưởng rất quan trọng.
22. Position Controller
Ví dụ:
forward_speed =
GO_TO_DISTANCE_KP *
distance;
Giới hạn:
forward_speed =
Clamp(
forward_speed,
0,
MAX_FORWARD_SPEED);
23. Turn Controller
turn_speed =
GO_TO_ANGLE_KP *
heading_error;
Giới hạn:
turn_speed =
Clamp(
turn_speed,
-MAX_TURN_SPEED,
MAX_TURN_SPEED);
24. Từ Forward + Turn → hai bánh
Đây là một công thức cực kỳ quan trọng.
Ta có:
V = tốc độ tiến
ω = tốc độ quay
Hai bánh:
[
V_L=V-\omega\frac{W}{2}
]
[
V_R=V+\omega\frac{W}{2}
]
Đây chính là inverse kinematics của differential-drive robot.
25. Hiểu trực quan
Nếu:
V = 100
ω = 0
thì:
Left = 100
Right = 100
→ đi thẳng.
Nếu:
V = 100
ω > 0
thì:
Left < 100
Right > 100
→ quay trái.
Nếu:
V = 100
ω < 0
thì:
Left > 100
Right < 100
→ quay phải.
26. Nhưng phải chú ý đơn vị
Nếu:
V = mm/s
thì:
ω = rad/s
và:
W = mm
sẽ cho:
VL, VR = mm/s
Đây là cách làm sạch và đúng về mặt vật lý.
27. Công thức đầy đủ
V
│
┌─────┴─────┐
│ │
▼ ▼
-ωW/2 +ωW/2
│ │
▼ ▼
VL VR
Trong C:
left_speed =
linear_speed -
angular_speed *
WHEEL_BASE_MM / 2.0f;
right_speed =
linear_speed +
angular_speed *
WHEEL_BASE_MM / 2.0f;
28. Đây là bước tiến rất lớn
Trước đây chúng ta điều khiển:
Left RPM
Right RPM
Bây giờ điều khiển ở tầng cao hơn:
Linear Speed V
Angular Speed ω
sau đó mới chuyển xuống:
Left wheel
Right wheel
Đây chính là cách tư duy robotics.
29. Kiến trúc điều khiển hoàn chỉnh
TARGET X,Y
│
▼
POSITION CONTROLLER
│
┌───────┴────────┐
▼ ▼
DISTANCE ERROR HEADING ERROR
│ │
▼ ▼
Linear V Angular ω
│ │
└───────┬────────┘
▼
INVERSE KINEMATICS
│
┌──────┴──────┐
▼ ▼
LEFT V RIGHT V
│ │
▼ ▼
SPEED PID SPEED PID
│ │
▼ ▼
PWM L PWM R
│ │
▼ ▼
MOTOR L MOTOR R
│ │
▼ ▼
ENCODER L ENCODER R
└──────┬──────┘
▼
ODOMETRY
X,Y,θ
│
└──── feedback
30. Hàm Robot_GoTo()
Ta tạo cấu trúc:
typedef struct
{
float target_x;
float target_y;
uint8_t active;
} GoToTarget_t;
Khai báo:
GoToTarget_t goto_target;
31. Hàm bắt đầu
void Robot_GoTo(
float x,
float y)
{
goto_target.target_x = x;
goto_target.target_y = y;
goto_target.active = 1;
}
Sau đó:
Robot_GoTo(1000.0f, 500.0f);
32. Tính sai số vị trí
float dx;
float dy;
dx =
goto_target.target_x -
odom.x;
dy =
goto_target.target_y -
odom.y;
33. Tính distance
float distance;
distance =
sqrtf(
dx * dx +
dy * dy);
34. Tính heading
float target_heading;
target_heading =
atan2f(dy, dx);
35. Tính heading error
float heading_error;
heading_error =
target_heading -
odom.theta;
heading_error =
NormalizeAngleRad(
heading_error);
36. Tính tốc độ tiến
float linear_speed;
linear_speed =
GO_TO_DISTANCE_KP *
distance;
Ví dụ:
#define GO_TO_DISTANCE_KP 0.5f
Nếu còn:
500 mm
thì:
V = 250 mm/s
Nhưng phải giới hạn.
37. Giới hạn tốc độ
linear_speed =
Clamp(
linear_speed,
0.0f,
MAX_LINEAR_SPEED);
Ví dụ:
#define MAX_LINEAR_SPEED 300.0f
→ tối đa 300 mm/s.
38. Tính tốc độ quay
float angular_speed;
angular_speed =
GO_TO_ANGLE_KP *
heading_error;
Ví dụ:
#define GO_TO_ANGLE_KP 2.0f
Nếu:
heading error = 0.2 rad
thì:
ω = 0.4 rad/s
39. Giới hạn angular speed
angular_speed =
Clamp(
angular_speed,
-MAX_ANGULAR_SPEED,
MAX_ANGULAR_SPEED);
Ví dụ:
#define MAX_ANGULAR_SPEED 2.0f
40. Một vấn đề: Robot không nên vừa quay quá mạnh vừa chạy nhanh
Nếu:
heading error = 90°
thì robot đang hướng hoàn toàn sai.
Nếu vẫn cho:
linear_speed = 300 mm/s
robot sẽ chạy thành một đường cong rất rộng.
Do đó ta cần giảm tốc độ tiến khi sai số góc lớn.
41. Chiến lược đơn giản
Nếu:
abs(heading_error) > 45°
thì:
linear_speed = 0
Robot quay tại chỗ trước.
Nếu:
abs(heading_error) < 45°
thì bắt đầu chạy.
42. Code
if (fabsf(heading_error) >
45.0f * DEG_TO_RAD)
{
linear_speed = 0.0f;
}
Như vậy:
Robot hướng sai nhiều
↓
QUAY
↓
Hướng gần đúng
↓
TIẾN + QUAY
43. Đây là State Machine tốt hơn
Ta có thể dùng:
typedef enum
{
GOTO_IDLE,
GOTO_ALIGN,
GOTO_DRIVE,
GOTO_REACHED
} GoToState_t;
44. GOTO_ALIGN
Robot chỉ quay:
V = 0
ω = ...
45. GOTO_DRIVE
Robot:
V > 0
ω ≠ 0
tức vừa tiến vừa tự chỉnh hướng.
46. GOTO_REACHED
Khi:
distance < tolerance
thì:
V = 0
ω = 0
Robot dừng.
47. Tolerance vị trí
Ví dụ:
#define POSITION_TOLERANCE_MM 10.0f
Nếu:
distance <= 10 mm
→ coi như đã tới.
Đừng đặt:
0.1 mm
với một robot nhỏ dùng encoder thông thường.
Đó là yêu cầu không thực tế.
48. Hàm điều khiển GoTo
Một phiên bản đơn giản:
void Robot_GoTo_Update(void)
{
float dx;
float dy;
float distance;
float target_heading;
float heading_error;
float linear_speed;
float angular_speed;
float left_speed;
float right_speed;
dx =
goto_target.target_x -
odom.x;
dy =
goto_target.target_y -
odom.y;
distance =
sqrtf(
dx * dx +
dy * dy);
if (distance <=
POSITION_TOLERANCE_MM)
{
left_speed = 0.0f;
right_speed = 0.0f;
goto_target.active = 0;
return;
}
target_heading =
atan2f(dy, dx);
heading_error =
NormalizeAngleRad(
target_heading -
odom.theta);
linear_speed =
GO_TO_DISTANCE_KP *
distance;
linear_speed =
Clamp(
linear_speed,
0.0f,
MAX_LINEAR_SPEED);
angular_speed =
GO_TO_ANGLE_KP *
heading_error;
angular_speed =
Clamp(
angular_speed,
-MAX_ANGULAR_SPEED,
MAX_ANGULAR_SPEED);
if (fabsf(heading_error) >
45.0f * DEG_TO_RAD)
{
linear_speed = 0.0f;
}
left_speed =
linear_speed -
angular_speed *
WHEEL_BASE_MM / 2.0f;
right_speed =
linear_speed +
angular_speed *
WHEEL_BASE_MM / 2.0f;
Robot_SetWheelSpeed(
left_speed,
right_speed);
}
Đây là bộ điều khiển Go-To cơ bản.
49. Nhưng Robot_SetWheelSpeed() không nên trực tiếp xuất PWM
Đây là điểm rất quan trọng.
Không nên:
GoTo
↓
PWM
mà:
GoTo
↓
Wheel target speed
↓
Speed PID
↓
PWM
Tức:
Robot_SetWheelSpeed(
left_speed,
right_speed);
chỉ đặt:
target_left_speed
target_right_speed
50. Sau đó Speed PID của Bài 50 xử lý
pwm_left =
PID_Update(
&left_pid,
target_left_speed,
left_actual_speed,
dt);
và:
pwm_right =
PID_Update(
&right_pid,
target_right_speed,
right_actual_speed,
dt);
51. Đây chính là Cascade Control
Ta có:
TARGET X,Y
│
▼
GO-TO CONTROLLER
│
V / ω
│
▼
INVERSE KINEMATICS
│
Left / Right Speed
│
▼
SPEED PID
│
PWM
│
MOTOR
│
ENCODER
│
▼
ODOMETRY
│
X,Y,θ
│
└──────────feedback
52. Ví dụ cụ thể
Robot khởi đầu:
X = 0
Y = 0
θ = 0°
Ta gọi:
Robot_GoTo(
1000,
500);
Bước 1
dx = 1000
dy = 500
Khoảng cách:
894 mm
Heading:
26.6°
Robot đang:
0°
→ cần quay trái.
Bước 2
Robot quay:
0°
→
10°
→
20°
→
26°
Khi sai số nhỏ hơn ngưỡng:
linear_speed > 0
Bước 3
Robot bắt đầu:
↗
và liên tục tính lại:
dx
dy
distance
heading_error
Bước 4
Robot càng gần:
distance ↓
thì:
linear_speed ↓
Bước 5
Cuối cùng:
X ≈ 1000
Y ≈ 500
và:
distance < 10 mm
→ STOP.
53. Nhưng có một vấn đề lớn
Bạn có thể nhận thấy:
Nếu robot luôn hướng thẳng vào điểm
(1000,500), nó có thể chạy theo một đường cong.
Đúng.
Đó là đặc điểm của bộ điều khiển đơn giản này.
Ta chưa kiểm soát quỹ đạo theo nghĩa chính xác.
54. Đây chưa phải Path Planning
GoTo(X,Y) chỉ trả lời:
Làm thế nào để tới điểm này?
Nó chưa trả lời:
Làm thế nào để đi theo một con đường tối ưu tránh vật cản?
Đó sẽ là những bài sau.
55. Một vấn đề khác: Odometry có drift
Ví dụ robot thực tế:
Target:
X = 1000
Y = 500
Odometry báo:
X = 1000
Y = 500
nhưng thực tế có thể:
X = 985
Y = 520
vì bánh trượt.
Do đó:
GoTo dựa hoàn toàn vào encoder chỉ chính xác trong giới hạn của odometry.
56. Cách cải thiện
Sau này ta có thể thêm:
Encoder
+
IMU
để cải thiện:
θ
hoặc:
Encoder
+
IMU
+
LiDAR
để định vị tốt hơn.
Nhưng chưa nên nhảy sang đó ngay.
Trước tiên phải làm encoder odometry thật chắc.
57. Bài tập cực kỳ quan trọng
Hãy thử trên robot thật:
Test 1
Robot_GoTo(500, 0);
Robot phải đi:
→ 500 mm
Test 2
Reset robot:
X = 0
Y = 0
θ = 0
sau đó:
Robot_GoTo(0, 500);
Robot phải:
quay trái 90°
+
đi 500 mm
Test 3
Robot_GoTo(500, 500);
Robot phải đi tới góc:
TARGET
●
/
/
/
●
ROBOT
58. Test hình vuông
Đây là bài test rất hay.
Bắt đầu:
(0,0)
Ra lệnh:
GoTo(500,0)
GoTo(500,500)
GoTo(0,500)
GoTo(0,0)
Robot sẽ cố tạo thành:
(0,500) ───────── (500,500)
│ │
│ │
│ │
│ │
(0,0) ───────── (500,0)
Nếu quay về gần:
(0,0)
thì hệ thống odometry khá tốt.
59. Nhưng có một điều bạn nên đo
Sau khi chạy hình vuông, in:
X
Y
θ
Ví dụ:
START
X=0
Y=0
θ=0
END
X=38
Y=-17
θ=4.8°
Đừng coi đây là thất bại.
Đây là dữ liệu calibration.
60. Từ sai số này có thể hiệu chỉnh
Ví dụ robot luôn:
đi xa hơn theo bánh phải
ta có thể hiệu chỉnh:
RIGHT_COUNTS_PER_MM
Nếu robot quay nhiều hơn dự kiến:
WHEEL_BASE_MM
có thể cần calibration lại.
61. Ba thông số hình học cực kỳ quan trọng
Trong robot 2 bánh, hãy nhớ:
LEFT_COUNTS_PER_MM
RIGHT_COUNTS_PER_MM
WHEEL_BASE_MM
Ba thông số này ảnh hưởng trực tiếp đến odometry.
62. Calibration WHEEL_BASE
Ví dụ khai báo:
#define WHEEL_BASE_MM 150.0f
Nhưng robot thực tế quay:
90° command
→
94°
thì WHEEL_BASE_MM đang có sai số hoặc còn các sai số cơ khí khác.
Ta có thể calibration bằng cách cho robot:
10 vòng quay tại chỗ
để giảm ảnh hưởng sai số đo góc nhỏ.
63. Calibration encoder
Tương tự:
Robot_MoveDistance(1000);
đo khoảng cách thực tế.
Nếu robot đi:
1030 mm
thay vì:
1000 mm
thì:
COUNTS_PER_MM
cần điều chỉnh.
64. Một kiến trúc code nên hướng tới
Project của bạn từ đây có thể chia:
STM32_ROBOT/
│
├── motor.c
├── motor.h
│
├── encoder.c
├── encoder.h
│
├── pid.c
├── pid.h
│
├── odometry.c
├── odometry.h
│
├── motion.c
├── motion.h
│
├── robot.c
├── robot.h
│
└── main.c
Trong đó:
motor
Chỉ biết:
PWM
DIR
encoder
Chỉ biết:
COUNT
RPM
DISTANCE
pid
Chỉ biết:
TARGET
ACTUAL
OUTPUT
odometry
Chỉ biết:
X
Y
THETA
motion
Biết:
GoTo
Move
Turn
Đây là cách tổ chức rất tốt khi project lớn dần.
65. Một nguyên tắc thiết kế rất quan trọng
Đừng viết:
if (distance > ...)
GPIO...
PWM...
encoder...
tất cả vào main.c.
Hãy để:
main.c
↓
Robot_Update()
↓
Motion_Update()
↓
PID
↓
Motor
và:
Encoder
↓
Odometry
độc lập.
Đây là cách giúp project robot sau này không biến thành một mớ if/else.
66. Bài 53 đã đưa chúng ta tới đâu?
Từ:
Bài 45
Encoder
đến:
Bài 46
Motor + Encoder
đến:
Bài 50
2 Motor + 2 PID
đến:
Bài 51
Distance Control
đến:
Bài 52
Angle + Odometry
và bây giờ:
Bài 53
X,Y,θ + GoTo
Bạn đã đi từ điều khiển một ngoại vi sang điều khiển một hệ thống robot hoàn chỉnh.
67. Điều quan trọng nhất cần nhớ trong Bài 53
Có 4 công thức bạn nên thực sự hiểu, không nhất thiết phải học thuộc máy móc.
① Quãng đường trung bình
[
\Delta S=
\frac{\Delta L+\Delta R}{2}
]
② Góc quay
[
\Delta\theta=
\frac{\Delta L-\Delta R}{W}
]
③ Cập nhật vị trí
[
\Delta X=
\Delta S\cos(\theta_{mid})
]
[
\Delta Y=
\Delta S\sin(\theta_{mid})
]
④ Hướng tới mục tiêu
[
\theta_{target}
atan2(\Delta Y,\Delta X)
]
Đây là bộ công thức nền tảng cho robot differential-drive.
68. Và công thức cực kỳ quan trọng tiếp theo
Sau khi có:
V = linear speed
ω = angular speed
ta chuyển thành tốc độ hai bánh:
[
V_L=V-\omega\frac{W}{2}
]
[
V_R=V+\omega\frac{W}{2}
]
Đây chính là inverse kinematics.
Nói cách khác:
Robot muốn:
tiến + quay
↓
V + ω
↓
Inverse Kinematics
↙ ↘
LEFT RIGHT
wheel wheel
Đây là một trong những khái niệm quan trọng nhất nếu bạn muốn đi tiếp sang robotics chuyên sâu.
69. Bài 54 sẽ là bước rất đáng học
Sau Bài 53, bài hợp lý tiếp theo là:
BÀI 54 – INVERSE KINEMATICS ROBOT 2 BÁNH: TỪ V, ω → TỐC ĐỘ 2 BÁNH + ĐIỀU KHIỂN QUỸ ĐẠO
Ta sẽ đi sâu vào:
Linear velocity V
Angular velocity ω
↓
Inverse Kinematics
↓
Left wheel velocity
Right wheel velocity
↓
2 × Speed PID
↓
2 Motor
và đặc biệt xây dựng một bộ điều khiển có thể nhận:
Robot_SetVelocity(200, 0);
→ đi thẳng,
Robot_SetVelocity(200, 0.5);
→ vừa tiến vừa quay,
Robot_SetVelocity(0, 1.0);
→ quay tại chỗ.
Đây sẽ là bước chuyển từ “đi tới một điểm” sang “điều khiển chuyển động của robot bằng vận tốc tuyến tính V và vận tốc góc ω”.