10
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?