本文仅用于学习。
论文标题:<<Computation Offloading in UAV-Enabled Edge Computing: A Stackelberg Game Approach>>
期刊:sensors
一、论文梳理
问题:在用户向边缘服务器进行任务卸载时,时延、卸载决策和传输能耗是很大的问题,如何提高系统的整体性能非常重要。
模型:为了解决上述问题,该论文引入了UAV进行辅助任务卸载,基站(Base Station, BS)雇佣无人机(Unmanned aerial vehicles, UAV)做任务,UAV既可以作为中继器也可以作为服务器,在BS、UAV和User之间建立三阶段斯塔克伯格博弈,最大化自身的效用。
目标:最大化BS的收益,最大化UAV的收益,最小化User的成本
算法:改论文的算法为博弈算法(Stackelberg game algorithm,SGA),实验的基准算法包含随机算法(RANDOM,用户决定卸载的比例)和本地卸载(LOCAL, 所有用户都在本都执行)算法。
实验:论文中图3表示了BS和UAV的迭代至收敛的过程。图4显示了用户的最优卸载策略。图5显示了三种算法的系统的总利润。图6显示了不同用户数下的系统总利润。
二、代码
2.1、距离计算
计算用户到UAV和BS,UAV到BS之间的距离。
def get_distance(uav, user, bs):
user_uav = np.zeros((user_num, uav_num)) # 8 * 6
uav_bs = np.zeros((uav_num, bs_num)) # 6 * 2
user_bs = np.zeros((user_num, bs_num)) # 8 * 2
# 欧氏距离
for i in range(0, user_num):
for j in range(0, uav_num):
# np.linalg.norm(x)为计算点之间的距离,默认为二范数
user_uav[i, j] = np.linalg.norm(user[i, :] - uav[j, :])
for k in range(0, bs_num):
user_bs[i, k] = np.linalg.norm(user[i, :] - bs[k, :])
for s in range(0, uav_num):
for r in range(0, bs_num):
uav_bs[s, r] = np.linalg.norm(uav[s, :] - bs[r, :])
return user_uav, uav_bs, user_bs
2.2、效用计算
2.2.1、BS效用
def utility_base_station(M_i, M_j, F_i, bs_now):
P_g = 0 # 收益
C_g = 0 # 成本
# 判断当前BS覆盖的用户
for i in range(0, user_num):
tmp1 = M_i[i] * F_i[i] # BS单位资源出价 * 卸载数据大小
# BS雇佣UAV的支付成本
tmp2 = M_j[selected_uav_relay[i]] * offload_relay[i] * D[i] # 给无人机的出价 * 卸载比例 * 任务数据量
P_g = P_g + tmp1 * (bs_now == selected_bs[i])
C_g = C_g + tmp2 * relay_ok[bs_now, selected_uav_relay[i]]
utility_bs = P_g - C_g
return utility_bs
2.2.2、UAV效用
def utility_uav(m_i, f_i, Rate_j_g_i, M_j, uav_now):
fai = 15 * D # 每单位数据的出价
cpi_cost = 8 # 每单位数据的计算成本
p_c_j, p_t_j, c_c_j, c_t_j = 0, 0, 0, 0
for i in range(0, user_num):
# 作为服务器收益
tmp1 = m_i[i] * f_i[i]
# 作为中继节点收益
tmp2 = M_j[selected_bs[i]] * offload_relay[i] * D[i]
# 作为服务器的计算成本
tmp3 = offload_uav[i] * fai[i] * cpi_cost / (f_i[i] + 1)
# 作为中继节点计算成本
tmp4 = power_uav[uav_now] * offload_relay[i] * D[i] / Rate_j_g_i[selected_bs[i]]
p_c_j = p_c_j + tmp1 * (uav_now == selected_uav[i])
p_t_j = p_t_j + tmp2 * relay_ok[selected_bs[i], uav_now]
c_c_j = c_c_j + tmp3 * (uav_now == selected_uav[i])
c_t_j = c_t_j + tmp4 * relay_ok[selected_bs[i], uav_now]
utility = p_c_j + p_t_j - c_c_j - c_t_j
return utility
2.2.3、用户效用
def utility_user(M_i, m_i, F_i, f_i, Rate_i_g, Rate_i_j, Rate_j_g_i, user_now):
fai = 15 * D # 任务所需的计算资源
cpi_cost = 8 # 计算任务所需的算力
alpha_local_trans, alpha_loc_e = 0.5, 0.5 # 本地计算,时延+能耗
alpha_g_trans, alpha_g_e, alpha_g_exe = 0.3, 0.3, 0.4 # 卸载到BS,时延+能耗+成本(0.3,0.3,0.4)
alpha_j_trans, alpha_j_e, alpha_j_exe = 0.3, 0.3, 0.4 # 卸载到UAV,时延+能耗+成本
alpha_u_trans, alpha_u_e, alpha_u_exe = 0.3, 0.3, 0.4 # uav为中继器,传输+传输能耗
min_bs, min_uav, min_relay = float('inf'), float('inf'), float('inf')
for b in range(0, bs_num):
# 用户卸载到成本最小的BS:
Cost_bs = alpha_g_trans * (D[user_now] / Rate_i_g[user_now, b] +fai[user_now] / (F_i[b] + 1)) + \
alpha_g_e * power_user[user_now] * D[user_now] / Rate_i_g[user_now, b] + alpha_g_exe * M_i[b] * F_i[b]
if Cost_bs < min_bs and F_i[b]:
min_bs = Cost_bs
selected_bs[user_now] = b
for j in range(0, uav_num):
# 用户卸载到成本最小的UAV
Cost_uav = alpha_j_trans * (D[user_now] / Rate_i_j[user_now, j] + fai[user_now] / (f_i[j] + 1)) + \
alpha_j_e * D[user_now] / Rate_i_j[user_now, j] * power_user[user_now] + alpha_j_exe * m_i[j] * f_i[j]
Cost_relay = alpha_u_trans * (D[user_now] / Rate_i_j[user_now, j] + D[user_now] / Rate_j_g_i[j, selected_bs[user_now]]) + \
alpha_u_e * power_user[user_now] * D[user_now] / Rate_i_j[user_now, j]
if Cost_uav < min_uav:
min_uav = Cost_uav
selected_uav[user_now] = j
if Cost_relay < min_relay:
min_relay = Cost_relay
selected_uav_relay[user_now] = j
local = alpha_local_trans * fai[user_now] / resource_user[user_now] + alpha_loc_e * cpi_cost * fai[user_now] / resource_user[user_now]
to_bs = alpha_g_trans * (D[user_now] / Rate_i_g[user_now, selected_bs[user_now]] + fai[user_now] / (F_i[selected_bs[user_now]] + 1)) + \
alpha_g_e * power_user[user_now] * D[user_now] / Rate_i_g[user_now, selected_bs[user_now]] + \
alpha_g_exe * M_i[selected_bs[user_now]] * F_i[selected_bs[user_now]]
toUAV = alpha_j_trans * (D[user_now] / Rate_i_j[user_now, selected_uav[user_now]] + fai[user_now] / (f_i[selected_uav[user_now]] + 1)) +\
alpha_j_e * D[user_now] / Rate_i_j[user_now, selected_uav[user_now]] * power_user[user_now] + \
alpha_j_exe * m_i[selected_uav[user_now]] * f_i[selected_uav[user_now]]
relay = alpha_u_trans * (D[user_now] / Rate_i_j[user_now, selected_uav_relay[user_now]] + fai[user_now] / (F_i[selected_bs[user_now]] + 1) +\
D[user_now] / Rate_j_g_i[selected_uav_relay[user_now], selected_bs[user_now]]) + \
alpha_u_e * power_user[user_now] * D[user_now] / Rate_i_j[user_now, selected_uav_relay[user_now]] + \
alpha_u_exe * M_i[selected_bs[user_now]] * F_i[selected_bs[user_now]]
return local, to_bs, toUAV, relay
2.3、卸载比例分配
def offload_allocate(at_local, to_bs, to_uav, by_relay, D_i, user_now):
loc, bs, uav, relay = 0, 0, 0, 0
# 获取到最小成本位置的索引
index = np.argmin([at_local, to_bs, to_uav, by_relay]) + 1
if index == 1: # 本地计算
if resource_user[user_now] < D_i[user_now]: # 本地计算资源 < 用户生成的数据
loc = resource_user[user_now]/D[user_now] # 本地计算的比例
D_i[user_now] = D_i[user_now] - resource_user[user_now] # 计算卸载的比例
# 将本地无法计算的任务进行卸载
_, bs, uav, relay = offload_allocate(float('inf'), to_bs, to_uav, by_relay, D_i, user_now)
else:
loc, bs, uav, relay = D_i[user_now]/D[user_now], 0, 0, 0 # 如果计算资源足够,则不用卸载
if index == 2:
loc, bs, uav, relay = 0, D_i[user_now]/D[user_now], 0, 0 # 卸载到BS
if index == 3:
loc, bs, uav, relay = 0, 0, D_i[user_now]/D[user_now], 0 # 卸载到UAV
if index == 4:
loc, bs, uav, relay = 0, 0, 0, D_i[user_now]/D[user_now] # UAV作为中继
return loc, bs, uav, relay
2.4博弈函数
# ======================斯塔克博弈========================================
def stackelberg_game(bs, uav, user, epoch):
# 数据的传输速度受约束条件限制
for b in range(0, bs_num):
for i in range(0, user_num):
Rate_i_g[i, b] = 1000 / dis_user_bs[i, b] # 用户传输到BS的速率
for j in range(0, uav_num):
Rate_j_g_i[j, b] = 1000 / dis_uav_bs[j, b] # uav到BS的速率
for i in range(0, user_num):
for j in range(0, uav_num):
Rate_i_j[i, j] = 1000 / dis_user_uav[i, j] # 用户到BS的传输速率
# 用户关联到BS和UAV
for u in range(0, user_num):
# print(dis_user_bs[u, :])
# print()
selected_bs[u] = np.argmin(dis_user_bs[u, :]) # 用户卸载任务到最近的BS
selected_uav[u] = np.argmin(dis_user_uav[u, :]) # 用户卸载到uav
selected_uav_relay[u] = selected_uav[u] # uav充当中继器
# Game Iteration
for episode in range(0, epoch):
now = episode
# Game of Leader layer: Base station
for b in range(0, bs_num):
for i in range(0, user_num):
# bs分配资源给用户i D:用户i生成的任务量,部分卸载
F_i[b, i] = (offload_bs[i] + offload_relay[i]) * D[i]
if not converge[i]: # 判断当前用户的效用是否收敛
if b == selected_bs[i] and (offload_bs[i] + offload_relay[i] > 0):
M_i_low[b, i] = M_i[b, i]
if M_i_low[b, i] > M_i_high[b,i]:
M_i_low[b, i] = M_i_high[b, i]
M_i[b, i] = 4 * M_i[b, i]
if M_i[b, i] >= 50 / Rate_i_g[i,b]:
M_i[b, i] = 50 / Rate_i_g[i, b]
else:
M_i_high[b, i] = M_i[b, i]
M_i[b, i] = (M_i_high[b, i] + M_i_low[b, i]) / 2
Record[i, b, now] = M_i[b, i]
# print(Record[i, b, now])
for j in range(0, uav_num):
if not converge[j]:
if relay_ok[b, j] == 1:
if M_j[b, j] < M_j_high[b, j]:
M_j_high[b, j] = M_j[b, j]
M_j[b, j] = (M_j_high[b, j] + M_j_low[b, j]) / 2
else:
M_j_low[b, j] = M_j[b, j]
M_j[b, j] = M_j[b, j] * 4
if M_j_low[b, j] > M_j_high[b, j]:
temp = M_j_low[b, j]
M_j_low[b, j] = M_j_high[b, j]
M_j_high[b, j] = temp
if M_j[b, j] >= 10 / Rate_j_g_i[j, b]:
M_j[b, j] = 10 / Rate_j_g_i[j, b]
bs_income[episode, b] = utility_base_station(M_i[b, :], M_j[b, :], F_i[b, :], b)
# Game of Vice-leader Layer : UAVs
for u in range(0, uav_num):
for i in range(0, user_num):
f_i[u, i] = offload_uav[i] * D[i]
if 0 != converge[i]:
if u == selected_uav[i] and offload_uav[i] > 0:
m_i_low[u, i] = m_i[u, i]
if m_i_low[u, i] > m_i_high[u, i]:
m_i_low[u, i] = m_i_high[u, i]
m_i[u, i] = 4 * m_i[u, i]
else:
m_i_high[u, i] = m_i[u, i]
m_i[u, i] = (m_i_high[u, i] + m_i_low[u, i]) / 2
if m_i[u,i] >= 50 / Rate_i_j[i,u]:
m_i[u, i] = 50 / Rate_i_j[i, u]
Record_2[i, u, now] = m_i[u, i]
uav_income[episode, u] = utility_uav(m_i[u,:], f_i[u,:], Rate_j_g_i[u,:], M_j[:, u], u)
# Game of follower layer: Users
for i in range(0, user_num):
# 卸载后的 时延和能耗成本权值之和
at_local[i], to_bs[i], to_uav[i], by_relay[i] = utility_user(M_i[:,i],m_i[:,i],F_i[:,i],f_i[:,i], Rate_i_g, Rate_i_j, Rate_j_g_i,i)
# 卸载到效用最小的那一个:0,1,0,0类似格式
local[i], offload_bs[i], offload_uav[i], offload_relay[i] = offload_allocate(at_local[i],to_bs[i],to_uav[i],by_relay[i],D,i)
record_1[i, now], record_2[i, now], record_3[i, now], record_4[i, now] = local[i],offload_bs[i],offload_uav[i],offload_relay[i]
if offload_relay[i] != 0: # uav作为中继
relay_ok[selected_bs[i], selected_uav_relay[i]] = 1
else:
relay_ok[selected_bs[i], selected_uav_relay[i]] = 0
user_outcome[episode, i] = local[i] * at_local[i] + offload_bs[i] * to_bs[i] + offload_uav[i] * to_uav[i] + offload_relay[i] * by_relay[i]
if episode > 50: # 是否收敛
cha_1 = user_outcome[episode, i] - user_outcome[episode - 49, i]
cha_2 = user_outcome[episode, i] - user_outcome[episode - 29, i]
rate_cha_1 = abs(cha_1) / user_outcome[episode, i]
rate_cha_2 = abs(cha_2) / user_outcome[episode, i]
if rate_cha_1 <= 0.05 and rate_cha_2 <= 0.05:
converge[i] = 1
return bs_income, uav_income, user_outcome
2.5 主函数
import numpy as np
import matplotlib.pyplot as plt
epoch = 50
bs_num = 2
uav_num = 6
user_num = 8
resource_bs = 65536 # 基站的总资源
resource_uav = [8096, 8096, 8096, 8096, 8096, 8096] # 每架无人机的计算资源
power_uav = [5, 5, 5, 5, 5, 5]
resource_user = [32, 32, 32, 32, 32, 32, 32, 32] # 每个用户本地本地具有的计算资源
power_user = [8, 8, 8, 8, 8, 8, 8, 8]
D = [843, 616, 543, 463, 408, 616, 543, 424] # 随机生成每个用户任务量
user = np.array([[633, 958], [98, 486], [859, 801], [547, 142], [576, 650], [60, 732], [235, 648], [354, 451]]) # 用户坐标
uav = np.array([[250, 250], [250, 750], [500, 250], [500, 750], [750, 250], [750, 750]]) # 无人机坐标
bs = np.array([[250, 500], [750, 500]]) # 基站坐标
M_i = np.ones((bs_num, user_num)) # price from BS to user
M_j = np.ones((bs_num, uav_num)) # price from BS to uav
F_i = np.zeros((bs_num, user_num)) # the resource allocate from BS to user i
m_i = np.ones((uav_num, user_num)) # price from uav to user
f_i = np.zeros((uav_num, user_num)) # the resource allocate from uav to user i
# bs_income, uav_income, user_outcome = np.zeros(bs_num), np.zeros(uav_num), np.zeros(user_num)
# 卸载比例初始化
local, offload_bs, offload_uav, offload_relay = 0.25 * np.ones(user_num), 0.25 * np.ones(user_num), 0.25 * np.ones(user_num), 0.25 * np.ones(user_num)
at_local, to_bs, to_uav, by_relay = np.zeros(user_num), np.zeros(user_num), np.zeros(user_num), np.zeros(user_num)
Rate_i_g, Rate_i_j, Rate_j_g_i = np.ones((user_num, bs_num)), np.ones((user_num, uav_num)), np.ones((uav_num, bs_num))
relay_ok = np.zeros((bs_num, uav_num), dtype=np.int64) # whether the uav is a relay
selected_uav, selected_bs, selected_uav_relay = np.zeros(user_num, dtype=np.int64), np.zeros(user_num,
dtype=np.int64), np.zeros(user_num, dtype=np.int64)
dis_user_uav, dis_uav_bs, dis_user_bs = get_distance(uav, user, bs)
# game methods
M_i_low, M_i_high = np.ones((bs_num, user_num)), 1.5*np.ones((bs_num, user_num))
M_j_low, M_j_high = np.ones((bs_num, uav_num)), 1.5*np.ones((bs_num, uav_num))
m_i_low, m_i_high = np.ones((uav_num, user_num)), 1.5*np.ones((uav_num, user_num))
bs_income, uav_income, user_outcome = np.zeros((epoch, bs_num)), np.zeros((epoch, uav_num)), np.zeros((epoch, user_num))
record_1, record_2, record_3, record_4 = np.zeros((user_num, epoch)), np.zeros((user_num, epoch)), np.zeros((user_num, epoch)), np.zeros((user_num, epoch))
Record, Record_2 = np.zeros((user_num, bs_num, epoch)), np.zeros((user_num, uav_num, epoch))
converge = np.zeros(user_num)
Stackelberg_bs, Stackelberg_uav, Stackelberg_user = stackelberg_game(bs, uav, user, epoch)
print(Stackelberg_user[:, 1])
sum_stackelberg = Stackelberg_bs.sum(axis=1) + Stackelberg_uav.sum(axis=1) - Stackelberg_user.sum(axis=1)
# plot
plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.3, hspace=0.5)
plt.subplot(221)
x = [i for i in range(len(Stackelberg_user))]
y1 = Stackelberg_bs[:, 0]
y2 = Stackelberg_bs[:, 1]
plt.title('Iterative Convergence Process of Base Station')
plt.xlabel('iterations')
plt.ylabel('The Profit of Base Station')
plt.plot(x, y1, color='r', label='BS1', marker='d')
plt.plot(x, y2, color='g', label='BS2', marker='o')
plt.legend()
plt.subplot(222)
v1 = Stackelberg_uav[:, 0]
v2 = Stackelberg_uav[:, 1]
v3 = Stackelberg_uav[:, 2]
v4 = Stackelberg_uav[:, 3]
v5 = Stackelberg_uav[:, 4]
v6 = Stackelberg_uav[:, 5]
plt.title('Iterative Convergence Process of UAV')
plt.xlabel('iterations')
plt.ylabel('The Profit of UAV')
plt.plot(x, v1, color='b', label='v1', marker='d')
plt.plot(x, v2, color='g', label='v2', marker='o')
plt.plot(x, v6, color='r', label='v6', marker='<')
plt.plot(x, v3, color='k', label='v3', marker='*')
plt.plot(x, v4, color='c', label='v4', marker='x')
plt.plot(x, v5, color='m', label='v5', marker='>')
plt.legend()
plt.subplot(223)
u1 = Stackelberg_user[:, 0]
u2 = Stackelberg_user[:, 1]
u3 = Stackelberg_user[:, 2]
u4 = Stackelberg_user[:, 3]
u5 = Stackelberg_user[:, 4]
u6 = Stackelberg_user[:, 5]
u7 = Stackelberg_user[:, 6]
u8 = Stackelberg_user[:, 7]
plt.title('Iterative Convergence Process of User')
plt.xlabel('iterations')
plt.ylabel('The Cost of User')
plt.plot(x, u1, color='b', label='u1', marker='d')
plt.plot(x, u2, color='g', label='u2', marker='o')
plt.plot(x, u6, color='r', label='u6', marker='<')
plt.plot(x, u3, color='k', label='u3', marker='*')
plt.plot(x, u4, color='c', label='u4', marker='x')
plt.plot(x, u5, color='m', label='u5', marker='>')
plt.plot(x, u7, color='y', label='u7', marker='v')
plt.plot(x, u8, color='aquamarine', label='u8', marker='^')
plt.legend()
plt.subplot(224)
sum1 = sum_stackelberg
sum2 = 12503.479930011801
plt.title('Iterative Convergence Process of Total Profit')
plt.xlabel('iterations')
plt.ylabel('Profit Totally')
plt.plot(x, sum1, color='r', label='SGA', marker='d')
plt.plot(x, sum2*np.ones(epoch), color='g', label='random', marker='o')
plt.legend()
plt.show()