Python - Диаграмма направленности излучения антенны
Это код, который я нашел и немного изменил. Как я могу масштабировать цвет от начала координат и установить оси от начала координат для визуализации? Я пытался найти информацию, но большинство из них для 2d участков.
Здесь я добавил два массива для тэты и фи с интервалами 45 градусов и массив случайных чисел, представляющих мощность сигнала. Сюжет работает, но сигналы и интервалы не совсем верны. Моя цель здесь - просто добавить оси от начала координат и масштабировать цвет от начала координат.
import pandas as pd
import numpy as np
import scipy as sci
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as Axes3D
from matplotlib import cm, colors
from array import *
import random
#theta
vals_theta = array('i',[0,0,0,0,0,0,0,0,0,45,45,45,45,45,45,45,45,45,90,90,90,
90,90,90,90,90,90,135,135,135,135,135,135,135,135,135,
180,180,180,180,180,180,180,180,180])
#phi
vals_phi = array('i',[0,45,90,135,180,225,270,315,360,
0,45,90,135,180,225,270,315,360,
0,45,90,135,180,225,270,315,360,
0,45,90,135,180,225,270,315,360,
0,45,90,135,180,225,270,315,360])
#random numbers simulating the power data
vals_power = np.random.uniform(low=-7.2E-21, high=7.2E-21, size=(45,))
theta1d = vals_theta
theta1d = np.array(theta1d);
theta2d = theta1d.reshape([5,9])
phi1d = vals_phi
phi1d = np.array(phi1d);
phi2d = phi1d.reshape([5,9])
power1d = vals_power
power1d = np.array(power1d);
power2d = power1d.reshape([5,9])
THETA = np.deg2rad(theta2d)
PHI = np.deg2rad(phi2d)
R = power2d
Rmax = np.max(R)
X = R * np.sin(THETA) * np.cos(PHI)
Y = R * np.sin(THETA) * np.sin(PHI)
Z = R * np.cos(THETA)
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
ax.grid(True)
ax.axis('on')
ax.set_xticks([])
ax.set_yticks([])
ax.set_zticks([])
N = R / Rmax
ax.plot_surface(
X, Y, Z, rstride=1, cstride=1, cmap=plt.get_cmap('jet'),
linewidth=0, antialiased=False, alpha=0.5, zorder = 0.5)
ax.set_title('Spherical 3D Plot', fontsize=20)
m = cm.ScalarMappable(cmap=cm.jet)
m.set_array(R)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
m = cm.ScalarMappable(cmap=cm.jet)
m.set_array(R)
fig.colorbar(m, shrink=0.8);
ax.view_init(azim=300, elev = 30)
# Add Spherical Grid
phi ,theta = np.linspace(0, 2 * np.pi, 40), np.linspace(0, np.pi, 40)
PHI, THETA = np.meshgrid(phi,theta)
R = Rmax
X = R * np.sin(THETA) * np.cos(PHI)
Y = R * np.sin(THETA) * np.sin(PHI)
Z = R * np.cos(THETA)
ax.plot_wireframe(X, Y, Z, linewidth=0.5, rstride=3, cstride=3)
print(theta1d)
print(theta2d)
print(power2d)
plt.show()
Пытаясь получить результат, приближенный к этому
0 ответов
Это основано на отличном ответе Андреа с парой дополнений, которые должны быть полезны для реальных данных, которые могут иметь довольно большой интервал между точками. Когда я впервые нарисовал что-то с интервалом в 45 градусов, это выглядело так:
Есть две очевидные проблемы:
- Грани очень большие и имеют только один цвет, хотя они охватывают широкий диапазон значений.
- Форма симметрична относительно начала координат, но цвета граней не симметричны.
Проблему 1 можно улучшить, выполнив линейную интерполяцию данных, чтобы каждое лицо было разделено на несколько секций, которые могут иметь разные цвета.
Проблема 2 возникает из-за способа назначения цветов лица. Представьте себе сетку точек 3x3 на двухмерной плоскости, где каждая точка имеет значение. Когда вы рисуете поверхности, будут только грани 2x2, поэтому последняя строка и столбец значений отбрасываются, а цвет каждой грани определяется только одним углом грани. Что нам действительно нужно, так это ценность в центре каждой грани. Мы можем оценить это, взяв среднее значение четырех угловых значений и используя его для назначения цвета.
С точки зрения вычислений это похоже на интерполяцию для проблемы 1, поэтому я использовал одну и ту же функцию "interp_array" для обоих. Я не особо разбираюсь в программировании на Python, поэтому, вероятно, есть более эффективный способ сделать это, но он выполняет свою работу.
Вот график с исправленной ошибкой 2, но без интерполяции. Симметрия фиксированная, но используются только 2 цвета, потому что грани расположены на одинаковом расстоянии от начала координат.
И вот финальный график с 8-кратной интерполяцией между точками. Теперь он ближе к тому типу непрерывного цветного графика, который вы можете увидеть в коммерческом программном обеспечении для измерения антенн.
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as Axes3D
from matplotlib import cm, colors
def interp_array(N1): # add interpolated rows and columns to array
N2 = np.empty([int(N1.shape[0]), int(2*N1.shape[1] - 1)]) # insert interpolated columns
N2[:, 0] = N1[:, 0] # original column
for k in range(N1.shape[1] - 1): # loop through columns
N2[:, 2*k+1] = np.mean(N1[:, [k, k + 1]], axis=1) # interpolated column
N2[:, 2*k+2] = N1[:, k+1] # original column
N3 = np.empty([int(2*N2.shape[0]-1), int(N2.shape[1])]) # insert interpolated columns
N3[0] = N2[0] # original row
for k in range(N2.shape[0] - 1): # loop through rows
N3[2*k+1] = np.mean(N2[[k, k + 1]], axis=0) # interpolated row
N3[2*k+2] = N2[k+1] # original row
return N3
vals_theta = np.arange(0,181,45)
vals_phi = np.arange(0,361,45)
vals_phi, vals_theta = np.meshgrid(vals_phi, vals_theta)
THETA = np.deg2rad(vals_theta)
PHI = np.deg2rad(vals_phi)
# simulate the power data
R = abs(np.cos(PHI)*np.sin(THETA)) # 2 lobes (front and back)
interp_factor = 3 # 0 = no interpolation, 1 = 2x the points, 2 = 4x the points, 3 = 8x, etc
X = R * np.sin(THETA) * np.cos(PHI)
Y = R * np.sin(THETA) * np.sin(PHI)
Z = R * np.cos(THETA)
for counter in range(interp_factor): # Interpolate between points to increase number of faces
X = interp_array(X)
Y = interp_array(Y)
Z = interp_array(Z)
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
ax.grid(True)
ax.axis('on')
ax.set_xticks([])
ax.set_yticks([])
ax.set_zticks([])
N = np.sqrt(X**2 + Y**2 + Z**2)
Rmax = np.max(N)
N = N/Rmax
axes_length = 1.5
ax.plot([0, axes_length*Rmax], [0, 0], [0, 0], linewidth=2, color='red')
ax.plot([0, 0], [0, axes_length*Rmax], [0, 0], linewidth=2, color='green')
ax.plot([0, 0], [0, 0], [0, axes_length*Rmax], linewidth=2, color='blue')
# Find middle points between values for face colours
N = interp_array(N)[1::2,1::2]
mycol = cm.jet(N)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=mycol, linewidth=0.5, antialiased=True, shade=False) # , alpha=0.5, zorder = 0.5)
ax.set_xlim([-axes_length*Rmax, axes_length*Rmax])
ax.set_ylim([-axes_length*Rmax, axes_length*Rmax])
ax.set_zlim([-axes_length*Rmax, axes_length*Rmax])
m = cm.ScalarMappable(cmap=cm.jet)
m.set_array(R)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fig.colorbar(m, shrink=0.8)
ax.view_init(azim=300, elev=30)
plt.show()
Вы можете добавить линии осей единичной длины с помощью:
ax.plot([0, 1], [0, 0], [0, 0], linewidth=2, color = 'red')
ax.plot([0, 0], [0, 1], [0, 0], linewidth=2, color = 'green')
ax.plot([0, 0], [0, 0], [0, 1], linewidth=2, color = 'blue')
Что касается цвета поверхности, вам необходимо определить выражение, которое представляет расстояние от начала координат, затем использовать это выражение для создания вашей цветовой карты и передать ее в facecolors
параметр ax.plot_surface
как здесь:
dist = np.sqrt(X**2 + Y**2 + Z**2)
dist_max = np.max(dist)
my_col = cm.jet(dist/dist_max)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=my_col, linewidth=0, antialiased=False)
Полный код:
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = 8*np.sin(R)
dist = np.sqrt(X**2 + Y**2 + Z**2)
dist_max = np.max(dist)
my_col = cm.jet(dist/dist_max)
axes_length = 1.5
ax.plot([0, axes_length*dist_max], [0, 0], [0, 0], linewidth=2, color = 'red')
ax.plot([0, 0], [0, axes_length*dist_max], [0, 0], linewidth=2, color = 'green')
ax.plot([0, 0], [0, 0], [0, axes_length*dist_max], linewidth=2, color = 'blue')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=my_col,
linewidth=0, antialiased=False)
ax.set_xlim([-axes_length*dist_max, axes_length*dist_max])
ax.set_ylim([-axes_length*dist_max, axes_length*dist_max])
ax.set_zlim([-axes_length*dist_max, axes_length*dist_max])
plt.show()
что дает мне такой результат:
Как видите, цвет поверхности меняется от синего около начала координат до красного вдали от него. Применить этот код к вашим данным не составит труда.