ESP32官方MPU6050组件介绍

前言

(1)因为我需要使用MPU6050的组件,但是又需要在这条I2C总线上挂载多个设备,所以我本人打算自己对官方的MPU6050的组件进行微调。建立一个I2C总线,设备依赖于这个总线挂载。
(2)既然要做移植工作,所以就需要弄明白这个组件应当如何使用。

MPU6050组件函数介绍

mpu6050_create()

使用方法

(1)此函数用于创建一个指向MPU6050初始化信息的mpu6050_handle_t无符号类型指针。我们可以通过这个指针访问到mpu6050.c这个组件中的mpu6050_dev_t结构体。
(2)这样做能够做到常说的高内聚,低耦合的作用。如果不明白也没事,记住他返回的这个数据是和MPU6050的信息有关,我们能用就行
(3)如何传入数据:
<1>当你的MPU6050是挂载在ESP32的I2C0时候,传入I2C_NUM_0。如果是I2C1,传入I2C_NUM_1。如果是挂载在低功耗I2C中,传入LP_I2C_NUM_0。需要注意的是,不是所有的ESP32都具有I2C1和低功耗I2C,这个需要自行查阅芯片手册。
<2>关于MPU6050的地址,有两种。当MPU6050的第9号引脚AD0为低电平的时候地址为0x68,即MPU6050_I2C_ADDRESS。当这个引脚为高电平时候地址为0x69,即MPU6050_I2C_ADDRESS_1

/*** @brief  初始化MPU6050相关信息* * @param   port       要挂载在哪个I2C总线上*         -dev_addr   MPU6050的地址* * @return  NULL       初始化MPU6050的信息失败*         -非NULL指针 初始化MPU6050的信息成功
*/
mpu6050_handle_t mpu6050_create(i2c_port_t port, const uint16_t dev_addr)

简单概述底层实现

(1)mpu6050_handle_t存放在头文件中,用于暴露接口。
(2)mpu6050_dev_t这个结构体具体作用讲实话我也没有搞太明白,只能说我知道的认为重要的两个部分。
<1>bus,存放mpu6050是挂载在哪个I2C下。
<2>dev_addr,MPU6050的地址。
(3)函数实现部分介绍,注意,我也只能讲我懂的部分:
<1>使用calloc()函数分配1个mpu6050_dev_t类型的空间,并且将这块空间全部初始化为0。
<2>对申请到的sensor变量进行初始化,将MPU6050挂载的I2C信息,MPU6050地址信息存入这个变量。可能有些人会有疑惑,为什么地址信息dev_addr << 1需要进行一次右移操作。这个和I2C的时序逻辑有关,一般I2C设备的地址为7bit,最后1bit负责存放是对设备读还是写的信息。因此这里需要进行右移一位。
<3>最后返回的数据进行强制类型转换为mpu6050_handle_t这样就能够实现我上述所说的高内聚,低耦合的功能。

/*--- mpu6050.h ---*/
typedef void *mpu6050_handle_t;/*--- mpu6050.c ---*/
typedef struct {i2c_port_t bus;gpio_num_t int_pin;uint16_t dev_addr;uint32_t counter;float dt;  /*!< delay time between two measurements, dt should be small (ms level) */struct timeval *timer;
} mpu6050_dev_t;mpu6050_handle_t mpu6050_create(i2c_port_t port, const uint16_t dev_addr)
{mpu6050_dev_t *sensor = (mpu6050_dev_t *) calloc(1, sizeof(mpu6050_dev_t));sensor->bus = port;sensor->dev_addr = dev_addr << 1;sensor->counter = 0;sensor->dt = 0;sensor->timer = (struct timeval *) calloc(1, sizeof(struct timeval));return (mpu6050_handle_t) sensor;
}

mpu6050_config()

使用介绍

(1)用于设置MPU6050的加速度计满量程和陀螺仪满量程。
(2)如何传入数据:
<1>传入mpu6050_create()函数创建的mpu6050_handle_t指针。
<2>ACCE_FS_2G,加速度计满量程为+/-2g。ACCE_FS_4G,为+/-4g。ACCE_FS_8G,为+/-8g。ACCE_FS_16G,为+/-16g。
<3>GYRO_FS_250DPS,陀螺仪满量程是+/- 250度每秒。GYRO_FS_500DPS,为500度每秒。GYRO_FS_1000DPS,为1000度每秒。GYRO_FS_2000DPS,为2000度每秒。
(3)关于这个设置由你自己看情况决定。如果值太高将会降低分辨率,值太低就无法测量过高的数据。

/*** @brief  设置MPU6050的加速度计满量程和陀螺仪满量程* * @param   sensor   mpu6050_create()函数创建的mpu6050_handle_t指针*         -acce_fs  设置加速度计满量程*         -gyro_fs  设置陀螺仪满量程* * @return  ESP_OK   MPU6050配置成功*         -ESP_FAIL MPU6050配置失败
*/
esp_err_t mpu6050_config(mpu6050_handle_t sensor, const mpu6050_acce_fs_t acce_fs,const mpu6050_gyro_fs_t gyro_fs)

简单概述底层实现

(1)进入mpu6050_config()函数,我们会看到他先建立了一个8bit的无符号数组,将陀螺仪配置放在前面,加速度计配置放在后面。这是因为MPU6050的陀螺仪配置寄存器GYRO_CONFIG在加速度计寄存器ACCEL_CONFIG的前面,而MPU6050的每个寄存器为8bit。

esp_err_t mpu6050_config(mpu6050_handle_t sensor, const mpu6050_acce_fs_t acce_fs, const mpu6050_gyro_fs_t gyro_fs)
{uint8_t config_regs[2] = {gyro_fs << 3,  acce_fs << 3};return mpu6050_write(sensor, MPU6050_GYRO_CONFIG, config_regs, sizeof(config_regs));
}

mpu6050_write()

MPU6050写数据I2C格式

(1)前面我们知道了mpu6050_config()配置如何实现的,但是又有一个问题mpu6050_write()里面做了什么。
(2)我们讲解mpu6050_write()函数前,需要知道主机和MPU6050的通讯格式。
(3)原图在MPU-6000 and MPU-6050 Product Specification Revision 3.4手册的35页,也就是9.3 I2C Communications Protocol章节。直接看图,我不想过多讲解,图依旧很清晰了。

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

函数解析

(1)前面了解了MPU6050的写数据的格式之后,就可以开始解析函数了。
<1>首先对sensor这个mpu6050_handle_t类型指针强制类型转换,前面我们说了,这个mpu6050_handle_t本质就是一个无符号类型指针,利用他能够做到高内聚低耦合。这里你只需传入创建的指针数据,然后强制类型转换即可对这个指针访问。
<2>i2c_cmd_link_create()创建一个I2C 连接的句柄。这个听起来是不是很术语,看不懂。讲实话,我看到这个我也是懵逼的。我查看了一下这个函数的源码,个人理解就是,一个双向链表,里面存储了一些I2C数据传输的信息。之后的I2C数据传输,需要利用这个链表来进行设置。至于为什么使用链表,原因很简单,你不知道I2C通讯会传输多少个数据,而且I2C数据传输肯定是从头往下走,所以采用的链表是很好的决定。
其实和上面对sensor这个mpu6050_handle_t类型指针强制类型转换是一个道理。I2C相关的函数都在i2c.c里面,用于实现高内聚低耦合。
<3>i2c_master_start(),由于I2C协议规定,我们需要先发送一个起始信号。所以这个函数就是将起始信号写入缓存区。
<4>i2c_master_write_byte(),由于I2C协议规定,你发送起始信号之后,I2C上的从机都被激活。这个时候主机需要告诉从机我是在和谁通讯,因此需要传入从机地址信息,也就是MPU6050地址信息。当从机知道主机是在和谁交互时候,I2C总线上其他没有被选中的从机将会进入休眠。只有主机和从机开始通讯。
<5>主机和从机通讯建立完成之后,主机将会告诉从机,我要对那个寄存器进行操作。
<6>i2c_master_write(),现在主机和从机联系和对那个寄存器操作都有一个清晰的认识,于是可以开始传输数据了。
<7>i2c_master_stop(),数据传输完成之后主机需要告诉从机,我数据写完了,你可以休息了。
<8>i2c_master_cmd_begin(),上述操作进行完之后,起始ESP32的I2C并没有真正的工作。上述就是在缓冲区写入数据,调用这个函数,才是真正的将缓冲区的数据输出。
<9>i2c_cmd_link_delete(),通讯结束之后,我们需要调用这个函数删除与I2C的连接。

/*** @brief  设置MPU6050的加速度计满量程和陀螺仪满量程* * @param   sensor         mpu6050_create()函数创建的mpu6050_handle_t指针*         -reg_start_addr 要进行写入数据的寄存器*         -data_buf       写入寄存器中的数据*         -data_len       写入寄存器中的数据长度* * @return  ESP_OK         MPU6050寄存器数据写入成功*         -ESP_FAIL       MPU6050寄存器数据写入失败
*/
static esp_err_t mpu6050_write(mpu6050_handle_t sensor, const uint8_t reg_start_addr, const uint8_t *const data_buf, const uint8_t data_len)
{mpu6050_dev_t *sens = (mpu6050_dev_t *) sensor;esp_err_t  ret;i2c_cmd_handle_t cmd = i2c_cmd_link_create();ret = i2c_master_start(cmd);assert(ESP_OK == ret);ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_WRITE, true);assert(ESP_OK == ret);ret = i2c_master_write_byte(cmd, reg_start_addr, true);assert(ESP_OK == ret);ret = i2c_master_write(cmd, data_buf, data_len, true);assert(ESP_OK == ret);ret = i2c_master_stop(cmd);assert(ESP_OK == ret);ret = i2c_master_cmd_begin(sens->bus, cmd, 1000 / portTICK_PERIOD_MS);i2c_cmd_link_delete(cmd);return ret;
}

mpu6050_wake_up()

使用介绍

(1)这里只需要传入MPU6050的句柄即可唤醒MPU6050。

/*** @brief  唤醒MPU6050* * @param   sensor   mpu6050_create()函数创建的mpu6050_handle_t指针* @return  RT_EOK   MPU6050唤醒成功*         -RT_ERROR MPU6050唤醒失败
*/
esp_err_t mpu6050_wake_up(mpu6050_handle_t sensor)

简单概述底层实现

(1)这里其实也不难,就是对MPU6050的PWR_MGMT_1寄存器bit6清零即可。因为PWR_MGMT_1寄存器的bit6如果为1,MPU6050将会进入低功耗休眠状态。
(2)至于为什么需要先读取MPU6050的数据,很简单,如果直接写入一个数据,可能导致其他数据位数据被破坏。

esp_err_t mpu6050_wake_up(mpu6050_handle_t sensor)
{esp_err_t ret;uint8_t tmp;ret = mpu6050_read(sensor, MPU6050_PWR_MGMT_1, &tmp, 1);if (ESP_OK != ret) {return ret;}tmp &= (~BIT6);ret = mpu6050_write(sensor, MPU6050_PWR_MGMT_1, &tmp, 1);return ret;
}

mpu6050_read()

MPU6050读数据I2C格式

(1)mpu6050_wake_up()函数里面有一个mpu6050_read()没有进行介绍,这里介绍一下。
(2)MPU6050的读数据有两次起始信号,而写数据只有一次起始信号。第二次起始信号开始之后,就可以读数据了。
(3)原图在MPU-6000 and MPU-6050 Product Specification Revision 3.4手册的36页,也就是9.3 I2C Communications Protocol章节。

在这里插入图片描述

函数解析

(1)这里要根据上图一起理解。与mpu6050_write()函数相同部分我就不再赘述了。
<1>依旧是强制类型转换,建立I2C连接,发送起始信号。
<2>两个i2c_master_write_byte(),这里注意,虽然我们主机ESP32是要读取数据,但是第一次还是写数据,因为从机需要知道,主机接下来是要读取那个寄存器的数据。
<3>第二次i2c_master_start()i2c_master_write_byte(),这个是告诉从机,主机已经可以开始读取你的数据了。
<4>i2c_master_read(),读取数据,这个函数最后一个参数为I2C_MASTER_LAST_NACK表示主机每次收到数据返回一个ACK,不过主机最后一次收到数据返回NACK,告诉从机要停止发数据了。
<5>最后依旧是发送停止信息,使用i2c_master_cmd_begin()函数将缓冲区数据输出。删除与I2C的联系。

/*** @brief  设置MPU6050的加速度计满量程和陀螺仪满量程* * @param   sensor         mpu6050_create()函数创建的mpu6050_handle_t指针*         -reg_start_addr 要进行读取数据的寄存器*         -data_buf       读取到的数据存入空间*         -data_len       要读取数据的长度* * @return  ESP_OK         MPU6050寄存器数据读取成功*         -ESP_FAIL       MPU6050寄存器数据读取失败
*/
static esp_err_t mpu6050_read(mpu6050_handle_t sensor, const uint8_t reg_start_addr, uint8_t *const data_buf, const uint8_t data_len)
{mpu6050_dev_t *sens = (mpu6050_dev_t *) sensor;esp_err_t  ret;i2c_cmd_handle_t cmd = i2c_cmd_link_create();ret = i2c_master_start(cmd);assert(ESP_OK == ret);ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_WRITE, true);assert(ESP_OK == ret);ret = i2c_master_write_byte(cmd, reg_start_addr, true);assert(ESP_OK == ret);ret = i2c_master_start(cmd);assert(ESP_OK == ret);ret = i2c_master_write_byte(cmd, sens->dev_addr | I2C_MASTER_READ, true);assert(ESP_OK == ret);ret = i2c_master_read(cmd, data_buf, data_len, I2C_MASTER_LAST_NACK);assert(ESP_OK == ret);ret = i2c_master_stop(cmd);assert(ESP_OK == ret);ret = i2c_master_cmd_begin(sens->bus, cmd, 1000 / portTICK_PERIOD_MS);i2c_cmd_link_delete(cmd);return ret;
}

mpu6050_get_xxx简单介绍

(1)因为本人精力有限,不可能每个函数都完完全全介绍一边。经过上面的了解,重要的部分读者应该都了解了。其他的一些函数,对底层实现感兴趣的可以自行查看MPU-6050_Register_Map手册,对照着代码进行理解。
(2)上面的几个函数了解了之后,就只有如下这三个函数需要了解了。
<1>mpu6050_get_acce()函数,获取MPU6050的加速度值。
<2>mpu6050_get_gyro()函数,获取MPU6050的陀螺仪值。
<2>mpu6050_get_temp()函数,获取MPU6050的温度值。
(4)这三个函数,第一个都是传入的I2C句柄。(mpu6050_create()函数创建的mpu6050_handle_t指针)第二个参数略有不同:
<1>mpu6050_get_acce()函数,他需要传入一个mpu6050_acce_value_t类型结构体指针,最终对数据进行处理是采用acce.acce_x方法。

typedef struct {float acce_x;    //x轴加速度float acce_y;    //y轴加速度float acce_z;    //z轴加速度
} mpu6050_acce_value_t;typedef struct {float gyro_x;    //x轴的角速度float gyro_y;    //y轴的角速度float gyro_z;    //z轴的角速度
} mpu6050_gyro_value_t;typedef struct {float temp;      //MPU6050温度
} mpu6050_temp_value_t;

MPU6050组件使用

单元测试函数简单介绍

(1)在官方MPU6050的组件中,你会看到很多TEST_ASSERT开头的函数,这个是Unity测试单元。作用类似于assert的断言,当测试结果失败,将会终止程序进行复位操作。
(2)GitHub链接:https://github.com/ThrowTheSwitch/Unity
(3)函数简单介绍:
<1>TEST_ASSERT_NOT_NULL_MESSAGE(),用于测试传入的第一个参数是不是空指针,如果是空指针控制台将会输出第二个参数数据,并且终止程序。
<2>TEST_ASSERT_EQUAL(),判断第二个参数是否和第一个参数相等。如果不相等将会终止程序,并且输出信息。
<3>TEST_ASSERT_EQUAL_MESSAGE(),判断第二个参数是否和第一个参数相等。如果不相等将会输出第三个参数数据。

官方例程

(1)如下为官方测试例程。

/** SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD** SPDX-License-Identifier: Apache-2.0*/#include <stdio.h>
#include "unity.h"
#include "driver/i2c.h"
#include "mpu6050.h"
#include "esp_system.h"
#include "esp_log.h"#define I2C_MASTER_SCL_IO 26      /*!< gpio number for I2C master clock */
#define I2C_MASTER_SDA_IO 25      /*!< gpio number for I2C master data  */
#define I2C_MASTER_NUM I2C_NUM_0  /*!< I2C port number for master dev */
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */static const char *TAG = "mpu6050 test";
static mpu6050_handle_t mpu6050 = NULL;/*** @brief i2c master initialization*/
static void i2c_bus_init(void)
{i2c_config_t conf;conf.mode = I2C_MODE_MASTER;conf.sda_io_num = (gpio_num_t)I2C_MASTER_SDA_IO;conf.sda_pullup_en = GPIO_PULLUP_ENABLE;conf.scl_io_num = (gpio_num_t)I2C_MASTER_SCL_IO;conf.scl_pullup_en = GPIO_PULLUP_ENABLE;conf.master.clk_speed = I2C_MASTER_FREQ_HZ;conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL;esp_err_t ret = i2c_param_config(I2C_MASTER_NUM, &conf);TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C config returned error");ret = i2c_driver_install(I2C_MASTER_NUM, conf.mode, 0, 0, 0);TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C install returned error");
}/*** @brief i2c master initialization*/
static void i2c_sensor_mpu6050_init(void)
{esp_err_t ret;i2c_bus_init();mpu6050 = mpu6050_create(I2C_MASTER_NUM, MPU6050_I2C_ADDRESS);TEST_ASSERT_NOT_NULL_MESSAGE(mpu6050, "MPU6050 create returned NULL");ret = mpu6050_config(mpu6050, ACCE_FS_4G, GYRO_FS_500DPS);TEST_ASSERT_EQUAL(ESP_OK, ret);ret = mpu6050_wake_up(mpu6050);TEST_ASSERT_EQUAL(ESP_OK, ret);
}TEST_CASE("Sensor mpu6050 test", "[mpu6050][iot][sensor]")
{esp_err_t ret;uint8_t mpu6050_deviceid;mpu6050_acce_value_t acce;mpu6050_gyro_value_t gyro;mpu6050_temp_value_t temp;i2c_sensor_mpu6050_init();ret = mpu6050_get_deviceid(mpu6050, &mpu6050_deviceid);TEST_ASSERT_EQUAL(ESP_OK, ret);TEST_ASSERT_EQUAL_UINT8_MESSAGE(MPU6050_WHO_AM_I_VAL, mpu6050_deviceid, "Who Am I register does not contain expected data");ret = mpu6050_get_acce(mpu6050, &acce);TEST_ASSERT_EQUAL(ESP_OK, ret);ESP_LOGI(TAG, "acce_x:%.2f, acce_y:%.2f, acce_z:%.2f\n", acce.acce_x, acce.acce_y, acce.acce_z);ret = mpu6050_get_gyro(mpu6050, &gyro);TEST_ASSERT_EQUAL(ESP_OK, ret);ESP_LOGI(TAG, "gyro_x:%.2f, gyro_y:%.2f, gyro_z:%.2f\n", gyro.gyro_x, gyro.gyro_y, gyro.gyro_z);ret = mpu6050_get_temp(mpu6050, &temp);TEST_ASSERT_EQUAL(ESP_OK, ret);ESP_LOGI(TAG, "t:%.2f \n", temp.temp);mpu6050_delete(mpu6050);ret = i2c_driver_delete(I2C_MASTER_NUM);TEST_ASSERT_EQUAL(ESP_OK, ret);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/146093.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【AI视野·今日Robot 机器人论文速览 第四十四期】Fri, 29 Sep 2023

AI视野今日CS.Robotics 机器人学论文速览 Fri, 29 Sep 2023 Totally 38 papers &#x1f449;上期速览✈更多精彩请移步主页 Interesting: &#x1f4da;NCF,基于Neural Contact Fields神经接触场的方法实现有效的外部接触估计和插入操作。 (from FAIR ) 操作插入处理结果&am…

凉鞋的 Godot 笔记 101. Hello Godot!

101. Hello Godot 学习任何一门技术&#xff0c;第一件事就是先完成 Hello World&#xff01;的输出 所以我们也来先完成 Godot 的 Hello World。 我们所使用的 Godot 版本是 4.x 版本。 安装的过程就不给大家展示了&#xff0c;笔者更推荐初学者用 Steam 版本的 Godot&…

第一次作业题解

第一次作业题解 P5717 【深基3.习8】三角形分类 思路 考的是if()的使用,还要给三条边判断大小 判断优先级&#xff1a; 三角形&#xff1f;直角、钝角、锐角等腰等边 判断按题给顺序来 代码 #include <stdio.h> int main() {int a 0, b 0, c 0, x 0, y 0, z 0…

紫光同创FPGA图像视频采集系统,基于OV7725实现,提供工程源码和技术支持

目录 1、前言免责声明 2、设计思路框架视频源选择OV7725摄像头配置及采集动态彩条HDMA图像缓存输入输出视频HDMA缓冲FIFOHDMA控制模块HDMI输出 3、PDS工程详解4、上板调试验证并演示准备工作静态演示动态演示 5、福利&#xff1a;工程源码获取 紫光同创FPGA图像视频采集系统&am…

[每周一更]-(第64期):Dockerfile构造php定制化镜像

利用php官网镜像php:7.3-fpm&#xff0c;会存在部分插件缺失的情况&#xff0c;自行搭建可适用业务的镜像&#xff0c;才是真理 Dockerhub 上 PHP 官方基础镜像主要分为三个分支&#xff1a; cli: 没有开启 CGI 也就是说不能运行fpm。只可以运行命令行。fpm: 开启了CGI&#x…

Docker从认识到实践再到底层原理(九)|Docker Compose 容器编排

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…

libtorch之tensor的使用

1. tensor的创建 tensor的创建有三种常用的形式&#xff0c;如下所示 ones创建一个指定维度&#xff0c;数据全为1的tensor. 例子中的维度是2维&#xff0c;5行3列。 torch::Tensor t torch::ones({5,3}); zeros创建一个指定维度&#xff0c;数据全为0的tensor&#xff0c;例子…

Java 基于 SpringBoot 的简历招聘系统

文章目录 1、效果演示2、 前言介绍3、主要技术4 **系统设计**4.1 系统体系结构4.2开发流程设计4.3 数据库设计原则 5 **系统详细设计**5.1管理员功能模块5.2用户功能模块5.3前台首页功能模块 6 源码咨询 1、效果演示 大家好&#xff0c;今天为大家带来的是基于 SpringBoot简历…

【AI视野·今日Robot 机器人论文速览 第四十一期】Tue, 26 Sep 2023

AI视野今日CS.Robotics 机器人学论文速览 Tue, 26 Sep 2023 Totally 73 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Extreme Parkour with Legged Robots Authors Xuxin Cheng, Kexin Shi, Ananye Agarwal, Deepak Pathak人类可以通过以高度动态…

初识Java 11-2 函数式编程

目录 高阶函数 闭包 函数组合 柯里化和部分求值 本笔记参考自&#xff1a; 《On Java 中文版》 高阶函数 ||| 高阶函数的定义&#xff1a;一个能接受函数作为参数或能把函数当返回值的函数。 把函数当返回值的情况&#xff1a; import java.util.function.Function;inter…

操作EXCEL计算3万条数据的NDVI并填入

Python操作EXCEL&#xff0c;计算3万条数据的NDVI并填入 问题描述 现在是有构建好了的查找表&#xff0c;不过构建了3万条数据&#xff0c;在excel中手动计算每行的NDVI值太麻烦了&#xff0c;也不会操作。 就试试python吧&#xff0c;毕竟python自动处理大型EXCEL数据很方便…

Sentinel学习(1)——CAP理论,微服务中的雪崩问题,和Hystix的解决方案 Sentinel的相关概念 + 下载运行

前言 Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。 本篇博客介绍CAP理论&#xff0c;微…

UE5.1编辑器拓展【一、脚本化资产行为,通知,弹窗,高效复制多个同样的资产】

目录​​​​​​​ 插件制作 添加新的类&#xff1a;AssetActionUtility 添加新的模块&#xff1a;EditorScriptingUtilities 路径了解 添加debug的头文件 代码【debug.h】内涵注释&#xff1a; 写函数 .h文件 .cpp文件 插件制作 首先第一步是做一个插件&#xff1a…

吉力宝:智能科技鞋品牌步力宝引领传统产业创新思维

在现代经济环境下&#xff0c;市场经济下产品的竞争非常的激烈&#xff0c;如果没有营销&#xff0c;产品很可能不被大众认可&#xff0c;酒香也怕巷子深&#xff0c;许多传统产业不得不面临前所未有的挑战。而为了冲出这个“巷子”&#xff0c;许多企业需要采用创新思维&#…

Java进阶必会JVM-深入浅出Java虚拟机

系列文章目录 送书第一期 《用户画像&#xff1a;平台构建与业务实践》 送书活动之抽奖工具的打造 《获取博客评论用户抽取幸运中奖者》 送书第二期 《Spring Cloud Alibaba核心技术与实战案例》 送书第三期 《深入浅出Java虚拟机》 文章目录 系列文章目录前言一、推荐书籍二…

vue+Vant,关闭Popup弹框,遮罩层并没有消失

遇到问题&#xff1a; 点击Popup弹框关闭按钮&#xff0c;弹框的遮罩不能正常关闭&#xff0c;如下图。经研究&#xff0c;排除了popup属性问题&#xff0c;最后只能删除代码排除法。 <!--弹框&#xff1a;选号--><van-popupv-model"showNumber"closeablero…

图像处理: ImageKit.NET 3.0.10704 Crack

关于 ImageKit.NET3 100% 原生 .NET 图像处理组件。 ImageKit.NET 可让您快速轻松地向 .NET 应用程序添加图像处理功能。从 TWAIN 扫描仪和数码相机检索图像&#xff1b;加载和保存多种格式的图像文件&#xff1b;对图像应用图像滤镜和变换&#xff1b;在显示屏、平移窗口或缩略…

【新版】系统架构设计师 - 软件架构的演化与维护

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 软件架构的演化与维护考点摘要软件架构演化和定义面向对象软件架构演化对象演化消息演化复合片段演化约束演化 软件架构演化方式静态演化动态演化 软件架构演化原则软件架构演化评估方法大型网站架…

pandas_datareader读取yahoo金融数据超时问题timeout解决方案

在《Python金融数据挖掘》一书中&#xff0c;学习到网络数据源这一章节&#xff0c;利用书中的方法安装了pandas_datareader包&#xff0c;但在获取雅虎数据&#xff08;get_data_yahoo&#xff09;时会出现以下问题&#xff1a; 经过仔细分析和尝试&#xff0c;排除了yahoo受中…

stm32之1602+DHT11+继电器

描述&#xff1a; 1、DHT11监测温室度&#xff0c;并显示到1602液晶上 2、通过串口打印&#xff08;或通过蓝牙模块在手机上查看&#xff09; 3、当温度大于24度时&#xff0c;开启继电器。小于时关闭继电器&#xff08;继电器可连接风扇---假想O(∩_∩)O哈哈~&#xff09; 一、…