3.4.7-卡尔曼滤波与运动估计算法介绍和使用 STM32串口通信 openmv+STM32串口通信 openmv串口通信openmv识别物体 openmv神经网络训练 openmv数字识

非常详细的视频和文字教程,讲解常见的openmv教程包括 巡线、物体识别、圆环识别、阈值自动获取等。非常适合学习openmv、K210、K230等项目
视频合集链接在:

openmv教程合集 openmv入门到项目开发 openmv和STM32通信 openmv和opencv区别 openmv巡线 openmv数字识别教程LCD


下面的代码是自己写几个点的坐标,模拟后面识别到的物体,然后把前几个点给卡尔曼算法,卡尔曼算法就会预测计算出后面的点轨迹

注意为了方便演示,这里给系统增加了500ms延时时间

    time. sleep_ms(500) #方便演示增加500ms延时时间

所以我们就想要大概调整下Ts时间

Ts = 1/2 #Ts = 1  帧率的倒数  这个要根据延时手动调节
import sensor, image, time
#*********[0]-增加卡尔曼计算需要数学计算的模块导入*********************************************
import sensor, image, time, math
from ulab import numpy as np
#教程作者:好家伙VCC
#欢迎交流群QQ: 771027961 作者邮箱: 1930299709@qq.com
#更多教程B站主页:[好家伙VCC的个人空间-好家伙VCC个人主页-哔哩哔哩视频](https://space.bilibili.com/434192043)
#淘宝主页链接:[首页-好家伙VCC-淘宝网](https://shop415231378.taobao.com)
#更多嵌入式手把手教程-尽在好家伙VCC
# 初始化摄像头模块
sensor.reset()  # 重置摄像头,确保设备正常工作
sensor.set_pixformat(sensor.RGB565)  # 设置摄像头的像素格式为RGB565,每个像素16位色深
sensor.set_framesize(sensor.QQVGA)  # 设置摄像头的分辨率为QQVGA(160x120),适合快速处理

# *************************** 如果不需要镜像就注释掉以下代码 **************************
# 摄像头镜像和翻转设置,根据摄像头的安装方向调整
sensor.set_vflip(True)  # 设置垂直翻转,适用于摄像头上下安装的情况
sensor.set_hmirror(True)  # 设置水平翻转,适用于摄像头左右安装的情况
# *************************** 如果不需要镜像就注释掉以上代码 **************************

sensor.skip_frames(time = 2000)  # 跳过前几帧的图像,确保图像稳定后再开始处理
sensor.set_auto_gain(False)  # 必须关闭自动增益,防止影响颜色追踪
sensor.set_auto_whitebal(False)  # 必须关闭自动白平衡,防止影响颜色追踪

# 创建一个时钟对象,用于计算和控制帧率
clock = time.clock()
#****************演示预测定义的变量 用来表示要识别到的点************
ball_positions = [#*演示预测定义的变量 这个是80个点
    [20, 50, 5],
    [22, 50, 5],
    [24, 50, 5],
    [26, 50, 5],
    [28, 50, 5],
    [30, 50, 5],
    [32, 50, 5],
    [34, 50, 5],
    [36, 50, 5],
    [38, 50, 5],
    [40, 50, 5],
    [42, 50, 5],
    [44, 50, 5],
    [46, 50, 5],
    [48, 50, 5],
    [50, 50, 5],
    [52, 50, 5],
    [54, 50, 5],
    [56, 50, 5],
    [58, 50, 5],
    [60, 50, 5],
    [62, 50, 5],
    [64, 50, 5],
    [66, 50, 5],
    [68, 50, 5],
    [70, 50, 5],
    [72, 50, 5],
    [74, 50, 5],
    [76, 50, 5],
    [78, 50, 5],
    [80, 50, 5],
    [82, 50, 5],
    [84, 50, 5],
    [86, 50, 5],
    [88, 50, 5],
    [90, 50, 5],
    [92, 50, 5],
    [94, 50, 5],
    [96, 50, 5],
    [98, 50, 5],
    [100, 50, 5],
    [102, 50, 5],
    [104, 50, 5],
    [106, 50, 5],
    [108, 50, 5],
    [110, 50, 5],
    [112, 50, 5],
    [114, 50, 5],
    [116, 50, 5],
    [118, 50, 5],
    [120, 50, 5],
    [122, 50, 5],
    [124, 50, 5],
    [126, 50, 5],
    [128, 50, 5],
    [130, 50, 5],
    [132, 50, 5],
    [134, 50, 5],
    [136, 50, 5],
    [138, 50, 5],
    [140, 50, 5],
    [142, 50, 5],
    [144, 50, 5],
    [146, 50, 5],
    [148, 50, 5],
    [150, 50, 5],
    [152, 50, 5],
    [154, 50, 5],
    [156, 50, 5],
    [158, 50, 5],
    [160, 50, 5],
    [162, 50, 5],
    [164, 50, 5],
    [166, 50, 5],
    [168, 50, 5],
    [170, 50, 5],
    [172, 50, 5],
    [174, 50, 5],
    [176, 50, 5],
    [178, 50, 5],
    [180, 50, 5],
    [182, 50, 5],
    [184, 50, 5],
    [186, 50, 5],
    [188, 50, 5],
    [190, 50, 5],
    [192, 50, 5],
    [194, 50, 5],
    [196, 50, 5],
    [198, 50, 5],
    [200, 50, 5]
]

# 设置遍历的起始索引和最大索引
start_index = 0  # 开始从哪个位置进行识别

#*********************[1]-增加卡尔曼相关变量定义**有些参数需要根据情况调整 比如Ts*************************
Ts = 1/2 #Ts = 1  帧率的倒数  这个要根据延时手动调节
# 状态空间矩阵定义
 # 状态转移矩阵 A,描述系统的状态变化
#A = np.array([[1,0,0,0,Ts,0],[0,1,0,0,0,Ts],[0,0,1,0,0,0],[0,0,0,1,0,0],[0,0,0,0,1,0],[0,0,0,0,0,1]])
# 改进状态转移矩阵A,将速度部分更明确地包含进来A 是状态转移矩阵,描述了系统状态(位置、速度等)的变化。该矩阵会把当前的状态转换到下一时刻的状态。
A = np.array([[1, 0, 0, 0, Ts, 0],
              [0, 1, 0, 0, 0, Ts],
              [0, 0, 1, 0, 0, 0],
              [0, 0, 0, 1, 0, 0],
              [0, 0, 0, 0, 1, 0],
              [0, 0, 0, 0, 0, 1]])

 # 观测矩阵 C,描述从状态到观测值的映射关系  C 是观测矩阵,它将状态向量(位置、速度)与观测量(图像中的矩形框信息)联系起来。这里假设观测量是位置和速度。
C = np.array([[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,1,0,0,0],[0,0,0,1,0,0],[0,0,0,0,1,0],[0,0,0,0,0,1]])
# 过程噪声协方差矩阵
# 过程噪声协方差矩阵 Q,用于描述过程的随机噪声
#Q_value = [1e-8 for _ in range(6)]
#Q = np.diag(Q_value) #创建对角矩阵
# 增大过程噪声矩阵Q_value,使得卡尔曼滤波更灵活地应对物体快速运动
#Q 是过程噪声协方差矩阵,用于描述系统过程中的不确定性。矩阵的对角元素表示每个状态变量的噪声水平。
Q_value = [1e-6 for _ in range(6)]  # 调整过程噪声值Q_value = [1e-6 for _ in range(6)]
Q = np.diag(Q_value)  # 更新过程噪声协方差矩阵

# 观测噪声协方差矩阵  R 是观测噪声协方差矩阵,表示观测过程中测量误差的大小。
R_value = [1e-6 for _ in range(6)]
R = np.diag(R_value)
# 定义观测量Z
x = 0 #左顶点x坐标
y = 0 #左顶点y坐标
last_frame_x = x #上一帧左顶点x坐标
last_frame_y = y #上一帧左顶点y坐标
w = 0 #矩形框宽度w
h = 0 #矩形框高度h
dx = 0 #左顶点x坐标移动速度
dy = 0 #左顶点y坐标移动速度
Z = np.array([x,y,w,h,dx,dy])
# 定义卡尔曼滤波函数变量
#x_hat = np.array([80,60,30,30,2,2]) # 初始估计的状态值(位置、速度等)
# 初始状态估计:根据实际应用情况调整位置和速度的初值
x_hat = np.array([80, 60, 30, 30, 2, 2])  # 根据你的应用需要调整这些值

x_hat_minus = np.array([0,0,0,0,0,0]) # 初始预测的状态值
p_value = [10 for _ in range(6)] # 状态误差的初始值 p 是状态误差的初始协方差矩阵。
p = np.diag(p_value)# 创建误差协方差矩阵 p
#*********************[2]-增加卡尔曼滤波函数**这个不需要动基本复制过去就可以用***********************************************************************
# 卡尔曼滤波函数
#预测阶段:利用状态转移矩阵和上一状态估计预测当前状态。
#校正阶段:通过卡尔曼增益对预测状态进行校正,使得估计值接近真实值。
#输入 Z:观测值(或测量值),通常是来自外部传感器(例如相机、雷达等)的数据。在这个代码中,Z 是一个包含目标的位置信息(如矩形框的四个角坐标)的向量,格式为 [x, y, w, h, dx, dy],其中 x 和 y 是目标的中心位置,w 和 h 是目标的宽度和高度,dx 和 dy 是目标的速度。
#输出 x_hat:更新后的状态估计,包括位置(x, y)、宽度(w, h)、速度(dx, dy)。该值是通过卡尔曼滤波器的预测和校正步骤计算得到的最优估计。
def Kalman_Filter(Z):
    global A,C,Q,R,x_hat,x_hat_minus,p
    # 预测部分
    x_hat_minus = np.dot(A,x_hat)
    p_minus = np.dot(A, np.dot(p, A.T)) + Q
    # 校正部分
    S = np.dot(np.dot(C, p_minus), C.T) + R
    # 选择一个小的正则化项
    regularization_term = 1e-4
    # 正则化 S 矩阵
    S_regularized = S + regularization_term * np.eye(S.shape[0])
    # 计算正则化后的 S 矩阵的逆
    S_inv = np.linalg.inv(S_regularized)
    # 计算卡尔曼增益 K
    K = np.dot(np.dot(p_minus, C.T), S_inv)
    x_hat = x_hat_minus + np.dot(K,(Z - np.dot(C,x_hat_minus)))
    p = np.dot((np.eye(6) - np.dot(K,C)),p_minus)
    return x_hat
#*********************[3]-定义用于存储给卡尔曼滤波使用的变量 用于保存一些历史数据**这个也不需要动***********************************************************************
last_frame_location = [0 for _ in range(4)] #用于存储上一帧的目标位置,这通常用于目标跟踪和计算目标移动等任务。一个长度为4的列表 last_frame_location,其中每个元素的初始值为 0
last_frame_rect = [0 for _ in range(4)]  #存储上一帧检测到的矩形框坐标 成了一个长度为4的列表 last_frame_rect,并且每个元素的初始值为 0。
box = [0 for _ in range(4)] #生成一个包含四个子列表的列表,存储x1,y1,x2,y2

# 主循环,不断获取摄像头图像并进行处理
while(True):
    clock.tick()  # 计时当前帧的处理时间,计算帧率
    # 获取当前图像并进行镜头畸变校正,纠正因镜头产生的畸变
    img = sensor.snapshot().lens_corr(1.8)  # 1.8是畸变系数,适当调整可以改善图像质量

    start_index += 1  # 遍历下一个位置
    if start_index >= 80: #如果值过大,就清零
        start_index = 0  # 将 start_index 重置为 0

    # 确定当前需要使用哪个位置的数据
    if start_index < 25 or start_index > 40:   #设置多少个点设置为可以被检测到
        #*********************[4]-赋值要输入卡尔曼的数据*************
        circle_center_x, circle_center_y, circle_radius = ball_positions[start_index]  # 从ball_positions获取当前球的中心坐标和半径
        #*********************[4]-赋值要输入卡尔曼的数据*************


        img.draw_circle(circle_center_x, circle_center_y, circle_radius, color=(192, 255, 0)) #绘制圆为绿色
        print("识别到的坐标          recognition : center_x = {}, center_y = {}, radius = {}".format(circle_center_x, circle_center_y, circle_radius))

        #*********************[4]-检测出要识别物体后通过物体位置坐标等信息输入卡尔曼**根据自己的实际情况赋值rect_x、rect_y、rect_w、rect_h*********
        #需要给赋值的是下面四个参数 rect_x、rect_y、rect_w、rect_h 这些参数是圈住识别物体的矩形框
        # 计算矩形框的左上角坐标和宽高 给要赋值的四个变量赋值
        rect_x = int(circle_center_x - circle_radius)  # 矩形框左上角的 x 坐标
        rect_y = int(circle_center_y - circle_radius)  # 矩形框左上角的 y 坐标
        rect_w = int(2 * circle_radius)  # 矩形框的宽度,等于圆的直径
        rect_h = int(2 * circle_radius)  # 矩形框的高度,等于圆的直径

        # 将矩形框的四个参数存储到 rect 数组中
        rect = [rect_x, rect_y, rect_w, rect_h]
        box = [rect[0], rect[1], rect[0] + rect[2], rect[1] + rect[3]] # 计算标记框的四个角坐标(左上角和右下角)
        x, y, w, h = rect[0], rect[1], rect[2], rect[3]# 获取新的矩形框的坐标 识别成功的时候这个值是识别值的数据赋值
        dx = (x - last_frame_x) / Ts# 计算x方向的速度,假设 Ts 为时间步长
        dy = (y - last_frame_y) / Ts# 计算y方向的速度
        Z = np.array([x, y, w, h, dx, dy]) # 构造测量向量 Z,包括位置和速度
        x_hat = Kalman_Filter(Z)# 使用卡尔曼滤波器进行状态估计
        last_frame_x, last_frame_y = x, y # 更新上一帧的x, y坐标
        last_frame_rect = rect# 更新上一帧的矩形框 识别成功的时候
        last_frame_location = box# 更新上一帧的位置 识别成功的时候
    else:
        #*********************[5]-检测检测不出物体时候 使用之前物体数据预测物体下面的位置等**不需要改动**********
        #解释:根据卡尔曼滤波的预测值和目标的速度来更新目标的位置:
        #x_hat[0] 和 x_hat[1] 是预测的位置,x_hat[4] 和 x_hat[5] 是目标的速度(速度分别对应 dx 和 dy)。
        #Ts 是时间步长。更新后的 x, y 位置是通过加入速度(dx 和 dy)的影响来计算的。
        #w 和 h 是目标的宽度和高度,这些信息没有改变,仍然使用 x_hat[2] 和 x_hat[3]。
        x,y,w,h = (x_hat[0] + (x_hat[4] * Ts)),(x_hat[1] + (x_hat[5] * Ts)),x_hat[2],x_hat[3]
        #解释:计算目标在 X 方向的速度(dx),通过当前帧的 x 位置与上一帧的 last_frame_x 位置之差来计算,除以时间步长 Ts
        dx = (x - last_frame_x) / Ts
        #解释:计算目标在 Y 方向的速度(dy),通过当前帧的 y 位置与上一帧的 last_frame_y 位置之差来计算,除以时间步长 Ts
        dy = (y - last_frame_y) / Ts
        #解释:将目标的位置、宽高、速度(dx 和 dy)组合成一个状态向量 Z,这个向量将作为卡尔曼滤波器的输入
        Z = np.array([x, y, w, h, dx, dy])
        #解释:将状态向量 Z 传递给卡尔曼滤波器 Kalman_Filter,以获得更新后的预测状态 x_hat
        x_hat = Kalman_Filter(Z)
        #更新上一帧的目标位置 last_frame_x 和 last_frame_y 为当前帧的目标位置 x 和 y,以便在下一帧计算速度时使用
        last_frame_x, last_frame_y = x, y
        #更新上一帧的目标矩形框 last_frame_rect 为当前帧的矩形框 x, y, w, h。
        last_frame_rect = [x,y,w,h]
        #更新上一帧的目标位置 last_frame_location 为当前帧的目标位置。这是一个矩形框的坐标,包括左上角 (x, y) 和右下角 (x + w, y + h)
        last_frame_location = [x,y,(x + w),(y + h)]

    #*********************[6]-无论是否可以检测到物体都 根据卡尔曼预测值绘制矩形框**这个不需要改动*********
    predicted_rect = [ # 使用卡尔曼滤波的预测值绘制预测的矩形框
        int(x_hat[0]),# 预测的矩形框左上角x坐标
        int(x_hat[1]),# 预测的矩形框左上角y坐标
        int(x_hat[2]), # 预测的矩形框宽度
        int(x_hat[3]) # 预测的矩形框高度
    ]
    # 绘制矩形框 这个就是一直绘制的卡尔曼预测的位置
    img.draw_rectangle(predicted_rect, color=(100, 100, 100))  # 使用灰色绘制矩形框
    #下面是把矩形转化会园绘制的,我们不需要
    # 计算圆心坐标和半径
    center_x = int(x_hat[0] + x_hat[2] / 2)  # 圆心x坐标(矩形框左上角x + 宽度的一半)
    center_y = int(x_hat[1] + x_hat[3] / 2)  # 圆心y坐标(矩形框左上角y + 高度的一半)
    radius = int(min(x_hat[2], x_hat[3]) / 2)  # 半径取矩形框宽度和高度的最小值的一半
    # 输出圆心坐标和半径信息
    print("卡尔曼预测       Predicted Circle: center_x = {}, center_y = {}, radius = {}".format(center_x, center_y, radius))


    # 打印当前帧率(每秒帧数),便于调试性能
    print("FPS %f" % clock.fps())  # 输出当前的帧率
    time. sleep_ms(500) #方便演示增加500ms延时时间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code_未来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值