Giới Thiệu
Trong bài viết này, chúng ta sẽ tìm hiểu cách xây dựng một robot tự hành không cần GPS sử dụng Python. Robot này sẽ sử dụng các công nghệ như định vị bằng cảm biến IMU, lập bản đồ lưới, và lập kế hoạch đường đi bằng A*. Chúng ta sẽ cung cấp mã nguồn mẫu và giải thích từng phần để bạn có thể dễ dàng áp dụng vào dự án của riêng mình.
Tổng Quan Về Dự Án
Robot của chúng ta sẽ sử dụng các công nghệ sau:
- Định vị bằng cảm biến IMU: Sử dụng cảm biến để xác định vị trí và hướng của robot.
- Lập bản đồ lưới: Sử dụng lưới để đại diện cho không gian và giúp robot nhận biết môi trường xung quanh.
- Lập kế hoạch đường đi: Sử dụng thuật toán A* để tìm đường đi tối ưu đến đích.
- Giao diện Dashboard bằng Flask: Cung cấp giao diện người dùng để theo dõi trạng thái của robot.
Cài Đặt Môi Trường
Trước khi bắt đầu, bạn cần cài đặt một số thư viện cần thiết. Đầu tiên, hãy tạo một môi trường ảo và cài đặt các thư viện cần thiết:
bash
python3 -m venv venv
source venv/bin/activate
pip install opencv-python-headless numpy flask matplotlib scipy pillow
Cấu Trúc Mã Nguồn
Chúng ta sẽ xây dựng mã nguồn trong một tệp duy nhất có tên robot.py. Dưới đây là cấu trúc chính của mã:
python
#!/usr/bin/env python3
import time
import threading
import math
import io
import base64
from typing import List, Tuple
import numpy as np
import cv2
from flask import Flask, Response, render_template_string, jsonify
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from PIL import Image
import heapq
Khởi Tạo Flask
Đầu tiên, chúng ta sẽ khởi tạo ứng dụng Flask:
python
app = Flask(__name__)
Định Nghĩa Các Lớp Cảm Biến
Chúng ta sẽ tạo một lớp SensorHealth để theo dõi tình trạng của các cảm biến:
python
class SensorHealth:
def __init__(self):
t = time.time()
self.last_imu = t
self.last_odom = t
self.last_cam = t
self.last_lidar = t
def update(self, imu=False, odom=False, cam=False, lidar=False):
now = time.time()
if imu: self.last_imu = now
if odom: self.last_odom = now
if cam: self.last_cam = now
if lidar: self.last_lidar = now
def ok(self, timeout=1.0):
now = time.time()
return {
'imu': (now - self.last_imu) < timeout,
'odom': (now - self.last_odom) < timeout,
'cam': (now - self.last_cam) < timeout,
'lidar': (now - self.last_lidar) < timeout
}
Đọc Giá Trị Từ Cảm Biến
Các hàm sau đây sẽ mô phỏng việc đọc dữ liệu từ cảm biến IMU, odometry và Lidar:
python
def read_imu():
wz = np.random.normal(0.0, 0.02)
ax = np.random.normal(0.0, 0.1)
return float(wz), float(ax)
def read_wheel_odometry():
d = 0.02 + np.random.normal(0.0, 0.004)
dtheta = np.random.normal(0.0, 0.004)
return float(d), float(dtheta)
def read_lidar_scan(num_beams=180, max_range=5.0):
angles = np.linspace(-np.pi/2, np.pi/2, num_beams)
ranges = max_range * np.ones_like(angles)
center_idx = num_beams // 2
ranges[center_idx-3:center_idx+4] = 1.5 + np.random.normal(0.0, 0.03, 7)
ranges += np.random.normal(0.0, 0.02, size=angles.shape)
ranges = np.clip(ranges, 0.02, max_range)
return ranges.astype(float), angles.astype(float)
Tạo Class CameraWrapper
Chúng ta cũng cần một lớp để quản lý camera:
python
class CameraWrapper:
def __init__(self, source=0, width=320, height=240):
self.cap = cv2.VideoCapture(source)
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
self.last_frame = None
def read(self):
ret, frame = self.cap.read()
if not ret or frame is None:
frame = np.zeros((240,320,3), dtype=np.uint8)
self.last_frame = frame
return frame
def release(self):
self.cap.release()
Lớp EKF (Kalman Filter Mở Rộng)
python
class EKF:
def __init__(self):
self.x = np.zeros((3,1)) # x, y, theta
self.P = np.diag([0.1, 0.1, 0.05]) # noise covariance
self.Q = np.diag([0.02, 0.02, 0.01]) # process noise
def predict(self, delta_dist, delta_theta):
x, y, th = self.x.flatten()
th_new = th + delta_theta
x_new = x + delta_dist * np.cos(th_new)
y_new = y + delta_dist * np.sin(th_new)
self.x = np.array([[x_new],[y_new],[th_new]])
self.P += self.Q
def update_pose(self, z, R):
H = np.eye(3)
y = z - H.dot(self.x)
y[2] = (y[2] + np.pi) % (2*np.pi) - np.pi
S = H.dot(self.P).dot(H.T) + R
K = self.P.dot(H.T).dot(np.linalg.inv(S))
self.x += K.dot(y)
self.P = (np.eye(3) - K.dot(H)).dot(self.P)
def get_state(self):
return self.x.flatten()
Lớp Lập Bản Đồ Lưới
python
class OccupancyGrid:
def __init__(self, width_m=10.0, height_m=10.0, resolution=0.05):
self.resolution = resolution
self.width = int(width_m / resolution)
self.height = int(height_m / resolution)
self.log_odds = np.zeros((self.height, self.width), dtype=np.float32)
self.l_free = -0.4
self.l_occ = 0.85
self.origin_x = -width_m / 2.0
self.origin_y = -height_m / 2.0
def integrate_scan(self, pose: Tuple[float,float,float], ranges: np.ndarray, angles: np.ndarray):
# Implement logic to integrate Lidar scan into occupancy grid
pass
Lớp Điều Khiển (Pure Pursuit)
python
class PurePursuitController:
def __init__(self, lookahead_m=0.3, max_speed=0.2):
self.lookahead = lookahead_m
self.max_speed = max_speed
def compute_control(self, pose: Tuple[float,float,float], path_world: List[Tuple[float,float]], obstacle_dist: float):
# Implement control logic
pass
Router Flask
python
@app.route('/')
def index():
return render_template_string("""
<h1>Robot Dashboard</h1>
<p>Đang chạy robot không GPS.</p>
""")
@app.route('/state')
def state():
return jsonify(_state)
Thực Thi Dashboard
Chúng ta sẽ chạy một luồng cho dashboard:
python
def run_dashboard_server():
app.run(host='0.0.0.0', port=5000, debug=False, use_reloader=False)
Vòng Lặp Chính
python
def main_loop(rate_hz=10):
dt = 1.0 / rate_hz
ekf = EKF()
vo = VisualOdometry()
grid = OccupancyGrid()
controller = PurePursuitController()
sensor_health = SensorHealth()
cam = CameraWrapper()
while True:
# Read sensors and update state
time.sleep(dt)
Kết Luận
Trong bài viết này, chúng ta đã khám phá cách xây dựng một robot tự hành không cần GPS bằng Python. Bằng cách kết hợp các công nghệ như định vị bằng IMU, lập bản đồ lưới, và A*, chúng ta đã tạo ra một hệ thống có thể di chuyển và lập bản đồ trong môi trường không có GPS. Hãy thử nghiệm với mã nguồn và điều chỉnh các tham số để tối ưu hóa hiệu suất robot của bạn!
Các Thực Hành Tốt Nhất
- Thử nghiệm với các cảm biến khác nhau để cải thiện độ chính xác của robot.
- Tối ưu hóa mã nguồn để giảm thiểu độ trễ trong quá trình xử lý.
Những Cạm Bẫy Thường Gặp
- Không kiểm tra tình trạng của cảm biến có thể dẫn đến lỗi khi robot hoạt động.
- Không cập nhật thường xuyên trạng thái của robot có thể làm cho robot mất định vị.
Mẹo Hiệu Suất
- Sử dụng đa luồng để xử lý song song các cảm biến.
- Giảm độ phân giải của hình ảnh nếu không cần thiết để tiết kiệm băng thông.
Giải Quyết Vấn Đề
- Nếu robot không di chuyển, hãy kiểm tra cảm biến IMU và odometry.
- Đảm bảo rằng thuật toán A* được cấu hình chính xác.
Câu Hỏi Thường Gặp
- Robot có thể sử dụng các cảm biến nào?
- Robot có thể sử dụng cảm biến IMU, Lidar, và camera.
- Có cần thiết phải có GPS không?
- Không, robot có thể hoạt động hoàn toàn mà không cần GPS thông qua cảm biến nội bộ.
- Làm thế nào để cải thiện độ chính xác của robot?
- Điều chỉnh các tham số trong bộ lọc Kalman và cải thiện độ chính xác của cảm biến.
Tham Khảo
Hãy bắt đầu xây dựng robot của bạn ngay hôm nay!