# 5. ROS Package và Workspace là gì?

Phần mềm trong ROS được sắp xếp thành các [packages](http://wiki.ros.org/Packages#:~:text=A%20ROS%20package%20is%20simply,and%20the%20unit%20of%20release.) - gói dữ liệu. **Một package là một thư mục chứa các chương trình (executable files) và tệp hỗ trợ phục vụ cho một mục đích cụ thể**. Ví dụ: bạn có thể có một package để đọc và xử lý hình ảnh từ camera. Sau khi build (biên dịch) package, bạn có thể gọi hoặc test chương trình của mình trong một terminal. **Các ROS package được đặt trong một thư mục khác gọi là *catkin workspace*** vì chúng được build bằng các công cụ *catkin*. Các khái niệm này sẽ được giải thích kỹ hơn ở bên dưới. Ngoài ra, mình sẽ giữ các thuật ngữ bằng tiếng Anh vì dịch ra cũng không được chuẩn nghĩa và sẽ tốt hơn nếu bạn nhớ chúng để khi đọc các tài liệu khác bằng tiếng Anh cũng không bị bỡ ngỡ.

![catkin_workspace.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1669564141260/GaJNou4S2.png align="center")

Trong ví dụ ở hình bên trên, có 3 package tên: *hand\_gesture\_recognition*, *my\_cam* và *ros\_mobile\_robot* được đặt trong thư mục *src* bên trong thư mục workspace có tên *catkin\_ws*. Các thư mục khác: *build, devel,* và *logs* được tạo tự động sau khi các package này được build. Tóm lại, package và workspace trong ROS thực chất là các thư mục với một vài yêu cầu cụ thể mà mình sẽ đề cập bên dưới.

# Tạo một catkin workspace

Theo [trang web chính thức của ROS](http://wiki.ros.org/catkin/conceptual_overview), "Cái tên *catkin* xuất phát từ cụm hoa hình đuôi được tìm thấy trên cây liễu -- ám chỉ đến Nhà để xe Willow nơi catkin được tạo ra." Nếu bạn muốn biết thêm về nó, hãy đọc [bài này](http://wiki.ros.org/catkin/conceptual_overview) còn hiện tại, bạn chỉ cần nhớ rằng *catkin* là hệ thống build chính thức của ROS.

Một workspace như đã nói ở trên là một thư mục, vì vậy bạn khởi tạo nó đơn giản bằng cách tạo một thư mục mới. Bạn có thể nhấp chuột phải vào thư mục Home (nơi bạn có thể dễ dàng truy cập thư mục đó) và chọn **New Folder** hoặc sử dụng phím tắt **Ctrl + Shift + N**. Sau đó, bạn cũng cần tạo một thư mục **src** bên trong thư mục workspace. Vì đang dùng Linux, hãy làm việc này bằng cách chạy lệnh dưới đây trong một terminal:

```bash
mkdir -p ~/catkin_ws/src
```

`mkdir` = **m**a**k**ing **dir**ectory (tạo thư mục mới), `-p` = parents và `~` là viết tắt của thư mục Home. Lệnh này sẽ tạo một thư mục có tên **catkin\_ws** (bạn có thể đặt một tên khác nếu muốn) với một thư mục con có tên **src**. Khá tiện đúng không? Sau đó, thay đổi đường dẫn tới workspace trong terminal hiện tại bằng cách sử dụng lệnh `cd` (`cd` = **c**hange **d**irectory):

```bash
cd ~/catkin_ws
```

Mặc dù hiện tại không có gói nào trong workspace, bạn vẫn có thể build nó bằng cách sử dụng lệnh `catkin_make` hoặc `catkin build`. Mình thích `catkin build` hơn vì với `catkin_make`, nó luôn tự động build tất cả các package nên sẽ mất nhiều thời gian nếu có nhiều package trong workpsace. Với `catkin build`, có thể build từng package riêng lẻ nếu muốn. Nhớ chạy `source ~/catkin_ws/build/setup.bash` trước nếu bạn chưa chạy khi mở terminal hoặc chưa đặt lệnh này trong `.bashrc`.

```bash
catkin build
```

Sau khi build xong, terminal của bạn sẽ trông giống như ảnh bên dưới và ba thư mục *build, devel, logs* cũng được tạo.

![build_catkin_ws.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1669582961614/js7dhqwIs.png align="center")

# Tạo một ROS package

ROS package cũng là một thư mục được đặt trong thư mục **src** của workspace. Tuy nhiên, có một số yêu cầu bắt buộc phải có trong một package ([nguồn](http://wiki.ros.org/catkin/Tutorials/CreatingPackage)):

* Tệp **package.xml** cung cấp các thông tin về package.
    
* Tệp **CMakeLists.txt** dùng cho việc build.
    
* Ngoài ra, mỗi package phải có thư mục riêng, nghĩa là không có package lồng nhau hoặc nhiều package chia sẻ cùng một thư mục.
    

Vậy tạo hai tệp *package.xml* và *CMakeLists.txt* như thế nào? Chúng chính xác là gì và tại sao chúng ta cần cả hai? Hãy tìm câu trả lời bằng cách thử tạo một package. May mắn là đã có lệnh `catkin_create_pkg` để làm việc này. Giả sử bạn muốn tạo một gói có tên *my\_cam* để đọc hình ảnh từ webcam và publish những hình ảnh đó (định nghĩa về `publish` sẽ có trong [chương tiếp theo](https://robodev.blog/nhung-khai-niem-co-ban-trong-ros)). Mở terminal và hướng nó tới `~/catkin_ws/src`:

```bash
cd ~/catkin_ws/src
```

, sau đó chạy

```bash
catkin_create_pkg my_cam rospy sensor_msgs
```

Thao tác này sẽ tạo một package (tức là thư mục) **my\_cam** với hai thành phần phụ thuộc *rospy* và *sensor\_msgs* (mình sẽ giải thích chúng là gì trong [chương tiếp theo](https://robodev.blog/nhung-khai-niem-co-ban-trong-ros)). Bên trong *my\_cam* có 2 tệp **package.xml** và **CMakeLists.txt** và một thư mục **src** như bên dưới.

![catkin_create_pkg_example.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1669703699384/xNzuMzmuQ.png align="center")

## Bên trong *package.xml* và *CMakeLists.txt*

Mở file *package.xml* và bạn sẽ thấy tất cả thông tin về package như tên của người bảo trì, loại giấy phép và các thành phần phụ thuộc, v.v. Rất nhiều dòng là những comment (ví dụ: đây là một comment: `<!-- The *depend tags are used to specify dependencies -->`). Trong hình dưới đây, mình đã xóa tất cả các comment:

![ros_package_xml.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1669788948395/6kwdcEPXv.png align="center")

Đối với những bạn chưa từng làm việc với [XML](https://en.wikipedia.org/wiki/XML), đây là một ngôn ngữ đánh dấu (markup language) tương tự như \[HTML\](https://vi.wikipedia.org/ wiki/HTML) và nó mô tả các thành phần bằng *thẻ* (tag\*)\*. Chẳng hạn, dòng `<name>my_cam</name>` có nghĩa là *tên package* được định nghĩa là `my_cam` bằng cách sử dụng thẻ mở `<name>` và thẻ đóng `</name>`.

Tiếp theo, mở *CMakeLists.txt*. Nó cũng có rất nhiều comment (bắt đầu bằng `#`) chủ yếu là để hướng dẫn/gợi ý. Nếu bạn đã từng làm việc với C/C++, thì bạn đã biết rằng [CMakeLists](https://www.jetbrains.com/help/clion/cmakelists-txt-file.html) được sử dụng cho các chương trình như [CMake](https://cmake.org/) (catkin cũng dùng CMake) để tìm và liên kết tất cả các thư viện cần thiết để build. Tại dòng `find_package`, bạn có thể thấy hai phụ thuộc `rospy` và `sensor_msgs`, mà chúng ta đã khai báo từ lệnh trên. Nếu bạn quên khai báo chúng, chỉ cần mở `CMakeLists` lên và chỉnh sửa.

```bash
find_package(catkin REQUIRED COMPONENTS
  rospy
  sensor_msgs
)
```

## Sự khác biệt giữa *package.xml* và *CMakeLists.txt*

Bạn có thể nhận thấy cả *package.xml* và *CMakeLists.txt* có nhiều phần thông tin giống nhau như tên dự án, thự viện liên quan, v.v. Có một [câu trả lời hay](https://answers.ros.org/question/217475/cmakeliststxt-vs-packagexml/) về sự khác biệt và lý do tại sao chúng ta cần cả hai. Bạn chỉ cần nhớ:

* package.xml chứa những thông tin (tác giả, người bảo trì, url, mô tả và giấy phép) không nhất thiết cần thiết để build (hoặc chạy) một package, nhưng vẫn cần để ROS tìm kiếm các thông tin về package (vì thông tin trong CMakeLists không dễ dàng đọc nếu không có CMake). Ngoài ra nó cũng cần cho việc hiển thị thông tin của package trên ROS wiki.
    
* CMakeLists.txt là build script được dùng cho các chương trình như [CMake](https://cmake.org/) để tìm và liên kết tất cả các thư viện cần thiết để build package.
    

Nếu bạn thấy có quá nhiều khái niệm mới trong bài này mà bạn chưa thể nhớ hết được. Đừng nản, bạn sẽ quen với chúng sớm thôi. Hiện tại, chỉ cần nhớ rằng một ROS package luôn yêu cầu 2 tệp *package.xml* và *CMakeLists.txt*, cộng với một thư mục *src* là nơi bạn lưu code của mình.

Trong các phần tiếp theo, chúng ta sẽ tìm hiểu tất cả các khái niệm cốt lõi của ROS: Topic, Node, Publisher, Subscriber, v.v. và viết code để hoàn thành package *my\_cam*.

# Tóm tắt

* ROS package là một thư mục chứa các tệp thực thi (executable file) và những tệp hỗ trợ để phục vụ một mục đích cụ thể.
    
* ROS workspace là một thư mục nơi bạn sửa đổi, build và cài đặt các package bằng các công cụ *catkin*.
    
* Một package phải có hai tệp *package.xml* & *CMakeLists.txt*. Sử dụng lệnh `catkin_create_pkg` để tạo 1 package.
    

# Trích dẫn

1. Ảnh bìa: [http://wiki.ros.org/groovy](http://wiki.ros.org/groovy)
