日本无码免费高清在线|成人日本在线观看高清|A级片免费视频操逼欧美|全裸美女搞黄色大片网站|免费成人a片视频|久久无码福利成人激情久久|国产视频一二国产在线v|av女主播在线观看|五月激情影音先锋|亚洲一区天堂av

  • 手機站
  • 小程序

    汽車測試網(wǎng)

  • 公眾號
    • 汽車測試網(wǎng)

    • 在線課堂

    • 電車測試

論文復現(xiàn)——基于預測的自動駕駛?cè)驅(qū)Ш叫l(wèi)星系統(tǒng)欺騙攻擊檢測

2021-03-09 17:46:58·  來源:軒轅實驗室  作者:軒轅實驗室  
 
本文將介紹如何利用已有的數(shù)據(jù)集訓練lstm網(wǎng)絡實現(xiàn)多變量時序預測模型,以此來檢測GNSS欺騙攻擊。我們使用了來自 Comma.ai 的真實數(shù)據(jù)集,名為 Comma2k19,其中包含各種自動駕駛車輛傳感器數(shù)據(jù)。該數(shù)據(jù)集的下載和處理過程請參考上篇文章Comma2k19數(shù)據(jù)集使用。
本文將介紹如何利用已有的數(shù)據(jù)集訓練lstm網(wǎng)絡實現(xiàn)多變量時序預測模型,以此來檢測GNSS欺騙攻擊。我們使用了來自 Comma.ai 的真實數(shù)據(jù)集,名為 Comma2k19,其中包含各種自動駕駛車輛傳感器數(shù)據(jù)。該數(shù)據(jù)集的下載和處理過程請參考上篇文章Comma2k19數(shù)據(jù)集使用。
 
*本文來自本實驗室涂俊的研究成果和學習筆記
 
1.數(shù)據(jù)準備
Comma.ai 使用的視聽設(shè)備有一個前置攝像頭、溫度計和9軸慣性測量單元。除了這些傳感器數(shù)據(jù),Comma2k19 數(shù)據(jù)集還包含來自全球?qū)Ш叫l(wèi)星系統(tǒng)(GNSS)和控制區(qū)域網(wǎng)絡(CAN)的測量值(見表 1 和表 2)。數(shù)據(jù)收集使用了可跟蹤全球?qū)Ш叫l(wèi)星系統(tǒng)的 u-blox M8 全球?qū)Ш叫l(wèi)星系統(tǒng)模塊,水平位置精度為 2.5 米。位置測量使用了全球定位系統(tǒng)(GPS)和全球軌道導航衛(wèi)星系統(tǒng)(GLONASS)信號。此外,使用開源的GNSS處理庫 Laika 來減少定位誤差,定位誤差降低了 40%。
在這里我們選擇部分數(shù)據(jù)用于接下來的模型中:
  • GNSS數(shù)據(jù)集包含來自 u-blox 和 Qcom 的實時和原始導航衛(wèi)星系統(tǒng)數(shù)據(jù)。每個實時數(shù)據(jù)包括緯度、經(jīng)度、速度、utc 時間戳、高度和方位角數(shù)據(jù)。但它們都是未被Laika優(yōu)化的數(shù)據(jù),為了得到更好的效果,我們采用 global_pose文件夾中的 frame_position、 frame_gps_times和 frame_velocities的數(shù)據(jù),后續(xù)可看情況加入 frame_orientations數(shù)據(jù)。綜合起來用于訓練的GNSS數(shù)據(jù)有時間,經(jīng)緯度(高度可不考慮)和速度,如下表所示: 其中 global_pose\frame_position中的坐標是ECEF的(x, y, z),須將其轉(zhuǎn)化為GPS常用的經(jīng)緯度坐標(wgs845),Python代碼實現(xiàn)如下:
import pyproj

transformer = pyproj.Transformer.from_crs(
    {"proj":'geocent', "ellps":'WGS84', "datum":'WGS84'},
    {"proj":'latlong', "ellps":'WGS84', "datum":'WGS84'},
    )
lon, lat, alt = transformer.transform(x,y,z,radians=False)
print (lat1, lon1, alt1 )

要注意返回的經(jīng)緯度順序是Longitude在前,與常規(guī)有所區(qū)別。

  • 相關(guān)的 CAN數(shù)據(jù)是CAN時間,車速和方向盤轉(zhuǎn)角數(shù)據(jù)(見表2),可分別從 processed_log/CAN/speed/t、 processed_log/CAN/speed/value和 processed_log/CAN/steering_angle/value讀取, 樣例如下表所示。 
  • 同樣,相關(guān)的IMU數(shù)據(jù)是三個方向的加速度,可分別從processed_log/IMU/accelerometer/t和processed_log/IMU/accelerometer/value中讀取,樣例如下表所示。
時間處理
值得注意的是,用于分析的數(shù)據(jù)集包含7200個GNSS觀測值、35726個CAN觀測值和72148個IMU觀測值,GNSS、CAN和IMU數(shù)據(jù)的頻率分別為10、50和100赫茲。這意味著不同數(shù)據(jù)源的時間是有略微不同的,在本項目中GNSS時間被用作參考時間,所有其他傳感器數(shù)據(jù)被同步,以便為LSTM模型準備訓練和測試數(shù)據(jù)集。為了在作為參考時間的準確時間獲得CAN和IMU的數(shù)據(jù),在GNSS的兩個最近觀測值之間要對CAN和IMU進行插值,在這里我采用scipy的樣條插值方法,Python代碼實現(xiàn)如下:
from scipy.interpolate import make_interp_spline

CAN_time = np.load(example_segment + 'processed_log/CAN/speed/t')
gps_time = np.load(example_segment + 'global_pose/frame_times')
# 對CAN的速度進行插值
CAN_speed = np.load(example_segment + 'processed_log/CAN/speed/value')
new_CAN_speed = make_interp_spline(CAN_time, CAN_speed)(gps_time)

plt.figure(figsize=(12, 12))
    plt.plot(CAN_time, CAN_speed, label='CAN')
    plt.plot(gps_time, new_CAN_speed, label='new_CAN')
    plt.legend(fontsize=25)
    plt.xlabel('boot time (s)', fontsize=18)
    plt.ylabel('speed (m/s)', fontsize=18)
    plt.show()

當處理時間數(shù)據(jù)的時候我發(fā)現(xiàn)它們存在一些小瑕疵——有重復值,而且即使是同樣來自CAN的數(shù)據(jù),speed的時間和steering_angle的時間也是不一致的,這意味著它們要分別讀取,分別插值。Python代碼實現(xiàn): # 數(shù)組去重
def unique(old_list):
    newList = []
    # 判斷相鄰時間是否相等
    if np.any(old_list[1:] == old_list[:-1]):
        for x in old_list:
            if x in newList:
                # 若相等,則加上一個微小的數(shù)使其不等
                x = x + 0.005
            newList.append(x)
        return np.array(newList)
    else: return old_list
    
temp_CAN_times = np.load(main_dir + '\\processed_log\\CAN\\speed\\t')
# 確保時間無重復值
temp_CAN_speed_times = unique(temp_CAN_times)
# CAN_angles_times和CAN_speed_times有時不一致
temp_CAN_angles_times = np.load(main_dir + '\\processed_log\\CAN\\steering_angle\\t')
temp_CAN_angles_times = unique(temp_CAN_angles_times)
temp_IMU_times = np.load(main_dir + '\\processed_log\\IMU\\accelerometer\\t')

距離計算

當我們預測自動駕駛車輛當前位置和最近未來位置之間的行駛距離時,從上一個時間步長開始的每個時間步長中的行駛距離是使用緯度和經(jīng)度坐標以及以下哈弗辛大圓公式計算的:Python實現(xiàn)如下: import math
EARTH_REDIUS = 6378.137

def rad(d):
    return d * math.pi / 180.0
def getDistance(lats1, lngs1, lats2, lngs2):
    # 對數(shù)組取元素做運算
    res = []
    for i in range(len(lat1)):
        radLat1 = rad(lat1[i])
        radLat2 = rad(lat2[i])
        a = radLat1 - radLat2
        b = rad(lng1[i]) - rad(lng2[i])
        s = 2 * math.asin(math.sqrt(math.pow(math.sin(a / 2), 2) + math.cos(radLat1) * math.cos(radLat2) * math.pow(
            math.sin(b / 2), 2)))
        s = s * EARTH_REDIUS * 1000
        res.append(s)
    return res
    
# 計算距離    
distance = getDistance(lats1, lngs1, lats2, lngs2)
plt.plot(times[:-1], distance, label='distance')
plt.title('Traveled distance between two consecutive timestamps', fontsize=20);
plt.legend(fontsize=20);
plt.xlabel('boot time (s)', fontsize=18);
plt.ylabel('distance(m) ', fontsize=18);
plt.show()

在這里插入圖片描述

與速度圖相比較,可以發(fā)現(xiàn)形狀相似,說明計算無誤。對一個segment的distance繪圖沒有問題,但如果對一個route的多個segment一個繪圖就會發(fā)現(xiàn)存在異常值,如下圖所示:可以看到異常值使得曲線非常不光滑藍色框中的數(shù)據(jù)和周邊數(shù)據(jù)明顯不同,對于這個問題我咨詢comma.ai得到的解釋是:每個segment的數(shù)據(jù)是分別優(yōu)化的,并不保證segment之間坐標的連續(xù)性,所以每跨一個段時(60s),就會出現(xiàn)這樣的異常值。由于這些異常值占比極小,可忽略不計。

合并數(shù)據(jù)集
對比GNSS的速度與CAN的速度我們會發(fā)現(xiàn)存在偏差,考慮到CAN速度會比GNSS速度更能反映實際情況,故只采用CAN的速度,我們使用LSTM模型來預測每個時間戳的當前位置和最近的未來位置之間的距離,使用無攻擊的CAN、IMU和GNSS數(shù)據(jù)。在本文中,我們使用的LSTM模型由一個輸入層、一個具有50個神經(jīng)元的遞歸隱藏層和一個輸出層組成。訓練LSTM模型的輸入數(shù)據(jù)包括來自控制器局域網(wǎng)的速度和轉(zhuǎn)向角數(shù)據(jù)以及來自慣性測量單元的前向加速度數(shù)據(jù)。輸出是每個時間戳當前位置和最近未來位置之間的距離。在訓練數(shù)據(jù)規(guī)模上我們有考量,每個chunk之間甚至同一個chunk內(nèi)不同route的數(shù)據(jù)都是不相關(guān)的,他們之間的不連續(xù)性使得它們沒法反映真實情況,所幸同一個route之間的segments是基本連續(xù)的,我們將訓練范圍縮小至一個route文件夾的數(shù)據(jù),如果用其他場景豐富且時間連續(xù)的數(shù)據(jù)集訓練效果會更好,讀取數(shù)據(jù)的代碼實現(xiàn)如下:
dataset_directory = 'D:\comma2k19'
    chunk_set = []
    for chunk in os.listdir(dataset_directory):
        # 忽略生成的csv文件
        if ".csv" in chunk:
            continue
        # 如果序號為單個時在前補零,以便后面排序
        if len(chunk) == 7:
            used_name = chunk
            chunk = str_insert(chunk,6,'0')
            os.rename(os.path.join(dataset_directory, used_name), os.path.join(dataset_directory, chunk))
        chunk_set.append(os.path.join(dataset_directory, chunk))
    # 將序號小的片段放在前面
    chunk_set.sort()
    # 選一個chunk來訓練(200分鐘)
    chunk_index = 0
    route_set = []
    for route_id in os.listdir(chunk_set[chunk_index]):
        # 忽略生成的csv文件
        if ".csv" in route_id:
            continue
        route_set.append(os.path.join(chunk_set[chunk_index], route_id))
    segment_set = []
    # 選一個路段訓練
    route_index = 9
    for segment in os.listdir(route_set[route_index]):
        # 如果序號為單個時在前補零,以便后面排序
        if len(segment) == 1:
            used_name = segment
            segment = '0'+segment
            os.rename(os.path.join(route_set[route_index], used_name),os.path.join(route_set[route_index], segment))
        segment_set.append(os.path.join(route_set[route_index], segment))
    # 將序號小的片段放在前面
    segment_set.sort()
    times = []
    lons = []
    lats = []
    orientations = []
    CAN_speeds = []
    steering_angles = []
    acceleration_forward = []
    for main_dir in segment_set:
        # 導入GNSS的時間和位置(pose)并將位置轉(zhuǎn)化為經(jīng)緯度
        temp_GNSS_time = np.load(main_dir + '\\global_pose\\frame_times')
        times = np.append(times, temp_GNSS_time)
        # 打印每一段的長度
        print(len(temp_GNSS_time))
        positions = np.load(main_dir + '\\global_pose\\frame_positions')
        positions = position_transformer.transform(positions[:, 0], positions[:, 1], positions[:, 2], radians=False)
        lats = np.append(lats, positions[1])
        lons = np.append(lons, positions[0])
        temp_CAN_times = np.load(main_dir + '\\processed_log\\CAN\\speed\\t')
        # 確保時間無重復值
        temp_CAN_speed_times = unique(temp_CAN_times)
        # 對CAN數(shù)據(jù)按照GNSS參考時間插值
        temp_CAN_speeds = make_interp_spline(temp_CAN_speed_times, np.load(main_dir + '\\processed_log\\CAN\\speed\\value'))(temp_GNSS_time).flatten()
        CAN_speeds = np.append(CAN_speeds, temp_CAN_speeds)
        # CAN_angles_times和CAN_speed_times有時不一致
        temp_CAN_angles_times = np.load(main_dir + '\\processed_log\\CAN\\steering_angle\\t')
        temp_steering_angles = np.load(main_dir + '\\processed_log\\CAN\\steering_angle\\value')
        temp_CAN_angles_times = unique(temp_CAN_angles_times)
        temp_steering_angles = make_interp_spline(temp_CAN_angles_times, temp_steering_angles)(temp_GNSS_time)
        steering_angles = np.append(steering_angles, temp_steering_angles)
        # 對IMU數(shù)據(jù)按照GNSS參考時間插值
        temp_IMU_times = np.load(main_dir + '\\processed_log\\IMU\\accelerometer\\t')
        temp_acceleration_forward = make_interp_spline(temp_IMU_times, np.load(main_dir +
                                '\\processed_log\\IMU\\accelerometer\\value')[:, 0])(temp_GNSS_time)
        acceleration_forward = np.append(acceleration_forward, temp_acceleration_forward)        
轉(zhuǎn)化為監(jiān)督學習問題

該問題本質(zhì)是監(jiān)督學習問題,當前是時刻的速度,轉(zhuǎn)角和前向加速度是feature,下一時刻位置離當前時刻位置的距離將作為標簽,更多有關(guān)時序數(shù)據(jù)預測問題轉(zhuǎn)化為監(jiān)督學習,參考https://blog.csdn.net/qq_28031525/article/details/79046718,Python代碼實現(xiàn)如下: def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
    n_vars = 1 if type(data) is list else data.shape[1]
    df = pd.Dataframe(data)
    column_names = ['lats', 'lons', 'CAN_speeds', 'steering_angles', 'acceleration_forward']
    cols, names = list(), list()
    # input sequence (t-n, ... t-1)
    for i in range(n_in, 0, -1):
        cols.append(df.shift(i))
        names += [('%s(t-%d)' % (j, i)) for j in column_names]
    # forecast sequence (t, t+1, ... t+n)
    for i in range(0, n_out):
        cols.append(df.shift(-i))
        if i == 0:
            names += [('%s(t)' % (j)) for j in column_names]
        else:
            names += [('%s(t+%d)' % (j, i)) for j in column_names]
    # put it all together
    agg = pd.concat(cols, axis=1)
    agg.columns = names
    # drop rows with NaN values
    if dropnan:
        agg.dropna(inplace=True)
    return agg
    
DataSet = list(zip(times, lats, lons, CAN_speeds, steering_angles, acceleration_forward))
column_names = ['times', 'lats', 'lons', 'CAN_speeds', 'steering_angles', 'acceleration_forward']
df = pd.Dataframe(data=DataSet, columns=column_names)
times = df['times'].values
df = df.set_index(['times'], drop=True)
values = df.values.astype('float64')
# 轉(zhuǎn)為監(jiān)督學習問題, 其實就是將下一時刻的特征(distance)作為當前時刻的標簽
reframed = series_to_supervised(values, 1, 1)
# 計算距離
lons_t = reframed['lons(t)'].values
lats_t = reframed['lats(t)'].values
distance = np.array(getDistance(lats[:-1], lons[:-1], lats_t, lons_t))
# drop columns we don't want to predict including(CAN_speed,steering_angel, acceleration_forward)
reframed.drop(reframed.columns[[0, 1, 5, 6, 7, 8, 9]], axis=1, inplace=True)
# 時間和計算的距離添加到數(shù)據(jù)集
reframed['distance'] = distance
reframed['times'] = times[: -1]
# for i in distance:
#     if i > 100:
#         print(i)
plt.plot(times[:-1], distance)
plt.xlabel('Boot time (s)', fontsize=18)
plt.ylabel('Distance travelled during single timestamp (m) ', fontsize=12)
plt.show()
# 將合并的數(shù)據(jù)保存為.csv文件
reframed.to_csv(route_set[route_index]+".csv", index=False, sep=',')

這部分工作可以讓我們在指定的范圍內(nèi)提取我們想要的數(shù)據(jù),生成可供讀寫的.csv文件,可供訓練和測試階段直接利用。

模型訓練和評估
讀取數(shù)據(jù)
首先你需要任意選擇兩個route文件夾的數(shù)據(jù)分別做訓練集合測試集,這可以通過data_prepare.py完成,接下來就是將他們分別讀進來,并做一些處理,其中歸一化可以提升訓練效果和收斂速度,示例代碼如下:
train_CSV_FILE_PATH = 'D:\\comma2k19\\Chunk_01\\b0c9d2329ad1606b_2018-08-02--08-34-47.csv'
test_CSV_FILE_PATH = 'D:\\comma2k19\\Chunk_01\\b0c9d2329ad1606b_2018-08-01--21-13-49.csv'
train_df = pd.read_csv(train_CSV_FILE_PATH)
test_df = pd.read_csv(test_CSV_FILE_PATH)
train_values = train_df.to_numpy()
train_times = train_values[:, -1]
train_distance = train_values[:, -2]
test_values = test_df.to_numpy()
test_times = test_values[:, -1]
test_distance = test_values[:, -2]
# 將輸入特征歸一化
scaler = MinMaxScaler(feature_range=(0, 1))
train_X, train_y = scaler.fit_transform(train_values[:, :-2]), train_distance
test_X, test_y = scaler.fit_transform(test_values[:, :-2]), test_distance
設(shè)計網(wǎng)絡并訓練

我們直接使用keras的lstm模塊來建立網(wǎng)絡模型,請確保你已搭建好相應環(huán)境?,F(xiàn)在可以搭建LSTM模型了。LSTM模型中,隱藏層有50個神經(jīng)元,輸出層1個神經(jīng)元(回歸問題),輸入變量是一個時間步(t-1)的特征,損失函數(shù)采用Mean Absolute Error(MAE),優(yōu)化算法采用Adam。  # 設(shè)計網(wǎng)絡
model = Sequential()
model.add(LSTM(50, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dense(1))
# 設(shè)置學習率等參數(shù)
# adam = optimizers.Adam(lr=0.01, beta_1=0.9, beta_2=0.999, epsilon=1e-08)
model.compile(loss='mae', optimizer='adam')
# fit network
history = model.fit(train_X, train_y, epochs=500, batch_size=1200, validation_data=(test_X, test_y), verbose=2,
                    shuffle=False)
model.save('lstm.model')
# full_X = values[:, :3]
# full_X = full_X.reshape((full_X.shape[0], 1, full_X.shape[1]))
train_yhat = model.predict(train_X)[:, 0]
test_yhat = model.predict(test_X)[:, 0]
rmse = math.sqrt(mean_squared_error(test_yhat, test_y))
print('Test RMSE: %.3f' % rmse)
# plot history
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.legend()
plt.show()

學習率可以通過optimizer來設(shè)置,adam默認為0.01,其他超參數(shù)可參考表4:評估的python代碼如下: import pandas as pd
import math
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
import tensorflow as tf
from sklearn.preprocessing import MinMaxScaler
gpus = tf.config.experimental.list_physical_devices('GPU')
for gpu in gpus:
    tf.config.experimental.set_memory_growth(gpu, True)
def average(seq, total=0.0):
  num = 0
  for item in seq:
    total += item
    num += 1
  return total / num

if __name__ == '__main__':

    CSV_FILE_PATH = 'D:\\comma2k19\\Chunk_03\\99c94dc769b5d96e_2018-05-01--08-13-53.csv'
    df = pd.read_csv(CSV_FILE_PATH)
    values = df.to_numpy()
    times = values[:, -1]
    distance = values[:, -2]
    model = tf.keras.models.load_model('lstm.model')
    test_X = values[:, :3]
    # 因為訓練的時候輸入特征是歸一化的,所以預測的時候也要將輸入特征歸一化
    scaler = MinMaxScaler(feature_range=(0, 1))
    test_X = scaler.fit_transform(test_X)
    test_X = test_X.reshape((test_X.shape[0], 1, test_X.shape[1]))
    # train_len = (int)(0.75 * len(values[:, 0]))
    # train = values[:train_len, :]
    # test = values[train_len:, :]
    test_y = distance
    yhat = model.predict(test_X)[:, 0]
    rmse = math.sqrt(mean_squared_error(yhat, test_y))
    print('Test RMSE: %.3f' % rmse)
    scores = model.evaluate(test_X, test_y)
    rmse = math.sqrt(mean_squared_error(yhat, test_y))
    plt.plot(times, yhat, label='prediction')
    plt.plot(times, distance, label="ground_truth")
    plt.title('Comparison between truth and prediction', fontsize=18)
    plt.xlabel('Boot time (s)', fontsize=18)
    plt.ylabel('Distance travelled during single timestamp (m) ', fontsize=12)
    plt.legend()
    plt.show()
    min = min((distance - yhat), key=abs)
    max = max((distance - yhat), key=abs)
    avr = average(distance-yhat)
    print('Min:%f' % min)
    print('Max:%f' % max)
    print('average:%f' % avr)

使用其他epoch和batch_size訓練

我令訓練集和測試集分別取自不同的route,來看訓練情況是否更好

  • 當batch_size = 50 epoch  = 100時,訓練時loss在epoch = 50左右就不動了 
測試集上的預測效果如下:重新挑選一個route進行預測,可以看出預測效果還是不錯的,誤差絕對值的最大最小和平均值分別如下:
在這里插入圖片描述
  • 由于每一段數(shù)據(jù)基本都由1200個數(shù)據(jù)組成,所以我想令batch_size = 1200,這樣分段訓練,每訓練一個segment再調(diào)整梯度,于是我令 batch_size = 1200 epoch = 300,發(fā)現(xiàn)訓練速度很快,誤差也比較理想。
說明batch_size = 1200  epoch = 300訓練效果更好,用非測試集去做預測評估,大部分的誤差都不大
誤差的最大最小和平均值分別如下:
這可能是訓練集太小導致的,訓練集只有10分鐘,不足以包含所有情況。模型本身很好,但需要尋找更好的數(shù)據(jù)集來進行訓練。
完整代碼已上傳到Github,地址https://github.com/Juntu-hub/Prediction-based-GNSS-Spoofing-Attack-Detection-for-Autonomous-Vehicle.git
分享到:
 
反對 0 舉報 0 收藏 0 評論 0
滬ICP備11026917號-25