Python - 绘制天线辐射图

3

这是我找到并稍作修改的代码。如何缩放颜色并设置从原点开始的坐标轴进行可视化?我尝试查找信息,但大多数是关于2D图的。

在这里,我添加了两个数组thetaphi,间隔为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()

尝试获取一个类似于这个的结果

原点* 哈哈..... - okrus
你有没有看过其他人的工作,比如 https://medium.com/@johngrant/antenna-arrays-and-python-plotting-with-pyplot-ae895236396a 或者尝试过 Pylayer 工具的示例,例如 https://pylayers.github.io/pylayers/notebook/2-AP/Antenna.html? - Cloud Cho
我发现你的辐射图案不对称...你能解释一下吗? - Cloud Cho
2个回答

2
这是对Andrea出色回答的进一步补充,加入了一些有助于处理实际数据的内容,这些数据可能在点之间具有相当大的间距。当我第一次绘制45度间隔的图形时,它看起来像这样:

Initial Plot

有两个明显的问题:
  1. 脸很大且只有单一颜色,即使它们跨越了广泛的值范围。
  2. 形状关于原点对称,但面的颜色不对称。
问题1可以通过对数据进行线性插值来改进,以便将每个面分成多个部分,这些部分可以具有不同的颜色。
问题2是由于面颜色的分配方式引起的。想象一个二维平面上的3x3点网格,每个点都有一个值。当您绘制表面时,只会有2x2个面,因此最后一行和列的值被丢弃,每个面的颜色仅由一个角决定。我们真正想要的是每个面中心的值。我们可以通过取四个角值的平均值并使用该值来分配颜色来估计这个值。
在计算上,这最终类似于问题1的插值,因此我为两者使用了相同的“interp_array”函数。我不是很擅长Python编程,所以可能有更有效的方法来完成它,但它能够完成工作。
这是修复了问题2的情节,但没有插值。对称性得到了修正,但仅使用了2种颜色,因为面与原点等距离分布。

Symmetry Fix

这是最终的图表,点之间进行了8倍插值。现在它更接近于商业天线测量软件中所看到的连续色彩图表的类型。

Interpolation Added

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()

1

您可以使用以下代码添加单位长度的坐标轴:

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')

关于表面的颜色,您需要定义一个表示距离原点的表达式,然后使用这个表达式创建您的colormap,并将其传递给ax.plot_surfacefacecolors参数,如下所示:

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()

这给了我这个结果:

enter image description here

正如您所看到的,表面的颜色从原点附近的蓝色变为远离原点的红色。将此代码应用于您的数据不应该很困难。

非常感谢您!时间非常巧合,因为我正在使用相同的示例代码解决同样的问题。唯一剩下的问题是,对于像真实天线测量系统这样更粗糙的数据集,每个表面的颜色都基于单个点固定。我想我需要以某种方式在这些点之间进行插值?这是我的输出:https://i.imgur.com/UcvydvX.png - AMTK
我会说是的,但这不是我的领域:你所问的问题是特定于电信工程的。 - Zephyr
很不幸,我是一名电信工程师哈哈。但我想说这不是一个工程问题,只是一个关于如何用Python更好地显示数据的问题。实际上,稍微调整一下间距(22.5度增量),它看起来已经非常好了,绘图在这里:https://i.imgur.com/zWHmDAE.png只是让我感到烦恼的是,它不对称,而显然应该是对称的。似乎它是从一侧绘制并根据第一个绘制的点选择颜色。我将尝试深入了解它是如何分配颜色的,并查看是否可以使其至少对称。 - AMTK
1
我想通了!这基本上是一个偏移错误,它发生的原因是,例如,如果您有一个3x3点的网格,则映射到2x2面的网格,因此最后一行和最后一列被丢弃。我通过创建一个新的颜色映射数组来解决它,其中所有值都在原始距离数组的值之间。我很快会将我的代码发布为新答案,但我想实现线性插值以增加面数。 - AMTK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接