【51单片机快速入门指南】4.4.1:python串口接收磁力计数据并进行最小二乘法椭球拟合

发布者:gamma13最新更新时间:2022-07-15 来源: csdn关键字:51单片机  python  串口接收 手机看文章 扫描二维码
随时随地手机看文章

STC15F2K60S2 16.384MHz

Keil uVision V5.29.0.0

PK51 Prof.Developers Kit Version:9.60.0.0

Python 3.8.11 (default, Aug 6 2021, 09:57:55) [MSC v.1916 64 bit (AMD64)] :: Anaconda, Inc. on win32


参考资料:

笔记:python读取串口数据并保到本地txt文件 —— 大头工程师笔记

最小二乘法拟合—基本原理 —— 铁头娃-wefly


硬知识

 image.png

image.png

Python代码

if not 1:   # 0为串口收集数据,1为椭球拟合

    import serial


    ser = serial.Serial(

        port='COM8',

        baudrate=1200,

        parity=serial.PARITY_NONE,  # 校验位

        stopbits=serial.STOPBITS_ONE,  # 停止位

        bytesize=serial.EIGHTBITS  # 数据位

    )


    First_Flag = 0


    while True:

        data = ser.readline()

        if First_Flag:  # 丢掉第一次的,避免写入半截数据

            f = open('./Data.txt', 'a')

            data = data.decode('utf-8')[:-1]

            f.write(data)

            f.close()

            print(data)

        First_Flag = 1

else:

    Data_Path = r'./Data.txt'

    f = open(Data_Path, 'r')

    X = []

    Y = []

    Z = []


    for _ in f:

        List = _.replace(",", " ").split()

        X.append(int(List[0]))

        Y.append(int(List[1]))

        Z.append(int(List[2]))


    f.close()


    from matplotlib.font_manager import FontProperties

    from numpy.linalg import inv

    from numpy import arange, zeros

    from math import sqrt, sin, cos


    from matplotlib import pyplot as plt



    def dot_Mul(arr1, arr2):

        return [a * b for a, b in zip(arr1, arr2)]



    PI = 3.1415926535897932384626433832795


    # 实测数据

    f = open(Data_Path, 'r')

    x = []

    y = []

    z = []


    for _ in f:

        List = _.replace(",", " ").split()

        x.append(int(List[0]))

        y.append(int(List[1]))

        z.append(int(List[2]))


    f.close()


    # 数据总数

    num_points = len(x)


    # 一次项均值

    x_avr = sum(x) / num_points

    y_avr = sum(y) / num_points

    z_avr = sum(z) / num_points


    # 二次项均值

    xx_avr = sum(dot_Mul(x, x)) / num_points

    yy_avr = sum(dot_Mul(y, y)) / num_points

    zz_avr = sum(dot_Mul(z, z)) / num_points

    xy_avr = sum(dot_Mul(x, y)) / num_points

    xz_avr = sum(dot_Mul(x, z)) / num_points

    yz_avr = sum(dot_Mul(y, z)) / num_points


    # 三次项均值

    xxx_avr = sum(dot_Mul(dot_Mul(x, x), x)) / num_points

    xxy_avr = sum(dot_Mul(dot_Mul(x, x), y)) / num_points

    xxz_avr = sum(dot_Mul(dot_Mul(x, x), z)) / num_points

    xyy_avr = sum(dot_Mul(dot_Mul(x, y), y)) / num_points

    xzz_avr = sum(dot_Mul(dot_Mul(x, z), z)) / num_points

    yyy_avr = sum(dot_Mul(dot_Mul(y, y), y)) / num_points

    yyz_avr = sum(dot_Mul(dot_Mul(y, y), z)) / num_points

    yzz_avr = sum(dot_Mul(dot_Mul(y, z), z)) / num_points

    zzz_avr = sum(dot_Mul(dot_Mul(z, z), z)) / num_points


    # 四次项均值

    yyyy_avr = sum(dot_Mul(dot_Mul(dot_Mul(y, y), y), y)) / num_points

    zzzz_avr = sum(dot_Mul(dot_Mul(dot_Mul(z, z), z), z)) / num_points

    xxyy_avr = sum(dot_Mul(dot_Mul(dot_Mul(x, x), y), y)) / num_points

    xxzz_avr = sum(dot_Mul(dot_Mul(dot_Mul(x, x), z), z)) / num_points

    yyzz_avr = sum(dot_Mul(dot_Mul(dot_Mul(y, y), z), z)) / num_points


    # 系数矩阵

    A_Matrix = [[yyyy_avr, yyzz_avr, xyy_avr, yyy_avr, yyz_avr, yy_avr],

                [yyzz_avr, zzzz_avr, xzz_avr, yzz_avr, zzz_avr, zz_avr],

                [xyy_avr, xzz_avr, xx_avr, xy_avr, xz_avr, x_avr],

                [yyy_avr, yzz_avr, xy_avr, yy_avr, yz_avr, y_avr],

                [yyz_avr, zzz_avr, xz_avr, yz_avr, zz_avr, z_avr],

                [yy_avr, zz_avr, x_avr, y_avr, z_avr, 1]]


    # 等式右边的常数项矩阵

    B_Matrix = [[-xxyy_avr], [-xxzz_avr], [-xxx_avr], [-xxy_avr], [-xxz_avr], [-xx_avr]]


    result = inv(A_Matrix) @ B_Matrix


    xo = -result[2] / 2  # 拟合出的x坐标

    yo = -result[3] / (2 * result[0])  # 拟合出的y坐标

    zo = -result[4] / (2 * result[1])  # 拟合出的z坐标


    # 拟合出的x方向上的轴半径

    A = sqrt(xo * xo + result[0] * yo * yo + result[1] * zo * zo - result[5])

    # 拟合出的y方向上的轴半径

    B = A / sqrt(result[0])

    # 拟合出的z方向上的轴半径

    C = A / sqrt(result[1])


    ABC_avr = (A + B + C) / 3

    kA = ABC_avr / A

    kB = ABC_avr / B

    kC = ABC_avr / C

    xo = xo[0]

    yo = yo[0]

    zo = zo[0]


    print("拟合结果: ")

    print("xo = ", xo)  # 椭球球心x坐标

    print("yo = ", yo)  # 椭球球心y坐标

    print("zo = ", zo)  # 椭球球心z坐标

    print("A = ", A)  # 拟合出的x方向上的轴半径

    print("B = ", B)  # 拟合出的y方向上的轴半径

    print("C = ", C)  # 拟合出的z方向上的轴半径

    print("kA = ", kA)

    print("kB = ", kB)

    print("kC = ", kC)


    num_alpha = 90

    num_sita = 45


    alfa = arange(0, num_alpha) * 1 * PI / num_alpha

    sita = arange(0, num_sita) * 2 * PI / num_sita

    X = zeros((num_alpha, num_sita))

    Y = zeros((num_alpha, num_sita))

    Z = zeros((num_alpha, num_sita))


    for i in range(0, num_alpha):

        for j in range(0, num_sita):

            X[i, j] = xo + A * sin(alfa[i]) * cos(sita[j])

            Y[i, j] = yo + B * sin(alfa[i]) * sin(sita[j])

            Z[i, j] = zo + C * cos(alfa[i])

    X = [i for arr in X for i in arr]

    Y = [i for arr in Y for i in arr]

    Z = [i for arr in Z for i in arr]


    fig = plt.figure()


    Font = FontProperties(fname=r"c:windowsfontssimsun.ttc", size=20)


    ax1 = fig.add_subplot(221, projection='3d')

    ax1.set_title('实测、拟合对比', fontproperties=Font)

    ax1.scatter3D(X, Y, Z)  # 拟合

    ax1.scatter3D(x, y, z)  # 实测


    ax2 = fig.add_subplot(222)

    ax2.set_title('x-y投影', fontproperties=Font)

    ax2.scatter(X, Y)

    ax2.scatter(x, y)


    ax3 = fig.add_subplot(223)

    ax3.set_title('x-z投影', fontproperties=Font)

    ax3.scatter(X, Z)

    ax3.scatter(x, z)


    ax4 = fig.add_subplot(224)

    ax4.set_title('y-z投影', fontproperties=Font)

    ax4.scatter(Y, Z)

    ax4.scatter(y, z)


    plt.show()


使用方法

HMC5883L、QMC5883L的驱动程序见【51单片机快速入门指南】4.4:I2C 读取HMC5883L / QMC5883L 磁力计


串口收集数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

转动板子到各个角度

当觉得收集够时停止脚本

在这里插入图片描述

椭球拟合

开始椭球拟合

在这里插入图片描述

得到拟合结果:

在这里插入图片描述
在这里插入图片描述

验证

将计算结果用于矫正输出

在这里插入图片描述

清理掉旧数据后重新收集并拟合,得到如下结果,可见新的球心偏移较未矫正前小,且得到的椭球更接近正球。

在这里插入图片描述

在这里插入图片描述


关键字:51单片机  python  串口接收 引用地址:【51单片机快速入门指南】4.4.1:python串口接收磁力计数据并进行最小二乘法椭球拟合

上一篇:【51单片机快速入门指南】4.4.2:Mahony AHRS 九轴姿态融合获取四元数、欧拉角
下一篇:【51单片机快速入门指南】4.4:I2C 读取HMC5883L / QMC5883L 磁力计

推荐阅读最新更新时间:2024-11-12 10:47

基于AT89C51单片机实现剪毛刀架高度自动控制系统的设计
概述 人造毛皮后整理联合烫剪机中剪毛刀架的定位调节,原是人工用按钮控制异步电动机正、反转,通过机械减速器(齿轮装置)后带动剪毛刀架蜗杆,使剪毛刀架做上、下运动至要求的位置。操作烦琐,需反复测量、调整,很难恰好到位,误差大。为此,我们用AT89C51单片机构成剪毛高度控制器,对剪毛刀架高度进行自动控制。 使用该控制器,只需由触摸开关设定工艺要求的剪毛高度(毫米数),控制器则发出相应的控制信号使剪毛刀架达到希望的高度,并可由工艺人员设置相对的机械零位。对运行状态和参数设置进行数码显示,相应地还有剪毛刀架高度减少及增大指示,控制趋势一目了然。 一.硬件设计 根据用户要求,为不增加成本,驱动电动机仍保持原异步电动机,原机械减速器及
[单片机]
基于AT89C<font color='red'>51单片机</font>实现剪毛刀架高度自动控制系统的设计
51单片机的酒精检测Proteus仿真+源代码
酒精传感器仿真中没有,其实就是传感器检测时相应的电阻值会变化,用滑动电位器代替就ok 仿真原理图如下 单片机源程序如下: //程序头函数 #include reg52.h //显示函数 #include display.h #include intrins.h #include eeprom52.h #include temp.h //宏定义 #define uint unsigned int #define uchar unsigned char #define Data_ADC0809 P1 //管脚声明 sbit P34=P3^4;//温度 sbit LED_R= P2^2; //红指示灯 sbit LE
[单片机]
<font color='red'>51单片机</font>的酒精检测Proteus仿真+源代码
89c51单片机控制数码管显示0和1的程序
在单片机系统中动态扫描显示接口是单片机中应用最为广泛的一种显示方式之一。其接口电路是把所有显示器的8个笔划段a-h同名端连在一起,而每一个显示器的公共极COM是各自独立地受I/O线控制。CPU向字段输出口送出字形码时,所有显示器接收到相同的字形码,但究竟是那个显示器亮,则取决于COM端,而这一端是由I/O控制的,所以我们就能自行决定何时显示哪一位了。而所谓动态扫描就是指我们采用分时的办法,轮流控制各个显示器的COM端,使各个显示器轮流点亮。 在轮流点亮扫描过程中,每位显示器的点亮时间是极为短暂的(约1ms),但由于人的视觉暂留现象及发光二极管的余辉效应,尽管实际上各位显示器并非同时点亮,但只要扫描的速度足够快,给人的印象就是一
[单片机]
89C51单片机和8254-2实际步进式PWM输出
简介:介绍一种新型PWM输出的方式。它是用89C51作为主控部分,用8254-2可编程定时器/计数器来实现1Hz~3kHz步进式PWM的输出;具有分辨率高、反应速度快及占用CPU时间少的优点。 引言 脉宽调制(PWM)技术最初是在无线电技术中用于信号的调制,后来在电机调速中得到了很好的应用。本设计中要求输出PWM从1Hz~3kHz步进式递增,单步为1Hz。由于89C51的时钟最大能取24MHz,单指令周期为0.5μs,计数频率为×10 6Hz。当输出2999Hz和3000Hz时,若采用89C51内部计数器来计数,根本无法区别。因为计数频率为2MHz,单指令周期0.5μs,而要输出2999Hz时,计数应为666.889;输出30
[单片机]
89C<font color='red'>51单片机</font>和8254-2实际步进式PWM输出
51单片机的引脚及各引脚功能介绍
当我们拿到一块MCS-51系列单片机芯片时,看到这么多的引脚,他们都有干什么用的? 8051单片机的引脚图 引脚功能: MCS-51是标准的40引脚双列直插式集成电路芯片,引脚分布请参照----单片机引脚图: l P0.0~P0.7 P0口8位双向口线(在引脚的39~32号端子)。 l P1.0~P1.7 P1口8位双向口线(在引脚的1~8号端子)。 l P2.0~P2.7 P2口8位双向口线(在引脚的21~28号端子)。 l P3.0~P3.7 P2口8位双向口线(在引脚的10~17号端子)。 这4个I/O口具有不完全相同的功能,大家可得学好了,其它书本里虽然有,但写的太深,初学者很难理解,这里都是按我自已的表达
[单片机]
51单片机蓝牙点灯程序
适用于:stc15f2k60s2型号单片机(其它型号的51单片机如果想用这个程序,就得修改程序的波特率和某一些的地方才能用) 蓝牙模块:hc-05模块 你用手机给蓝牙发个0,led灯亮,发个1,led灯灭。 单片机源程序如下: #include stc15f2k60s2.h #include intrins.h #define uchar unsigned char #define uint unsigned int uchar receiveData; sbit led=P0^0; void UartInit(void) //9600bps@ 11.0592MHz { PCON &
[单片机]
<font color='red'>51单片机</font>蓝牙点灯程序
STM32 串口3 总是进入接收中断
近日,调试stm32f103串口3 发现一个奇怪的问题 USART3------------RXD----------PB11 悬空会导致程序频繁进入串口接收中断!!! //USART3_RX PB11 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB11 原因就是 PB11引脚配置成了浮空输入模式!!!! 改成上拉输入问题解决
[单片机]
GD32串口接收异常的几个原因
前面我们介绍过GD32 485发送时出现异常的最常见原因,有小伙伴反馈想要知道GD32 串口接受异常的可能原因,今天我们就来安排。 一、波特率异常导致收发出错 我们知道,串口是异步通讯接口,通讯双方或者多方都需要工作在相同波特率下,如果波特率不对,则发送和接收都会异常。通常引起波特率异常的原因有以下几种: 外部晶振频率设置错误 GD32 固件库中,波特率的运算是需要去获取挂载这个串口的内部总线的频率 而获取总线频率的的函数中需要用到HXTAL_VALUE这个值,即外部晶振的实际频率。 GD32库中这个值默认是8M或25M,而有的小伙伴在用其他频率的晶振时,虽然根据实际频率修改了配置主频的函数,但没将HXTAL_VA
[单片机]
GD32<font color='red'>串口</font><font color='red'>接收</font>异常的几个原因
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved