Bài 9 · Nâng cao · 30 phút
Dự án cuối khoá
Biên soạn bởi Nguyễn Anh Tuấn
Ghép mọi thứ, chạy trên trình duyệt: tự định vị bằng trilateration, tính khoảng cách đường chim bay bằng công thức Haversine, và mổ xẻ một câu NMEA ($GPGGA).
Tám bài qua, mèo con đã đi từ vệ tinh phát tín hiệu tới chấm xanh trên bản đồ. Giờ là lúc tự tay làm ba thử thách nhỏ, mỗi cái dùng một mảnh đã học - không phải học thuộc, mà là chạy thật.
Nhớ Bài 4 chứ? Mỗi vệ tinh cho một khoảng cách = một vòng tròn; ba vòng giao tại đúng một điểm. Kéo thử lại, đặt mình vào vai máy thu:
Ba vệ tinh: ba vòng giao nhau tại đúng MỘT điểm - chính là bạn.
Kéo 🛰️ vệ tinh hoặc 🐱 máy thu. Mỗi vòng tròn = “máy thu cách vệ tinh này đúng bấy nhiêu”.
- ▸1 vệ tinh = một vòng (vô số khả năng); 2 = hai điểm; 3 = một điểm.
- ▸Đây chính là phép toán máy thu chạy mỗi lần định vị.
- ▸Đời thực là 3D mặt cầu, cộng vệ tinh thứ tư cho đồng hồ.
Có hai toạ độ GPS rồi, tính khoảng cách giữa chúng thế nào? Vì Trái Đất cong, ta dùng công thức Haversine để ra đường chim bay (đường ngắn nhất trên mặt cầu, gọi là great-circle):
hav.py - khoang cach duong chim bay giua hai toa do
import math
def haversine(lat1, lon1, lat2, lon2):
R = 6371.0 # ban kinh Trai Dat (km)
p1, p2 = math.radians(lat1), math.radians(lat2)
dp = math.radians(lat2 - lat1)
dl = math.radians(lon2 - lon1)
a = math.sin(dp/2)**2 + math.cos(p1)*math.cos(p2)*math.sin(dl/2)**2
return 2 * R * math.asin(math.sqrt(a))
ha_noi = (21.028, 105.834) # Ho Hoan Kiem (xap xi)
sai_gon = (10.776, 106.701) # Cho Ben Thanh (xap xi)
print(f"Duong chim bay Ha Noi <-> Sai Gon: {haversine(*ha_noi, *sai_gon):.0f} km") Kết quả khi chạy
Duong chim bay Ha Noi <-> Sai Gon: 1144 km
Trung thực
Máy thu GPS thật “nhả” ra những dòng chữ theo chuẩn NMEA. Câu $GPGGA gói kết quả một lần định vị. Tách nó ra để lấy vĩ độ, kinh độ, độ cao:
nmea.py - mo xe mot cau $GPGGA
cau = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"
def ddmm_sang_do(chuoi, huong):
diem = chuoi.find('.')
do = int(chuoi[:diem - 2])
phut = float(chuoi[diem - 2:])
kq = do + phut / 60
return -kq if huong in ('S', 'W') else kq
p = cau.split(',')
gio = p[1]
print(f"Gio (UTC) : {gio[0:2]}:{gio[2:4]}:{gio[4:6]}")
print(f"Vi do : {ddmm_sang_do(p[2], p[3]):.4f}")
print(f"Kinh do : {ddmm_sang_do(p[4], p[5]):.4f}")
print(f"So ve tinh: {int(p[7])}")
print(f"Do cao : {float(p[9])} m") Kết quả khi chạy
Gio (UTC) : 12:35:19 Vi do : 48.1173 Kinh do : 11.5167 So ve tinh: 8 Do cao : 545.4 m
Trung thực
Ghép lại cả khoá, giờ bạn hiểu trọn con đường từ trời xuống màn hình điện thoại:
- ▸Vệ tinh ở MEO phát thời gian (đồng hồ nguyên tử) + vị trí (ephemeris).
- ▸Máy thu đổi thời gian bay thành khoảng cách (pseudorange).
- ▸Trilateration giao nhiều khoảng cách ra vị trí; vệ tinh thứ 4 trị đồng hồ.
- ▸Sai số được hiểu và hiệu chỉnh; mất sóng thì WiFi/Bluetooth/4G và cảm biến gánh.
Chúc mừng mèo con! 🐱🛰️
Câu hỏi thường gặp
Haversine cho “đường chim bay” - đường ngắn nhất trên mặt cầu. Google Maps tính đường đi THỰC theo đường xá, luôn dài hơn vì phải vòng theo đường có sẵn.
Vì Trái Đất cong. Ở khoảng cách ngắn (vài km) tính trên mặt phẳng cũng tạm, nhưng đi xa (hàng trăm, hàng nghìn km) mà coi Trái Đất phẳng thì sai số lớn dần.
Là chuẩn các câu chữ (text) mà máy thu GPS xuất ra. $GPGGA là một loại (chứa vị trí và độ cao); còn $GPRMC, $GPGSV… chứa thông tin khác như tốc độ, danh sách vệ tinh.
Là “checksum” - một mã kiểm tra ngắn để máy biết câu có bị lỗi khi truyền không. Nếu tính lại không khớp, câu đó bị bỏ.
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
Công thức Haversine tính kiểu khoảng cách nào giữa hai toạ độ GPS?
- 1
Tự định vị bằng trilateration
Dùng mô phỏng ở Thử thách 1: đặt 3 vệ tinh và 1 máy thu, kéo thử, xác nhận ba vòng tròn luôn giao đúng một điểm.
Hoàn thành khi: Với 3 vệ tinh, giao điểm chốt đúng vị trí máy thu; bớt còn 2 vệ tinh thì xuất hiện “điểm ma”.
- 2
Đường chim bay vs đường đi
Chạy hav.py ở Thử thách 2. Đường chim bay Hà Nội - Sài Gòn ra bao nhiêu? Vì sao đường đi xe (~1.700 km) lại dài hơn?
Hoàn thành khi: Khoảng 1.144 km (đường chim bay). Đường xe dài hơn vì phải đi vòng theo đường xá thực tế, không bay thẳng được.
- 3
Đổi toạ độ
Trong hav.py, đổi một điểm thành nơi mèo con đang ở (tra toạ độ trên bản đồ), chạy lại và đọc kết quả.
Hoàn thành khi: Ra một con số km hợp lý cho cặp toạ độ mới; luôn nhớ đó là đường chim bay, ngắn hơn đường đi thực.
- 4
Mổ xẻ câu NMEA
Chạy nmea.py ở Thử thách 3. Vĩ độ, kinh độ và độ cao máy thu báo là bao nhiêu?
Hoàn thành khi: Vĩ độ 48,1173; kinh độ 11,5167; độ cao 545,4 m (và 8 vệ tinh, lúc 12:35:19 UTC).
- 5
Đổi độ-phút bằng tay
Không dùng code, tự đổi “4807.038 N” sang độ thập phân.
Hoàn thành khi: 48 + 7,038 ÷ 60 = 48,1173 độ Bắc.
- 6
Hành trình của mèo con
Bằng lời của mèo con, kể lại mạch chính cả khoá trong 2 - 3 câu: từ tín hiệu vệ tinh tới chấm xanh trên bản đồ.
Hoàn thành khi: Nhắc được mạch: vệ tinh phát thời gian + vị trí → đo khoảng cách → trilateration ra vị trí → sai số & hiệu chỉnh → định vị lai khi mất sóng.