0%

本文主要记录c#下TCPsocket编程的方法。
代码来源:https://blog.csdn.net/yangwohenmai1/article/details/92589072

长连接和短连接

(以下来源于上面的链接)
对于Socket来说,链接类型一般分为长连接和短连接。

长连接和短连接在程序上基本没有区别,区别是短连接每次发送完消息都要调用Close()方法来释放资源,而长连接则不调用Close()方法,从而保持持续不断的通信功能。

服务器端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SocketServer
{
class Program
{
static Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static byte[] result = new byte[1024];

static void Main(string[] args)
{
SocketServie();
}


public static void SocketServie()
{
Console.WriteLine("服务端已启动");
string host = "127.0.0.1";//IP地址
int port = 81;//端口
socket.Bind(new IPEndPoint(IPAddress.Parse(host), port));
socket.Listen(100);//设定最多100个排队连接请求
Thread myThread = new Thread(ListenClientConnect);//通过多线程监听客户端连接
myThread.Start();
Console.ReadLine();
}


/// <summary>
/// 监听客户端连接
/// </summary>
private static void ListenClientConnect()
{
while (true)
{
Socket clientSocket = socket.Accept();
clientSocket.Send(Encoding.UTF8.GetBytes("我是服务器"));
Thread receiveThread = new Thread(ReceiveMessage);
receiveThread.Start(clientSocket);
}
}


/// <summary>
/// 接收消息
/// </summary>
/// <param name="clientSocket"></param>
private static void ReceiveMessage(object clientSocket)
{
Socket myClientSocket = (Socket)clientSocket;
while (true)
{
try
{
//通过clientSocket接收数据
int receiveNumber = myClientSocket.Receive(result);
if (receiveNumber == 0)
return;
Console.WriteLine("接收客户端{0} 的消息:{1}", myClientSocket.RemoteEndPoint.ToString(), Encoding.UTF8.GetString(result, 0, receiveNumber));
////给Client端返回信息
//string sendStr = "已成功接到您发送的消息";
//byte[] bs = Encoding.UTF8.GetBytes(sendStr);//Encoding.UTF8.GetBytes()不然中文会乱码
//myClientSocket.Send(bs, bs.Length, 0); //返回信息给客户端
////myClientSocket.Close(); //发送完数据关闭Socket并释放资源//长连接的话就不关闭
////Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
myClientSocket.Close();//关闭Socket并释放资源
//myClientSocket.Shutdown(SocketShutdown.Both);//禁止发送和上传
break;
}
}
}





}
}

这里需要注意的是“给Client端返回信息”,若Client已经关闭,会进入catch部分;而myClientSocket.Shutdown这句话会报错,因此注释。
此外,这里的ip地址:127.0.0.1代表本机;端口可以随意取(80除外,这代表HTTP)
服务器端先开始运行,并进入监听状态;客户端在此期间运行并寻求连接,然后连接成立。
第一行的ProtocolType.Tcp表示TCP协议。

客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace SocketClient
{
class Program
{
static void Main(string[] args)
{
int port = 81;
//短连接
shotlink("",port);
//长连接
//longlink();
}



/// <summary>
/// 短连接,最后调用Close释放资源
/// </summary>
/// <param name="input"></param>
public static void shotlink(string input,int port)
{
//设定服务器IP地址
IPAddress ip = IPAddress.Parse("127.0.0.1");
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientSocket.Connect(new IPEndPoint(ip, port)); //配置服务器IP与端口
Console.WriteLine("连接服务器成功");
}
catch
{
Console.WriteLine("连接服务器失败,请按回车键退出!");
Console.ReadLine();
return;
}

string sendMessage = "你好";//发送到服务端的内容
clientSocket.Send(Encoding.UTF8.GetBytes(sendMessage));//向服务器发送数据,需要发送中文则需要使用Encoding.UTF8.GetBytes(),否则会乱码
Console.WriteLine("向服务器发送消息:" + sendMessage);

//接受从服务器返回的信息
string recvStr = "";
byte[] recvBytes = new byte[1024];
int bytes;
bytes = clientSocket.Receive(recvBytes, recvBytes.Length, 0); //从服务器端接受返回信息
recvStr += Encoding.UTF8.GetString(recvBytes, 0, bytes);
Console.WriteLine("服务端发来消息:{0}", recvStr);//回显服务器的返回信息
//每次完成通信后,关闭连接并释放资源
clientSocket.Close();
Console.ReadLine();
}


/// <summary>
/// 长连接不调用Close释放资源
/// </summary>
public static void longlink(int port)
{
//设定服务器IP地址
IPAddress ip = IPAddress.Parse("127.0.0.1");
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientSocket.Connect(new IPEndPoint(ip, port)); //配置服务器IP与端口
Console.WriteLine("连接服务器成功");
}
catch
{
Console.WriteLine("连接服务器失败,请按回车键退出!");
Console.ReadLine();
return;
}

//循环读取输入数据
while (true)
{
Console.WriteLine("请输入");
string sentstr = Console.ReadLine();
SentMsg(sentstr, clientSocket);
}
}


/// <summary>
/// 长连接,不释放资源
/// </summary>
/// <param name="sentstr"></param>
/// <param name="clientSocket"></param>
public static void SentMsg(string sentstr, Socket clientSocket)
{
string sendMessage = "你好";//发送到服务端的内容
sendMessage = sentstr;//发送到服务端的内容
//向服务器发送数据,需要发送中文则需要使用Encoding.UTF8.GetBytes(),否则会乱码
clientSocket.Send(Encoding.UTF8.GetBytes(sendMessage));
Console.WriteLine("向服务器发送消息:" + sendMessage);

//接受从服务器返回的信息
string recvStr = "";
byte[] recvBytes = new byte[1024];
int bytes;
bytes = clientSocket.Receive(recvBytes, recvBytes.Length, 0); //从服务器端接受返回信息
recvStr += Encoding.UTF8.GetString(recvBytes, 0, bytes);
Console.WriteLine("服务端发来消息:{0}", recvStr); //回显服务器的返回信息
//clientSocket.Close();//关闭连接并释放资源//如果是长连接,注释掉close
}
}
}

这里需要注意的是:ip地址和端口号需要和服务器端保持一致。而且一定要服务器端先运行,客户端后运行。
Socket类的构造函数的参数中,ProtocolType.Tcp表示TCP协议。

bug及其解决方案

跑demo过程中遇到的bug及其解决方法:

  1. Unity中生成的工程进入VS后,要在C盘build,而且确保磁盘空间充足(3GB以上)否则会缺少GameAssembly.lib,依据:https://answers.unity.com/questions/1688472/how-to-solve-mapfileparserexe-exited-with-code-532.htmlG
  2. Win10开发者模式开启时:错误0x800f0922
    解决方法:https://www.youtube.com/watch?v=v2ZVtAIsEm4,方法2
  3. Unity发布到UWP平台error:“IOException: Win32 IO returned 234. Path: C:\Program Files (x86)\Windows Kits.”
    解决方法:https://blog.csdn.net/qq_41452267/article/details/106851395,方法1

相关网址记录

一些入门资料网站:
介绍MR的网站https://docs.microsoft.com/zh-cn/windows/mixed-reality/
MRTK介绍网址https://microsoft.github.io/MixedRealityToolkit-Unity/README.html

通过PC端可以监视hololens 2的网站
http://127.0.0.1:10080/default.htm#Home
(USB连接,typec)
对应说明:
https://docs.microsoft.com/zh-cn/windows/mixed-reality/using-the-windows-device-portal

iros2020中hololens2和ros联动控制机器人,人机交互
https://www.microsoft.com/en-us/research/event/mixed-reality-and-robotics-tutorial-iros-2020/#!agenda
https://github.com/microsoft/mixed-reality-robot-interaction-demo

hololens基于socket的UDP通信
https://blog.csdn.net/qq_44197337/article/details/105943914

Vuforia与hololens联动介绍
https://docs.microsoft.com/zh-cn/windows/mixed-reality/develop/unity/vuforia-development-overview

unity开发hololens的教程
https://docs.microsoft.com/zh-cn/windows/mixed-reality/develop/unity/tutorials

使用matlab推导和求解符号公式的程序,记录在此:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
% 方法1:
%计算异面直线的公垂线及其交点
clc;clear;
syms p1x p1y p1z
syms p2x p2y p2z
syms p3x p3y p3z
syms p4x p4y p4z
syms d1x d1y d1z
syms d2x d2y d2z
syms m n

p1Vector = [p1x,p1y,p1z];
p2Vector = [p2x,p2y,p2z];
p3Vector = [p3x,p3y,p3z];
p4Vector = [p4x,p4y,p4z];

d1Vector = p2Vector-p1Vector;
d2Vector = p4Vector-p3Vector;

% d1Vector = [d1x,d1y,d1z];
% d2Vector = [d2x,d2y,d2z];


i1Vector = p1Vector+m*d1Vector;
i2Vector = p3Vector+n*d2Vector;

vVector = i2Vector-i1Vector;

eqn1 = [vVector(1)*d1Vector(1)+vVector(2)*d1Vector(2)+vVector(3)*d1Vector(3)==0];
eqn2 = [vVector(1)*d2Vector(1)+vVector(2)*d2Vector(2)+vVector(3)*d2Vector(3)==0];
eqn = [eqn1,eqn2];

[m,n]=solve(eqn,[m,n]);

simplify(m);
simplify(n);

stewart平台的工作空间的求解,使用matlab编写
参考书目:file:///C:/Users/giuyyt/Desktop/并联机器人机构学理论及控制.pdf
页码:p172、173等

总函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Stewart平台工作空间求解
%尝试两种方法:扫描方法和蒙特卡洛法
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clc;clear;
tic
%% stewart平台的参数给定
%global参数

%%动平台相对静平台初始位置坐标
global xp yp zp;

xp = 0;
yp = 0;
zp = 5.5;
% zp = 90;

%%R:动平台外接圆半径;r:静平台外接圆半径
global R r;

R = 1;
r = 3;


%%动平台铰点安装角
global upAngle0 upAngle1 upAngle2 upAngle3 upAngle4 upAngle5;

upAngle0 = 15;
upAngle1 = 105;
upAngle2 = 135;
upAngle3 = 225;
upAngle4 = 255;
upAngle5 = 345;

%并联机器人的限制范围,求工作空间时用

%电推杆最大长度与最小长度(绝对量)
global poleMaxLen poleMinLen;
poleMaxLen = 7.5;
poleMinLen = 4.5;
%虎克铰(下)最大转角和最小转角(°)
global hookeMaxAngle hookeMinAngle;
hookeMaxAngle = 45;
hookeMinAngle = -45;
%球铰(上)最大转角和最小转角(°)
global sphereMaxAngle sphereMinAngle;
sphereMaxAngle = 45;
sphereMinAngle = -45;
%电推杆半径(mm)
global poleRad;
poleRad = 0;


%%静平台铰点安装角
global downAngle0 downAngle1 downAngle2 downAngle3 downAngle4 downAngle5;


downAngle0 = 30;
downAngle1 = 90;
downAngle2 = 150;
downAngle3 = 210;
downAngle4 = 270;
downAngle5 = 330;

%运动范围限制
global xMin xMax;
global yMin yMax;
global zMin zMax;
global rollMin rollMax;
global pitchMin pitchMax;
global yawMin yawMax;


xMin = -10;xMax = 10;
yMin = -10;yMax = 10;
zMin = -10; zMax = 10;
% xMin = -40;xMax = 40;
% yMin = -40;yMax = 40;
% zMin = -40; zMax =40;
rollMin = -13.5; rollMax = 13.5;
pitchMin = -14.2; pitchMax = 14.2;
yawMin = -38; yawMax = 38;


%% 蒙特卡洛法
% expeNum = 10000;%随机取样次数
% acceptPointArray = [];
% totalPointArray = [];
% i = 0;
%
% while(i<expeNum)
% %取样,rand在0-1取样
% x = (xMax-xMin)*rand+xMin;
% y = (yMax-yMin)*rand+yMin;
% z = (zMax-zMin)*rand+zMin;
%
% %姿态需要固定
% roll = 0;
% pitch = 0;
% yaw = 0;
%
% totalPointArray = [
% totalPointArray;
% x,y,z
% ];
%
% %判断是否可行
% isSatisfy = isSatisfyTotal(x,y,z,roll,pitch,yaw);
%
% if(isSatisfy==1)
% acceptPointArray = [
% acceptPointArray;
% x,y,z
% ];
% i = i +1;
% else
% end
% end


for m = 1:3
%% 扫描法
%%%参数设定
i = 0;
j = 0;
k = 0;
zNum = 400;
gammaNum = 200;
deltaRho = 0.01;

%姿态需要固定
roll = (m-1)*5;
pitch = (m-1)*5;
yaw = (m-1)*5;

%%%实现
deltaGamma = 2*pi/gammaNum;
deltaZ = (zMax-zMin)/zNum;
edgePointArray = [];%记录边缘上的点的数组

for i = 1:zNum
rhoTemp = 0;
gammaTemp = 0;

zTemp = zMin+deltaZ*i;
for j = 1:gammaNum
isSatisfyLast = 0;%记录上一次是否符合要求
gammaTemp = gammaTemp+deltaGamma;
k = 0;
if(rhoTemp<0)
rhoTemp = 0;
end
deltaRho = abs(deltaRho);

while(1)
%将极坐标转换成直角坐标
[xTemp,yTemp] = polarCoo2CarsCoo(rhoTemp,gammaTemp);
%判断是否可行
isSatisfy = isSatisfyTotal(xTemp,yTemp,zTemp,roll,pitch,yaw);

%第一次
if(k==0)
isSatisfyLast = isSatisfy;
%第一次在范围内,不断增大极径直到到达极限
if(isSatisfy == 1)
deltaRho = deltaRho*1;
%第一次不在范围内,不断缩小极径直到到达极限
else
deltaRho = deltaRho*(-1);
end
rhoTemp = rhoTemp+deltaRho;

%非第一次
else
%若上一次状态与本次相同,继续变化极径;
if(isSatisfy==isSatisfyLast&&rhoTemp>0)
rhoTemp = rhoTemp+deltaRho;
isSatisfyLast = isSatisfy;
%若上一次状态与本次不同,记录本次点,进入下一个gamma角
else
if(k>1)
edgePointArray = [edgePointArray;xTemp,yTemp,zTemp];
end
break;
end
end
k = k+1



end
end
end



%% 画图
plot3(edgePointArray(:,1),edgePointArray(:,2),edgePointArray(:,3));
hold on;
view(0,0);
toc
end
%标出(0,0)点
scatter3(0,0,0);






% surf(X,Y,Z),view(90,0)
% title('侧视图')
% subplot(2,2,3)
% surf(X,Y,Z),view(0,0)
% title('正视图')
% subplot(2,2,4)
% surf(X,Y,Z),view(0,90)
% title('俯视图')

分函数:

将极坐标转换为直角坐标

157行的函数->polarCoo2CarsCoo:

1
2
3
4
5
6
7
function [xOut,yOut] = polarCoo2CarsCoo(rhoIn,gammaIn)
%polarCoo2CarsCoo 将极坐标转换为直角坐标

xOut = rhoIn*cos(gammaIn);
yOut = rhoIn*sin(gammaIn);

end

输入并联机器人的平动量和姿态,输出其是否能到达

159行的函数->isSatisfyTotal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function [isSatisfy] = isSatisfyTotal(x,y,z,roll,pitch,yaw)
%isSatisfyTotal 输入并联机器人的平动量和姿态,输出其是否能到达

%电推杆最大长度与最小长度(绝对量)
global poleMaxLen poleMinLen;
%虎克铰(下)最大转角和最小转角
global hookeMaxAngle hookeMinAngle;
%球铰(上)最大转角和最小转角
global sphereMaxAngle sphereMinAngle;
%电推杆半径
global poleRad;


[lenArray,vectorArray,brArray,BrArray] = stewartInverseKinematicFunction(x,y,z,roll,pitch,yaw);

%电推杆长度
lenSatisfy = isLenSatisfy(lenArray,poleMinLen,poleMaxLen);
%虎克铰角度
hookeAngleSatisfy = isHookeAngleSatisfy(vectorArray,hookeMinAngle,hookeMaxAngle);
% hookeAngleSatisfy = 1;
%球铰角度
sphereAngleSatisfy = isSphereAngleSatisfy(vectorArray,sphereMinAngle,sphereMaxAngle,roll,pitch,yaw);
% sphereAngleSatisfy = 1;
%干涉
interferenceSatisfy = isInterferenceSatisfy(lenArray,vectorArray,brArray,BrArray,poleRad);
% interferenceSatisfy = 1;

isSatisfy = lenSatisfy*hookeAngleSatisfy*sphereAngleSatisfy*interferenceSatisfy;






end

stewart平台运动学反解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
function [lenArray,vectorArray,brArray,BrArray] = stewartInverseKinematicFunction(x,y,z,roll,pitch,yaw)
%stewartInverseKinematicFunction 求解stewart平台逆解
%输入6个自由度上的位移量
%输出6个杆的杆长和对应的杆向量(静平台指向动平台)

%-----------------------stewart平台本体参数-------------------------------------
global xp yp zp;
global R r;
global upAngle0 upAngle1 upAngle2 upAngle3 upAngle4 upAngle5;
global downAngle0 downAngle1 downAngle2 downAngle3 downAngle4 downAngle5;

%-------------------------动平台的位恣-------------------------------------

P = [ x+xp; y+yp; z+zp ];%动平台圆心点相对静平台的坐标

%--------------------------平台的基本尺寸----------------------------------
%% 方法1:输入角度
%----------动平台的6个铰点,在动平台坐标系中的位置矢量---------------------
bR1 = [ R*cosd( upAngle0 ); R*sind( upAngle0 );0 ];
bR2 = [ R*cosd( upAngle1 ); R*sind( upAngle1 ); 0 ];
bR3 = [ R*cosd( upAngle2 ); R*sind( upAngle2 ); 0 ];
bR4 = [ R*cosd( upAngle3 ); R*sind( upAngle3 ); 0 ];
bR5 = [ R*cosd( upAngle4 ); R*sind( upAngle4 ); 0 ];
bR6 = [ R*cosd( upAngle5 ); R*sind( upAngle5 ); 0 ];


%
% ----------静平台的6个铰点,在静平台坐标系中的位置矢量---------------------
Br1 = [ r*cosd( downAngle0 ) ;r*sind( downAngle0 );0 ];
Br2 = [ r*cosd( downAngle1 ) ; r*sind( downAngle1 );0 ];
Br3 = [ r*cosd( downAngle2 ) ; r*sind( downAngle2 );0 ];
Br4 = [ r*cosd( downAngle3 ) ; r*sind( downAngle3 );0 ];
Br5 = [ r*cosd( downAngle4 ) ; r*sind( downAngle4 );0 ];
Br6 = [ r*cosd( downAngle5 ) ; r*sind( downAngle5 );0 ];


%% 方法2:直接输入坐标
% ----------动平台的6个铰点,在动平台坐标系中的位置矢量---------------------
% bR1 = [ 13.39; 15.81;0 ];
% bR2 = [ -13.39; 15.81; 0 ];
% bR3 = [-20.39; 3.69; 0 ];
% bR4 = [-7; -19.5; 0 ];
% bR5 = [7;-19.5; 0 ];
% bR6 = [20.39;3.69; 0 ];
%
%
%
% % ----------静平台的6个铰点,在静平台坐标系中的位置矢量---------------------
% Br1 = [10.7;25.025;0 ];
% Br2 = [-10.7; 25.025;0 ];
% Br3 = [-27;-3.23;0 ];
% Br4 = [-16.3;-21.77;0 ];
% Br5 = [16.3;-21.77;0 ];
% Br6 = [27;-3.23;0 ];

%% 其它
%相对固定轴,RPY的XYZ
%相当于欧拉角中的ZYX,绕动轴转动
TransM = rotz(yaw) * roty(pitch) * rotx(roll); % XYZ旋转矩阵


%----------动平台的6个铰点,在静平台坐标系中的位置矢量---------------------
br1 = TransM * bR1 + P;
br2 = TransM * bR2 + P;
br3 = TransM * bR3 + P;
br4 = TransM * bR4 + P;
br5 = TransM * bR5 + P;
br6 = TransM * bR6 + P;

%--动平台的6个铰点位置矢量,减去,静平台的6个铰点位置矢量,得到每个杆长矢量
L1 = br1 - Br1;
L2 = br2 - Br2;
L3 = br3 - Br3;
L4 = br4 - Br4;
L5 = br5 - Br5;
L6 = br6 - Br6;

%-----------求模,得到每个杆的杆长-----------------------------------------
LenL1 = norm(L1);
LenL2 = norm(L2);
LenL3 = norm(L3);
LenL4 = norm(L4);
LenL5 = norm(L5);
LenL6 = norm(L6);

L1 = L1/LenL1;
L2 = L2/LenL2;
L3 = L3/LenL3;
L4 = L4/LenL4;
L5 = L5/LenL5;
L6 = L6/LenL6;

lenArray = [LenL1;LenL2;LenL3;LenL4;LenL5;LenL6];
vectorArray = [
L1,L2,L3,L4,L5,L6];

brArray = [br1,br2,br3,br4,br5,br6];
BrArray = [Br1,Br2,Br3,Br4,Br5,Br6];






end

判断电推杆长度是否符合要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function [isSatisfy] = isLenSatisfy(lenArray,minLen,maxLen)
%isLenSatisfy 输入长度数组,判断电机长度是否达标(6*1)
%1达标,0不达标
ArrayNum = size(lenArray,1);

i = 0;
isSatisfy = 1;

for i = 1:ArrayNum
temp = lenArray(i);
if(temp<=maxLen&&temp>=minLen)
isSatisfy = isSatisfy*1;
else
isSatisfy = 0;
end

end


end

判断虎克铰的角度是否符合要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function [isSatisfy] = isHookeAngleSatisfy(vectorArray,minHookeAngle,maxHookeAngle)
%isHookeAngleSatisfy 输入向量数组,虎克铰(下面的铰)最小角和最大角
%1达标,0不达标

%静平台姿态:总是(0,0,1)
staticVector = [0;0;1];


ArrayNum = size(vectorArray,2);

i = 0;
isSatisfy = 1;

for i = 1:ArrayNum
temp = vectorArray(:,i);
hookeAngleTemp = acosd(dot(temp,staticVector));

if(hookeAngleTemp<=maxHookeAngle&&hookeAngleTemp>=minHookeAngle)
isSatisfy = isSatisfy*1;
else
isSatisfy = 0;
end

end


end

判断球铰的角度是否符合要求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function [isSatisfy] = isSphereAngleSatisfy(vectorArray,minSphereAngle,maxSphereAngle,roll,pitch,yaw)
%isSphereAngleSatisfy 输入向量数组,球铰(上面的铰)最小角和最大角,rpy角,输出是否符合球铰角度要求
%1达标,0不达标


ArrayNum = size(vectorArray,2);
%相对固定轴,RPY的XYZ
%相当于欧拉角中的ZYX
TransM = rotz(yaw) * roty(pitch) * rotx(roll); % XYZ旋转矩阵

%z轴在静平台坐标系中的方向矢量
%机器人学蔡自兴p30
axisZ = TransM(:,3);

%球铰的向量方向:取负号
sphereAxis = -axisZ;

i = 0;
isSatisfy = 1;


for i = 1:ArrayNum
temp = -vectorArray(:,i);%注意要取负号
sphereAngleTemp = acosd(dot(temp,sphereAxis));

if(sphereAngleTemp<=maxSphereAngle&&sphereAngleTemp>=minSphereAngle)
isSatisfy = isSatisfy*1;
else
isSatisfy = 0;
end

end









end

判断杆是否干涉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function [isSatisfy] = isInterferenceSatisfy(lenArray,vectorArray,brArray,BrArray,poleRad)
%isInterferenceSatisfy 判断连杆之间是否干涉
%输入连杆的直径,长度和方向,以及动静平台的铰点在静坐标系下的坐标;
%是为1,否为0

ArrayNum = size(lenArray,1);

i = 0;
isSatisfy = 1;


for i = 1:ArrayNum
%判断相邻两杆之间的最短距离
if(i==ArrayNum)
brArrayTwo = [brArray(:,i),brArray(:,1)];
BrArrayTwo = [BrArray(:,i),BrArray(:,1)];
vectorArrayTwo = [vectorArray(:,i),vectorArray(:,1)];
lenArrayTwo = [lenArray(i),lenArray(1)];
else
brArrayTwo = [brArray(:,i),brArray(:,i+1)];
BrArrayTwo = [BrArray(:,i),BrArray(:,i+1)];
vectorArrayTwo = [vectorArray(:,i),vectorArray(:,i+1)];
lenArrayTwo = [lenArray(i),lenArray(i+1)];
end
a = 0;
minDis = brokenLineIntersaction(brArrayTwo,BrArrayTwo,vectorArrayTwo,lenArrayTwo);
%将最短距离和杆的半径进行对比
if(minDis>2*poleRad)
isSatisfy = isSatisfy*1;
else
isSatisfy = 0;
end

end




end
求空间中两条线段之间最短距离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
function [dis] = brokenLineIntersaction(brArrayTwo,BrArrayTwo,vectorArrayTwo,lenArrayTwo)

%brokenLineIntersaction
%输入两条线段的起始点(两个列向量),方向向量(两个列向量),长度;以数组形式输入
%输出两者的最短距离
%分成平行、异面、相交三种情形,每种情形再分类讨论。
epsilon = 1e-2;

dis = 0;
%求公法线向量
ni = cross(vectorArrayTwo(:,1),vectorArrayTwo(:,2));
%若平行
if(norm(ni)<epsilon)
pLine = brArrayTwo(:,2)-brArrayTwo(:,1);
dis = norm(cross(pLine,vectorArrayTwo(:,1)));
return;
end

ni = ni/norm(ni);

%异面
%最短距离
%求出公垂线和两者的交点
[m,n] = linePerpCalFun(BrArrayTwo(:,1),brArrayTwo(:,1),BrArrayTwo(:,2),brArrayTwo(:,2));
%判断交点是否分别在两者上
if(m<=1&&m>=0&&n<=1&&n>=0)
%是,则距离就是垂线长度
deltaTemp = abs(dot(ni,brArrayTwo(:,2)-brArrayTwo(:,1)));
else
%否,距离要根据端点到另一条线段的距离来算
%a点和线段cd距离;b点和线段cd距离
%c点和线段ab距离;d点和线段ab距离
%四者取最小者
%点br1与线段br2,Br2的距离
dis1 = disPointLineSeg(brArrayTwo(:,1),brArrayTwo(:,2),BrArrayTwo(:,2));
%点Br1与线段br2,Br2的距离
dis2 = disPointLineSeg(BrArrayTwo(:,1),brArrayTwo(:,2),BrArrayTwo(:,2));
%点br2与线段br1,Br1的距离
dis3 = disPointLineSeg(brArrayTwo(:,2),brArrayTwo(:,1),BrArrayTwo(:,1));
%点Br2与线段br1,Br1的距离
dis4 = disPointLineSeg(BrArrayTwo(:,2),brArrayTwo(:,1),BrArrayTwo(:,1));
%最短
deltaTemp = min([dis1,dis2,dis3,dis4]);
end


if(deltaTemp<epsilon)
%若最短距离为0,则在同一平面内
%a点和线段cd距离;b点和线段cd距离
%c点和线段ab距离;d点和线段ab距离
%四者取最小者
%点br1与线段br2,Br2的距离
dis1 = disPointLineSeg(brArrayTwo(:,1),brArrayTwo(:,2),BrArrayTwo(:,2));
%点Br1与线段br2,Br2的距离
dis2 = disPointLineSeg(BrArrayTwo(:,1),brArrayTwo(:,2),BrArrayTwo(:,2));
%点br2与线段br1,Br1的距离
dis3 = disPointLineSeg(brArrayTwo(:,2),brArrayTwo(:,1),BrArrayTwo(:,1));
%点Br2与线段br1,Br1的距离
dis4 = disPointLineSeg(BrArrayTwo(:,2),brArrayTwo(:,1),BrArrayTwo(:,1));
%最短
dis = min([dis1,dis2,dis3,dis4]);
else
dis = deltaTemp;
end



end
空间中点到线段最短距离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function [minDis] = disPointLineSeg(pointCoo,linePoint1Coo,linePoint2Coo)
%disPointLineSeg 输入三维点的坐标,三维线段的坐标(即始末两点的坐标),均为列向量形式
% 输出最短距离
%https://blog.csdn.net/Mr_HCW/article/details/82816490
x0 = pointCoo(1);
y0 = pointCoo(2);
z0 = pointCoo(3);

x1 = linePoint1Coo(1);
y1 = linePoint1Coo(2);
z1 = linePoint1Coo(3);

x2 = linePoint2Coo(1);
y2 = linePoint2Coo(2);
z2 = linePoint2Coo(3);

%求垂足
a = (x0 - x1) * (x2 - x1) + (y0 - y1) * (y2 - y1) + (z0 - z1) * (z2 - z1);
b = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1);
k = a/b;

xN = k * (x2 - x1) + x1;
yN = k * (y2 - y1) + y1;
zN = k * (z2 - z1) + z1;

pointDropFeet = [xN;yN;zN];

%分析三种情况
%linePoint1:A;
%linePoint2:B;
%pointCoo:P;
%pointDropFeet:C;
vectorAP = pointCoo-linePoint1Coo;
vectorAB = linePoint2Coo-linePoint1Coo;
vectorBP = pointCoo-linePoint2Coo;
vectorCP = pointCoo-pointDropFeet;

r = dot(vectorAP,vectorAB)/norm(vectorAB)^2;

if(r<=0)%r<=0
minDis = norm(vectorAP);
elseif(r>=1)%r>=1
minDis = norm(vectorBP);
else%其它
minDis = norm(vectorCP);
end





end
求异面两条直线的公垂线和这两条直线的交点的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function [m,n] = linePerpCalFun(p1Vector,p2Vector,p3Vector,p4Vector)
%linePerpCalFun 输入异面的两条直线(p1p2和p3p4),点向量均为列向量
% 输出它们的公垂线交点在线段上的位置m,n
%https://blog.csdn.net/weixin_40332490/article/details/89133859

d1Vector = p2Vector-p1Vector;
d2Vector = p4Vector-p3Vector;

a = dot(d1Vector,d2Vector);
b = dot(d1Vector,d1Vector);
c = dot(d2Vector,d2Vector);
d = dot(p3Vector-p1Vector,d1Vector);
e = dot(p3Vector-p1Vector,d2Vector);

epsilon = 1e-2;

if(a<epsilon)%两直线垂直
m = d/b;
n = -e/c;
else
m = (a*e-c*d)/(a*a-b*c);
n = (b/a) * m - (d/a);
end



end

项目地址:https://github.com/giuyyt/unity_3d_character_project
3D游戏中控制角色的一些技巧。包括让角色移动,跳跃;以及镜头的跟随。
将一些技巧记录在此:

1.将镜头固定在角色上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class CameraMove : MonoBehaviour
{
public Transform playerTransform;
public float smoothing;

private Vector3 offset;

void Start()
{
offset = transform.position - playerTransform.position;
}

void Update()//update表示平台的帧数而fixedupdate表示现实时间的时间
{
Vector3 newCameraPos = playerTransform.position + offset;
transform.position = Vector3.Lerp(transform.position, newCameraPos, smoothing * Time.deltaTime);

}

}

2.角色的移动:使用四个方向键:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class characterMove : MonoBehaviour
{

public float speed = 1.5f;//控制移动速度
private Transform m_transform;

// Start is called before the first frame update
void Start()
{
m_transform = this.transform;
}

// Update is called once per frame
void Update()
{
//均为局部坐标
//向左
if (Input.GetKey(KeyCode.LeftArrow))
{

m_transform.Translate(Vector3.left * Time.deltaTime * speed);
}

//向右
if (Input.GetKey(KeyCode.RightArrow))
{

m_transform.Translate(Vector3.right * Time.deltaTime * speed);
}
//向前
if (Input.GetKey(KeyCode.UpArrow))
{

m_transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
//向后
if (Input.GetKey(KeyCode.DownArrow))
{

m_transform.Translate(Vector3.back * Time.deltaTime * speed);
}
}
}

3.角色跳跃:按空格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class characterJump : MonoBehaviour
{
// Start is called before the first frame update
public float force = 5f;//向角色上面添加的力的大小,决定能跳多高

private Rigidbody myRigidbody;
void Start()
{
myRigidbody = gameObject.GetComponent<Rigidbody>();
}

// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.Space))//按下空格
{
//判断是否在地面上
if (IsGrounded(1f))
{
myRigidbody.AddForce(new Vector3(0, force, 0), ForceMode.Impulse);
}
}
}


/// <summary>
/// https://blog.csdn.net/lengyoumo/article/details/107015378
/// 当没落地的时候不能重复起跳
/// </summary>
/// <param name="distance"></param>
/// <returns></returns>
public bool IsGrounded(float distance)
{
//distance:检测物体与地面的距离,
//https://docs.unity3d.com/ScriptReference/Physics.Raycast.html
bool b = Physics.Raycast(new Vector3(transform.position.x, transform.position.y, transform.position.z), -Vector3.up, distance);
return b;
}
}

此处注意要把角色的RigidBody中Constraints中的三个Rotation给Freeze掉,否则角色跳跃可能会翻倒。

本贴记录git和github交互的一些常用方法。


git初始配置

首先需要生成密钥,从而将计算机本地和github联系起来。

1
ssh-keygen -t rsa -C "youremail@example.com"

输入以上指令之后,一直按回车直到结束就好。密钥位置在C盘的用户文件夹里的.ssh文件中,文件名为id_rsa,.pub文件。
将这个文件用vscode打开(或其它文本编辑器),复制其中密钥,打开github中如下图所示的位置,新建SSH keys,将复制的密钥粘贴进去保存。

pic_1

完成以上步骤之后,本地和github便联系起来了。

git新建仓库并上传文件

首先在本地新建仓库,使用git Bash实现,将文件位置切换到仓库所在文件夹,然后依次执行以下命令:

1
git init  //仓库初始化
1
git add .  //将文件夹中所有文件加入仓库
1
git commit -m "first commit" //上传的修改记录为“first commit”,也可以改成其它,这是自定义的。

然后在github中新建仓库,但是要注意的是这里不要打勾。然后确定。
pic_2
之后进入到新的页面,将这个模块下的语句依次输入到git Bash里就可以了。
pic_3

git修改仓库中文件

如果仅仅需要修改的话,按照“以下命令有先后顺序”部分的方法运行就可以了。部分1为方法的补充。

以下命令不分先后

使用如下命令,查看目前要更改的远程仓库的名字:

1
git remote -v

使用如下命令添加要更改的远程仓库:

1
git remote add origin https://github.com/user/repo.git

使用如下命令修改要更改的远程仓库的url:

1
git remote set-url origin https://github.com/USERNAME/REPOSITORY.git

或者:

1
git remote set-url origin git@github.com:USERNAME/REPOSITORY.git

更详细的可以参考:https://help.github.com/cn/github/using-git/managing-remote-repositories

以下命令有先后顺序

如果仅仅需要修改的话,按照以下方法运行就可以了。部分1为一些命令的补充:

使用如下命令添加要更改的远程仓库:

1
git remote add origin https://github.com/user/repo.git

修改项目中的文件,使用如下命令查看项目状态,观察哪些文件被修改了:

1
git status

将修改的文件名填入“file”中,即把修改的文件“add”进去:

1
git add file

提交时的修改信息,引号中内容可以自定义:

1
git commit -m“修改”

正式提交:

1
git push

更详细的参考:https://zhuanlan.zhihu.com/p/33055638

参照了一个十分钟游戏制作挑战的视频:https://www.bilibili.com/video/BV1Ca4y1x7RP
完整的工程在:https://github.com/giuyyt/unity_new_game_1

将一些技巧记录在此:

1.Material作用(之一):可以调整反弹系数,在物体和其发生碰撞并反弹时可以用到

应用的地方:碰撞器
pic_1

创建的方法:新建Physics Material 2D
pic_2

2.Canvas:制作UI时使用,直接创建即可,起到类似于底板的作用。然后在它下面创建text之类的元素。

3.Sprite:2D时使用较多。

4.球的脚本Ball:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System.Collections;
using System.Collections.Generic;
using UnityEngine;



public class ball : MonoBehaviour
{
public GameObject player;

// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{

}

/// <summary>
///触发了底层的隐形墙壁,重开
/// </summary>
/// <param name="collision"></param>
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Wall_game_finish"))//判断一个object的名称是否是指定的,这里的object是底层的隐形墙壁,碰到就要重开
{
transform.position = player.transform.position;//球的位置和玩家的板位置重合
GetComponent<Rigidbody2D>().velocity = Vector2.zero;//将球的速度减为0
transform.SetParent(player.transform);//将球的父亲物体设置为玩家的板,这样在移动板子时球也会跟着移动
}
}



}

5.玩家的板子Player的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
public bool isStart;//判断游戏此时是否开始
public float force;//在按下空格时给小球的力
public float speed;//板子横向移动的速度

public GameObject ball;//指代小球物体
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
/// <summary>
/// 判断是否开始游戏,若按下空格表示开始游戏,给小球一个初始力使其开始弹射
/// </summary>
void Update()
{
if (!isStart)
{
if (Input.GetKeyDown(KeyCode.Space))//按下空格键
{
ball.GetComponent<Rigidbody2D>().AddForce(Vector2.up * force, ForceMode2D.Impulse);//给小球力
transform.DetachChildren();//使得板子的所有子物体脱离自己,这样当板子移动时它们不再跟随
}

float horizonalMove = Input.GetAxisRaw("Horizontal") * speed * Time.deltaTime;//移动速度,向右为正,以秒为单位(而不是帧)

transform.Translate(Vector2.right * horizonalMove);//让板移动


}
}
}

长期更新。

基础

redis的安装以及密码等的设置可以参考:https://www.w3cschool.cn/redis/redis-install.html

如何使用

首先打开一个cmd,将文件路径调整到redis的文件夹的路径下,然后输入:

1
redis-server.exe redis.windows.conf

这时redis的客户端已经打开了,这个cmd的窗口不要关闭,转而打开另一个cmd窗口,将路径调整到redis文件夹下,并输入:

1
redis-cli

此时就可以输入命令来和redis客户端交互了,但是注意第一个cmd一定不要关掉,否则就无法交互了。

本题是ee261中Problem Set 6的一道matlab代码题,主要用到了采样定理和混淆相关的知识。
ee261相关知识点整理博客:https://www.cnblogs.com/TaigaCon/p/5079156.html


结果分析

alpha=0.99

当alpha = 0.99时,linear和nearest两种方法的结果都很不错,毕竟此时采样率还是基本符合采样定理的。

linear

alpha=0.99_linear

nearest

alpha=0.99_nearest

alpha=0.95

alpha=0.95时,可以发现图片已经受到混淆的严重影响了,“nearest”下图片体现出了“像素风”,也就是说图片的精细化程度下降很多;
而“linear”相比于“nearest”,它的边缘要更模糊一些。推测是因为linear相比nearest更加平均,过渡更均匀。

linear

alpha=0.95_linear

nearest

alpha=0.95_nearest

alpha=0.90

混淆的作用在这里比alpha=0.95更要严重,图片已经面目全非。当然,linear依然比nearest模糊。

linear

alpha=0.9_linear

nearest

alpha=0.9_nearest

matlab代码

代码中alpha=0.90,插值类型“nearest”,若要修改这两个参数,修改26行和59行的参数值即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
%https://see.stanford.edu/materials/lsoftaee261/PS-6-2007.pdf
%第五题,matlab编程题
%题目大意:给定一张图片,采样频率的改变、插值方式对原图像的影响(和采样定理有关)

%读取图片
clc;clear;
img = imread('man.gif');
[row,col] = size(img);
% imshow(img);

%第一步,从256*256转化成1*256^2,并将像素值由uint8转化为double,保证归一化
imgVector = [];
for i = 1:row
imgVector = [imgVector,img(i,:)];
end
imgVector = double(imgVector)/256;
vectorLength = length(imgVector);

%第二步,fft,保证0点在中心
imgVectorFren = fftshift(fft(imgVector));
fValues = -vectorLength/2:1:vectorLength/2-1;
zeroPoint = length(fValues)/2+1;
plot(fValues,imgVectorFren);

%第三步:求不同alpha值时的带宽
alpha = 0.90;%0.9、0.95、0.99
energyTotal = 0;%代表信号总能量
energyP = abs(imgVectorFren(zeroPoint))^2;%代表近似带宽内信号总能量

%计算信号总能量
for i = 1:vectorLength
energyTotal = energyTotal + abs(imgVectorFren(i))^2;
end

%计算近似带宽
p = 0;
while(energyP/energyTotal<alpha)
p = p+1;
energyP = energyP+abs(imgVectorFren(p+zeroPoint))^2+abs(imgVectorFren(-p+zeroPoint))^2;
end

%第四步:求得近似带宽后,根据采样定理,以奈奎斯特采样频率取对原时域信号采样
rate = p*2/vectorLength;
interval = floor(1/rate);

indexTotal = 1;
vectorSampled = [];
indexSampled = [];
while(indexTotal<=vectorLength)
indexSampled = [indexSampled,indexTotal];
vectorSampled = [vectorSampled,imgVector(indexTotal)];

indexTotal = indexTotal + interval;
end


%第五步,对采样后的时域信号进行插值,插值方式两种:直线、复制近邻的值。
indexInterped = 1:vectorLength;
vectorInterped = interp1(indexSampled,vectorSampled,indexInterped,'nearest'); %直线:linear;最近邻:nearest

%第六步,将vectorInterped复原为图像,并与原图进行对比
imgInterped = [];
for i = 0:row-1
imgInterped = [imgInterped;vectorInterped(1,i*col+1:(i+1)*col)];
end

subplot(1,2,1);
imshow(img);
title('imgOri');

subplot(1,2,2);
imshow(imgInterped);
title('imgInterped');

本题是ee261中Problem Set 4的一道matlab代码题,主要用到了fft和频域的相关知识。
ee261相关知识点整理博客:https://www.cnblogs.com/TaigaCon/p/5079156.html


坑点

1.matlab的fft函数

首先,根据傅里叶变换的公式可以得知,频域的F(s)关于y轴共轭对称,也就是说两边实部相同而虚部相反。然而,现实中正数频率才有意义,负数频率的数据则使用其它方法处理。
在matlab中,对时域数据进行fft处理后,得到的频域数据前一半为正数频率下的频谱,也就是现实中有意义的数据;而后一半为负数频率下的数据,可以直接删除掉;或者使用fftshift。
但是在频域处理之后,ifft之前要注意恢复到matlab fft之后的模式(即前一半正频率,后一半负频率,两部分关于中间对称),否则点数N都对不上,这肯定是不对的。

参照:https://www.zhihu.com/question/39212146/answer/80239362
第三个回答

2.ifft后取时域信号的实部而非模值

见matlab代码最后,如下写法是错误的:

1
xDecode = abs(ifft(yDecode));

正确如下式:

1
xDecode = real(ifft(yDecode));

matlab代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
%https://see.stanford.edu/materials/lsoftaee261/PS-4-2007.pdf
%第五题,matlab编程题
%题目大意:一段人声音频信号采取了加密,加密方式如下:
%将频域均分成四份,分别称为ABCD;题目中的音频将ABCD打乱为CBDA。
%现在请你还原该音频,并转录。

%%导入信号并进行时域上观察
[x,Fs]=audioread('PS-4-scramble.wav');%可以在官网上下载到:https://see.stanford.edu/materials/lsoftaee261/PS-4-scramble.wav
T=1/Fs; %采样周期
t=(1:length(x))*T;%采样时间
figure(1);
% plot(t,x);
% title('原始信号时域图形');
% xlabel('时间t/s');
% ylabel('音量');

%%对信号fft,变换到频域
y=fft(x); %做FFT变换
yMod = abs(y);
f=(0:length(y)-1).*Fs/length(y);
plot(f,y) %画出原始信号的频谱图
s = 1/(length(y)*T);%delta_s


%%解码
%CBDA->ABCD
yCell = cell(4,1);
sliceNum = length(y)/8;
for i = 0:3
yCell{i+1,1} = y(i*sliceNum+1:(i+1)*sliceNum,1);
end

yDecode = [
yCell{4,1};
yCell{2,1};
yCell{1,1};
yCell{3,1};
conj(yCell{3,1});
conj(yCell{1,1});
conj(yCell{2,1});
conj(yCell{4,1});
];
% plot(f,yDecode);
%%解码之后ifft,获得时域上音频信号xDecode
xDecode = real(ifft(yDecode));
sound(xDecode,Fs);