LoginSignup
10
8

More than 3 years have passed since last update.

u-blox F9P から相対立体座標を受け取る

Last updated at Posted at 2019-11-13

前回の記事(https://qiita.com/m_take/items/5dfe43f9c145bbec0e21)
では u-blox ZED-F9P(以下F9P)内部でRTK演算を行い、NMEAから相対立体座標へはラズパイ内でしていましたが回りくどいやり方が気になってました。
そこでF9P内部で相対立体座標まで変換し出力されたバイナリデータをpythonを使って解読できたのでその紹介です

参考資料

まずF9Pのプロトコル資料を読みます
https://www.u-blox.com/sites/default/files/u-blox_ZED-F9P_InterfaceDescription_%28UBX-18010854%29.pdf
(こちらはM8Pの資料)
https://www.u-blox.com/sites/default/files/products/documents/u-blox8-M8_ReceiverDescrProtSpec_(UBX-13003221)_Public.pdf

バイナリデータの解読方法についてはこちらを参考にしました(arduino用)
https://github.com/aortner/f9dualheadingarduinomega

関係するところはこのあたり
(page44)

3.2 UBX Frame Structure

(page47)

3.4 UBX Checksum

(page134)

3.15.12 UBX-NAV-RELPOSNED (0x01 0x3C)

baseを原点としたN(北)E(東)D(下)の座標とベクトル(距離、方位)が出力されます

Little-Endian とか Checksum とか説明してあるのでググってください

u-centerの設定でF9Pの出力を
MSG-(01-3C)NAV-RELPOSNED をUSBから出力
RTCMは前回と同じくstr2strを使いF9P-UART2から入力

Pythonで解読

readNED.py
import serial

nowPoint=[0]*8
HEADER = 6

def readPosned():
    ackPacket=[b'\xB5',b'\x62',b'\x01',b'\x3C',b'\x00',b'\x00']
    i = 0
    payloadlength = 6
    with serial.Serial('/dev/ttyACM0', 115200, timeout=1) as ser:
        while i < payloadlength+8: 
            incoming_char = ser.read()            
            if (i < 3) and (incoming_char == ackPacket[i]):
                i += 1
            elif i == 3:
                ackPacket[i]=incoming_char
                i += 1              
            elif i == 4 :
                ackPacket[i]=incoming_char
                i += 1
            elif i == 5 :
                ackPacket[i]=incoming_char        
                payloadlength = int.from_bytes(ackPacket[4]+ackPacket[5], byteorder='little',signed=False) 
                i += 1
            elif (i > 5):
                ackPacket.append(incoming_char)
                i += 1

    if checksum(ackPacket,payloadlength) :
        perseNED(ackPacket)


def checksum(ackPacket,payloadlength ):
    CK_A =0
    CK_B =0
    for i in range(2, payloadlength+6):
        CK_A = CK_A + int.from_bytes(ackPacket[i], byteorder='little',signed=False) 
        CK_B = CK_B +CK_A
        #print(j)
    CK_A &=0xff
    CK_B &=0xff
    if (CK_A ==  int.from_bytes(ackPacket[-2], byteorder='little',signed=False)) and (CK_B ==  int.from_bytes(ackPacket[-1], byteorder='little',signed=False)):
        #print("ACK Received")
        return True
    else :
        print("ACK Checksum Failure:")  
        return False

def perseNED(ackPacket):
    #relPosN
    byteoffset =8 +HEADER
    bytevalue =  ackPacket[byteoffset] 
    for i in range(1,4):
        bytevalue  +=  ackPacket[byteoffset+i] 
    nowPoint[0] = int.from_bytes(bytevalue, byteorder='little',signed=True) 
    nowPoint[0] += (int.from_bytes(ackPacket[32 + HEADER], byteorder='little',signed=True) )/100
    print("N:%0.2f cm" %nowPoint[0]  )
    #relPosE
    byteoffset =12 +HEADER
    bytevalue = ackPacket[byteoffset] 
    for i in range(1,4):
        bytevalue  +=  ackPacket[byteoffset+i] 
    nowPoint[1] = int.from_bytes(bytevalue, byteorder='little',signed=True) 
    nowPoint[1] += (int.from_bytes(ackPacket[33 + HEADER], byteorder='little',signed=True) )/100
    print("E:%0.2f cm" %nowPoint[1]  )
    #relPosD
    byteoffset =16 +HEADER
    bytevalue = ackPacket[byteoffset] 
    for i in range(1,4):
        bytevalue  +=  ackPacket[byteoffset+i] 
    nowPoint[2] = int.from_bytes(bytevalue, byteorder='little',signed=True) 
    nowPoint[2] += (int.from_bytes(ackPacket[33 + HEADER], byteorder='little',signed=True) )/100
    print("D:%0.2f cm" %nowPoint[2]  )
    #Carrier solution status
    flags = int.from_bytes(ackPacket[60 + HEADER], byteorder='little',signed=True) 
    nowPoint[3] =  flags  & (1 << 0) #gnssFixOK 
    nowPoint[4] =  (flags   & (0b11 <<3)) >> 3 #carrSoln0:no carrier 1:float 2:fix
    print("gnssFixOk:%d" %nowPoint[3])
    print("carrSoln:%d" %nowPoint[4])
    #GPS time
    byteoffset =4 +HEADER
    bytevalue = ackPacket[byteoffset] 
    for i in range(1,4):
        bytevalue  +=  ackPacket[byteoffset+i] 
    nowPoint[5] = int.from_bytes(bytevalue, byteorder='little',signed=True) 
    print("iTow:%0.1f" %float(nowPoint[5]/1000))
    #relPosLength
    byteoffset =20 +HEADER
    bytevalue = ackPacket[byteoffset] 
    for i in range(1,4):
        bytevalue  +=  ackPacket[byteoffset+i] 
    nowPoint[6] = int.from_bytes(bytevalue, byteorder='little',signed=False) 
    nowPoint[6] += (int.from_bytes(ackPacket[35 + HEADER], byteorder='little',signed=True) ) /100
    print("length:%0.1f cm" %float(nowPoint[6]))
    #relPosHeading
    byteoffset =24 +HEADER
    bytevalue = ackPacket[byteoffset] 
    for i in range(1,4):
        bytevalue  +=  ackPacket[byteoffset+i] 
    nowPoint[7] = int.from_bytes(bytevalue, byteorder='little',signed=True) 
    print("heading:%f deg" %float(nowPoint[7]/100000))

    return nowPoint
while 1:
    readPosned()

N:-187.40 cm
E:432.11 cm
D:405.27 cm
gnssFixOk:1
carrSoln:1
iTow:302468.0
length:621.4 cm
heading:113.445720
こんな感じで出ました

応用できること

ラズパイZEROやESP32などのマイコンでも簡単な計算で座標が使える(別途RTCM補正信号の入力は必要)
ので、サイクルコンピューターのような走行距離の計算や、面積計算、高度差測定などがcm精度で行える
2台使って(moving base)ジャイロセンサーのようにRoll,Yaw,Pitchが求められる

Github

https://github.com/mnltake/readF9P_UBX
で公開

10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8