树莓派智能车摄像头循迹

实验设备

树莓派3B,电机电路控制板,红外传感器,超声波传感器,蜂鸣器,3.7V电池。

实验目的

实现小车自动循迹,道路是两条黑线之间,与循单线不同。

实现思路

端口设置

按照实体小车进行连接

PWM电机控制

用占空比来控制小车速度。

1
2
3
4
5
6
7
8
9
10
11

def turn_up(speed,t_time): #定义两个参数,一个是速度,一个是时间
L_Motor.ChangeDutyCycle(speed) #设置占空比,控制速度,speed的取值范围0-35,原理是控制电源通断的时间占比
GPIO.output(IN1,GPIO.HIGH)
GPIO.output(IN2,GPIO.LOW)

R_Motor.ChangeDutyCycle(speed)
GPIO.output(IN3,GPIO.HIGH)
GPIO.output(IN4,GPIO.LOW)
time.sleep(t_time)

超声波避障

用两个GPIO端口分别接受和发送。

1
2
3
4
5
6
7
8
9
10
11
12
13
#超声波测距函数
def get_distance():
GPIO.output(Trig,GPIO.HIGH) #给Trig发送高电平,发出触发信号
time.sleep(0.00015) #需要至少10us的高电平信号,触发Trig测距
GPIO.output(Trig,GPIO.LOW)
while GPIO.input(Echo)!=GPIO.HIGH: #等待接收高电平
pass
t1=time.time() #记录信号发出的时间
while GPIO.input(Echo)==GPIO.HIGH: #接收端还没接收到信号变成低电平就循环等待(等高电平结束)
pass
t2=time.time() #记录接收到反馈信号的时间
distance=(t2-t1)*340*100/2 #计算距离,单位换成cm
return distance

图像循迹

  • 预处理:灰度化+低通滤波+0-1二值化+两轮膨胀,效果如下三条黑线为检测算法取的三条线标。效果如下:

  • 双线程分别处理图像和电机驱动,通过全局变量进行通信。

线程一 ,计算道路中心点:分别在灰度图像中取近距离,中距离,远距离三条水平线,用np.where求出每一条线中黑色像素的索引。假设只能看到赛道的一条边,按边界补齐另一条赛道并求平均值。求取中心点用了两种方案,各有所长:
方案一:用类似PD思路,将近距离赛道作为基准,将中距离和远距离做差并加权加到近距离赛道中心点上
方案二:三条线的中心点加权平均。
线程二 ,驱动电机运转:根据线程一维护的全局变量中心点来进行电机驱动。

  • 应急处理

应急处理一:底线倒车。此方法需要和赛道适配,在赛道间距较宽时可以使用,如果赛道间距过窄,需要调低摄像头位置。具体操作为,在视频图像的底线中间附近取像素点,判断其中是否存在黑色道路边界,如果存在边界,则进行0.1秒的倒车操作。

应急处理二:红外传感。用图像处理存在冲出赛道的可能,加上红外传感器,在赛车即将冲出赛道时候进行急转弯。

猫脸识别

使用Haar基于统计的分类器,Haar分类器=Haar-like特征+AdaBoost算法+级联+积分图快速计算。
Haar分类器算法的要点如下:
① 使用Haar-like特征做检测。
② 使用积分图(Integral Image)对Haar-like特征求值进行加速。
③ 使用AdaBoost算法训练区分人脸和非人脸的强分类器。
④ 使用筛选式级联把强分类器级联到一起,提高准确率。

结论及感悟

在本次智能车实验中,我充分学习了树莓派的结构以及使用方法,熟悉了一些树莓派使用的小技巧。同时在我负责的图像处理方面,将上学期学习的数字图像处理课程的一些方法用到实处。在循迹的过程中有很多小技巧,比如倒车,虽然冲出赛道并不是很好检测,但是可以投机取巧的计算最下面一行的黑像素,当然这种方法只是用于路比较宽的赛道,窄的赛道还需另寻他法。使用一些“小聪明”可以让我们非常好的完成任务,体现了矛盾普遍性和特殊性的关系,我们不断的学习和做实验就是为了在特殊性中总结普遍性,从个例中提取共性。只有经过不断的实践,才可以总结出更多的经验学会更多的知识。

代码

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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
#coding=utf-8
import RPi.GPIO as GPIO
import time
from tkinter import *
from PIL import Image,ImageTk
import tkinter.filedialog
import cv2
import threading
import numpy as np
#定义电机模块的GPIO口

Path_Dect_px=320

#---------------------------------
PIN_NO =29 #GPIO蜂鸣器
PWMA=11 #调速端A(左)
IN1=12
IN2=16
PWMB=13 #调速端B(右)
IN3=18
IN4=22
#定义超声波模块的GPIO口
Trig=38 #发射端
Echo=37 #接收端
#定义红外避障传感器GPIO口
L_Senso=36
R_Senso=35
#定义红外循迹传感器GPIO口
LSenso=40
RSenso=33
def init():
#设置解除警告
GPIO.setwarnings(False)
#设置引脚模式为物理模式
GPIO.setmode(GPIO.BOARD)
#对四个电机进行初始化,将引脚设置为输出
GPIO.setup(IN1,GPIO.OUT)
GPIO.setup(IN2,GPIO.OUT)
GPIO.setup(IN3,GPIO.OUT)
GPIO.setup(IN4,GPIO.OUT)
#调速端的初始化
GPIO.setup(PWMA,GPIO.OUT)
GPIO.setup(PWMB,GPIO.OUT)
#超声波传感器引脚初始化
GPIO.setup(Trig,GPIO.OUT) #将发射端引脚设置为输出
GPIO.setup(Echo,GPIO.IN) #将接收端引脚设置为输入
#红外避障传感器引脚初始化,设置为输入,接受红外信号
GPIO.setup(L_Senso,GPIO.IN)
GPIO.setup(R_Senso,GPIO.IN)
#红外循迹传感器引脚初始化,设置为输入,接受红外信号
GPIO.setup(LSenso,GPIO.IN)
GPIO.setup(RSenso,GPIO.IN)
#蜂鸣器初始化
GPIO.setup(PIN_NO, GPIO.OUT)
GPIO.output(PIN_NO, GPIO.LOW)



#根据上面的控制原理,我们通过控制每个引脚的电平信号,来控制小车的转向

#蜂鸣器函数
#哔1次,时长作为参数传递

def beep(seconds):

GPIO.output(PIN_NO, GPIO.HIGH)

time.sleep(seconds)

GPIO.output(PIN_NO, GPIO.LOW)

#哔N次,时长、间隔时长、重复次数作为参数传递

def beepAction(secs, sleepsecs, times):

for i in range(times):

beep(secs)

time.sleep(sleepsecs)

#beepAction(0.02,0.02,30)
#让小车前进
def turn_up(speed,t_time): #定义两个参数,一个是速度,一个是时间
L_Motor.ChangeDutyCycle(speed) #设置占空比,控制速度,speed的取值范围0-35,原理是控制电源通断的时间占比
GPIO.output(IN1,GPIO.HIGH)
GPIO.output(IN2,GPIO.LOW)

R_Motor.ChangeDutyCycle(speed)
GPIO.output(IN3,GPIO.HIGH)
GPIO.output(IN4,GPIO.LOW)
time.sleep(t_time)

#让小车后退
def turn_back(speed,t_time):
L_Motor.ChangeDutyCycle(speed)
GPIO.output(IN1,GPIO.LOW)
GPIO.output(IN2,GPIO.HIGH)

R_Motor.ChangeDutyCycle(speed)
GPIO.output(IN3,GPIO.LOW)
GPIO.output(IN4,GPIO.HIGH)
time.sleep(t_time)

#让小车向左转
def turn_left(speed,t_time):
L_Motor.ChangeDutyCycle(speed-8)
GPIO.output(IN1,GPIO.LOW)
GPIO.output(IN2,GPIO.HIGH)

R_Motor.ChangeDutyCycle(speed)
GPIO.output(IN3,GPIO.HIGH)
GPIO.output(IN4,GPIO.LOW)
time.sleep(t_time)

#让小车向右转
def turn_right(speed,t_time):
L_Motor.ChangeDutyCycle(speed)
GPIO.output(IN1,GPIO.HIGH)
GPIO.output(IN2,GPIO.LOW)

R_Motor.ChangeDutyCycle(speed-8)
GPIO.output(IN3,GPIO.LOW)
GPIO.output(IN4,GPIO.HIGH)
time.sleep(t_time)

#让小车停止
def car_stop():
L_Motor.ChangeDutyCycle(0)
GPIO.output(IN1,False)
GPIO.output(IN2,False)

L_Motor.ChangeDutyCycle(0)
GPIO.output(IN3,False)
GPIO.output(IN4,False)
#超声波测距函数
def get_distance():
GPIO.output(Trig,GPIO.HIGH) #给Trig发送高电平,发出触发信号
time.sleep(0.00015) #需要至少10us的高电平信号,触发Trig测距
GPIO.output(Trig,GPIO.LOW)
while GPIO.input(Echo)!=GPIO.HIGH: #等待接收高电平
pass
t1=time.time() #记录信号发出的时间
while GPIO.input(Echo)==GPIO.HIGH: #接收端还没接收到信号变成低电平就循环等待(等高电平结束)
pass
t2=time.time() #记录接收到反馈信号的时间
distance=(t2-t1)*340*100/2 #计算距离,单位换成cm
return distance
#避障功能函数(超声波避障结合红外避障)
def bizhang():

safe_dis=8 #设置一个安全距离
while True:
barrier_dis=get_distance() #获取当前障碍物的距离
#当测得前方障碍物距离小于安全距离时,先让小车停止
while (barrier_dis < safe_dis) == True:
print (barrier_dis,'cm')
L_S=GPIO.input(L_Senso)
R_S=GPIO.input(R_Senso) #接受红外避障传感器的信号
#如果红外传感器检测到左边有障碍物,右边没有,小车向右转
if L_S == False and R_S == True:

print ("左有障碍物先后退再右转")
turn_back(35,1)
turn_right(50,0.5)
#如果红外传感器检测到右边有障碍物,左边没有,小车向左转
elif (L_S == True and R_S == False):
print ("右有障碍物先后退再左转")
turn_back(35,1)
turn_left(50,0.5)
#如果红外传感器检测到左右两边都有障碍物,小车后退
elif L_S ==False and R_S == False:
print ("两边都有障碍物后退")
turn_back(35,1)
#再次接收红外信号(说明刚才的路线已经不能再走,退到一定程度,小车就得左转或者右转,提前寻找新的路线)
L_S=GPIO.input(L_Senso)
R_S=GPIO.input(R_Senso)
#退到左前和右前都没有障碍物的位置
if L_S == True and R_S == True:
print ("右转")
turn_right(50,0.5)
#退到了右前没有障碍物的位置
elif L_S == False and R_S == True:
print ("右转")
turn_right(50,0.5)
#退到了左前没有障碍物的位置
elif L_S == True and R_S == False:
print ("左转")
turn_left(50,0.5)
#还没有退出刚才的死路,继续后退

elif L_S == False and R_S == False:
print ("后退")
turn_back(35,1)
#如果红外传感器检测到两边都没有障碍物,此时让小车后退一点,然后向右转
elif L_S == True and R_S == True:

print ("两边都没有障碍物,后退再右转")
turn_back(35,1)
turn_right(50,0.5)

barrier_dis=get_distance()

while (barrier_dis < safe_dis) == False:
print (barrier_dis,'cm')

#小车在安全区里内,但是由于超声波传感器无法检测到除了正前方其他方向的障碍物,所以在此接收红外信号,通过左前和右前有没有障碍物,来判断小车该怎样运行
L_S=GPIO.input(L_Senso)
R_S=GPIO.input(R_Senso) #接受红外避障传感器的信号
#在安全距离内同时红外传感器检测到左前和有前方没有障碍物,小车前进
if L_S == True and R_S == True:
print ("前方3cm内没有障碍物,且左前和右前方没有障碍物,前进")
turn_up(35,1)
#在安全距离内,但左前有障碍物,让小车后退,再右转
elif L_S == False and R_S == True:
print ("前方3cm内没有障碍物,左前有障碍物,右前方没有障碍物,后退右转")
turn_back(35,1)
turn_right(50,0.5)
#在安全距离内,但右前有障碍物,让小车后退,再左转
elif L_S == True and R_S == False:
print ("前方3cm内没有障碍物,右前有障碍物,左前方没有障碍物,后退右转")
turn_back(35,1)
turn_left(50,0.5)
#在安全距离内,但左前,右前都有障碍物,让小车后退
elif L_S == False and R_S == False:
print ("前方3cm内没有障碍物,左前,右前方都有障碍物,后退")
turn_back(35,1)
#再次接收红外信号(说明刚才的路线已经不能再走,退到一定程度,小车就得左转或者右转,提前寻找新的路线)
L_S=GPIO.input(L_Senso)
R_S=GPIO.input(R_Senso)
#退到左前和右前都没有障碍物的位置
if L_S == True and R_S == True:
print ("右转")
turn_right(50,0.5)
#退到了右前没有障碍物的位置
elif L_S == False and R_S == True:
print ("右转")
turn_right(50,0.5)
#退到了左前没有障碍物的位置

elif L_S == True and R_S == False:
print ("左转")
turn_left(50,0.5)
#还没有退出刚才的死路,继续后退
elif L_S == False and R_S == False:
print ("后退")
turn_back(35,1)

barrier_dis=get_distance()

#红外循迹函数
def track():

while True:
#接收两个红外传感器的信号
LS=GPIO.input(LSenso)
RS=GPIO.input(RSenso)
#左右两个传感器都检测到黑色,小车在赛道上,前进
if LS==True and RS==True:
print ("前进")
turn_up(35,0.2)
#左边的传感器没检测到黑色,说明小车车身偏离赛道靠左,右转将小车车身向右调整
elif LS==False and RS==True:
print ("右转")
turn_right(50,0.2)
#右边的传感器没检测到黑色,说明小车车身偏离赛道靠右,左转将小车车身向左调整
elif LS==True and RS==False:
print ("左转")
turn_left(50,0.2)
#两个传感器都没有检测到黑色,说明小车完全偏离赛道,停止
else:
print ("停止")
car_stop()

#控制界面函数
def ConInterface():
#播放本地视频的函数
def playLocalVideo():
moviePath=filedialog.askopenfilename() #文件对话框,用来打开文件夹,选择文件
print(moviePath)
playBtn.place_forget() #隐藏播放按钮
movie=cv2.VideoCapture(moviePath)
waitTime= 1000/movie.get(5) #获取每帧需要的时间
print(waitTime)
movieTime = (movie.get(7) / movie.get(5))/60 #用视频文件的总帧数除以视频文件的帧速率得到视频的总时长,并换算成分钟
print(movieTime)
playSc.config(to=movieTime)
while movie.isOpened():
ret,readyFrame=movie.read() #read()返回两个值,ret 表示帧是否为空,readyFrame表示当前帧
if ret==True:
movieFrame=cv2.cvtColor(readyFrame,cv2.COLOR_BGR2RGBA) #校正颜色
newImage=Image.fromarray(movieFrame).resize((675,360)) #调整大小
newCover=ImageTk.PhotoImage(image=newImage)
video.configure(image=newCover) #将图像加入到video标签中
video.image=newCover
video.update()
cv2.waitKey(int(waitTime)) #播放完每帧等待的时间
else:
break

#使用opencv调用树莓派摄像头
def playVideo():
playBtn.place_forget() #点击播放按钮之后开始播放视频,隐藏按钮
movie=cv2.VideoCapture(0) #打开摄像头
movie_fps=movie.get(5) #得到画面的帧速率
print(movie_fps)
waitTime=int(1000/movie_fps)
print(waitTime)
while movie.isOpened():
#读取帧,返回布尔值和当前帧,ret 表示帧是否为空,readyFrame表示当前帧
ret,readyFrame=movie.read()
if ret==True:
#校正图像颜色
movieFrame=cv2.cvtColor(readyFrame,cv2.COLOR_BGR2RGBA)
#设置图像大小
newImage=Image.fromarray(movieFrame).resize((675,360))
#将照片设置成和tkinter兼容的图像
newCover=ImageTk.PhotoImage(image=newImage)
#将图像放入Label中
video.configure(image=newCover)
video.image=newCover
video.update()
cv2.waitKey(20)
else:
break
movie.release() #释放资源



#猫脸检测function
def det_catface():
# 加载猫脸检测器,此处使用data/haarcascade的猫脸识别训练集
catPath = "/home/pi/Desktop/haarcascade_frontalcatface.xml"
faceCascade = cv2.CascadeClassifier(catPath)
#humanPath = '/home/pi/Desktop/haarcascade_frontalface_default.xml'
#face_cascade = cv2.CascadeClassifier(humanPath)
# 读取图片
movie=cv2.VideoCapture(0) #打开摄像头
movie_fps=movie.get(5) #得到画面的帧速率
#print(movie_fps)
waitTime=int(1000/movie_fps)
#print(waitTime)
while movie.isOpened():
#读取帧,返回布尔值和当前帧,ret 表示帧是否为空,readyFrame表示当前帧
ret,readyFrame=movie.read()
if ret==True:
img=readyFrame
gray=cv2.cvtColor(readyFrame,cv2.COLOR_BGR2RGBA)
# 猫脸检测
catfaces = faceCascade.detectMultiScale(
gray,
scaleFactor= 1.02,
minNeighbors=3,
minSize=(150, 150),
flags=cv2.CASCADE_SCALE_IMAGE
)
''''humanfaces = face_cascade.detectMultiScale(
gray,
scaleFactor=1.15,
minNeighbors=5,
minSize=(3, 3)
)'''
# 框出猫脸并加上文字说明
for (x, y, w, h) in catfaces:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.putText(img,'Cat',(x,y-7), 3, 1.2, (0, 255, 0), 2, cv2.LINE_AA)
# 绘制人脸的矩形区域(红色边框)
'''for (x, y, w, h) in humanfaces:
cv2.rectangle(img, (x,y), (x+w,y+h), (0,0,255), 2)
cv2.putText(img,'Human',(x,y-7), 3, 1.2, (0, 255, 0), 2, cv2.LINE_AA)'''
# 显示视频(live)
#cv2.imshow('Detecting',readyFrame)
#刘泽家新加的部分,将视频放入控制框
#设置图像大小
newImage=Image.fromarray(readyFrame).resize((675,360))
#将照片设置成和tkinter兼容的图像
newCover=ImageTk.PhotoImage(image=newImage)
#将图像放入Label中
video.configure(image=newCover)
video.image=newCover
video.update()
num_catface=len(catfaces)
# 猫脸个数
#num_humanface=len(humanfaces)
# 人脸个数
print('检测到'+str(num_catface)+'只猫')#'检测到'+str(num_humanface)+'个人')
if num_catface>=1: #or num_humanface>=1:
#猫脸个数输出,给喇叭高电平
beepAction(1,1,num_catface)


print('停止前进')
car_stop()
#car_stop()
else:
print('正常前进')
turn_up(35,0.5)
cv2.imwrite("Finding.jpg",img)
cv2.waitKey(1)
else:
break
movie.release() #释放资源

#设置线程执行播放视频程序

def denoise(areas, pos):
#剔除误差过大数据
#因为距离近的话,摄像头拍的黑线会宽一点,所以这里的len有细微差异

result = []
result = areas
#for a in areas:
# if pos == 'near' and abs(len(a) - 125) < 15:
# result.append(a)
# elif pos == 'medium' and abs(len(a) - 90) < 15:
# result.append(a)
# elif pos == 'far' and abs(len(a) - 75) < 15:
# result.append(a)
#print(result)
#取最长的两组作为黑线两边
result1 = []
if len(areas) == 0:
return [[320]]
a_last = areas[0]
for a in areas:
if a[-1]>320:
if a[0]<320:
result1.append(a)
result1.append(a)
else:
result1.append(a_last)
result1.append(a)
a_last = a

if a[0]>320:
break


# for i in range(len(result)):
# len_list.append(len(result[i]))
# if len_list == []:
# return [340]
# result1.append(result[len_list.index(max(len_list))])
# if len(len_list)>1:
# len_list.remove(len(result1[0]))
# result1.append(result[len_list.index(max(len_list))])
#print('111')
return result1

def group_consecutives(vals, step=1):
#将黑线分组,如果abs超过2没有黑线,就切断(是不是有点苛刻?)
#print(vals)
run = []
result = []
expect = None
for v in vals:
if expect is None or abs(v - expect) < 2:
run.append(v)
else:
print(1)
result.append(run)
run = []
expect = v + step
#if result != []:
#print(len(result[0]))
#print(vals)
return vals
def get_size(line):
res = 0
for i in range(len(line)):
res+=len(line[i])
return res
def calc_m(line):
pre = line[0]
left = line[0]
right = line[0]
if line[0]>320:
return (line[0]-40)/2
if line[-1]<320:
return (line[-1]+680)/2
for i in line:
if i>320:
left = pre
right = i
pre = i
return (left+right)/2
def calc_eachline_center(line):


if len(line) == 1:



#print('m')
#print(m)
if len(line[0]) == 0:
return 320
m = np.mean(line[0])
#m = calc_m(line[0])
#return m
if m<315:
return (m+680)/2
elif m>325:
return (m-40)/2
else:
return m
return m
if len(line) == 2:
first_m = int(np.sum(np.sum(line))/get_size(line))
#sec = calc_eachline_center([[first_m]])
return first_m
else:
return 320
def get_center(near_line,medium_line,far_line):
#假设1:near的点必定取到两个点
#假设2:如果medium的点只能取到一边,则far的点不用考虑另外一边
#用大津法模糊二值化,寻找赛道
#主要思路类似PD控制,用差分来估测未来走向
#print(np.array(near_line).size)
center1,center2,center3 = calc_eachline_center(near_line),calc_eachline_center(medium_line),calc_eachline_center(far_line)
# center1 = int(np.sum(np.sum(near_line))/get_size(near_line))
# center3 = int(np.sum(np.sum(far_line))/get_size(far_line))
# center2 = int(np.sum(np.sum(medium_line))/get_size(medium_line))
print(center1,center2,center3)
#if len(near_line) == 2:
#如果要用左右center就取消注释
#center_l = int(np.sum(near_line[0])/near_line[0].size)
#center_r = int(np.sum(near_line[1])/near_line[1].size)
#center_l,center_r = min(center_l,center_r),max(center_l,center_r)
if len(medium_line) == 1:
#使用假设2
if center2>center3:
ans_center = np.mean([center1,center2-0*(center2 - center3)])
else:
ans_center = np.mean([center1,center2+0*(center3 - center2)])#常数根据实际情况调整
elif len(far_line) == 1:
ans_center = 0.4*center1+0.4*center2+0.2*center3
else:
ans_center = 0.5*center1+0.3*center2+0.2*center3
#ans_center = 0.5*center1+0.3*center2+0.2*center3
return ans_center

def looking_for_path():
#一些需要调试的参数,先摆在这里
#-----------------------
width_flag = 20#检测路线的宽度下限
near_pos = 300
medium_pos = 200
far_pos = 80
back_pos = 479
#-----------------------
cap = cv2.VideoCapture(0) #实例化摄像头
Path_Dect_px_sum = 0 #坐标值求和
threads = []
t1 = threading.Thread(target=PathDect,args=(u'监听',))
threads.append(t1)
for t in threads:
t.setDaemon(True)#奖pathdect作为子线程,应该是守护线程的设置
t.start()
while True:
ret,frame = cap.read() #capture frame_by_frame
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) #获取灰度图像
gray = cv2.blur(gray,(5,5))#有必要低通一下
ret,thresh1=cv2.threshold(gray,30,255,cv2.THRESH_BINARY)#对灰度图像进行二值化,thresh是
thresh1 = cv2.dilate(thresh1,None,iterations = 2)#膨胀操作一下,增加白色区域

#接收两个红外传感器的信号

#print(thresh1[near_pos])
#for i in range(len(thresh1)):
# for j in range(len(thresh1)):
# thresh1[i,j] = 255-thresh1[i,j]
near_line = group_consecutives(np.where(thresh1[near_pos] == 0))
medium_line = group_consecutives(np.where(thresh1[medium_pos] == 0))
far_line = group_consecutives(np.where(thresh1[far_pos] == 0))
back_line = group_consecutives(np.where(thresh1[back_pos] == 0))
#print(near_line,medium_line,far_line)
#near_line = denoise(near_line,'near')
#medium_line = denoise(medium_line,'medium')
#far_line = denoise(far_line,'far')

global Path_Dect_px
Path_Dect_px = get_center(near_line,medium_line,far_line)
print(Path_Dect_px)
if len(back_line[0]) != 0:
Path_Dect_px = 0
# Path_Dect_fre_count = 1
# for j in range(0,640,5): #采样像素点,5为步进,一共128个点
# if thresh1[240,j] == 0:
# Path_Dect_px_sum = Path_Dect_px_sum + j #黑色像素点坐标值求和
# Path_Dect_fre_count = Path_Dect_fre_count + 1 #黑色像素点个数求和
# Path_Dect_px = (Path_Dect_px_sum )/(Path_Dect_fre_count) #黑色像素中心点为坐标和除以个数
# Path_Dect_px_sum = 0

#我们需要多取几个点才能算斜率
#取三组,六个点,但可能存在取不到的情况。
#应该保证至少最下面的一组点能取到,这个做一个基本假设。
green = (0,255,0)
cv2.line(thresh1,(0,near_pos),(640,near_pos),green,1)
cv2.line(thresh1,(0,medium_pos),(640,medium_pos),green,1)
cv2.line(thresh1,(0,far_pos),(640,far_pos),green,1)
#刘泽家新加的部分,将视频放入控制框
#设置图像大小
newImage=Image.fromarray(thresh1).resize((675,360))
#将照片设置成和tkinter兼容的图像
newCover=ImageTk.PhotoImage(image=newImage)
#将图像放入Label中
video.configure(image=newCover)
video.image=newCover
video.update()
#cv2.imshow('BINARY',thresh1) #树莓派桌面显示二值化图像,比较占资源默认注释掉调试时可以打开
if cv2.waitKey(1)&0XFF ==ord('q'):#检测到按键q退出
car_stop()
break
cap.release()
def PathDect(func):
run_time = 0.01

while True:
if Path_Dect_px == 0:
turn_back(40,0.1)
LS=GPIO.input(LSenso)
RS=GPIO.input(RSenso)
#左边的传感器没检测到黑色,说明小车车身偏离赛道靠左,右转将小车车身向右调整
if LS==False and RS==True:
print ("infrared 右转")
#turn_left(50,0.1)
#右边的传感器没检测到黑色,说明小车车身偏离赛道靠右,左转将小车车身向左调整
if LS==True and RS==False:
print ("infrared 左转")
#turn_right(50,0.1)
print(sensitive)
print(Path_Dect_px)
#print 'Path_Dect_px %d '%Path_Dect_px #打印巡线中心点坐标值
if (Path_Dect_px < 290)&(Path_Dect_px > 0): #如果巡线中心点偏左,就需要左转来校正。
print("turn left")
turn_left(60,run_time)
elif Path_Dect_px > 350:#如果巡线中心点偏右,就需要右转来校正。
print("turn right")
turn_right(60,run_time)
else : #如果巡线中心点居中,就可以直行。
print("go stright")
turn_up(45,run_time)
time.sleep(0.007)
car_stop()
time.sleep(0.007)
def Thread_playVideo():

thread_playVideo=threading.Thread(target=looking_for_path)
thread_playVideo.start()


def Thread_detcatface():
thread_detcatface=threading.Thread(target=det_catface)
thread_detcatface.start()


root=Tk() #初始化tk
root.title("控制界面") #设置标题
root.geometry("1080x720") #设置窗口大小
root['bg']="#333333" #设置窗口背景颜色
#root.iconbitmap('/home/pi/ZRcode/go.ico')
root.resizable(width=False,height=False) #设置窗口长和宽是否可以变化


#分别设置三个Frame 模块用于存放Label,Button
videof=Frame(root,height=360,width=675,cursor="cross")
videof.place(x=199,y=0)

f1=Frame(root,height=270,width=540,cursor="circle",bg="#333333")
f1.place(x=269,y=359)

f2=Frame(root,height=180,width=540,cursor="plus",bg="#333333")
f2.place(x=269,y=629)

#视频窗口
movieImage=Image.open('/home/pi/Desktop/coverimg.jpg').resize((675,360))
cover=ImageTk.PhotoImage(image=movieImage)

video=Label(videof,width=675,height=360,bd=0,bg="pink",image=cover)
video.place(x=0,y=0)

#播放按钮的布局
iconImag=Image.open('/home/pi/Desktop/niubi2.PNG').resize((32,32))
icoBtn=ImageTk.PhotoImage(image=iconImag)
playBtn=Button(videof,image=icoBtn,cursor="hand2",command=Thread_playVideo,relief="groove")
playBtn.place(x=319,y=159)
#playSc=Scale(videof,from_=0,to=90,length=675,orient=HORIZONTAL,resolution=0.2,showvalue=0,bd=0,cursor="hand2",troughcolor="white")
#playSc.place(x=0,y=310)


#加载猫脸检测器
catImag=Image.open('/home/pi/Desktop/niubi3.jpg').resize((50,34))
catBtn=ImageTk.PhotoImage(image=catImag)
playBtn=Button(videof,image=catBtn,cursor="hand2",command=Thread_detcatface,relief="groove")
playBtn.place(x=309,y=259)


#控制按钮
up=Button(f1,text="前进",command=lambda:turn_up(40,1),activeforeground="green",activebackground="yellow",height=1,width=4)
up.place(x=267,y=39)
left=Button(f1,text="左转",command=lambda:turn_left(40,1),activeforeground="green",activebackground="yellow",height=1,width=4)
left.place(x=132,y=134)
right=Button(f1,text="右转",command=lambda:turn_right(40,1),activeforeground="green",activebackground="yellow",height=1,width=4)
right.place(x=412,y=134)
back=Button(f1,text="后退",command=lambda:turn_back(40,1),activeforeground="green",activebackground="yellow",height=1,width=4)
back.place(x=267,y=230)
stop=Button(f1,text="停止",command=car_stop,activeforeground="green",activebackground="yellow",height=1,width=4)
stop.place(x=267,y=134)
xunji=Button(f2,text="循迹",command=Thread_track,activeforeground="green",activebackground="yellow",height=1,width=4)
xunji.place(x=68,y=44)
bz=Button(f2,text="避障",command=Thread_bizhang,activeforeground="green",activebackground="yellow",height=1,width=4)
bz.place(x=461,y=44)
over=Button(f2,text="OVER",command=Thread_gpio_clean,activeforeground="green",activebackground="yellow",height=1,width=6)
over.place(x=263,y=44)

root.mainloop()

#清除GPIO占用
def gpio_clean():
print ("清除GPIO占用")
GPIO.cleanup()


#创建线程执行避障函数
def Thread_bizhang():
thread_bizhang=threading.Thread(target=bizhang)
thread_bizhang.start()

#创建线程执行循迹函数
def Thread_track():
thread_track=threading.Thread(target=track)
thread_track.start()

#创建线程执行清理GPIO函数
def Thread_gpio_clean():
Thread_gpio_clean=threading.Thread(target=gpio_clean)
Thread_gpio_clean.start()



#主函数
if __name__=="__main__":
init()
L_Motor=GPIO.PWM(PWMA,100)
L_Motor.start(0)
R_Motor=GPIO.PWM(PWMB,100)
R_Motor.start(0)
try:
ConInterface()
except KeyboardInterrupt:
GPIO.cleanup() #清除GPIO占用



留数计算反常积分

移步https://blog.csdn.net/weixin_42578412/article/details/110149981
这个hexo不支持latex公式。引html又有很多的加载项影响速度。

这是一道用分部积分法无法解出的题目,故写博客以记之。

题目

$$
\int_{0}^{+\infty}\frac{ln x}{a^2+x^2}dx
$$
其中a为常数。

方法一:朴素换元法

这是一道反常积分的题目,理论上应该分为两段计算,分别是一个第一类反常积分和一个第二类反常积分。不过如果求出原函数并且两边极限都存在的话就免去了分段这一步。

$$
\int_{0}^{+\infty}\frac{ln x}{a^2+x^2}dx
$$
首先进行换元,令x = atan(u)
$$
\overset{令x = a
tan(u)}{=}\int_{0}^{\pi/2}\frac{lna+ln(tan(u))}{\frac{a^2}{cos^2u}}d(a*tanu)
$$
$$
=\frac{1}{a}\int_{0}^{\pi/2}lna + ln(cosu)-ln(sinu)du
$$

又因为
$$
\int_{0}^{\pi/2}ln(cosu)du=\int_{0}^{\pi/2}ln(sinu)du
$$

所以原式可以化为
$$
\frac{1}{a}\int_{0}^{+\infty}lna,du=\frac{lna·\pi}{2a}
$$

方法二:留数

设$z_0$为$f(z)$的孤立奇点,$f(z)$在$H:0<|z-z_0|<R$内解析,C为H内包含$z_0$的简单正向闭曲线,则称一下为$f(z)$在$z_0$处的留数。

$$
Res[f(z_),z_0] = \frac{1}{2\pi i}\oint_{c}^{}f(z)dz
$$

留数和定理,设函数$f(z)$在扩充复平面内只有有限个孤立奇点,则$f(z)$在所有奇点(包括无穷)的留数总和为零。

观察原积分,将其扩展为

$$
\frac{1}{2}\int_{-\infty}^{+\infty}\frac{ln |x|}{a^2+x^2}dx
$$
然后取整个虚平面的上半平面的一个顺时针大半圆为积分路径则在上半圆内部只有两个奇点,一个是原点另一个是$ai$

$$
\int_{-\infty}^{+\infty}\frac{ln |x|}{a^2+x^2}dx =\int_{-\infty}^{+\infty}\frac{ln |x|}{(x+ai)(x-ai)}dx
$$
由于原点处的极点乘以$(x-0)$为0,所以该处的留数为0.
$$
\int_{-\infty}^{+\infty}\frac{ln |x|}{(x+ai)(x-ai)}dx = 2\pi i , Res[\frac{ln |x|}{(x+ai)(x-ai)},ai]
$$

然后计算即可

$$=2\pi i , \frac{ln,a}{2ai}=\frac{lna·\pi}{a}
$$

所以原式为该积分的一半

$$
\int_{0}^{+\infty}\frac{ln x}{a^2+x^2}dx =\frac{1}{2}\int_{-\infty}^{+\infty}\frac{ln |x|}{a^2+x^2}dx =\frac{lna·\pi}{2a}
$$

与方法一结果一致。

kmeans聚类手动实现-python版

聚类算法

是在没有给定划分类别的情况下,根据数据的相似度进行分组的一种方法,分组的原则是组内距离最小化而组间距离最大化。

K-means算法是典型的基于距离的非层次聚类算法,在最小化误差函数的基础上将数据划分为预定的K类别,采用距离作为相似性的评级指标,即认为两个对象的距离越近,其相似度越大。

kmeans流程

算法过程:

  1. 从N个样本数据中随机选取K个对象作为初始的聚类质心。
  2. 分别计算每个样本到各个聚类中心的距离,将对象分配到距离最近的聚类中。
  3. 所有对象分配完成之后,重新计算K个聚类的质心。
  4. 与前一次的K个聚类中心比较,如果发生变化,重复过程2,否则转过程5.
  5. 当质心不再发生变化时,停止聚类过程,并输出聚类结果。

数据集介绍

使用sklearn自带的鸢尾花数据集

from sklearn.datasets import load_iris
data = load_iris()

即可导入数据。

数据集中的特征包括:

print("鸢尾花数据集的返回值:\n", iris)
# 返回值是一个继承自字典的Bench
print("鸢尾花的特征值:\n", iris["data"])
print("鸢尾花的目标值:\n", iris.target)
print("鸢尾花特征的名字:\n", iris.feature_names)
print("鸢尾花目标值的名字:\n", iris.target_names)
print("鸢尾花的描述:\n", iris.DESCR)

我们这里只使用目标是和特征值,这个特征值经过print是一个四维向量。
鸢尾花的target只包括三种花,所以k=3时候理论上最佳。编写代码的时候将target一并写入数组进行组合,这样在聚类完成后进行验证分类的错误率来衡量算法好坏。

代码简介

代码除了调用数据集和划分数据集使用到了sklearn的库函数之外,其余全部使用python自带的list结构或set结构和numpy相关函数实现。

主函数

第一次随机选取核心,就取数据集的前k个分别作为中心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

def main(x,y,k,max_iter,interrupte_distance = 0):
#x是数据集,y是标签,k是聚类数目,终止距离和最大迭代数目.
#第一步初始化ak
a=[]
a_check = []#用于之后检查准确率
for i in range(k):
a.append([x.pop(0)])
a_check.append([y.pop(0)])
#初始化完了,来一遍第一次
for i in range(len(x)):
dist = []
for num in range(k):
dist.append(calc_distance(a[num][0],x[i]))
min_index = indexofMin(dist)
a[min_index].append(x[i])
a_check[min_index].append(y[i])
check_print(a,a_check)
#c初始化完成,a是一个k*特征数,的矩阵,a_check是检查矩阵。
for i in range(max_iter):
print('\n------------')
print('第'+str(i+1)+'次循环')
main_loop(a,a_check,interrupte_distance)

主循环

先检查终止条件是否满足。

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

def main_loop(a,a_check,interrupte_distance=0):
#a的一重下表是k,a_check跟着最后算准确率.第二下标是数据集。
k = len(a)
ll = len(a[0][0])#特征的数量

## a的第一个是上次的质心
#计算质心
a_array = np.array(a)
heart_calc = []
flag_interrupt = 0
for i in range(k):
heart_calc.append([])

#print(len())
#print(a[2])
#exit(0)
for i in range(k):
#对a[i]求均值了
for j in range(ll):
#print(a[i][:][j][1])
#print(i,j)
#heart_calc[i].append(np.mean(a[i][:][j]))
heart_calc[i].append(middle_mean(a,i,j))
if calc_distance(heart_calc[i],a[i][0])>interrupte_distance:
flag_interrupt = 1
if flag_interrupt == 0:
#满足距离要求
return 0

#聚类中心算完,并且不满足距离要求,开始迭代

for i in range(k):
for j in range(len(a[i])):
tmp = a[i].pop(0)
tmp_lab = a_check[i].pop(0)
tmp_index = find_heart_index(heart_calc,tmp)
a[tmp_index].append(tmp)
a_check[tmp_index].append(tmp_lab)
print('中心点坐标:')
print(heart_calc)
check_print(a,a_check)
return a,a_check

实验结果

当k=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

初始化错误率:0.35
初始化方差:0.2637878757089331


------------
第1次循环
中心点坐标:
[[5.173076923076924, 3.661538461538462, 1.4653846153846157, 0.27692307692307694], [6.528333333333333, 2.986666666666667, 5.255000000000001, 1.8366666666666667], [5.161764705882353, 2.8058823529411767, 2.85, 0.776470588235294]]
错误率:0.20833333333333334
方差:0.12704004558840606


------------
第2次循环
中心点坐标:
[[4.9975000000000005, 3.47, 1.4625, 0.2475], [6.495238095238095, 2.9777777777777783, 5.204761904761905, 1.8126984126984127], [5.447058823529411, 2.5529411764705885, 3.7588235294117647, 1.1588235294117646]]
错误率:0.15
方差:0.126100321239607


------------
第3次循环
中心点坐标:
[[4.9975000000000005, 3.47, 1.4625, 0.2475], [6.583928571428572, 3.001785714285714, 5.310714285714285, 1.8678571428571427], [5.545833333333333, 2.620833333333333, 3.9333333333333336, 1.2208333333333334]]
错误率:0.125
方差:0.12874688121486713


------------
第4次循环
中心点坐标:
[[4.9975000000000005, 3.47, 1.4625, 0.2475], [6.609433962264151, 3.020754716981132, 5.350943396226417, 1.8962264150943395], [5.61111111111111, 2.625925925925926, 4.007407407407407, 1.237037037037037]]
错误率:0.10833333333333334
方差:0.13044657523262912


------------
第5次循环
中心点坐标:
[[4.9975000000000005, 3.47, 1.4625, 0.2475], [6.631372549019607, 3.0156862745098034, 5.3803921568627455, 1.911764705882353], [5.641379310344826, 2.662068965517242, 4.048275862068966, 1.2551724137931033]]
错误率:0.10833333333333334
方差:0.13267293238109287

实验结论

整体来看,由于kmeans主要使用的是距离来分类,导致算法迅速收敛,一般两三个循环就可以收敛到最终结果了,即使一开始的中心点是随机选取的。

从实验结果可以看出,k=3和k=4时候差距不大,如果在增加k,效果就会变得不佳。由于k=4比三多一类,所以每一类内部的样本个数会变少,所以方差会略低与k=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
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

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import numpy as np

def indexofMin(arr):
minindex = 0
currentindex = 1
while currentindex < len(arr):
if arr[currentindex] < arr[minindex]:
minindex = currentindex
currentindex += 1
return minindex
def get_var(a):
#狗屁需求还要弄什么密度,用方差来反映离散程度吧
l = len(a)
ans = []
for i in range(l):
ans.append(np.array(a[i]).var(axis=0))
return ans
def calc_distance(list_a,list_b):
l = len(list_a)
#约束是最小均方误差MSE
ans = 0
for i in range(l):
ans += pow(list_a[i] - list_b[i],2)
return np.sqrt(ans)

def main(x,y,k,max_iter,interrupte_distance = 0):
#x是数据集,y是标签,k是聚类数目,终止距离和最大迭代数目.
#第一步初始化ak
a=[]
a_check = []#用于之后检查准确率
for i in range(k):
a.append([x.pop(0)])
a_check.append([y.pop(0)])
#初始化完了,来一遍第一次
for i in range(len(x)):
dist = []
for num in range(k):
dist.append(calc_distance(a[num][0],x[i]))
min_index = indexofMin(dist)
a[min_index].append(x[i])
a_check[min_index].append(y[i])
check_print(a,a_check)
#c初始化完成,a是一个k*特征数,的矩阵,a_check是检查矩阵。
for i in range(max_iter):
print('\n------------')
print('第'+str(i+1)+'次循环')
main_loop(a,a_check,interrupte_distance)

def middle_mean(l,i,j):
#妈的heart_calc[i].append(np.mean(a[i][:][j]))用不了,why doesnt np work?
tmp =[]
for ii in range(len(l[i])):
tmp.append(l[i][ii][j])
return np.mean(tmp)

def main_loop(a,a_check,interrupte_distance=0):
#a的一重下表是k,a_check跟着最后算准确率.第二下标是数据集。
k = len(a)
ll = len(a[0][0])#特征的数量

## a的第一个是上次的质心
#计算质心
a_array = np.array(a)
heart_calc = []
flag_interrupt = 0
for i in range(k):
heart_calc.append([])

#print(len())
#print(a[2])
#exit(0)
for i in range(k):
#对a[i]求均值了
for j in range(ll):
#print(a[i][:][j][1])
#print(i,j)
#heart_calc[i].append(np.mean(a[i][:][j]))
heart_calc[i].append(middle_mean(a,i,j))
if calc_distance(heart_calc[i],a[i][0])>interrupte_distance:
flag_interrupt = 1
if flag_interrupt == 0:
#满足距离要求
return 0

#聚类中心算完,并且不满足距离要求,开始迭代

for i in range(k):
for j in range(len(a[i])):
tmp = a[i].pop(0)
tmp_lab = a_check[i].pop(0)
tmp_index = find_heart_index(heart_calc,tmp)
a[tmp_index].append(tmp)
a_check[tmp_index].append(tmp_lab)
print('中心点坐标:')
print(heart_calc)
check_print(a,a_check)
return a,a_check
def check_print(a,a_check):
sum_num = 0
wrong_num = 0
for i in range(len(a_check)):
tmp = max(a_check[i],key = a_check.count)
for j in a_check[i]:
sum_num+=1
if j != tmp:
wrong_num += 1
#print(a_check)
print("错误率:"+str(wrong_num/sum_num))
print("方差:"+str(np.mean(get_var(a))))
print()
def find_heart_index(heart,undef):
#heart是各个中心节点,undef是待确定的特征
k = len(heart)
#print(heart)
min_index = 0
min_dis = 100000
for i in range(k):
tmp_dis = calc_distance(undef,heart[i])
#print(tmp_dis)
if(tmp_dis<min_dis):
min_index = i
min_dis = tmp_dis
#print('min')
#print(min_index)
return min_index


if __name__ == '__main__':
data = load_iris()

#print(data.keys())
x_train, x_test, y_train, y_test = train_test_split(data.data, data.target,test_size=0.2, random_state=222)


#print("测试集的目标值:\n", y_train)
main(x_train.tolist(),y_train.tolist(),4,5)

实轴上开环零极点之间的分离会和点问题

问题

判断题:

若实轴上某开环极点与开环零点之间具有根轨迹,那么他们之间肯定没有分离汇合点。

答案:错,可以有分离汇合点,反例如下。

例子

sys =
 
     s + 1
  -----------
  s^3 + 9 s^2

或者

sys =
 
     s + 2
  ------------
  s^3 + 20 s^2

  • Copyrights © 2015-2024 galaxy
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信