Cảm biến chuyển động

Nền tảng Android cung cấp một số cảm biến cho phép bạn theo dõi chuyển động của thiết bị.

Cấu trúc có thể có của các cảm biến khác nhau tuỳ theo loại cảm biến:

  • Các cảm biến trọng lực, gia tốc tuyến tính, vectơ xoay, chuyển động đáng kể, bộ đếm bước và cảm biến của máy phát hiện bước có thể dựa trên phần cứng hoặc phần mềm.
  • Cảm biến gia tốc kế và con quay hồi chuyển luôn dựa trên phần cứng.

Hầu hết thiết bị chạy Android đều có gia tốc kế và nhiều thiết bị hiện còn có con quay hồi chuyển. Khả năng sử dụng của các cảm biến dựa trên phần mềm sẽ thay đổi hơn vì chúng thường dựa vào một hoặc nhiều cảm biến phần cứng để lấy dữ liệu. Tuỳ thuộc vào thiết bị, các cảm biến dựa trên phần mềm này có thể lấy dữ liệu từ gia tốc kế và từ kế hoặc từ con quay hồi chuyển.

Cảm biến chuyển động rất hữu ích để theo dõi chuyển động của thiết bị, chẳng hạn như nghiêng, lắc, xoay hoặc xoay. Chuyển động thường phản ánh hoạt động đầu vào trực tiếp của người dùng (ví dụ: người dùng lái ô tô trong trò chơi hoặc người dùng điều khiển bóng trong trò chơi), nhưng cũng có thể phản ánh môi trường thực tế nơi thiết bị đang chứa (ví dụ: di chuyển cùng bạn khi bạn lái xe). Trong trường hợp đầu tiên, bạn đang theo dõi chuyển động liên quan đến khung tham chiếu của thiết bị hoặc khung tham chiếu của ứng dụng; trong trường hợp thứ hai, bạn đang theo dõi chuyển động tương ứng với hệ quy chiếu của thế giới. Bản thân cảm biến chuyển động thường không được dùng để theo dõi vị trí thiết bị nhưng có thể dùng với các cảm biến khác, chẳng hạn như cảm biến địa từ, để xác định vị trí của thiết bị so với hệ quy chiếu của thế giới (xem phần Cảm biến vị trí để biết thêm thông tin).

Tất cả cảm biến chuyển động đều trả về các mảng đa chiều của các giá trị cảm biến cho mỗi SensorEvent. Ví dụ: trong một sự kiện cảm biến, gia tốc kế sẽ trả về dữ liệu lực gia tốc cho 3 trục toạ độ và con quay hồi chuyển trả về tốc độ xoay của dữ liệu cho 3 trục toạ độ. Các giá trị dữ liệu này được trả về trong một mảng float (values) cùng với các tham số SensorEvent khác. Bảng 1 tóm tắt các cảm biến chuyển động có trên nền tảng Android.

Bảng 1. Cảm biến chuyển động được hỗ trợ trên nền tảng Android.

Cảm biến Dữ liệu sự kiện cảm biến Nội dung mô tả Đơn vị đo
TYPE_ACCELEROMETER SensorEvent.values[0] Lực gia tốc dọc theo trục x (bao gồm cả trọng lực). m/giây2
SensorEvent.values[1] Lực gia tốc dọc theo trục y (bao gồm cả trọng lực).
SensorEvent.values[2] Lực gia tốc dọc theo trục z (kể cả trọng lực).
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] Gia tốc đo được dọc theo trục X mà không bù độ lệch. m/giây2
SensorEvent.values[1] Gia tốc đo được dọc theo trục Y mà không bù độ lệch.
SensorEvent.values[2] Gia tốc đo được dọc theo trục Z mà không bù độ lệch.
SensorEvent.values[3] Gia tốc đo được dọc theo trục X với bù độ chệch ước tính.
SensorEvent.values[4] Gia tốc đo được dọc theo trục Y với bù lệch ước tính.
SensorEvent.values[5] Gia tốc đo được dọc theo trục Z với bù độ lệch ước tính.
TYPE_GRAVITY SensorEvent.values[0] Lực hấp dẫn dọc theo trục x. m/giây2
SensorEvent.values[1] Lực hấp dẫn dọc theo trục y.
SensorEvent.values[2] Lực hấp dẫn dọc theo trục z.
TYPE_GYROSCOPE SensorEvent.values[0] Tốc độ xoay quanh trục x. Rad/giây
SensorEvent.values[1] Tốc độ xoay quanh trục y.
SensorEvent.values[2] Tốc độ quay quanh trục z.
TYPE_GYROSCOPE_UNCALIBRATED SensorEvent.values[0] Tốc độ xoay (không bù độ lệch) xung quanh trục x. Rad/giây
SensorEvent.values[1] Tốc độ xoay (không bù độ lệch) xung quanh trục y.
SensorEvent.values[2] Tốc độ xoay (không bù độ lệch) xung quanh trục z.
SensorEvent.values[3] Độ lệch ước tính xung quanh trục x.
SensorEvent.values[4] Độ lệch ước tính xung quanh trục y.
SensorEvent.values[5] Độ lệch ước tính quanh trục z.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] Lực gia tốc dọc theo trục x (không bao gồm trọng lực). m/giây2
SensorEvent.values[1] Lực gia tốc dọc theo trục y (không tính trọng lực).
SensorEvent.values[2] Lực gia tốc dọc theo trục z (không tính trọng lực).
TYPE_ROTATION_VECTOR SensorEvent.values[0] Thành phần vectơ xoay dọc theo trục x (x * sin(=/2)). Không có đơn vị
SensorEvent.values[1] Thành phần vectơ xoay dọc theo trục y (y * sin(=/2)).
SensorEvent.values[2] Thành phần vectơ xoay dọc theo trục z (z * sin(=/2)).
SensorEvent.values[3] Thành phần vô hướng của vectơ xoay ((cos(=1)/2)).1
TYPE_SIGNIFICANT_MOTION Không áp dụng Không có câu trả lời thích hợp Không áp dụng
TYPE_STEP_COUNTER SensorEvent.values[0] Số bước người dùng đã thực hiện kể từ lần khởi động lại gần đây nhất trong khi cảm biến được kích hoạt. Các bước
TYPE_STEP_DETECTOR Không áp dụng Không có câu trả lời thích hợp Không áp dụng

1 Thành phần vô hướng là một giá trị không bắt buộc.

Cảm biến vectơ xoay và cảm biến trọng lực là những cảm biến được dùng thường xuyên nhất để phát hiện và theo dõi chuyển động. Cảm biến vectơ xoay đặc biệt linh hoạt và có thể dùng cho nhiều nhiệm vụ liên quan đến chuyển động, chẳng hạn như phát hiện cử chỉ, theo dõi sự thay đổi góc và theo dõi các thay đổi về hướng tương đối. Ví dụ: cảm biến vectơ xoay là lựa chọn lý tưởng nếu bạn đang phát triển một trò chơi, ứng dụng thực tế tăng cường, la bàn 2 chiều hoặc 3 chiều, hoặc một ứng dụng ổn định máy ảnh. Trong hầu hết các trường hợp, việc sử dụng các cảm biến này là lựa chọn phù hợp hơn so với việc sử dụng cảm biến gia tốc kế và cảm biến địa từ hoặc cảm biến hướng.

Cảm biến trong Dự án nguồn mở Android

Dự án nguồn mở Android (AOSP) cung cấp 3 cảm biến chuyển động dựa trên phần mềm: cảm biến trọng lực, cảm biến gia tốc tuyến tính và cảm biến vectơ xoay. Những cảm biến này đã được cập nhật trong Android 4.0 và hiện sử dụng con quay hồi chuyển của thiết bị (cùng với các cảm biến khác) để cải thiện độ ổn định và hiệu suất. Nếu muốn dùng thử các cảm biến này, bạn có thể xác định chúng bằng cách sử dụng phương thức getVendor() và phương thức getVersion() (nhà cung cấp là Google LLC; số phiên bản là 3). Bạn cần xác định các cảm biến này theo nhà cung cấp và số phiên bản vì hệ thống Android coi 3 cảm biến này là các cảm biến phụ. Ví dụ: nếu một nhà sản xuất thiết bị cung cấp cảm biến trọng lực riêng, thì cảm biến trọng lực AOSP sẽ xuất hiện dưới dạng cảm biến trọng lực phụ. Cả 3 cảm biến này đều dựa vào con quay hồi chuyển: nếu thiết bị không có con quay hồi chuyển, thì các cảm biến này sẽ không xuất hiện và không dùng được.

Dùng cảm biến trọng lực

Cảm biến trọng lực cung cấp một vectơ 3 chiều cho biết hướng và cường độ của trọng lực. Thông thường, cảm biến này dùng để xác định hướng tương đối của thiết bị trong không gian. Mã sau đây cho bạn biết cách tải thực thể của cảm biến trọng lực mặc định:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

Các đơn vị giống như đơn vị mà cảm biến gia tốc sử dụng (m/s2) và hệ toạ độ cũng giống như đơn vị mà cảm biến gia tốc sử dụng.

Lưu ý: Khi một thiết bị ở trạng thái nghỉ, đầu ra của cảm biến trọng lực phải giống với đầu ra của gia tốc kế.

Sử dụng gia tốc kế tuyến tính

Cảm biến gia tốc tuyến tính cung cấp cho bạn một vectơ 3 chiều biểu thị gia tốc dọc theo mỗi trục của thiết bị, không bao gồm trọng lực. Bạn có thể sử dụng giá trị này để phát hiện cử chỉ. Giá trị này cũng có thể đóng vai trò là dữ liệu đầu vào cho hệ thống điều hướng quán tính, hệ thống này sử dụng tính năng tính toán chết. Đoạn mã sau đây cho bạn biết cách lấy thực thể của cảm biến gia tốc tuyến tính mặc định:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

Về mặt lý thuyết, cảm biến này cung cấp cho bạn dữ liệu tăng tốc theo mối quan hệ sau:

linear acceleration = acceleration - acceleration due to gravity

Bạn thường sử dụng cảm biến này khi muốn lấy dữ liệu gia tốc mà không chịu ảnh hưởng của trọng lực. Ví dụ: bạn có thể sử dụng cảm biến này để xem tốc độ của ô tô. Cảm biến tăng tốc tuyến tính luôn có một độ lệch mà bạn cần xoá. Cách đơn giản nhất để thực hiện việc này là tích hợp một bước hiệu chỉnh vào ứng dụng. Trong quá trình hiệu chỉnh, bạn có thể yêu cầu người dùng đặt thiết bị trên một bàn, sau đó đọc độ lệch cho cả 3 trục. Sau đó, bạn có thể trừ đi mức chênh lệch đó từ kết quả đọc trực tiếp của cảm biến gia tốc để có được mức tăng tốc tuyến tính thực tế.

Hệ thống toạ độ cảm biến cũng giống như hệ thống toạ độ mà cảm biến gia tốc sử dụng, và các đơn vị đo lường (m/s2).

Sử dụng cảm biến vectơ xoay

Vectơ xoay biểu thị hướng của thiết bị dưới dạng kết hợp của một góc và một trục, trong đó thiết bị đã xoay qua một góc and xung quanh một trục (x, y hoặc z). Mã sau đây cho bạn biết cách tải một thực thể của cảm biến vectơ xoay mặc định:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

Ba phần tử của vectơ xoay được biểu thị như sau:

x*sin(=/2), y*sin(=/2), z*sin(=/2)

Khi độ lớn của vectơ xoay bằng sin(=2) và hướng của vectơ xoay bằng hướng của trục xoay.

Hình 1. Hệ toạ độ mà cảm biến vectơ xoay sử dụng.

Ba phần tử của vectơ xoay bằng với ba thành phần cuối cùng của một đơn vị quaternion (cos(=/2), x*sin((=)/2), y*sin((=/2), z*sin((((((/)))))))) Các phần tử của vectơ xoay là vô vị. Trục x, y và z được xác định giống như cách cảm biến gia tốc. Hệ toạ độ tham chiếu được định nghĩa là cơ sở thuận trực tiếp theo chuẩn (xem hình 1). Hệ toạ độ này có các đặc điểm sau:

  • X được xác định là tích vectơ Y x Z. Đường này tiếp xúc với mặt đất tại vị trí hiện tại của thiết bị và chỉ về phía Đông.
  • Y tiếp xúc với mặt đất tại vị trí hiện tại của thiết bị và hướng về Bắc Cực địa từ.
  • Z trỏ về phía bầu trời và vuông góc với mặt phẳng.

Để xem ứng dụng mẫu hướng dẫn cách sử dụng cảm biến vectơ xoay, hãy xem bài viết RotationVectordemo.java.

Sử dụng cảm biến chuyển động mạnh

Cảm biến chuyển động lớn kích hoạt một sự kiện mỗi khi phát hiện thấy chuyển động đáng kể, sau đó cảm biến này tự tắt. Chuyển động đáng kể là chuyển động có thể dẫn đến thay đổi về vị trí của người dùng; ví dụ: đi bộ, đi xe đạp hoặc ngồi trong ô tô đang di chuyển. Mã sau đây cho bạn biết cách tải thực thể của cảm biến chuyển động có ý nghĩa mặc định và cách đăng ký trình nghe sự kiện:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val mSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
val triggerEventListener = object : TriggerEventListener() {
    override fun onTrigger(event: TriggerEvent?) {
        // Do work
    }
}
mSensor?.also { sensor ->
    sensorManager.requestTriggerSensor(triggerEventListener, sensor)
}

Java

private SensorManager sensorManager;
private Sensor sensor;
private TriggerEventListener triggerEventListener;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);

triggerEventListener = new TriggerEventListener() {
    @Override
    public void onTrigger(TriggerEvent event) {
        // Do work
    }
};

sensorManager.requestTriggerSensor(triggerEventListener, mSensor);

Để biết thêm thông tin, hãy xem TriggerEventListener.

Sử dụng cảm biến bộ đếm bước

Cảm biến bộ đếm bước cung cấp số bước mà người dùng đã thực hiện kể từ lần khởi động lại gần đây nhất trong khi cảm biến được kích hoạt. Bộ đếm bước có độ trễ cao hơn (tối đa 10 giây) nhưng độ chính xác cao hơn cảm biến của trình phát hiện bước.

Lưu ý: Bạn phải khai báo quyền ACTIVITY_RECOGNITION để ứng dụng của bạn có thể sử dụng cảm biến này trên các thiết bị chạy Android 10 (API cấp 29) trở lên.

Mã sau đây cho bạn biết cách lấy thực thể của cảm biến bộ đếm bước mặc định:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);

Để duy trì thời lượng pin trên các thiết bị chạy ứng dụng, bạn nên sử dụng lớp JobScheduler để truy xuất giá trị hiện tại từ cảm biến bộ đếm bước tại một khoảng thời gian cụ thể. Mặc dù các loại ứng dụng khác nhau có yêu cầu khoảng thời gian đọc cảm biến khác nhau, nhưng bạn nên tạo khoảng thời gian này càng dài càng tốt trừ phi ứng dụng yêu cầu dữ liệu theo thời gian thực từ cảm biến.

Sử dụng cảm biến của máy phát hiện bước

Cảm biến của máy phát hiện bước kích hoạt một sự kiện mỗi khi người dùng thực hiện một bước. Độ trễ dự kiến dưới 2 giây.

Lưu ý: Bạn phải khai báo quyền ACTIVITY_RECOGNITION để ứng dụng của bạn có thể sử dụng cảm biến này trên các thiết bị chạy Android 10 (API cấp 29) trở lên.

Mã sau đây cho bạn biết cách tải thực thể của cảm biến phát hiện bước mặc định:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

Làm việc với dữ liệu thô

Các cảm biến sau đây cung cấp cho ứng dụng của bạn dữ liệu thô về các lực tuyến tính và lực xoay được áp dụng cho thiết bị. Để sử dụng giá trị trong các cảm biến này một cách hiệu quả, bạn cần lọc ra các yếu tố từ môi trường, chẳng hạn như trọng lực. Bạn cũng có thể cần áp dụng thuật toán làm mượt cho xu hướng của các giá trị để giảm độ nhiễu.

Sử dụng gia tốc kế

Cảm biến gia tốc đo lường gia tốc áp dụng cho thiết bị, bao gồm cả lực hấp dẫn. Mã sau đây cho bạn biết cách tải thực thể của cảm biến gia tốc mặc định:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)

Java

private SensorManager sensorManager;
private Sensor sensor;
  ...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

Lưu ý: Nếu ứng dụng của bạn nhắm đến Android 12 (API cấp 31) trở lên, thì cảm biến này sẽ bị giới hạn số lượng.

Về mặt lý thuyết, cảm biến gia tốc xác định gia tốc áp dụng cho một thiết bị (Ad) bằng cách đo các lực tác dụng lên chính cảm biến đó (Fs) theo mối quan hệ sau:

A_D=-(1/khối lượng)∑F_S

Tuy nhiên, lực hấp dẫn luôn ảnh hưởng đến gia tốc đo được theo mối quan hệ sau:

A_D=-g-(1/khối lượng)∑F_S

Vì lý do này, khi thiết bị ở trên bàn (và không tăng tốc), gia tốc kế sẽ đọc cường độ g = 9,81 m/s2. Tương tự, khi thiết bị đang rơi tự do và do đó nhanh chóng tăng tốc hướng về mặt đất với tốc độ 9,81 m/s2, gia tốc kế của thiết bị sẽ đọc cường độ g = 0 m/s2. Do đó, để đo lường gia tốc thực của thiết bị, phần đóng góp của lực hấp dẫn phải được loại bỏ khỏi dữ liệu của gia tốc kế. Bạn có thể đạt được điều này bằng cách áp dụng một bộ lọc thông cao. Ngược lại, bạn có thể sử dụng bộ lọc thông thấp để cô lập lực hấp dẫn. Ví dụ sau cho thấy cách bạn có thể thực hiện việc này:

Kotlin

override fun onSensorChanged(event: SensorEvent) {
    // In this example, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    val alpha: Float = 0.8f

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0]
    linear_acceleration[1] = event.values[1] - gravity[1]
    linear_acceleration[2] = event.values[2] - gravity[2]
}

Java

public void onSensorChanged(SensorEvent event){
    // In this example, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    final float alpha = 0.8;

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0];
    linear_acceleration[1] = event.values[1] - gravity[1];
    linear_acceleration[2] = event.values[2] - gravity[2];
}

Lưu ý: Bạn có thể sử dụng nhiều kỹ thuật để lọc dữ liệu cảm biến. Mã mẫu ở trên sử dụng một hằng số bộ lọc đơn giản (alpha) để tạo bộ lọc thông thấp. Hằng số bộ lọc này được lấy từ hằng số thời gian (t). Đây là giá trị biểu thị gần đúng của độ trễ mà bộ lọc thêm vào các sự kiện của cảm biến và tốc độ phân phối sự kiện của cảm biến (dt). Mã mẫu sử dụng giá trị alpha là 0,8 cho mục đích minh hoạ. Nếu sử dụng phương pháp lọc này, bạn có thể phải chọn một giá trị alpha khác.

Gia tốc kế sử dụng hệ toạ độ cảm biến tiêu chuẩn. Trong thực tế, điều này có nghĩa là các điều kiện sau sẽ áp dụng khi thiết bị nằm phẳng trên bàn theo hướng tự nhiên:

  • Nếu bạn đẩy thiết bị sang bên trái (để thiết bị di chuyển sang phải), thì giá trị gia tốc x sẽ là số dương.
  • Nếu bạn đẩy thiết bị xuống dưới cùng (để thiết bị di chuyển ra xa bạn), thì giá trị gia tốc y là số dương.
  • Nếu bạn đẩy thiết bị về phía bầu trời với gia tốc A m/s2, thì giá trị gia tốc z sẽ bằng A + 9,81, tương ứng với gia tốc của thiết bị (+A m/s2) trừ đi lực hấp dẫn (-9,81 m/s2).
  • Thiết bị đứng yên sẽ có giá trị gia tốc +9,81, tương ứng với gia tốc của thiết bị (0 m/s2 trừ đi lực trọng lực là -9,81 m/s2).

Nói chung, gia tốc kế là cảm biến phù hợp để bạn sử dụng nếu đang theo dõi chuyển động của thiết bị. Hầu hết mọi điện thoại di động và máy tính bảng chạy Android đều có gia tốc kế và gia tốc kế này sử dụng ít năng lượng hơn khoảng 10 lần so với các cảm biến chuyển động khác. Một hạn chế là bạn có thể phải triển khai các bộ lọc thông thấp và thông cao để loại bỏ lực hấp dẫn và giảm nhiễu.

Sử dụng con quay hồi chuyển

Con quay hồi chuyển đo tốc độ xoay tính bằng Rad/s xung quanh trục x, y và z của thiết bị. Mã sau đây hướng dẫn bạn cách tải một phiên bản của con quay hồi chuyển mặc định:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

Lưu ý: Nếu ứng dụng của bạn nhắm đến Android 12 (API cấp 31) trở lên, thì cảm biến này sẽ bị giới hạn số lượng.

Hệ toạ độ của cảm biến giống với hệ toạ độ được sử dụng cho cảm biến gia tốc. Xoay có giá trị dương theo chiều ngược kim đồng hồ; tức là trình quan sát nhìn từ một số vị trí dương trên trục x, y hoặc z tại một thiết bị được đặt trên gốc sẽ báo cáo độ xoay dương nếu thiết bị có vẻ như đang xoay ngược chiều kim đồng hồ. Đây là định nghĩa toán học tiêu chuẩn về chế độ xoay dương và không giống với định nghĩa cho cuộc cuộn mà cảm biến hướng sử dụng.

Thông thường, đầu ra của con quay hồi chuyển được tích hợp theo thời gian để tính toán chế độ xoay mô tả sự thay đổi góc trong từng bước thời gian. Ví dụ:

Kotlin

// Create a constant to convert nanoseconds to seconds.
private val NS2S = 1.0f / 1000000000.0f
private val deltaRotationVector = FloatArray(4) { 0f }
private var timestamp: Float = 0f

override fun onSensorChanged(event: SensorEvent?) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0f && event != null) {
        val dT = (event.timestamp - timestamp) * NS2S
        // Axis of the rotation sample, not normalized yet.
        var axisX: Float = event.values[0]
        var axisY: Float = event.values[1]
        var axisZ: Float = event.values[2]

        // Calculate the angular speed of the sample
        val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ)

        // Normalize the rotation vector if it's big enough to get the axis
        // (that is, EPSILON should represent your maximum allowable margin of error)
        if (omegaMagnitude > EPSILON) {
            axisX /= omegaMagnitude
            axisY /= omegaMagnitude
            axisZ /= omegaMagnitude
        }

        // Integrate around this axis with the angular speed by the timestep
        // in order to get a delta rotation from this sample over the timestep
        // We will convert this axis-angle representation of the delta rotation
        // into a quaternion before turning it into the rotation matrix.
        val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f
        val sinThetaOverTwo: Float = sin(thetaOverTwo)
        val cosThetaOverTwo: Float = cos(thetaOverTwo)
        deltaRotationVector[0] = sinThetaOverTwo * axisX
        deltaRotationVector[1] = sinThetaOverTwo * axisY
        deltaRotationVector[2] = sinThetaOverTwo * axisZ
        deltaRotationVector[3] = cosThetaOverTwo
    }
    timestamp = event?.timestamp?.toFloat() ?: 0f
    val deltaRotationMatrix = FloatArray(9) { 0f }
    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
}

Java

// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0) {
      final float dT = (event.timestamp - timestamp) * NS2S;
      // Axis of the rotation sample, not normalized yet.
      float axisX = event.values[0];
      float axisY = event.values[1];
      float axisZ = event.values[2];

      // Calculate the angular speed of the sample
      float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

      // Normalize the rotation vector if it's big enough to get the axis
      // (that is, EPSILON should represent your maximum allowable margin of error)
      if (omegaMagnitude > EPSILON) {
        axisX /= omegaMagnitude;
        axisY /= omegaMagnitude;
        axisZ /= omegaMagnitude;
      }

      // Integrate around this axis with the angular speed by the timestep
      // in order to get a delta rotation from this sample over the timestep
      // We will convert this axis-angle representation of the delta rotation
      // into a quaternion before turning it into the rotation matrix.
      float thetaOverTwo = omegaMagnitude * dT / 2.0f;
      float sinThetaOverTwo = sin(thetaOverTwo);
      float cosThetaOverTwo = cos(thetaOverTwo);
      deltaRotationVector[0] = sinThetaOverTwo * axisX;
      deltaRotationVector[1] = sinThetaOverTwo * axisY;
      deltaRotationVector[2] = sinThetaOverTwo * axisZ;
      deltaRotationVector[3] = cosThetaOverTwo;
    }
    timestamp = event.timestamp;
    float[] deltaRotationMatrix = new float[9];
    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
}

Con quay hồi chuyển tiêu chuẩn cung cấp dữ liệu quay thô mà không cần lọc hoặc hiệu chỉnh độ nhiễu và độ trôi (độ lệch). Trong thực tế, tiếng ồn và độ trôi của con quay hồi chuyển sẽ gây ra các lỗi cần được bù. Bạn thường xác định độ trôi (độ lệch) và tiếng ồn bằng cách theo dõi các cảm biến khác, chẳng hạn như cảm biến trọng lực hoặc gia tốc kế.

Sử dụng con quay hồi chuyển chưa được hiệu chỉnh

Con quay hồi chuyển chưa được hiệu chỉnh sẽ tương tự như con quay hồi chuyển, ngoại trừ việc không áp dụng bù cho con quay hồi chuyển cho tốc độ xoay. Hiệu chuẩn nhà máy và bù nhiệt độ vẫn được áp dụng cho tốc độ xoay. Con quay hồi chuyển chưa được hiệu chỉnh rất hữu ích cho việc xử lý hậu kỳ và dữ liệu về hướng hợp nhất. Nói chung, gyroscope_event.values[0] sẽ gần với uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3]. Tức là,

calibrated_x ~= uncalibrated_x - bias_estimate_x

Lưu ý: Cảm biến chưa hiệu chỉnh cung cấp kết quả thô hơn và có thể có một vài sai lệch, nhưng số đo của cảm biến có ít bước nhảy hơn từ các hiệu chỉnh được áp dụng thông qua quá trình hiệu chuẩn. Một số ứng dụng có thể muốn kết quả chưa được hiệu chỉnh như vậy mượt mà và đáng tin cậy hơn. Ví dụ: nếu một ứng dụng đang cố gắng tiến hành quá trình hợp nhất cảm biến của riêng mình, thì việc cung cấp tính năng hiệu chuẩn thực sự có thể làm sai lệch kết quả.

Ngoài tốc độ xoay, con quay hồi chuyển chưa được hiệu chỉnh cũng cung cấp độ trôi ước tính xung quanh mỗi trục. Mã sau đây cho bạn biết cách tải một thực thể của con quay hồi chuyển mặc định chưa hiệu chỉnh:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);

Mã mẫu khác

Mẫu BatchStepSensor minh hoạ thêm về cách sử dụng các API được trình bày trên trang này.

Bạn cũng nên đọc