Accelerometer and magnetometer (Bosch BNO055)


ouzle

Very Active Member
Joined
Jun 3, 2019
Messages
127
Location
England
I started playing with the accelerometer and magnetometer today. I've got a text only bubble-level working that could be the basis of a graphical bubble level. We can reset the horizontal (or any base surface) with another script. I'll share the scripts first and then describe how I got the useful numbers from the sensors.

The pyra has a Bosch BNO055 chip. I don't know how to change settings on the sensor chip from user space yet so I'm just processing values from /sys/bus/iio/devices/iio\:device1/ here.

To use the bash scripts, unzip the attachment to your home directory. You can then execute the script from a terminal with:
Bash:
cd ~/bno055
watch -n0.2 ./pyra-bno055
./pyra-bubble-reset
watch -n0.2 ./pyra-bubble

The first script shows how to find the BNO055 driver output in the /sys folder and how to read and scale all the values. The units look to be set to [m/s^2], [rad/s], and micro tesla by default, but this can be configured on the chip.

Bash:
me@pyra:~/bno055$ ./pyra-bno055
Acceleration -0.15 -0.18 -9.88
Rotation     0.01 -0.02 -0.01
Magnetometer 121.34 15.33 122.83

Bash:
#!/usr/bin/bash
# Find the SYSFS path the bno055 sensor chip
SYSNAME=
for d in "/sys/bus/iio/devices/iio*/name" ; do
    SYSNAME=$(grep -l bno055 $d)
    if [ -f $SYSNAME ]; then break; fi
done

if [ ! -f $SYSNAME ] ; then
    echo "bno055 sensor chip not found in /sys/bus/iio/devices"
    exit 1
fi
SYS=$(dirname $SYSNAME)

# Read the acceleration
ascale=$(cat $SYS/in_accel_scale)
ax0=$(cat $SYS/in_accel_x_offset)
ay0=$(cat $SYS/in_accel_y_offset)
az0=$(cat $SYS/in_accel_z_offset)
axr=$(cat $SYS/in_accel_x_raw)
ayr=$(cat $SYS/in_accel_y_raw)
azr=$(cat $SYS/in_accel_z_raw)
accel_x=$(awk "BEGIN{ printf(\"%.2f\",$axr*$ascale+$ax0) }")
accel_y=$(awk "BEGIN{ printf(\"%.2f\",$ayr*$ascale+$ay0) }")
accel_z=$(awk "BEGIN{ printf(\"%.2f\",$azr*$ascale+$az0) }")

# Read the angular velocity
avscale=$(cat $SYS/in_anglvel_scale)
avx0=$(cat $SYS/in_anglvel_x_offset)
avy0=$(cat $SYS/in_anglvel_y_offset)
avz0=$(cat $SYS/in_anglvel_z_offset)
avxr=$(cat $SYS/in_anglvel_x_raw)
avyr=$(cat $SYS/in_anglvel_y_raw)
avzr=$(cat $SYS/in_anglvel_z_raw)
avx=$(awk "BEGIN{ printf(\"%.2f\",$avxr*$avscale+$avx0) }")
avy=$(awk "BEGIN{ printf(\"%.2f\",$avyr*$avscale+$avy0) }")
avz=$(awk "BEGIN{ printf(\"%.2f\",$avzr*$avscale+$avz0) }")

# Read the magnetometer
mscale=$(cat $SYS/in_magn_scale)
mx0=$(cat $SYS/in_magn_x_offset)
my0=$(cat $SYS/in_magn_y_offset)
mz0=$(cat $SYS/in_magn_z_offset)
mxr=$(cat $SYS/in_magn_x_raw)
myr=$(cat $SYS/in_magn_y_raw)
mzr=$(cat $SYS/in_magn_z_raw)
mx=$(awk "BEGIN{ printf(\"%.2f\",$mxr*$mscale+$mx0) }")
my=$(awk "BEGIN{ printf(\"%.2f\",$myr*$mscale+$my0) }")
mz=$(awk "BEGIN{ printf(\"%.2f\",$mzr*$mscale+$mz0) }")

echo "Acceleration $accel_x $accel_y $accel_z"
echo "Rotation     $avx $avy $avz"
echo "Magnetometer $mx $my $mz"

The next script reports two numbers that are between -1.0 and +1.0. These are like the X, Y coordinate of a bubble in a bubble level. When the bubble is level, they are both zero.
Bash:
#!/usr/bin/bash

# A bubble level using the pyra's bno055 accelerometer
# Call pyra-bubble-reset to reset the bubble offset
MATRIX="${HOME}/.bno055-bubble"

# Find the SYSFS path the ino055 sensor chip
SYSNAME=
for d in "/sys/bus/iio/devices/iio*/name" ; do
    SYSNAME=$(grep -l bno055 $d)
    if [ -f $SYSNAME ]; then break; fi
done

if [ ! -f $SYSNAME ] ; then
    echo "bno055 sensor chip not found in /sys/bus/iio/devices"
    exit 1
fi
SYS=$(dirname $SYSNAME)

# Read the acceleration
ascale=$(cat $SYS/in_accel_scale)
ax0=$(cat $SYS/in_accel_x_offset)
ay0=$(cat $SYS/in_accel_y_offset)
az0=$(cat $SYS/in_accel_z_offset)
axr=$(cat $SYS/in_accel_x_raw)
ayr=$(cat $SYS/in_accel_y_raw)
azr=$(cat $SYS/in_accel_z_raw)
a[0]=$(awk "BEGIN{ printf(\"%.2f\",$axr*$ascale+$ax0) }")
a[1]=$(awk "BEGIN{ printf(\"%.2f\",$ayr*$ascale+$ay0) }")
a[2]=$(awk "BEGIN{ printf(\"%.2f\",$azr*$ascale+$az0) }")

# Read the rotation matrix, or use the default
R[00]=1 ; R[01]=0 ; R[02]=0
R[10]=0 ; R[11]=1 ; R[12]=0
R[20]=0 ; R[21]=0 ; R[22]=1

if [ -f ${MATRIX} ] ; then
    row=0
    while IFS= read -r line; do
        if [ $row -eq 0 ]; then
            R[00]=$(echo $line | cut -d ' ' -f 1)
            R[01]=$(echo $line | cut -d ' ' -f 2)
            R[02]=$(echo $line | cut -d ' ' -f 3)
        elif [ $row -eq 1 ]; then
            R[10]=$(echo $line | cut -d ' ' -f 1)
            R[11]=$(echo $line | cut -d ' ' -f 2)
            R[12]=$(echo $line | cut -d ' ' -f 3)
        elif [ $row -eq 2 ]; then
            R[20]=$(echo $line | cut -d ' ' -f 1)
            R[21]=$(echo $line | cut -d ' ' -f 2)
            R[22]=$(echo $line | cut -d ' ' -f 3)
        fi
        row=$((row+1))
    done < ${MATRIX}
fi
#echo ${R[00]} ${R[01]} ${R[02]}
#echo ${R[10]} ${R[11]} ${R[12]}
#echo ${R[20]} ${R[21]} ${R[22]}

# Rotate the acceleration vector
a[0]=$(awk "BEGIN{ printf(\"%.2f\",${a[0]}*${R[00]} + ${a[1]}*${R[01]} + ${a[2]}*${R[02]} ) }")
a[1]=$(awk "BEGIN{ printf(\"%.2f\",${a[0]}*${R[10]} + ${a[1]}*${R[11]} + ${a[2]}*${R[12]} ) }")
a[2]=$(awk "BEGIN{ printf(\"%.2f\",${a[0]}*${R[20]} + ${a[1]}*${R[21]} + ${a[2]}*${R[22]} ) }")

# Usage: normalize v[@]
# Result stored in normal[0], normal[1], normal[2]
function normalize
{
    # Declare first parameter as indexed array
    declare -a v1=("${!1}")

    mag=$(awk "BEGIN{ printf(\"%f\", sqrt(${v1[0]}*${v1[0]} + ${v1[1]}*${v1[1]} + ${v1[2]}*${v1[2]}) ) }")
    normal[0]=$(awk "BEGIN{ printf(\"%.2f\", ${v1[0]}/$mag ) }")
    normal[1]=$(awk "BEGIN{ printf(\"%.2f\", ${v1[1]}/$mag ) }")
    normal[2]=$(awk "BEGIN{ printf(\"%.2f\", ${v1[2]}/$mag ) }")
}

normalize a[@]
echo ${normal[0]} ${normal[1]}

The last script is used to reset the bubble level to horizontal or vertical as you like. Put the pyra on a reference surface then run the script.
Bash:
#!/usr/bin/bash

# Measures the direction of down and creates a rotation matrix
# that can be used to correct the down direction later
MATRIX="${HOME}/.bno055-bubble"

# Find the SYSFS path the ino055 sensor chip
SYSNAME=
for d in "/sys/bus/iio/devices/iio*/name" ; do
    SYSNAME=$(grep -l bno055 $d)
    if [ -f $SYSNAME ]; then break; fi
done

if [ ! -f $SYSNAME ] ; then
    echo "bno055 sensor chip not found in /sys/bus/iio/devices"
    exit 1
fi
SYS=$(dirname $SYSNAME)

# Read the acceleration
ascale=$(cat $SYS/in_accel_scale)
ax0=$(cat $SYS/in_accel_x_offset)
ay0=$(cat $SYS/in_accel_y_offset)
az0=$(cat $SYS/in_accel_z_offset)
axr=$(cat $SYS/in_accel_x_raw)
ayr=$(cat $SYS/in_accel_y_raw)
azr=$(cat $SYS/in_accel_z_raw)
a[0]=$(awk "BEGIN{ printf(\"%.2f\",$axr*$ascale+$ax0) }")
a[1]=$(awk "BEGIN{ printf(\"%.2f\",$ayr*$ascale+$ay0) }")
a[2]=$(awk "BEGIN{ printf(\"%.2f\",$azr*$ascale+$az0) }")

echo "Acceleration ${a[0]} ${a[1]} ${a[2]}"

# Usage: normalize v[@]
# Result stored in normal[0], normal[1], normal[2]
function normalize
{
    # Declare first parameter as indexed array
    declare -a v1=("${!1}")

    mag=$(awk "BEGIN{ printf(\"%f\", sqrt(${v1[0]}*${v1[0]} + ${v1[1]}*${v1[1]} + ${v1[2]}*${v1[2]}) ) }")
    normal[0]=$(awk "BEGIN{ printf(\"%f\", ${v1[0]}/$mag ) }")
    normal[1]=$(awk "BEGIN{ printf(\"%f\", ${v1[1]}/$mag ) }")
    normal[2]=$(awk "BEGIN{ printf(\"%f\", ${v1[2]}/$mag ) }")
}

# Usage: normalize v1[@] v2[@]
# Result stored in dot
function dotProduct
{
    declare -a v1=("${!1}")
    declare -a v2=("${!2}")

    dot=$(awk "BEGIN{ printf(\"%f\", ${v1[0]}*${v2[0]} + ${v1[1]}*${v2[1]} + ${v1[2]}*${v2[2]}) }")
}

# Usage: normalize v1[@] v2[@]
# Result stored in cross[0], cross[1], cross[2]
function crossProduct
{
    declare -a v1=("${!1}")
    declare -a v2=("${!2}")

    cross[0]=$(awk "BEGIN{ a=${v1[1]}*${v2[2]}; b=${v1[2]}*${v2[1]}; printf(\"%f\", a - b) }")
    cross[1]=$(awk "BEGIN{ a=${v1[0]}*${v2[2]}; b=${v1[2]}*${v2[0]}; printf(\"%f\", b - a) }")
    cross[2]=$(awk "BEGIN{ a=${v1[0]}*${v2[1]}; b=${v1[1]}*${v2[0]}; printf(\"%f\", a - b) }")
}

function rotateAlign
{
    k=$(awk "BEGIN{ printf(\"%f\", 1.0 / ( 1.0 + ${dot} ) )}")
    R[00]=$(awk "BEGIN{ printf(\"%f\", ${cross[0]} * ${cross[0]} * ${k} + ${dot}      )}")
    R[01]=$(awk "BEGIN{ printf(\"%f\", ${cross[0]} * ${cross[1]} * ${k} + ${cross[2]} )}")
    R[02]=$(awk "BEGIN{ printf(\"%f\", ${cross[0]} * ${cross[2]} * ${k} - ${cross[1]} )}")
    R[10]=$(awk "BEGIN{ printf(\"%f\", ${cross[1]} * ${cross[0]} * ${k} - ${cross[2]} )}")
    R[11]=$(awk "BEGIN{ printf(\"%f\", ${cross[1]} * ${cross[1]} * ${k} + ${dot}      )}")
    R[12]=$(awk "BEGIN{ printf(\"%f\", ${cross[1]} * ${cross[2]} * ${k} + ${cross[0]} )}")
    R[20]=$(awk "BEGIN{ printf(\"%f\", ${cross[2]} * ${cross[0]} * ${k} + ${cross[1]} )}")
    R[21]=$(awk "BEGIN{ printf(\"%f\", ${cross[2]} * ${cross[1]} * ${k} - ${cross[0]} )}")
    R[22]=$(awk "BEGIN{ printf(\"%f\", ${cross[2]} * ${cross[2]} * ${k} + ${dot}      )}")
}

# Calculate the rotation matrix needed to make the vector a point upwards
up[0]=0
up[1]=0
up[2]=-1

normalize a[@]
dotProduct up[@] normal[@]
crossProduct up[@] normal[@]
rotateAlign

#echo "magnitude: $mag"
#echo "normal: ${normal[0]} ${normal[1]} ${normal[2]}"
#echo "dot: $dot"
#echo "cross: ${cross[0]} ${cross[1]} ${cross[2]}"

echo
echo Saving matrix to ${MATRIX}
echo ${R[00]} ${R[01]} ${R[02]} > ${MATRIX}
echo ${R[10]} ${R[11]} ${R[12]} >> ${MATRIX}
echo ${R[20]} ${R[21]} ${R[22]} >> ${MATRIX}
cat ${MATRIX}
 

Attachments

  • pyra-bno055.zip
    6.4 KB · Views: 20
Last edited:

ouzle

Very Active Member
Joined
Jun 3, 2019
Messages
127
Location
England
Here are two scripts to turn the pyra into a compass. This was more difficult because of the calibration calculations. I get good result as long as I put the pyra down while it is giving a compass reading.

The scripts are attached to the opening post or you can read them here.

Python:
#!/usr/bin/env python3

# Collects readings from the magnetometer while the user rotates the pyra.
# The magnetometer vectors are on the surface of a sphere.
# The centre of the sphere is the compass offset and the radius is the strength of earths magnetic field.
# The least squares method is used to fit a sphere to a sample of vectors.
# The radius and origin of the sphere is saved to a configuration file.

import numpy as np
import os
import sys
import time
import math
import subprocess

bno055cmd="./pyra-bno055"
bno055compass="~/.bno055-compass"

#    fit a sphere to X,Y, and Z data points
#    returns the radius and center points of
#    the best fit sphere
def sphereFit(spX,spY,spZ):
    #   Assemble the A matrix
    spX = np.array(spX)
    spY = np.array(spY)
    spZ = np.array(spZ)
    A = np.zeros((len(spX),4))
    A[:,0] = spX*2
    A[:,1] = spY*2
    A[:,2] = spZ*2
    A[:,3] = 1

    #   Assemble the f matrix
    f = np.zeros((len(spX),1))
    f[:,0] = (spX*spX) + (spY*spY) + (spZ*spZ)
    C, residules, rank, singval = np.linalg.lstsq(A,f,rcond=None)

    #   solve for the radius
    t = (C[0]*C[0])+(C[1]*C[1])+(C[2]*C[2])+C[3]
    radius = math.sqrt(t)

    return radius, C[0][0], C[1][0], C[2][0]

def main():
    print("Compass calibration")
    print("   Rotate the pyra in a complex motion")
    print("   Including figure of 8s and full rotations.")
    input("Press any key to start...")


    # Sample the magnetometer
    samples_x=[]
    samples_y=[]
    samples_z=[]
  
    start_time = time.time()
    duration=10
    remaining=duration
    while remaining>0: 
        remaining=duration-(time.time()-start_time)
        print("{}   ".format(int(remaining)),end="\r")

        try:
            bno055output = subprocess.check_output(bno055cmd, shell=True)
            lines = bno055output.splitlines()
            for line in lines:
                if b"Magnetometer" in line:
                    col = line.split()
                    samples_x.append(float(col[1].strip()))
                    samples_y.append(float(col[2].strip()))
                    samples_z.append(float(col[3].strip()))

        except:
            print ("Could not parse output from {}".format(bno055cmd))
            sys.exit(1)
  
    print( "Collected {} samples".format(len(samples_x)))
      
    if len(samples_x)<30:
        print ("Not enough magnetometer samples collected")
        sys.exit(1)
      
    # Fit a sphere to the magnetic field vectors
    radius, cx, cy, cz = sphereFit(samples_x,samples_y,samples_z)
    print()
    print('Field strength={:.2f}uT'.format(radius))
    print('Offset={0:.2f},{1:.2f},{2:.2f}'.format(cx,cy,cz))
  
    if radius<2 or radius>20:
        print("Not enough change in the magnetic field to calibrate.")
      
    # Save the result
    path = os.path.expanduser(bno055compass)
    text_file = open(path, "w")
    with open(path, "w") as text_file:
        text_file.write("{} {} {} {}\n".format(radius,cx,cy,cz))

    text_file.close()
if __name__== "__main__":
  main()

Bash:
#!/usr/bin/bash
# A compass using the pyra's bno055 magnetmeter
# Call pyra-compass-reset first to calibrate the compass

COMPASS_CALIBRATION="${HOME}/.bno055-compass"
NUMBER_OF_SAMPLES=10

if ! [ -f ${COMPASS_CALIBRATION} ] ; then
    echo "Call pyra-compass-reset first to calibrate the compass"
    exit 1
fi

# Find the SYSFS path the bno055 sensor chip
SYSNAME=
for d in "/sys/bus/iio/devices/iio*/name" ; do
    SYSNAME=$(grep -l bno055 $d)
    if [ -f $SYSNAME ]; then break; fi
done

if [ ! -f $SYSNAME ] ; then
    echo "bno055 sensor chip not found in /sys/bus/iio/devices"
    exit 1
fi
SYS=$(dirname $SYSNAME)

# Read the accelerometer scale
ascale=$(cat $SYS/in_accel_scale)
ax0=$(cat $SYS/in_accel_x_offset)
ay0=$(cat $SYS/in_accel_y_offset)
az0=$(cat $SYS/in_accel_z_offset)

# Read the magnetometer scale
mscale=$(cat $SYS/in_magn_scale)
mx0=$(cat $SYS/in_magn_x_offset)
my0=$(cat $SYS/in_magn_y_offset)
mz0=$(cat $SYS/in_magn_z_offset)

# Average several readings from the sensors
mxa=0
mya=0
mza=0
axa=0
aya=0
aza=0
for i in $(seq 1 ${NUMBER_OF_SAMPLES}); do
    # Collect the raw readings
    mxr=$(cat $SYS/in_magn_x_raw)
    myr=$(cat $SYS/in_magn_y_raw)
    mzr=$(cat $SYS/in_magn_z_raw) 
    axr=$(cat $SYS/in_accel_x_raw)
    ayr=$(cat $SYS/in_accel_y_raw)
    azr=$(cat $SYS/in_accel_z_raw)
  
    # Scale the readings
    mx=$(awk "BEGIN{ printf(\"%f\",$mxr*$mscale+$mx0) }")
    my=$(awk "BEGIN{ printf(\"%f\",$myr*$mscale+$my0) }")
    mz=$(awk "BEGIN{ printf(\"%f\",$mzr*$mscale+$mz0) }")
    ax=$(awk "BEGIN{ printf(\"%f\",$axr*$ascale+$ax0) }")
    ay=$(awk "BEGIN{ printf(\"%f\",$ayr*$ascale+$ay0) }")
    az=$(awk "BEGIN{ printf(\"%f\",$azr*$ascale+$az0) }")

    # Sum the readings for the average
    mxa=$(awk "BEGIN{ printf(\"%f\",$mxa + $mx) }")
    mya=$(awk "BEGIN{ printf(\"%f\",$mya + $my) }")
    mza=$(awk "BEGIN{ printf(\"%f\",$mza + $mz) }")     
    axa=$(awk "BEGIN{ printf(\"%f\",$axa + $ax) }")
    aya=$(awk "BEGIN{ printf(\"%f\",$aya + $ay) }")
    aza=$(awk "BEGIN{ printf(\"%f\",$aza + $az) }")     

    # Give the sensor chance to update
    #sleep 0.05
done
mx=$(awk "BEGIN{ printf(\"%f\",$mxa/$i) }")
my=$(awk "BEGIN{ printf(\"%f\",$mya/$i) }")
mz=$(awk "BEGIN{ printf(\"%f\",$mza/$i) }")
a[0]=$(awk "BEGIN{ printf(\"%f\",$axa/$i) }")
a[1]=$(awk "BEGIN{ printf(\"%f\",$aya/$i) }")
a[2]=$(awk "BEGIN{ printf(\"%f\",$aza/$i) }")

# Usage: normalize v[@]
# Result stored in normal[0], normal[1], normal[2]
function normalize
{
    # Declare first parameter as indexed array
    declare -a v1=("${!1}")

    mag=$(awk "BEGIN{ printf(\"%f\", sqrt(${v1[0]}*${v1[0]} + ${v1[1]}*${v1[1]} + ${v1[2]}*${v1[2]}) ) }") 
    normal[0]=$(awk "BEGIN{ printf(\"%f\", ${v1[0]}/$mag ) }")
    normal[1]=$(awk "BEGIN{ printf(\"%f\", ${v1[1]}/$mag ) }")
    normal[2]=$(awk "BEGIN{ printf(\"%f\", ${v1[2]}/$mag ) }")
}

# Usage: dotProduct v1[@] v2[@]
# Result stored in dot
function dotProduct
{
    declare -a v1=("${!1}")
    declare -a v2=("${!2}")

    dot=$(awk "BEGIN{ printf(\"%f\", ${v1[0]}*${v2[0]} + ${v1[1]}*${v2[1]} + ${v1[2]}*${v2[2]}) }")
}

# Usage: crossProduct v1[@] v2[@]
# Result stored in cross[0], cross[1], cross[2]
function crossProduct
{
    declare -a v1=("${!1}")
    declare -a v2=("${!2}")

    cross[0]=$(awk "BEGIN{ a=${v1[1]}*${v2[2]}; b=${v1[2]}*${v2[1]}; printf(\"%f\", a - b) }")
    cross[1]=$(awk "BEGIN{ a=${v1[0]}*${v2[2]}; b=${v1[2]}*${v2[0]}; printf(\"%f\", b - a) }")
    cross[2]=$(awk "BEGIN{ a=${v1[0]}*${v2[1]}; b=${v1[1]}*${v2[0]}; printf(\"%f\", a - b) }")
}

function rotateAlign
{     
    k=$(awk "BEGIN{ printf(\"%f\", 1.0 / ( 1.0 + ${dot} ) )}")     
    R[00]=$(awk "BEGIN{ printf(\"%f\", ${cross[0]} * ${cross[0]} * ${k} + ${dot}      )}")
    R[01]=$(awk "BEGIN{ printf(\"%f\", ${cross[0]} * ${cross[1]} * ${k} + ${cross[2]} )}")
    R[02]=$(awk "BEGIN{ printf(\"%f\", ${cross[0]} * ${cross[2]} * ${k} - ${cross[1]} )}")
    R[10]=$(awk "BEGIN{ printf(\"%f\", ${cross[1]} * ${cross[0]} * ${k} - ${cross[2]} )}")
    R[11]=$(awk "BEGIN{ printf(\"%f\", ${cross[1]} * ${cross[1]} * ${k} + ${dot}      )}")   
    R[12]=$(awk "BEGIN{ printf(\"%f\", ${cross[1]} * ${cross[2]} * ${k} + ${cross[0]} )}")
    R[20]=$(awk "BEGIN{ printf(\"%f\", ${cross[2]} * ${cross[0]} * ${k} + ${cross[1]} )}")
    R[21]=$(awk "BEGIN{ printf(\"%f\", ${cross[2]} * ${cross[1]} * ${k} - ${cross[0]} )}")
    R[22]=$(awk "BEGIN{ printf(\"%f\", ${cross[2]} * ${cross[2]} * ${k} + ${dot}      )}")
}

# Calculate the rotation matrix needed to make the acceleration vector a point upwards
up[0]=0
up[1]=0
up[2]=1

normalize a[@]
dotProduct normal[@] up[@]
crossProduct normal[@] up[@]
rotateAlign

# Read the compass calibration
cscale=$(awk -F' ' 'NR=1 {print $1}' ${COMPASS_CALIBRATION})
cx0=$(awk -F' ' 'NR=1 {print $2}' ${COMPASS_CALIBRATION})
cy0=$(awk -F' ' 'NR=1 {print $3}' ${COMPASS_CALIBRATION})
cz0=$(awk -F' ' 'NR=1 {print $4}' ${COMPASS_CALIBRATION})

# Calculate the compass reading with calibration offset removed and scale applied
cx=$(awk -F' ' "BEGIN {printf(\"%f\",(${mx}-${cx0})/${cscale})}")
cy=$(awk -F' ' "BEGIN {printf(\"%f\",(${my}-${cy0})/${cscale})}")
cz=$(awk -F' ' "BEGIN {printf(\"%f\",(${mz}-${cz0})/${cscale})}")

# Rotate the magnetometer reading according to the direction of gravity
rx=$(awk "BEGIN{ printf(\"%.2f\",${cx}*${R[00]} + ${cy}*${R[01]} + ${cz}*${R[02]} ) }")
ry=$(awk "BEGIN{ printf(\"%.2f\",${cx}*${R[10]} + ${cy}*${R[11]} + ${cz}*${R[12]} ) }")
rz=$(awk "BEGIN{ printf(\"%.2f\",${cx}*${R[20]} + ${cy}*${R[21]} + ${cz}*${R[22]} ) }")

deg=$(awk -F' ' "BEGIN {printf(\"%d\", (180.0/3.14159)*atan2(${ry},${rx}) + 90) }")

# Make the angle range 0 to 360 rather than -180 to 180
if (( $deg < 0 )); then deg=$(($deg + 360)); fi

# Map the angle to cardinal direction
c=$(awk "BEGIN{ printf(\"%d\", 16.0*${deg}/360.0 + 0.5) }")
cardinal=("N" "NNE" "NE" "ENE" "E" "ESE" "SE" "SSE" "S" "SSW" "SW" "WSW" "W" "WNW" "NW" "NNW" "N")

# Display the result
echo "${deg}° ${cardinal[c]}"

Bash:
me@pyra:~/code/bno055$ ./pyra-compass-reset
Compass calibration
   Rotate the pyra in a complex motion
   Including figure of 8s and full rotations.
Press any key to start...
Collected 57 samples

Field strength=5.08uT
Offset=119.22,14.46,127.19

me@pyra:~/code/bno055$ ./pyra-compass
55° NE
 
Last edited:

ouzle

Very Active Member
Joined
Jun 3, 2019
Messages
127
Location
England
For me, the compass is not so reliable when charging. So when you're out in the wilds with the pyra running open street maps, don't forget to unplug the battery pack.
 
  • Like
Reactions: rSl

ouzle

Very Active Member
Joined
Jun 3, 2019
Messages
127
Location
England
@aTc What's the right way to get scripts like these into the pyra repo? Should I create a pyra-bno055 debian package? Is it better to have separate pyra-accelerometer and pyra-compass packages? Are these more fundamental scripts that belong in pyra-scripts?
 

aTc

Very Active Member
Joined
Apr 25, 2009
Messages
221
Not quite sure.
A pyra-sensors package would probably the solution. There are quite a few sensors, and they all seem to need various support scripts.
Although if one of them needs some huge dependencies that might make it a better option to have one package for each sensor.

pyra-scripts does need a huge cleanup :)
 

F_Slim

IDDQD
Joined
Aug 25, 2010
Messages
1,415
Age
41
Location
Germany
I once tried to use the compass in my tablet and it also did not work very well. But it made a huge different if you are indoors or outdoors.
Also there is some kind of calibration which can be done by turning the device around all axis. Is this also true for the chip in the Pyra?
 

hns

Well-Known Member
Joined
Dec 4, 2011
Messages
566
Location
Oberhaching
Handling sensors is quite a standard problem for all handheld devices software stacks. First activities go back to the (OpenMoko) GTA02. But AFAIR there is no "standard" besides Android/Replicant (or iOS). There, all sensors are mapped to some high level abstraction Java/Swift/Obj-C classes which can be queried by any application. All specific things like handling calibration and non-linear effects are hidden from the normal programmer...
This may include calibration matrices, taking magnetic fields by the internal speakers into account and potentially if the device is charged or discharged or a headset or USB device is connected or not.
And, there is a discipline called "sensor fusion" which uses Kalman filters to merge accelerometer/gyroscope data with GPS for more precise attitude reports to applications.
It would be nice if such a "libsensor.so" could be used with a plugin-architecture where specific devices and chips can add their specifics.
Maybe it exists and I am just not aware of it...
 

Kazuki

Kazuki Suzuki Przyborowski · Number 139
Joined
Jul 21, 2010
Messages
25
Age
33
Location
Chicago, Illinois, USA
Website
www.instagram.com
WEBSITE
https://idb.osdn.jp/support/index.php?act=view
LOCATION
Chicago, Illinois, USA
FACEBOOK
kazuki.przyborowski
TWITTER
kazuki2k
YOUTUBE
KazukiPrzyborowski
I once tried to use the compass in my tablet and it also did not work very well. But it made a huge different if you are indoors or outdoors.
Also there is some kind of calibration which can be done by turning the device around all axis. Is this also true for the chip in the Pyra?
Yes you have to tilt and move your device in a figure eight Like this.
All my phones I ever had have always had a bad compass so I had to calibrate it everytime I opened Google maps.
 
Top