เซ็นเซอร์ตรวจจับความเคลื่อนไหว

แพลตฟอร์ม Android มีเซ็นเซอร์หลายตัวที่ช่วยให้คุณตรวจสอบการเคลื่อนไหวของอุปกรณ์ได้

สถาปัตยกรรมที่เป็นไปได้ของเซ็นเซอร์จะแตกต่างกันไปตามประเภทเซ็นเซอร์ ดังนี้

  • เซ็นเซอร์แรงโน้มถ่วง ความเร่งเชิงเส้น เวกเตอร์การหมุน การเคลื่อนไหวที่มีนัยสำคัญ ตัวนับก้าว และตัวตรวจจับก้าวเป็นแบบฮาร์ดแวร์หรือซอฟต์แวร์ก็ได้
  • เซ็นเซอร์ตัวตรวจวัดความเร่งและเครื่องวัดการหมุนจะทำงานโดยใช้ฮาร์ดแวร์เสมอ

อุปกรณ์ที่ทำงานด้วยระบบ Android ส่วนใหญ่มีตัวตรวจวัดความเร่ง และปัจจุบันหลายรุ่นมีเครื่องวัดการหมุนด้วย ความพร้อมใช้งานของเซ็นเซอร์ที่ใช้ซอฟต์แวร์จะมีความผันแปรมากกว่า เนื่องจากมักต้องอาศัยเซ็นเซอร์ฮาร์ดแวร์อย่างน้อย 1 ตัวเพื่อรับข้อมูล เซ็นเซอร์ที่ใช้ซอฟต์แวร์เหล่านี้สามารถรับข้อมูลจากตัวตรวจวัดความเร่งและเครื่องวัดค่าความเข้มข้นของสนามแม่เหล็ก หรือจากเครื่องวัดการหมุน ทั้งนี้ขึ้นอยู่กับอุปกรณ์

เซ็นเซอร์ตรวจจับการเคลื่อนไหวมีประโยชน์สำหรับการตรวจสอบการเคลื่อนไหวของอุปกรณ์ เช่น การเอียง การสั่น การหมุน หรือการแกว่ง โดยปกติแล้ว การเคลื่อนไหวจะสะท้อนถึงการป้อนข้อมูลของผู้ใช้โดยตรง (เช่น ผู้ใช้บังคับรถในเกมหรือผู้ใช้ควบคุมลูกบอลในเกม) แต่ก็อาจสะท้อนถึงสภาพแวดล้อมทางกายภาพที่อุปกรณ์ตั้งอยู่ได้ด้วย (เช่น เคลื่อนที่ไปพร้อมกับคุณขณะขับรถ) ในกรณีแรก คุณกำลังตรวจสอบการเคลื่อนไหวที่สัมพันธ์กับกรอบอ้างอิงของอุปกรณ์หรือกรอบอ้างอิงของแอปพลิเคชัน ในกรณีที่ 2 คุณกำลังตรวจสอบการเคลื่อนไหวที่สัมพันธ์กับกรอบอ้างอิงของโลก โดยปกติแล้วเซ็นเซอร์ตรวจจับการเคลื่อนไหวเพียงอย่างเดียวจะไม่ใช้เพื่อตรวจสอบตำแหน่งของอุปกรณ์ แต่สามารถใช้ร่วมกับเซ็นเซอร์อื่นๆ เช่น เซ็นเซอร์สนามแม่เหล็กโลก เพื่อระบุตำแหน่งของอุปกรณ์ที่สัมพันธ์กับกรอบอ้างอิงของโลก (ดูข้อมูลเพิ่มเติมได้ที่เซ็นเซอร์ตำแหน่ง)

เซ็นเซอร์ตรวจจับการเคลื่อนไหวทั้งหมดจะแสดงผลอาร์เรย์หลายมิติของค่าเซ็นเซอร์สำหรับ SensorEvent แต่ละรายการ ตัวอย่างเช่น ในระหว่างเหตุการณ์เซ็นเซอร์เดียว ตัวตรวจวัดความเร่งจะแสดงผลข้อมูลแรงเร่งสำหรับแกนพิกัด 3 แกน และเครื่องวัดการหมุนจะแสดงผลข้อมูลอัตราการหมุนสำหรับแกนพิกัด 3 แกน ระบบจะแสดงค่าข้อมูลเหล่านี้ในอาร์เรย์ float (values) พร้อมกับพารามิเตอร์ SensorEvent อื่นๆ ตารางที่ 1 สรุปเซ็นเซอร์ตรวจจับการเคลื่อนไหวที่พร้อมใช้งานในแพลตฟอร์ม Android

ตารางที่ 1 เซ็นเซอร์ตรวจจับการเคลื่อนไหวที่รองรับในแพลตฟอร์ม Android

เซ็นเซอร์ ข้อมูลเหตุการณ์จากเซ็นเซอร์ คำอธิบาย หน่วยวัด
TYPE_ACCELEROMETER SensorEvent.values[0] แรงเร่งตามแกน x (รวมถึงแรงโน้มถ่วง) m/s2
SensorEvent.values[1] แรงเร่งตามแกน y (รวมถึงแรงโน้มถ่วง)
SensorEvent.values[2] แรงเร่งตามแกน Z (รวมถึงแรงโน้มถ่วง)
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] ความเร่งที่วัดได้ตามแกน X โดยไม่มีการชดเชยความเอนเอียง m/s2
SensorEvent.values[1] การเร่งความเร็วที่วัดได้ตามแกน Y โดยไม่มีการชดเชยความเอนเอียง
SensorEvent.values[2] ความเร่งที่วัดได้ตามแกน Z โดยไม่มีการชดเชยความเอนเอียง
SensorEvent.values[3] ความเร่งที่วัดได้ตามแกน X พร้อมการชดเชยความคลาดเคลื่อนโดยประมาณ
SensorEvent.values[4] ความเร่งที่วัดได้ตามแกน Y พร้อมการชดเชยความคลาดเคลื่อนโดยประมาณ
SensorEvent.values[5] ความเร่งที่วัดได้ตามแกน Z พร้อมการชดเชยความคลาดเคลื่อนโดยประมาณ
TYPE_GRAVITY SensorEvent.values[0] แรงโน้มถ่วงตามแกน x m/s2
SensorEvent.values[1] แรงโน้มถ่วงตามแกน y
SensorEvent.values[2] แรงโน้มถ่วงตามแกน Z
TYPE_GYROSCOPE SensorEvent.values[0] อัตราการหมุนรอบแกน x rad/s
SensorEvent.values[1] อัตราการหมุนรอบแกน y
SensorEvent.values[2] อัตราการหมุนรอบแกน Z
TYPE_GYROSCOPE_UNCALIBRATED SensorEvent.values[0] อัตราการหมุน (ไม่มีการชดเชยการเบี่ยงเบน) รอบแกน x rad/s
SensorEvent.values[1] อัตราการหมุน (ไม่มีการชดเชยการเบี่ยงเบน) รอบแกน Y
SensorEvent.values[2] อัตราการหมุน (ไม่มีการชดเชยการเบี่ยงเบน) รอบแกน Z
SensorEvent.values[3] การเบี่ยงเบนโดยประมาณรอบแกน x
SensorEvent.values[4] การเบี่ยงเบนโดยประมาณรอบแกน Y
SensorEvent.values[5] การเบี่ยงเบนโดยประมาณรอบแกน Z
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] แรงเร่งตามแกน x (ไม่รวมแรงโน้มถ่วง) m/s2
SensorEvent.values[1] แรงเร่งตามแกน Y (ไม่รวมแรงโน้มถ่วง)
SensorEvent.values[2] แรงเร่งตามแกน Z (ไม่รวมแรงโน้มถ่วง)
TYPE_ROTATION_VECTOR SensorEvent.values[0] องค์ประกอบเวกเตอร์การหมุนตามแกน x (x * sin(θ/2)) ไม่มีหน่วย
SensorEvent.values[1] องค์ประกอบเวกเตอร์การหมุนตามแกน y (y * sin(θ/2))
SensorEvent.values[2] องค์ประกอบเวกเตอร์การหมุนตามแกน z (z * sin(θ/2))
SensorEvent.values[3] องค์ประกอบสเกลาร์ของเวกเตอร์การหมุน ((cos(θ/2))1
TYPE_SIGNIFICANT_MOTION ไม่มี ไม่มี ไม่มี
TYPE_STEP_COUNTER SensorEvent.values[0] จำนวนก้าวที่ผู้ใช้เดินนับตั้งแต่การรีบูตครั้งล่าสุดขณะที่เซ็นเซอร์ทำงานอยู่ ขั้นตอน
TYPE_STEP_DETECTOR ไม่มี ไม่มี ไม่มี

1 องค์ประกอบสเกลาร์เป็นค่าที่ไม่บังคับ

เซ็นเซอร์เวกเตอร์การหมุนและเซ็นเซอร์แรงโน้มถ่วงเป็นเซ็นเซอร์ที่ใช้บ่อยที่สุดสำหรับการตรวจจับและการตรวจสอบการเคลื่อนไหว เซ็นเซอร์เวกเตอร์การหมุนมีความอเนกประสงค์เป็นพิเศษและสามารถใช้กับงานที่เกี่ยวข้องกับการเคลื่อนไหวได้หลากหลาย เช่น การตรวจจับท่าทางสัมผัส การตรวจสอบการเปลี่ยนแปลงเชิงมุม และการตรวจสอบการเปลี่ยนแปลงการวางแนวสัมพัทธ์ ตัวอย่างเช่น เซ็นเซอร์เวกเตอร์การหมุนเหมาะอย่างยิ่งหากคุณกำลังพัฒนาเกม แอปพลิเคชันเทคโนโลยีความจริงเสริม (AR) เข็มทิศ 2 มิติหรือ 3 มิติ หรือแอปป้องกันภาพสั่นไหวของกล้อง ในกรณีส่วนใหญ่ การใช้เซ็นเซอร์เหล่านี้เป็นตัวเลือกที่ดีกว่าการใช้ตัวตรวจวัดความเร่งและเซ็นเซอร์สนามแม่เหล็กโลกหรือเซ็นเซอร์การวางแนว

เซ็นเซอร์ของโครงการโอเพนซอร์ส Android

โครงการโอเพนซอร์ส Android (AOSP) มีเซ็นเซอร์ตรวจจับการเคลื่อนไหวที่ทำงานด้วยซอฟต์แวร์ 3 แบบ ได้แก่ เซ็นเซอร์แรงโน้มถ่วง เซ็นเซอร์ความเร่งเชิงเส้น และเซ็นเซอร์เวกเตอร์การหมุน เซ็นเซอร์เหล่านี้ได้รับการอัปเดตใน Android 4.0 และตอนนี้ใช้เครื่องวัดการหมุนของอุปกรณ์ (นอกเหนือจากเซ็นเซอร์อื่นๆ) เพื่อปรับปรุงความเสถียรและประสิทธิภาพ หากต้องการลองใช้เซ็นเซอร์เหล่านี้ คุณสามารถระบุเซ็นเซอร์ได้โดยใช้เมธอด getVendor() และเมธอด getVersion() (ผู้ให้บริการคือ Google LLC และหมายเลขเวอร์ชันคือ 3) การระบุเซ็นเซอร์เหล่านี้ตามผู้ให้บริการและหมายเลขเวอร์ชันเป็นสิ่งจำเป็น เนื่องจากระบบ Android ถือว่าเซ็นเซอร์ทั้ง 3 ตัวนี้เป็นเซ็นเซอร์รอง ตัวอย่างเช่น หากผู้ผลิตอุปกรณ์มีเซ็นเซอร์แรงโน้มถ่วงของตนเอง เซ็นเซอร์แรงโน้มถ่วง AOSP จะแสดงเป็นเซ็นเซอร์แรงโน้มถ่วงรอง เซ็นเซอร์ทั้ง 3 ตัวนี้อาศัยเครื่องวัดการหมุน หากอุปกรณ์ไม่มีเครื่องวัดการหมุน เซ็นเซอร์เหล่านี้จะไม่ปรากฏและไม่พร้อมใช้งาน

ใช้เซ็นเซอร์แรงโน้มถ่วง

เซ็นเซอร์แรงโน้มถ่วงจะแสดงเวกเตอร์ 3 มิติที่ระบุทิศทางและขนาดของแรงโน้มถ่วง โดยปกติแล้ว เซ็นเซอร์นี้จะใช้เพื่อระบุการวางแนวสัมพัทธ์ของอุปกรณ์ในพื้นที่ว่าง โค้ดต่อไปนี้แสดงวิธีรับอินสแตนซ์ของเซ็นเซอร์แรงโน้มถ่วงเริ่มต้น

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);

หน่วยจะเหมือนกับหน่วยที่เซ็นเซอร์ตรวจวัดความเร่งใช้ (m/s2) และระบบพิกัดจะเหมือนกับระบบที่เซ็นเซอร์ตรวจวัดความเร่งใช้

หมายเหตุ: เมื่ออุปกรณ์หยุดนิ่ง เอาต์พุตของเซ็นเซอร์แรงโน้มถ่วงควรเหมือนกับเอาต์พุตของตัวตรวจวัดความเร่ง

ใช้ตัวตรวจวัดความเร่งเชิงเส้น

เซ็นเซอร์ความเร่งเชิงเส้นจะให้เวกเตอร์ 3 มิติที่แสดงถึงความเร่งตามแกนของอุปกรณ์แต่ละแกน โดยไม่รวมแรงโน้มถ่วง คุณสามารถใช้ค่านี้เพื่อตรวจจับท่าทางสัมผัสได้ นอกจากนี้ ค่าดังกล่าวยังใช้เป็นอินพุตสำหรับระบบนำทางด้วยแรงเฉื่อยซึ่งใช้การคำนวณตำแหน่งจากความเร็วและทิศทางได้ด้วย โค้ดต่อไปนี้แสดงวิธีรับอินสแตนซ์ของเซ็นเซอร์ความเร่งเชิงเส้นเริ่มต้น

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);

ในทางแนวคิด เซ็นเซอร์นี้จะให้ข้อมูลการเร่งความเร็วตามความสัมพันธ์ต่อไปนี้

linear acceleration = acceleration - acceleration due to gravity

โดยปกติแล้ว คุณจะใช้เซ็นเซอร์นี้เมื่อต้องการรับข้อมูลการเร่งความเร็วโดยไม่มีอิทธิพลของแรงโน้มถ่วง เช่น คุณสามารถใช้เซ็นเซอร์นี้เพื่อดูความเร็วของรถได้ เซ็นเซอร์ความเร่งเชิงเส้นจะมีออฟเซ็ตเสมอ ซึ่งคุณต้องนำออก วิธีที่ง่ายที่สุดในการดำเนินการนี้คือ สร้างขั้นตอนการปรับเทียบลงในแอปพลิเคชัน ในระหว่างการปรับเทียบ คุณสามารถขอให้ผู้ใช้ตั้งค่าอุปกรณ์บนโต๊ะ แล้วอ่านค่าออฟเซ็ตของทั้ง 3 แกน จากนั้นคุณสามารถลบค่าออฟเซ็ตนั้นออกจากการอ่านค่าโดยตรงของเซ็นเซอร์ตรวจวัดความเร่งเพื่อให้ได้ความเร่งเชิงเส้นจริง

ระบบพิกัดของเซ็นเซอร์จะเหมือนกับที่เซ็นเซอร์ความเร่งใช้ เช่นเดียวกับหน่วยวัด (ม./วินาที2)

ใช้เซ็นเซอร์เวกเตอร์การหมุน

เวกเตอร์การหมุนแสดงการวางแนวของอุปกรณ์เป็นมุมและแกนรวมกัน โดยอุปกรณ์จะหมุนผ่านมุม θ รอบแกน (x, y หรือ z) โค้ดต่อไปนี้แสดงวิธีรับอินสแตนซ์ของเซ็นเซอร์เวกเตอร์การหมุนเริ่มต้น

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);

องค์ประกอบ 3 อย่างของเวกเตอร์การหมุนจะแสดงดังนี้

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

โดยที่ขนาดของเวกเตอร์การหมุนเท่ากับ sin(θ/2) และทิศทางของเวกเตอร์การหมุนเท่ากับทิศทางของแกนการหมุน

รูปที่ 1 ระบบพิกัดที่ใช้โดยเซ็นเซอร์เวกเตอร์การหมุน

องค์ประกอบ 3 อย่างของเวกเตอร์การหมุนจะเท่ากับองค์ประกอบ 3 อย่างสุดท้ายของควอเทอร์เนียนหน่วย (cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)) องค์ประกอบของเวกเตอร์การหมุนไม่มีหน่วย แกน x, y และ z จะได้รับการกำหนดในลักษณะเดียวกับเซ็นเซอร์ตรวจวัดความเร่ง ระบบพิกัดอ้างอิงจะกำหนดเป็นฐานออร์โธนอร์มัลโดยตรง (ดูรูปที่ 1) ระบบพิกัดนี้ มีลักษณะดังต่อไปนี้

  • X หมายถึงผลคูณเชิงเวกเตอร์ Y x Z โดยจะสัมผัสกับพื้น ณ ตำแหน่งปัจจุบันของอุปกรณ์และชี้ไปทางทิศตะวันออกโดยประมาณ
  • Y เป็นเส้นสัมผัสกับพื้นดินที่ตำแหน่งปัจจุบันของอุปกรณ์และชี้ไปยังขั้วแม่เหล็กโลกเหนือ
  • Z ชี้ไปทางท้องฟ้าและตั้งฉากกับระนาบพื้น

ดูตัวอย่างแอปพลิเคชันที่แสดงวิธีใช้เซ็นเซอร์เวกเตอร์การหมุนได้ที่ RotationVectorDemo.java

ใช้เซ็นเซอร์ตรวจจับการเคลื่อนไหวที่มีนัยสำคัญ

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

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);

ดูข้อมูลเพิ่มเติมได้ที่ TriggerEventListener

ใช้เซ็นเซอร์ตัวนับก้าว

เซ็นเซอร์ตัวนับก้าวจะแสดงจำนวนก้าวที่ผู้ใช้เดินนับตั้งแต่การรีบูตครั้งล่าสุดขณะที่เซ็นเซอร์เปิดใช้งานอยู่ เครื่องนับก้าวมีความหน่วงมากกว่า (สูงสุด 10 วินาที) แต่มีความแม่นยำมากกว่าเซ็นเซอร์ตรวจจับก้าว

หมายเหตุ: คุณต้องประกาศสิทธิ์ ACTIVITY_RECOGNITION เพื่อให้แอปใช้เซ็นเซอร์นี้ในอุปกรณ์ที่ใช้ Android 10 (ระดับ API 29) ขึ้นไปได้

โค้ดต่อไปนี้แสดงวิธีรับอินสแตนซ์ของเซ็นเซอร์ตัวนับจำนวนก้าวเริ่มต้น

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);

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

ใช้เซ็นเซอร์ตรวจจับก้าว

เซ็นเซอร์ตรวจจับการก้าวจะทริกเกอร์เหตุการณ์ทุกครั้งที่ผู้ใช้ก้าวเท้า คาดว่าเวลาในการตอบสนองจะต่ำกว่า 2 วินาที

หมายเหตุ: คุณต้องประกาศสิทธิ์ ACTIVITY_RECOGNITION เพื่อให้แอปใช้เซ็นเซอร์นี้ในอุปกรณ์ที่ใช้ Android 10 (ระดับ API 29) ขึ้นไปได้

โค้ดต่อไปนี้แสดงวิธีรับอินสแตนซ์ของเซ็นเซอร์ตรวจจับการก้าวเริ่มต้น

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);

ทำงานกับข้อมูลดิบ

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

ใช้ตัวตรวจวัดความเร่ง

เซ็นเซอร์ตรวจวัดความเร่งจะวัดความเร่งที่ใช้กับอุปกรณ์ รวมถึงแรงโน้มถ่วง โค้ดต่อไปนี้แสดงวิธีรับอินสแตนซ์ของเซ็นเซอร์ความเร่งเริ่มต้น

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);

หมายเหตุ: หากแอปกำหนดเป้าหมายเป็น Android 12 (ระดับ API 31) ขึ้นไป เซ็นเซอร์นี้จะถูกจำกัดอัตรา

ในทางแนวคิด เซ็นเซอร์ตรวจวัดความเร่งจะระบุความเร่งที่ใช้กับอุปกรณ์ (Ad) โดยการวัดแรงที่ใช้กับตัวเซ็นเซอร์เอง (Fs) โดยใช้ความสัมพันธ์ต่อไปนี้

A_D=-(1/mass)∑F_S

อย่างไรก็ตาม แรงโน้มถ่วงจะส่งผลต่อความเร่งที่วัดได้เสมอตามความสัมพันธ์ต่อไปนี้

A_D=-g-(1/mass)∑F_S

ด้วยเหตุนี้ เมื่ออุปกรณ์วางอยู่บนโต๊ะ (และไม่ได้เร่งความเร็ว) ตัวตรวจวัดความเร่งจะอ่านค่าขนาด g = 9.81 ม./วิ.2 ในทำนองเดียวกัน เมื่ออุปกรณ์ตกอย่างอิสระและเร่งความเร็วอย่างรวดเร็วสู่พื้นดินที่ 9.81 ม./วินาที2 ตัวตรวจวัดความเร่งจะอ่านค่าขนาด g = 0 ม./วินาที2 ดังนั้น หากต้องการวัดความเร่งที่แท้จริงของอุปกรณ์ คุณต้องนำผลกระทบจากแรงโน้มถ่วงออกจากข้อมูลตัวตรวจวัดความเร่ง ซึ่งทำได้โดยใช้ตัวกรองความถี่สูง ในทางกลับกัน ตัวกรองความถี่ต่ำผ่านสามารถใช้เพื่อแยกแรงโน้มถ่วงได้ ตัวอย่างต่อไปนี้แสดงวิธีดำเนินการ

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];
}

หมายเหตุ: คุณใช้เทคนิคต่างๆ มากมายเพื่อกรองข้อมูลเซ็นเซอร์ได้ ตัวอย่างโค้ดด้านบนใช้ค่าคงที่ตัวกรองแบบง่าย (อัลฟา) เพื่อสร้างตัวกรองความถี่ต่ำ ค่าคงที่ของตัวกรองนี้ได้มาจากค่าคงที่ของเวลา (t) ซึ่งเป็นการแสดงเวลาในการตอบสนองโดยคร่าวๆ ที่ตัวกรองเพิ่มลงในเหตุการณ์ของเซ็นเซอร์ และอัตราการส่งเหตุการณ์ของเซ็นเซอร์ (dt) ตัวอย่างโค้ดใช้ค่าอัลฟ่าเป็น 0.8 เพื่อวัตถุประสงค์ในการสาธิต หากใช้วิธีการกรองนี้ คุณอาจต้องเลือกค่าอัลฟาอื่น

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

  • หากคุณดันอุปกรณ์ทางด้านซ้าย (เพื่อให้อุปกรณ์เคลื่อนที่ไปทางขวา) ค่าความเร่ง x จะเป็นบวก
  • หากคุณดันอุปกรณ์ที่ด้านล่าง (เพื่อให้อุปกรณ์เคลื่อนออกจากตัวคุณ) ค่าความเร่ง y จะเป็นบวก
  • หากคุณดันอุปกรณ์ขึ้นไปบนท้องฟ้าด้วยความเร่ง A ม./วิ.2 ค่าความเร่งในแกน Z จะเท่ากับ A + 9.81 ซึ่งสอดคล้องกับความเร่งของอุปกรณ์ (+A ม./วิ.2) ลบด้วยแรงโน้มถ่วง (-9.81 ม./วิ.2)
  • อุปกรณ์ที่อยู่กับที่จะมีค่าความเร่ง +9.81 ซึ่งสอดคล้องกับความเร่งของอุปกรณ์ (0 ม./วินาที2 ลบด้วยแรงโน้มถ่วงซึ่งเท่ากับ -9.81 ม./วินาที2)

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

ใช้เครื่องวัดการหมุน

เครื่องวัดการหมุนจะวัดอัตราการหมุนเป็นหน่วยเรเดียน/วินาทีรอบแกน x, y และ z ของอุปกรณ์ โค้ดต่อไปนี้แสดงวิธีรับอินสแตนซ์ของเครื่องวัดการหมุนเริ่มต้น

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);

หมายเหตุ: หากแอปกำหนดเป้าหมายเป็น Android 12 (ระดับ API 31) ขึ้นไป เซ็นเซอร์นี้จะถูกจำกัดอัตรา

ระบบพิกัดของเซ็นเซอร์จะเหมือนกับระบบที่ใช้สำหรับเซ็นเซอร์ตรวจวัดความเร่ง การหมุนเป็นบวกในทิศทางทวนเข็มนาฬิกา กล่าวคือ ผู้สังเกตการณ์ที่มองจากตำแหน่งที่เป็นบวกบนแกน x, y หรือ z ที่อุปกรณ์ซึ่งอยู่ที่จุดเริ่มต้นจะรายงานการหมุนเป็นบวกหากอุปกรณ์ดูเหมือนจะหมุนทวนเข็มนาฬิกา นี่คือคำจำกัดความทางคณิตศาสตร์มาตรฐานของการหมุนเชิงบวก และไม่เหมือนกับคำจำกัดความของการหมุนที่เซ็นเซอร์การวางแนวใช้

โดยปกติแล้ว เอาต์พุตของเครื่องวัดการหมุนจะรวมเข้าด้วยกันเมื่อเวลาผ่านไปเพื่อคำนวณการหมุนที่อธิบายการเปลี่ยนแปลงของมุมในช่วงเวลาหนึ่งๆ เช่น

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;
}

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

ใช้เครื่องวัดการหมุนที่ไม่ได้ปรับเทียบ

เครื่องวัดการหมุนที่ไม่ได้ปรับเทียบจะคล้ายกับเครื่องวัดการหมุน ยกเว้นว่าจะไม่มีการชดเชยการเบี่ยงเบนของเครื่องวัดการหมุนกับอัตราการหมุน การปรับเทียบจากโรงงานและการชดเชยอุณหภูมิจะยังคงมีผลกับอัตราการหมุน เครื่องวัดการหมุนที่ไม่ได้ปรับเทียบมีประโยชน์สำหรับการประมวลผลภายหลังและการรวมข้อมูลการวางแนว โดยทั่วไปแล้ว gyroscope_event.values[0] จะใกล้เคียงกับ uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3] ซึ่งหมายความว่า

calibrated_x ~= uncalibrated_x - bias_estimate_x

หมายเหตุ: เซ็นเซอร์ที่ไม่ได้ปรับเทียบจะให้ผลลัพธ์ดิบมากกว่าและอาจมีอคติบางอย่าง แต่การวัดจะมีค่าที่กระโดดน้อยกว่าจากการแก้ไขที่ใช้ผ่านการปรับเทียบ แอปพลิเคชันบางรายการอาจต้องการผลลัพธ์ที่ไม่ได้ปรับเทียบเหล่านี้เนื่องจากมีความราบรื่นและเชื่อถือได้มากกว่า เช่น หากแอปพลิเคชันพยายามทำการรวมเซ็นเซอร์ของตัวเอง การปรับเทียบอาจทำให้ผลลัพธ์บิดเบือนได้

นอกจากอัตราการหมุนแล้ว เครื่องวัดการหมุนที่ไม่ได้ปรับเทียบยังแสดงค่าดริฟต์โดยประมาณรอบแกนแต่ละแกนด้วย โค้ดต่อไปนี้แสดงวิธีรับอินสแตนซ์ของเครื่องวัดการหมุนที่ไม่ได้ปรับเทียบเริ่มต้น

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);

ตัวอย่างโค้ดเพิ่มเติม

ตัวอย่าง BatchStepSensor จะสาธิตการใช้ API ที่ครอบคลุมในหน้านี้เพิ่มเติม

คุณควรอ่านบทความต่อไปนี้ด้วย