คราวนี้ขอข้ามมาเรื่อง Sensor ในอุปกรณ์แอนดรอยด์กันบ้าง โดยที่บทความนี้ก็จะขอเริ่มกับ Sensor ที่ชื่อว่า Accelerometer หลายๆคนจะรู้จักกันในชื่อเซนเซอร์วัดความเอียงนั่นเอง

สำหรับ Accelerometer เรียกได้ว่าเป็นเซนเซอร์ที่โคตรจะพื้นฐานของอุปกรณ์แอนดรอยด์เลยก็ว่าได้ เรียกได้ว่าทุกๆรุ่นนั้นต้องมีเซนเซอร์ตัวนี้เป็นอย่างน้อย และก็ยังเป็นเซนเซอร์ที่ผู้ที่หลงเข้ามาอ่านส่วนใหญ่จะเขียนใช้งานกันด้วย

โดยที่ Accelerometer จะวัดความเร่งในการเอียงเครื่องทั้ง 3 ทิศ สำหรับแกน XYZ บนอุปกรณ์แอนดรอยด์ใดๆจะมีตำแหน่งดังนี้

สำหรับแกน X กับ Y จะขึ้นอยู่กับตัวอุปกรณ์แอนดรอยด์ ในภาพข้างบนนี้จะเป็นเครื่องที่เป็นแทบเลตที่เป็น X-Large หรือเครื่องที่หน้าจอใหญ่กว่า 7 นิ้วขึ้นไป (ไม่รวม 7 นิ้ว) มีแกน X ตามแนวกว้างของจอ และแกน Y ตามแนวสูง

ส่วนภาพข้างบนนี้คือแกน XYZ บนเครื่องที่เป็นมือถือและแทบเลตที่มีขนาดหน้าจอ Large หรือตั้งแต่ 7 นิ้วลงมา จะเห็นว่าแนวแกน X กับ Y ไม่เหมือนกับบนแทบเลต X-Large เพราะโดยธรรมชาติแล้ว มือถือและแทบเลตขนาด 7 นิ้วลงมา การใช้งานเครื่องจะอยู่ในลักษณะการถือแนวตั้งเป็นหลัก แต่ไซส์ที่ใหญกว่านั้นจะอยู่ในลักษณะการถือแนวนอน ดังนั้นเวลาใช้ Accelerometer ก็ให้คำนึงถึงเรื่องนี้ด้วย

การวัดความเร่งในการเอียงก็คือการเอียงในแต่ละทิศที่จะมีลักษณะการเอียงในทิศทางต่างๆ ดังนี้

ทีนี้ให้สังเกตว่า แกน X และ Y จะมีแค่ขึ้นลงเท่านั้น แต่แกน Z จะพิเศษกว่าคือมีทั้งสองแกนที่เคลื่อนที่

ดังนั้นเวลาที่เอียงไปทางแกน X แกน Z ก็จะเอียงด้วย และเมื่อเอียงไปทางแกน Y แกน Z ก็จะเอียงตาม

จากที่บอกไปแล้วว่า Accelerometer จะวัดความเร่งในแต่ละแกน ง่ายๆก็คือ เวลาที่เครื่องอยู่นิ่งๆไม่มีการขยับ ค่าแต่ละแกนก็เป็น 0 แต่ในความเป็นจริงอย่าลืมว่ายังมีแรงโน่มถ่วงของโลกอยู่ด้วย ดังนั้นค่าจาก Accelerometer จึงไม่ได้เป็น 0 ทั้งหมด เวลาไม่เคลื่อนที่ ถ้าเราตั้งเครื่องให้แกน Z ตั้งฉากกับพื้นโลก แกน X และ Y จะเป็น 0 แต่ว่าแกน Z จะไม่เป็น 0 เพราะมีแรงโน้มถ่วงของโลกกระทำอยู่ ดังนั้นค่าที่ได้จากแกน Z จะเป็น 9.8 m/s² แต่มันเป็นค่าในอุดมคติ

ในความเป็นจริงเป็นไปไม่ได้อยู่แล้วที่จะได้ค่าเป็น 9.8 ตลอดเวลา แต่ค่าจะเปลี่ยนแปลงไปมาระหว่างค่า 9.8 ต่ำกว่าบ้าง มากกว่าบ้าง ในบทความนี้ขอปัดเป็น 10 ไปเลยนะ (แต่จริงๆแล้วเป็นค่า 9.8)

ค่าแกน XYZ จะเป็นไปตามทิศทางของเครื่องที่เป็นมือถือดังนี้

ในกรณีนี้คือเครื่องอยู่นิ่ง ค่า 10 ที่ได้ก็จะเป็นผลของแรง G ของโลก ทีนี้ให้ดูภาพที่เครื่องวางตั้งฉากกับพื้นโลก ที่ค่า X = 0, Y = 10 และ Z = 0 สมมติว่าเครื่องเคลื่อนที่ลง (ความเร่งทิศเดียวกับแรง G ของโลก) ค่าความเร่งที่ได้ก็จะมีมากกว่า 10 (แรงโน้มถ่วงโลก + ความเร่งจากเครื่อง) แต่ถ้าเคลื่อนที่ขึ้นข้างบน ก็จะเป็นการสวนทางกับแรงโน้มถ่วงโลก ค่าที่ได้ก็จะน้อยกว่า 10 (แรงโน้มถ่วงโลก — ความเร่งจากเครื่อง)

ในกรณีที่อยู่นิ่งๆ แต่แกน XYZ ไม่ได้ตั้งฉากกับพื้นโลกโดยตรงล่ะ? แรงโน้มถ่วงของโลกที่กระทำกับแต่ละแกนของ Accelerometer ก็จะกระจายออกไปในแต่ละแกน ขึ้นอยู่กับการเอียงนั้นๆ แต่เมื่อคิดเวคเตอร์ลัพธ์ที่ตั้งฉากกับพื้นโลกก็มีค่าประมาณ 9.8 อยู่ดี

และเมื่อเครื่องเคลื่อนที่ ความเร่งก็จะเปลี่ยนแปลงไปตามทิศทางการเคลื่อนที่

ดังนั้นค่าความเอียง จริงๆแล้วก็คือความเร่งที่เกิดจากแรงโน้มถ่วงของโลกและตอนอ่านค่าจริงจาก Accelerometer ให้ดูวีดีโอข้างล่างนี้เลย

ถึงแม้ว่า Accelerometer จะอ่านค่าจากความเอียงได้ก็จริง แต่ก็ยังมีจุดบอดคือกรณีที่หมุนตัวเครื่องระนาบกับพื้นโลก Acceletometer จะไม่สามารถรับรู้ถึงความเปลี่ยนแปลงนี้ได้ ทั้งนี้ก็เพราะว่าการที่รับรู้จากการเอียงเครื่องเท่านั้น จึงไม่สามารถรับรู้ถึงการหมุนที่ระนาบกับพื้นโลกได้ ซึ่งในกรณีแบบนี้ก็ให้ใช้ Gyroscope ที่อ่านค่าในเชิงมุม

ในการใช้งาน Acceleromter นิยมใช้กับ “การเอียงเครื่องหรือเขย่า”

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


โดยที่จะให้ sensorManager ดึง Service จำพวกเซนเซอร์มาเก็บไว้ แล้วให้ sensor ซึ่งเป็นคลาสของ Sensor ดึงค่าของ Accelerometer จากตัวแปร sensorManager ตอนนี้ตัวแปร sensor ก็คือ Accelerometer

sensorManager.registerListener(Listener, Sensor, SensorManager.SENSOR_DELAY_NORMAL);


สำหรับ Listener ก็คือตัว Listener ที่จะต้องสร้างไว้ เพื่อรอรับค่าจาก Accelerometer เดี๋ยวจะให้ดูทีหลัง ส่วน Sensor ก็คือตัวแปรของคลาส Sensor นั่นเอง ซึ่งจากคำสั่งก่อนหน้า ตัวแปรนั้นก็คือ sensor ดังนั้นในตรงนี้ก็ให้ใส่จาก Sensor เป็น sensor ลงไป ส่วน SensorManager.SENSOR_DELAY_NORMAL คือความเร็วในการอ่านค่าจากเซนเซอร์ มีทั้งหมดดังนี้

จากที่เจ้าของบล็อกได้ลอง ก็จะได้ระยะเวลาในการอัพเดตของแต่ละแบบดังนี้

SENSOR_DELAY_NORMAL : 63 - 64 ms SENSOR_DELAY_UI : 63 - 64 ms SENSOR_DELAY_GAME : 16 - 18 ms SENSOR_DELAY_FASTEST : 7 - 10 ms


อัตราเร็วของข้อมูลเหล่านี้ไม่ได้ตายตัวเสมอไป แต่จะขึ้นอยู่กับการทำงานของเครื่องในตอนนั้นๆด้วยโดยในตัวอย่างนี้ไม่ได้ต้องการความเร็วที่มากมายนัก ก็ขอใช้แค่ SENSOR_DELAY_NORMAL ก็พอแล้ว

SensorManager.unregisterListener(Listener);


โดยที่ Listener ก็คือตัวแปรของ Listener ที่ประกาศไว้ตอนแรก ในคำสั่ง registerListener นั่นเอง กรณีนี้ก็จะยกเลิก Listener ตัวนั้นๆ

public void onResume() { super.onResume(); sensorManager.registerListener(Listener, Sensor, SensorManager.SENSOR_DELAY_NORMAL); } public void onStop() { super.onStop(); sensorManager.unregisterListener(Listener); }


ทีนี้มาดูกันต่อที่ส่วนของ Listener ที่ใช้กับเซนเซอร์ โดยจะใช้เป็น Listener แบบ SensorEventListener

SensorEventListener accelListener = new SensorEventListener() { public void onAccuracyChanged(Sensor sensor, int acc) { } public void onSensorChanged(SensorEvent event) { float x = event.values[0]; float y = event.values[1]; float z = event.values[2]; ... ... } };


โดยที่ฟังก์ชัน onAccuracyChanged จะเป็นกรณีที่ ค่าความแม่นยำของเซนเซอร์มีการเปลี่ยนแปลง ซึ่งในฟังก์ชันนี้ ปกติจะไม่ค่อยได้ใช้อะไรกัน จะไปใช้งานในฟังก์ชัน onSensorChanged มากกว่า ซึ่งเป็นฟังก์ชันที่ทำงานเมื่อเซนเซอร์อ่านค่าใหม่หรือก็คือค่าจากเซนเซอร์มีการเปลี่ยนแปลงนั่นเอง

float value = event.values[int];


โดย int ก็คือลำดับของอาร์เรย์ที่ต้องการอ่านค่า

import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.widget.TextView; public class Main extends Activity { TextView textX, textY, textZ; SensorManager sensorManager; Sensor sensor; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); textX = (TextView) findViewById(R.id.textX); textY = (TextView) findViewById(R.id.textY); textZ = (TextView) findViewById(R.id.textZ); } public void onResume() { super.onResume(); sensorManager.registerListener(accelListener, sensor, SensorManager.SENSOR_DELAY_NORMAL); } public void onStop() { super.onStop(); sensorManager.unregisterListener(accelListener); } SensorEventListener accelListener = new SensorEventListener() { public void onAccuracyChanged(Sensor sensor, int acc) { } public void onSensorChanged(SensorEvent event) { float x = event.values[0]; float y = event.values[1]; float z = event.values[2]; textX.setText("X : " + (int)x); textY.setText("Y : " + (int)y); textZ.setText("Z : " + (int)z); } }; }


1. ประกาศตัวแปรสำหรับคลาส TextView, Sensormanager และ Sensor

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" > <TextView android:id="@+id/textX" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" android:text="" /> <TextView android:id="@+id/textY" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20sp" android:textSize="30sp" android:text="" /> <TextView android:id="@+id/textZ" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20sp" android:textSize="30sp" android:text="" /> </LinearLayout>