AICurious Logo
Published on
Monday, August 24, 2020

Chúng tôi đã xây dựng xe tự hành trên giả lập thế nào?

2725 words14 min read
Authors

Trong năm 2020, tôi và một số anh em cùng lớp đại học đã tham dự Cuộc Đua Số, một cuộc thi lập trình xe tự hành cho sinh viên. Ở vòng trường đại học, các đội chơi phải thực hiện thử thách lập trình điều khiển xe tự hành di chuyển trên đường theo biển báo và tránh các chướng ngại vật trong môi trường giả lập. Trong bài viết này, tôi sẽ chia sẻ với các bạn các kĩ thuật xử lý và các mô hình học máy nhóm tôi đã sử dụng để điều khiển xe trong thử thách này. Tôi cũng sẽ chia sẻ toàn bộ mã nguồn của nhóm để bạn đọc có thể tham khảo.

Video demo:

Về mô hình năm trước đó: Năm trước (2019), tôi cũng tham gia xây dựng xe tự hành cho giả lập tương tự như nhiệm vụ trong bài viết này. Các kĩ thuật từng được áp dụng năm trước đó là các thuật toán: (1) FloodfillWatershed cho phân đoạn đường đi và (2) lọc màu (HSV) kết hợp HOG (Histogram of Oriented Gradients), SVM (Support Vector Machine) cho nhận dạng và phân loại biển báo. Các thuật toán năm trước chưa áp dụng các kĩ thuật học sâu. Các bạn có thể tham khảo video demo cho thiết kế này tại đây và mã nguồn tại repo này.

I. Thử thách và môi trường giả lập

Năm 2020, BTC Cuộc đua số cung cấp cho các đội chơi các môi trường giả lập xe đua xây dựng trên nền tảng Unity. Giả lập này sẽ sử dụng môi trường ROS (Robot Operating System) để trao đổi thông tin với phần mềm điều khiển xe. Giả lập Unity và phần mềm điều khiển đóng vai trò là các "node" trong hệ thống ROS, giao tiếp với nhau thông qua cơ chế publisher - subscriber. Phần mềm giả lập sẽ liên tục truyền hình ảnh thu được phía trước mũi xe vào hệ thống ROS, đồng thời cũng liên tục nhận lại và thực thi các lệnh điều khiển (bao gồm góc lái và tốc độ). Mỗi team tham gia cần xây dựng một hoặc nhiều "node" để nhận hình ảnh, xử lý và truyền lại các tín hiệu điều khiển. Hình dưới mô tả kiến trúc chung của hệ thống được chúng tôi thiết kế.

Overall architecture

Phần mềm xử lý tín hiệu và điều khiển xe có thể được triển khai thành một hoặc nhiều "node" trên hệ thống ROS. Tuy nhiên, để đơn giản, chúng tôi thiết kế hệ thống điều khiển chỉ gồm 1 "node" là "Race car node" như hình trên. Hệ thống này, sau khi nhận hình ảnh (RGB) từ môi trưởng giả lập đua xe (Simulation node) sẽ sử dụng 2 mạng deep learning để xử lý dữ liệu thu được: 1 mạng semantic segmentation (phân đoạn ngữ nghĩa) và một mạng traffic sign detection (phát hiện biển báo). Ở đây chúng tôi chưa sử dụng hình ảnh chiều sâu (depth image) cho kết quả cuối cùng. Tuy nhiên tôi cũng sẽ giới thiệu tới các bạn nhưng hướng đi nhóm đã thử với luồng ảnh này. Các phần II và III sẽ lần lượt nói về thiết kế và triển khai các mạng deep learning cho thử thách này.

II. Mạng semantic segmentation cho xác định đường đi và vật cản

Segmentation Model

Trong thử thách này, chúng tôi sử dụng mạng phân đoạn ảnh để xác định đường đi và 2 vật cản chính (xe ô tô và người đi bộ). Mạng phân đoạn ảnh sẽ được huấn luyện để nhận 4 lớp: road (đường đi), car (xe ô tô), pedestrian (người đi bộ) và other (những phần khác).

1. Dữ liệu huấn luyện

Về dữ liệu, vì giả lập ban tổ chức đưa ra là một giả lập giống như game đua xe, nhóm đã quyết định sử dụng bộ dữ liệu khá tương đồng là bộ dữ liệu phân đoạn hình ảnh của Carla - một giả lập cho xe tự hành. Carla được ứng dụng khá nhiều cho việc tạo dữ liệu và thử nghiệm cho xe tự hành do có thể mô tả rất nhiều điều kiện đường xá, thời tiết khác nhau. Ở đây chúng tôi sử dụng khoảng 10k ảnh từ tập dữ liệu này. Các bạn có thể tìm kiếm các bộ dữ liệu phân đoạn sinh ra từ giả lập Carla bằng Google. Để tăng thêm độ chính xác trên giả lập của FPT, nhóm đã quyết định tự gán nhãn khoảng 1k ảnh, trích xuất từ giả lập của FPT. Tập dữ liệu cuối cùng thu được khoảng 11k ảnh.

Sau khi thực hiện huấn luyện mô hình và chỉnh tham số, chúng tôi nhận thấy mô hình nhận chưa tốt các khung hình có chứa tuyết như hình dưới.

Tuyết

Giải pháp: Giải pháp được đưa ra là thêm một vài dòng code sinh ảnh chứa tuyết vào tập huần luyện. Việc tạo tuyết chỉ đơn giản là vẽ nhiều các hình tròn và hình elip màu trắng vào ảnh có sẵn. Các bạn có thể tham khảo phần "Add snow" của file này. Việc này chỉ được thực hiện với dữ liệu training.

Quá trình chuẩn bị dữ liệu được mô tả như hình dưới.

Chuẩn bị dữ liệu cho phân đoạn ảnh

2. Lựa chọn và tối ưu mô hình

Trong repo code chúng tôi giới thiệu, nhóm đã thử nghiệm 2 mô hình phân đoạn ảnh cho nhiệm vụ này, đó là ENet (trong ENet: A Deep Neural Network Architecture for Real-Time Semantic Segmentation) và U-Net (trong bài U-Net: Convolutional Networks for Biomedical Image Segmentation). U-Net là một mô hình được tạo ra với mục đích để phân đoạn ảnh y tế, tuy nhiên nó lại cho kết quả khá tốt trong nhiều task khác. ENet là mạng phân đoạn tập trung vào tốc độ. Sau quá trình tối ưu tham số cho từng mạng. Chúng tôi rút ra kết luận kết quả mô hình ENet có độ chính xác thấp hơn một chút so với kiến trúc U-Net được sử dụng, tuy nhiên kiến trúc ENet ở đây lại cho tốc độ cao hơn nhiều. Bài toán của chúng tôi đang tập trung vào tốc độ, vì thế, mô hình ENet được lựa chọn để triển khai cuối cùng. Các bạn có thể xem một bài báo so sánh tốc độ của U-NetENet tại đây. Tuy vậy, bài báo này chỉ mang tính chất tham khảo, tốc độ, độ chính xác của mô hình vẫn phụ thuộc rất nhiều vào việc cài đặt và chỉnh sửa kiến trúc mạng của chúng ta. Cuối cùng việc lựa chọn mô hình cũng phụ thuộc vào bài toán chúng ta muốn giải quyết.

Ở năm trước, chúng tôi giải quyết việc phân đoạn đường bằng thuật toán Floodfill kết hợp Watershed. Cách này chạy khá nhanh, tuy nhiên nhược điểm nằm ở chỗ chúng ta cần lựa chọn tốt các vị trí bắt đầu của thuật toán Floodfill và mặt nạ (mask) của thuật toán Watershed. Việc sử dụng mô hình học sâu đã giải quyết được vấn đề này. Không những thế, ngoài đường đi, chúng ta còn tìm được vị trí các vật cản (xe ô tô, người đi bộ) dựa vào kết quả đầu ra của mạng phân đoạn. Có thể nói, khi có đủ dữ liệu, sử dụng học sâu giúp chúng ta giải quyết được bài toán khó còn nhanh hơn so với việc đi tối ưu các thuật toán xử lý ảnh.

III. Phát hiện biển báo

Ở thử thách này, chúng tôi chỉ cần nhận ra biển rẽ trái và biển rẽ phải có trong ảnh.

1. Phát hiện biển báo với blob detector và một mạng phân loại

Mã nguồn thuật toán này có thể được tìm thấy tại đây: https://github.com/vietanhdev/autonomous-car-2020/blob/master/main_ws/src/team613/src/sign_detection/sign_detector.py.

Trong thư viện OpenCV có thuật toán blob detector có thể được sử dụng để phát hiện các hình tròn trong ảnh chiều sâu. Qua thử nghiệm, tôi thấy thuật toán này có khả năng hoạt động rất tốt với ảnh chiều sâu thu được từ giả lập của ban tổ chức, do vậy nhóm đã đề xuất một thuật toán đơn giản để phát hiện biển báo như hình dưới.

Phát hiện biển báo với blob detector và một mạng phân loại nhỏ

Trong thuật toán này, chúng tôi sử dụng cả ảnh chiều sâu (depth image) và ảnh màu (RGB image) cho đầu vào. Ảnh chiều sâu sẽ được sử dụng trong thuật toán blob detection của OpenCV để phát hiện các hình tròn trong ảnh. Đầu ra của thuật toán này là các hình tròn (có thể là biển báo hoặc các vật tròn khác). Tiếp theo chúng tôi sử dụng kết quả này để cắt ra một vùng tương ứng trong ảnh màu và dùng một mạng CNN là sign classifier để thực hiện phân loại kết quả thành 3 lớp: biển rẽ trái, biển rẽ phải và không phải biển báo giao thông. Kết quả của thuật toán là vị trí và loại biển báo. Toàn bộ thuật toán này hoạt động gần như hoàn hảo với điều kiện giả lập của xe. Tuy nhiên, chúng tôi nghĩ việc tinh chỉnh các tham số của thuật toán blob detection khó mà hiệu quả trong thực tế, khi mà nhiễu từ môi trường, từ thiết bị camera sẽ tạo ra những trường hợp rất khó để phát hiện các hình tròn chính xác. Do vậy, chúng tôi tính đến việc xây dựng một mô hình học sâu cho phát hiện biển báo - mô hình dựa trên FaceBoxes được mô tả trong phần dưới.

2. Phát hiện biển báo với mô hình FaceBoxes

FaceBoxes (trong FaceBoxes: A CPU Real-time Face Detector with High Accuracy) ban đầu được thiết kế là một mạng phát hiện mặt (face detection) khá nhanh và hiệu quả. Do vậy chúng tôi đã tối ưu lại kiến trúc mạng này để trở thành mạng phát hiện và phân loại biển báo. Ban đầu, vì được thiết kế để phân loại mặt, mạng FaceBoxes chỉ output ra được 1 class là mặt người. Nhóm đã thực hiện tối ưu lại mạng để tạo ra output phân loại được biển rẽ trái và rẽ phải. Việc phát hiện và phân loại biển báo đã trở nên hết sức đơn giản như hình dưới. Từ input là ảnh màu, mạng FaceBoxes của chúng tôi có thể cho ra luôn kết quả là vị trí và loại biển báo. Thiết kế này làm giảm độ phức tạp của pipeline xử lý và đẩy phần khó khăn cho việc chuẩn bị dữ liệu.

Phát hiện biển báo với FaceBoxes

Mạng FaceBoxes phát hiện - phân loại biển được chúng tôi huấn luyện với đa dạng các nguồn dữ liệu từ các bộ dữ liệu thu thập thực tế, các dataset mở cho đến các hình ảnh tự sinh bằng code. Cuối cùng, chúng tôi thu được mạng phát hiện biển báo với độ chính xác rất cao. Vậy là, chúng tôi có thể yên tâm tích hợp mạng này thành giải pháp phát hiện biển báo cho hệ thống.

IV. Phần điều khiển xe

1. Phát hiện vật cản bằng ảnh chiều sâu

Ngoài giải pháp phát hiện vật cản là sử dụng kết quả phân đoạn ảnh của mô hình ở mục II, chúng tôi cũng thử nghiệm việc phát hiện vật cản bằng cách xử lý ảnh chiều sâu. Tuy vậy ảnh chiều sâu sinh ra bởi giả lập khá khác so với thực tế, do vậy chúng tôi quyết định không sử dụng cách này làm hướng đi chính.

2. Điều khiển xe

Phần điều khiển xe chủ yếu là tương tác với ROS (Robot Operating System), lấy kết quả tự mạng phân đoạn, mạng phát hiện biển và xử lý, đưa ra tốc độ và góc lái cho xe. Phần code chính có thể được tìm thấy tại đây.

Điều khiển góc lái: Việc điều khiển góc lái của xe sử dụng chủ yếu là output từ mạng phân đoạn đường (road segmentation mask). Qua quá trình biến đối Perspective Transform, ta thu được góc nhìn từ trên xuống của đường (ảnh bird-view). Từ góc nhìn này, chúng tôi ước lượng tâm đường, và góc lái xe. Cuối cùng tín hiệu điều khiển được làm mượt bằng thuật toán PID.

Điều khiển góc lái

Điều khiển rẽ khi đến ngã ba, ngã tư và đi theo biển báo: Trong việc phát hiện ngã ba, ngã tư, chúng tôi sử dụng phương pháp hết sức đơn giản bằng các biến đổi và xét diện tích đường trong ảnh bird-view. Từ đó, chúng tôi sẽ tìm được điểm xe cần rẽ và thực hiện đi theo các biển báo phát hiện được.

Điều khiển xe tránh vật cản: Trong việc tránh các vật cản (xe, người đi bộ) trên đường, sau khi xác định vị trí các vật cản, thuật toán điều khiển chỉ thực hiện di chuyển tâm đường cần hướng đến để bẻ lái xe tránh vật cản. Việc này thực hiện khá đơn giản, và tôi nghĩ nó mới chỉ hoạt động tốt trong điều kiện giả lập của thử thách này.

V. Mã nguồn - Kết quả

Toàn bộ mã nguồn được chia sẻ tại các repo sau:

Lưu ý: Phần codebase bằng Python cho xe chưa hoàn thiện và phần giả lập cho Vòng 2 cần được chỉnh sửa theo mục đích sử dụng. Tôi chỉ chia sẻ để các bạn tham khảo. Nhóm tôi cũng chưa có ý định viết tiếp những phần này.

Sa hình giả lập trên Unity

Hiện tại tôi chưa có thời gian đi sâu vào phân tích các kết quả của mô hình mà nhóm đã xây dựng. Đây là video demo cuối cùng tôi còn giữ lại được: https://www.youtube.com/watch?v=SP6ipnIiAO0. Cuối cùng nhóm cũng xây dựng được giải pháp kết hợp xử lý ảnh và học sâu để điều khiển xe chạy khá tốt trong giả lập. Việc xây dựng giải pháp và cài đặt được thực hiện trong vài tuần trước cuộc thi. Phương pháp của chúng tôi chắc chắn còn nhiều thiếu sót. Chúng tôi rất mong nhận được các góp ý từ các bạn.

Nếu các bạn quan tâm đến Cuộc đua số, các bạn có thể xem thêm thông tin về cuộc thi tại https://cuocduaso.fpt.com.vn/. Nhân tiện, nếu các bạn thấy bài viết hay và mã nguồn tốt, hãy thả sao (star) trên link github nhé. Xin cảm ơn mọi người!