文章目录

  • 一、演示视频
  • 二、程序框架
  • 三、硬件设计
  • 四、模块介绍
    • 1、语音识别模块
      • 离线语音识别
      • 优化语音识别
    • 2、BLE模块
    • 3、MQTT模块
      • 3.1、命令下发
      • 3.2、设备属性上报
      • 3.3、平台查询设备属性
      • 3.4、应用侧接口
    • 4、音频播放模块
      • 4.1、播放本地mp3
      • 4.3、文本转语音
    • 5、红外模块
      • 5.1、红外发射
      • 5.2、红外学习
    • 6、传感器模块
    • 7、定时模块
    • 8,无线检测模块
      • 8.1、使用相关系数进行人体检测
      • 8.2、使用振幅平均值进行人体检测
      • 8.3、路由器与esp32相对位置对实验的影响

一、演示视频

离线版地址:https://gitee.com/killerp/off_asr
语音识别优化版地址:https://gitee.com/killerp/smart_control

本项目基于esp32a1s模组,设计了一个遥控器,除了实现基本的红外遥控功能,还利用ESP32芯片具备的AI能力,WIFI及蓝牙功能,实现多种方式的输入输出控制,使人们能通过语音,手机远程进行红外遥控。同时又加入温度传感器,并支持将温度数据上传云端,使人们能随时随地查看、分析数据。
项目演示视频如下:

基于esp32语音助手 语音红外遥控

二、程序框架

项目软件分为上位机端和嵌入式终端,程序框架如图:

位于底层的模块可为其上层的模块提供接口

三、硬件设计

四、模块介绍

1、语音识别模块

语言识别模块有两种实现方式,一种是离线识别,一种是针对离线版的优化。(目前离线版不再维护)

离线语音识别

离线语音的实现依赖于乐鑫提供ESP-Skainet库,该库实现了语音唤醒,语言识别功能,可支持100条自定义的语言识别命令。

ESP-Skainet 由两部分组成:

1,唤醒词模型 WakeNet:其实现了语音唤醒的功能,目前普通用户能使用限定的唤醒词,如:嗨乐鑫。模型由乐鑫训练。

2,命令词识别模型 MultiNet:其实现语音识别功能,将需要识别的语音命令写入程序,MultiNet就能在程序运行中进行识别,目前支持100条命令词。

在整个程序运行过程中,WakeNet和MultiNet两个模型以进程的形式运行,实时地读取麦克风的音频输入,将音频经过降噪处理等,输入模型,输出结果。其程序运行如图所示:


以下代码在main()中运行,该代码初始化了wakenet,multinet 模型,并创建一个音频流通道(ESP-ADF中的概念),该通道的作用即上图中箭头所示。main()以一个RTOS的任务运行,在循环中实时读取通道的音频,输入模型识别。

命令词识别成功后,在asr_multinet_control函数中编写对命令词的响应动作,如调用红外模块提供的接口打开空调等。

    ESP_LOGI(TAG, "Initialize SR wn handle");esp_wn_iface_t *wakenet;    //唤醒模型model_coeff_getter_t *model_coeff_getter;   //神经网络系数获取model_iface_data_t *model_wn_data;  //识别模型的数据const esp_mn_iface_t *multinet = &MULTINET_MODEL;   //识别模型// Initialize wakeNet model dataget_wakenet_iface(&wakenet);    //初始化唤醒模型get_wakenet_coeff(&model_coeff_getter); //获取系数model_wn_data = wakenet->create(model_coeff_getter, DET_MODE_90);   //创建唤醒模型,设置灵敏度90%int wn_num = wakenet->get_word_num(model_wn_data);  //唤醒词数量for (int i = 1; i <= wn_num; i++){char *name = wakenet->get_word_name(model_wn_data, i);  //唤醒词文本ESP_LOGI(TAG, "keywords: %s (index = %d)", name, i);}float wn_threshold = wakenet->get_det_threshold(model_wn_data, 1);  //获取唤醒阈值int wn_sample_rate = wakenet->get_samp_rate(model_wn_data); //唤醒词采样率16kint audio_wn_chunksize = wakenet->get_samp_chunksize(model_wn_data);    //内存块大小ESP_LOGI(TAG, "keywords_num = %d, threshold = %f, sample_rate = %d, chunksize = %d, sizeof_uint16 = %d", wn_num, wn_threshold, wn_sample_rate, audio_wn_chunksize, sizeof(int16_t));model_iface_data_t *model_mn_data = multinet->create(&MULTINET_COEFF, 4000); //语音识别时间,single模式下最大5sint audio_mn_chunksize = multinet->get_samp_chunksize(model_mn_data);   //识别内存块int mn_num = multinet->get_samp_chunknum(model_mn_data);    //唤醒词数量int mn_sample_rate = multinet->get_samp_rate(model_mn_data);    //采样率16kESP_LOGI(TAG, "keywords_num = %d , sample_rate = %d, chunksize = %d, sizeof_uint16 = %d", mn_num, mn_sample_rate, audio_mn_chunksize, sizeof(int16_t));//选择所需的较大的内存块int size = audio_wn_chunksize;if (audio_mn_chunksize > audio_wn_chunksize){size = audio_mn_chunksize;}int16_t *buffer = (int16_t *)malloc(size * sizeof(short));  //buffer用于缓存经过流水线处理的音频/*[ac101]-->i2s_stream-->filter-->raw-->[SR]*/audio_pipeline_handle_t pipeline;   //音频输入流水线audio_element_handle_t i2s_stream_reader, filter, raw_read; //流水线车间bool enable_wn = true;  //唤醒使能uint32_t mn_count = 0;ESP_LOGI(TAG, "[ 1 ] Start codec chip");ESP_LOGI(TAG, "[ 2.0 ] Create audio pipeline for recording");//流水线初始化audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();pipeline = audio_pipeline_init(&pipeline_cfg);mem_assert(pipeline);//i2s初始化,用于与ac101通信ESP_LOGI(TAG, "[ 2.1 ] Create i2s stream to read audio data from codec chip");i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();i2s_cfg.i2s_config.sample_rate = 48000;i2s_cfg.type = AUDIO_STREAM_READER; //输入流i2s_stream_reader = i2s_stream_init(&i2s_cfg);ESP_LOGI(TAG, "[ 2.2 ] Create filter to resample audio data");//滤波器初始化,将源采样率变为16krsp_filter_cfg_t rsp_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();rsp_cfg.src_rate = 48000;rsp_cfg.src_ch = 2;rsp_cfg.dest_rate = 16000;rsp_cfg.dest_ch = 1;filter = rsp_filter_init(&rsp_cfg);//raw初始化,缓存经过处理的音频数据ESP_LOGI(TAG, "[ 2.3 ] Create raw to receive data");raw_stream_cfg_t raw_cfg = {.out_rb_size = 8 * 1024,.type = AUDIO_STREAM_READER,    //输入流};raw_read = raw_stream_init(&raw_cfg);//将各个车间流连接到流水线ESP_LOGI(TAG, "[ 3 ] Register all elements to audio pipeline");audio_pipeline_register(pipeline, i2s_stream_reader, "i2s");audio_pipeline_register(pipeline, raw_read, "raw");audio_pipeline_register(pipeline, filter, "filter");ESP_LOGI(TAG, "[ 4 ] Link elements together [codec_chip]-->i2s_stream-->filter-->raw-->[SR]");const char *link_tag[3] = {"i2s", "filter", "raw"};audio_pipeline_link(pipeline, &link_tag[0], 3);//运行流水线ESP_LOGI(TAG, "[ 5 ] waiting to be awake");audio_pipeline_run(pipeline);while (1){//读取raw的音频到bufferraw_stream_read(raw_read, (char *)buffer, size * sizeof(short));if (enable_wn){//检测buffer是否有唤醒词if (wakenet->detect(model_wn_data, (int16_t *)buffer) == WAKE_UP){LED_ON;ESP_LOGI(TAG, "wake up");enable_wn = false;}}else{mn_count++;//检测buffer中是否有命令词int commit_id = multinet->detect(model_mn_data, buffer);//进入命令词控制函数if (asr_multinet_control(commit_id) == ESP_OK){LED_OFF;enable_wn = true;mn_count = 0;}if (mn_count == mn_num){ESP_LOGI(TAG, "stop multinet");LED_OFF;enable_wn = true;mn_count = 0;}}}ESP_LOGI(TAG, "[ 6 ] Stop audio_pipeline");audio_pipeline_stop(pipeline);audio_pipeline_wait_for_stop(pipeline);audio_pipeline_terminate(pipeline);/* Terminate the pipeline before removing the listener */audio_pipeline_remove_listener(pipeline);audio_pipeline_unregister(pipeline, raw_read);audio_pipeline_unregister(pipeline, i2s_stream_reader);audio_pipeline_unregister(pipeline, filter);/* Release all resources */audio_pipeline_deinit(pipeline);audio_element_deinit(raw_read);audio_element_deinit(i2s_stream_reader);audio_element_deinit(filter);ESP_LOGI(TAG, "[ 7 ] Destroy model");wakenet->destroy(model_wn_data);model_wn_data = NULL;free(buffer);buffer = NULL;

进入项目的配置程序

idf.py menuconfig>ESP Speech Recognition>Add speech commands

即可添加命令词

优化语音识别

相对于离线版,优化版除了具有本地的语音识别之外,还加入了百度的语音识别API,当本地的语音识别未能匹配到命令时,将音频数据发送到百度语音识别接口,并对返回的文本结果进行解析,逻辑如图(百度智能云语音识别返回结果是utf-8编码的字符串,所以代码编辑器中最好设置utf-8编码.)

语音模块的工作流程如下:

麦克风输入的音频经过音频解码芯片,通过IIS进入到ESP32内存,经过降噪处理输入WakeNet检测是否存在唤醒词,若有唤醒词则进入语音识别,将音频输入MultiNet匹配命令词,同时缓存音频数据。

若匹配成功则会执行命令内容,若经过设置的时间(eg 4s)匹配失败,则将缓存的音频数据通过Baidu ASR发送到百度智能云并读取返回的文本结果,对结果解析出命令内容执行。

2、BLE模块

ESP32支持蓝牙双模,目前仅使用蓝牙进行配网(即给esp32发送wifi名称和密码,使esp32连接到wifi)。所以使用ble更合适。

BLE(低功耗蓝牙),适合数据量较小的场合,ESP32支持完整的BLE协议栈,以ESP32作为BLE从机,发送广播、建立GATT Server并等待客户端连接。客户端以微信小程序BLE为例。小程序与ESP32蓝牙通信过程如下图:

ESP32建立一张profile、并创建一个wifi service、用于配置ESP32的wifi。小程序可向wii service 的相应属性中写入数据,来配置ESP32的wifi。

如图,向SSID、PSWD、CONFIG分别写入下值,ESP32会自动连接到路由器esp

3、MQTT模块

该模块使用mqtt_client库实现mqtt客户端进程的登陆,订阅,发布等功能。MQTT broker则使用华为云的设备接入IOT平台。

华为云平台为每个设备定义了以下多个主题,用于最基本的mqtt消息上传和下发,本模块参考了设备接入Iota->API参考->设备侧mqtt接口参考
上传

  • 设备消息上报:设备无法按照产品模型中定义的属性格式进行数据上报时,将设备的自定义数据通过设备消息上报接口上报给平台,平台将设备上报的消息转发给应用服务器或华为云其他云服务上进行存储和处理。

  • 设备属性上报:用于设备按产品模型中定义的格式将属性数据上报给平台。

  • 网关批量属性上报:用于网关设备将多个设备的属性数据一次性上报给平台。

  • 设备事件上报:用于设备按产品模型中定义的格式将事件数据上报给平台。

下发

  • 平台消息下发:用于平台下发自定义格式的数据给设备。

  • 平台设置设备属性:设备的产品模型中定义了平台可向设备设置的属性,应用服务器可通过属性设置的方式修改指定设备的属性值。

  • 平台查询设备属性:应用服务器通过属性查询的方式,实时查询指定设备的属性数据。

  • 平台命令下发:应用服务器按产品模型中定义的命令格式下发控制命令给设备。

  • 平台事件下发:应用服务器按产品模型中定义的事件格式下发事件给设备。

目前实现的软件框架如图所示:

3.1、命令下发

该功能实现参考华为云平台命令下发文档
华为云平台下发的mqtt命令topic格式固定为:

$oc/devices/{device_id}/sys/commands/request_id={request_id}

数据段中的json数据是命令的具体内容(由用户定义),如空调的控制命令如下:

{"paras":{"ac_power":1,"ac_temp":27,"ac_wind_speed":2,"ac_mode":0},"service_id":"ac_control","command_name":"ac_control"}

3.2、设备属性上报

设备端可主动上报设备的属性(空调的状态,温度),mqtt消息发布的topic为:

Topic: $oc/devices/{device_id}/sys/properties/report

并将设备属性以json格式放在数据段中。

3.3、平台查询设备属性

平台通过给该设备主题发布消息,通知设备端上报属性信息,主题格式为:

$oc/devices/{device_id}/sys/properties/get/request_id={request_id}

数据段为json数据,包含要求上报的属性。

3.4、应用侧接口

还可以通过华为云提供的应用侧接口,查询,控制设备的信息,实现在手机/网页端对设备的管理。

4、音频播放模块

本项目板子上装备了两个喇叭,可以用于播放音频,可播放 本地音频 和 网络音频流 。如图所示,由于嵌入式设备内存有限,故本地音频主要是一些简单的提示语音,其优点是播放速度快,响应及时;而HTTP音频流能灵活播放音频,但其受限于网络环境,故将二者结合互补。

4.1、播放本地mp3

该模块的实现方式是在PC端,将mp3文件转换成二进制的音频文件,将音频文件嵌入到芯片的flash中。在程序运行时可通过读取flash中的二进制音频文件,经过mp3解码,输出到音频芯片播放。

由于flash大小有限,故音频文件只能很短小,适合一些提示性短语,其播放速度也更快。
具体实现细节,参考:esp32播放本地mp3

4.3、文本转语音

文本转语音的核心就是依靠百度AI平台的文本转语音接口,设备端只需要准备好文本数据,发送到AI平台的文本转语音接口,并从该接口中获取音频数据,将该音频数据输出到音频芯片进行播放。

实现的详细代码参考:
esp32a1s 百度文本转语音实现

5、红外模块

目前的家具,电器中,仍有很多电器使用红外遥控,特别是空调。同类型的设备使用的红外协议大同小异,并且都可以查看其协议内容。

红外模块可发送不同类型,品牌的产品的红外控制信号,同时具备学习功能,能通过遥控器,学习设备的红外协议。

5.1、红外发射

红外遥控是通过特定的LED发射信号,设备接收该信号来响应。

在ESP32端来看,所有的红外信号都可看成一个方波信号,ESP32只要能产生所需要的方波信号,结合发射硬件,就能实现红外发射。ESP32中提供了一个叫RMT控制器的设备来控制信号,可利用该RMT控制器,产生所需的方波信号。

如何使用RMT产生方波,可参考:红外发射与接收;

了解了如何产生方波,但更重要的是如何获取方波信号的电平状态和持续时长,这些信息就是红外传输的具体内容。很幸运的是,一些开源红外遥控码库提供了这些信息,本模块使用的Irext就是一个开源万能红外遥控码库、编解码压缩算法以及免费周边服务。

将所需要的码库二进制文件下载到文件系统,就能在程序运行时,通过文件系统打开码库,输入电器类型及遥控命令,即可生成一个特定的数组,该数组表示了方波信号的电平高低及电平时长,即可产生所需的方波信号。

如何使用irext实现该功能,请参考:基于irext实现万能遥控器

5.2、红外学习

红外学习主要用于快速配置空调协议。用本地空调遥控器,给esp32发射制冷模式 开机 26° 一级风速 自动扫风的特定命令,程序会从本地码库中匹配对应的协议。

红外接收的重点是解析输入的信号并将其与本地数据库进行匹配。目前由人工分别完成了对格力,美的,海尔三种空调的不同协议的分析并生成数据库,并在代码中实现对他们的识别。

红外接收任务负责接收与识别的实现,由parse_items()和ir_code_lib_update()完成信号识别,匹配。

//接收到的红外信号
struct RX_signal
{uint32_t item_num;  //item数量uint32_t lowlevel;  //低电平时间 usuint32_t highlevel_1;   //高电平1的时间uint32_t highlevel_0;   //高电平0的时间uint32_t encode;    //由0和1组成的编码
};
/** 红外接收任务 rmt_rx_start()执行后才会接收数据* 接收的数据以item的数据结构存放到nvs
*/
void rmt_ir_rxTask(void *agr)
{size_t rx_size = 0;RingbufHandle_t rb = NULL;rmt_get_ringbuf_handle(rx_channel, &rb); //获取红外接收器接收的数据 放在ringbuff中rmt_rx_stop(rx_channel); //暂停接收while (rb){//从ringbuff读取items 会进入阻塞 直到ringbuff中有新的数据rmt_item32_t *item = (rmt_item32_t *)xRingbufferReceive(rb, &rx_size, portMAX_DELAY);if (item){ESP_LOGI(TAG, "rx_size = %u", rx_size);//!红外线接收器有干扰,需要滤波if (rx_size > 30){struct RX_signal sig;   //接收信号结构体size_t item_num = rx_size / 4;  //一个item32bitsig.item_num = item_num;sig.highlevel_1 = 0;sig.highlevel_0 = 0;sig.encode = 0;//解析itemparse_items(item, item_num, &sig);ir_code_lib_update(&sig); //更新ac_handlermt_rx_stop(rx_channel);  //暂停接收xSemaphoreGive(IR_sem);   //释放信号量}//解析出数据后释放ringbuff的空间vRingbufferReturnItem(rb, (void *)item);}}vTaskDelete(NULL);
}

6、传感器模块

目前我们的板子上添加了ds18b20温度传感器,用于准确的读取室内的温度。

7、定时模块

定时模块主要用于记录当前的年份,月份,日期,时间,提供精确到秒的定时提醒服务,支持大时间尺度上的定时,如一天,两天甚至更长时间的定时提醒。其定时的时钟源是freertos的系统时钟。
其实现参考博客:基于esp32 的时间系统

8,无线检测模块

暂时未完成,预期功能为:能实时检测人体/物体在一定空间内的活动状态,如人体是否静止OR活动中,从而来判断空间内是否有人类活动。

其工作原理是利用ESP32的wifi无线信号在复杂空间中传播时,在发送端和接收端之间因为物体移动影响电磁波的多径效应,可用系统的信道频率响应来描述多径传播特性。

8.1、使用相关系数进行人体检测

根据谈青青的论文,wifi子载波之间相关系数会随着空间环境的变换而变换,当室内无人体活动时,各个载波之间的相关系数相对小,当空间内有人活动时,各个载波会因人体活动而产生变换,此时相关系数会增大。

我的实验:提取2s内的载波振幅,计算相关系数,发现无论何种情况,大部分的子载波的相关系数都接近1,与论文不是很符合,且人体的活动反而导致了相关系数的减小,与论文也不符合。但初期的实验确实证实相关系数可以 反映人体的活动。

8.2、使用振幅平均值进行人体检测

根据其他论文,一定时间窗口2s内,计算载波的振幅的均值。当空间环境稳定的情况下,振幅均值应该不会出现过大的波动,当人体活动时,振幅均值会受到影响而产生波动。对于esp32的51条子载波,不同的子载波对活动的灵敏度不一样。有的子载波会产生较大的波动,而有的子载波没有明显变化。描述子载波的幅度均值变化程度可以用方差,但实验过程中,全部子载波的方差值似乎不能很好的反映人体活动。但单独一条子载波的振幅均值对人体活动却比较敏感。

8.3、路由器与esp32相对位置对实验的影响

实验路由器有两种:一般路由器和手机热点。在相关系数实验中,以手机热点为路由的人体检测预期较好,但换成一般路由器则效果很差。

将路由器摆放到较高位置进行实验,实验效果也不明显。

esp32的智能遥控相关推荐

  1. 云智能遥控开关设备再物联网领域的应用:智能养殖高效、生态、安全!

    随着我国水土流失日益严重,土地资源严重紧缺,水产养殖池塘已经成为一种趋势,但由于现代气候突变现象日益发生,效益下降等问题突出,如何提高养殖产品品质,直接增加了渔民的经济收入,实现高效.生态.安全的现代 ...

  2. 基于arduino的ESP32 学习笔记(一) 基于ESP32的智能花盆

    前言 本文的目的是为了给将要制作的ESP32手环做技术储备 准备学习下ESP32,还有嵌入式GUI框架LVGL,通过做几个小项目练手是不错的选择,最终目标是做一个ESP32的手环 做一个ESP32手环 ...

  3. ATmega16智能遥控小车

    AVR-ATmega16智能遥控小车 AVR单片机相信不少的伙伴在大学的电设课中接触过,不像51单片机和32单片机的资料这么多,但是学校又要求去学,还要设计结题项目,这是最头疼的.该项目可以作为AVR ...

  4. 基于ESP32的智能家庭健康系统

    M5Stick-c ESP8266 项目背景及概述: 项目最初的想法来源于当下疫情局势,每个人都关心自己及家人的健康状况.因此本产品旨在应用于人们居家隔离或复工复产过程中,对个人身体情况的实时监测.同 ...

  5. 基于ESP32的智能家居控制系统-微信小程序

    一. 课题研究意义.现状及应用分析 1.1课题研究意义及现状 目前,科学技术发展十分迅速,其渗透到各行各业以及生活的方方面面,室内设计和高科技结合便出现了"智能家居".所谓智能家居 ...

  6. 三菱V3菱悦智能遥控匹配详细的(两种)方法 配钥匙

    三菱V3菱悦智能遥控匹配详细的(两种)方法 蜂鸣器, V3菱悦, 主机, 遥控器, 报警喇叭 方法遥控器学习 当遥控器损或丢失时可通过重新学习操作将原遥控器信息从主机记忆中删除然后重新学习遥控器 1先 ...

  7. ESP32+arduino智能浇水系统

    一.ESP32+arduino智能浇水系统 随着人类居住条件的改善及对生态生活环境的关注,花卉养殖得到社会和人类个体的重视.这些具有生命特征的植物需 要科学合理的人工照顾.本研究提出了利用ESP WR ...

  8. iWiscloud智能遥控触摸开关

    iWiscloud智能遥控触摸开关具有强大的处理器,采用钢化玻璃面板, 电容触摸感应, 能够自动检测电流, 自动检测故障, 自动组网,开关之间可相互学习控制,一键实现多处灯光联动. 钢化玻璃面板时尚美 ...

  9. 基于 ESP32 的智能家居系统设计

    基于 ESP32 的智能家居系统设计 摘 要:智能家居科技是在电子信息技术和无线通信技术以及软件和信息技术方面进一步开发所形成的新兴科学技术,这项科技可以改善我们的生活条件,并可以使居家条件显得更为适 ...

最新文章

  1. 【Luogu】P1013进制位(搜索)
  2. plotly可视化绘制双子图(subplots)
  3. http://www.cnblogs.com/dolphin0520/p/3949310.html
  4. pstools psexec 执行文件
  5. MySQL【案例讲解】单行函数
  6. Oracle中通过Function,存储过程,触发器,调用实现解析Clob字段中存在的xml字符串...
  7. oracle11g session,Oracle11g中Killsession心得
  8. sql中怎么根据汉字的拼音首字母查询
  9. 世界首富比尔盖茨花钱全过程!
  10. 《Spring Security3》第四章第一部分翻译下(自定义的UserDetailsServic
  11. Python 父与子的编程之旅 第七章答案
  12. 全国计算机一级成绩分配,计算机一级ms分值分配
  13. 【从零开始学架构-李运华】01|架构到底是指什么?
  14. php模版推送方法,PHP快速推送微信模板消息
  15. Three.js - 加载 .OBJ 格式模型(十六)
  16. 用Keil工具搭建S3C2440编译环境
  17. 2MSL的特点及意义
  18. Android N 程序适配要点
  19. linux c 数字字符串互转 相关函数 atoi、atof、atol、atrtod、strtol、strtoul
  20. 很多事情看似很完美,一不小心就成了杯具~

热门文章

  1. HackTheBox::Admirer
  2. 鼠标选中后会自动删除文件的现状及解决方案
  3. 退完押金要200年的途歌,是怎么把自己玩儿坏的?
  4. Android面试老生常谈的 View 事件分发机制,看这一篇就够了
  5. 什么是cmd?能做啥?告诉你...
  6. 阿里云视频点播测试问题解决 (获取播放地址播放)
  7. 微信不会把关注取消事件推送给服务器,微信公众平台开发关注及取消关注事件的方法...
  8. 【程序人生】:腾讯的职级系统
  9. Java8集合之HashMap的hash计算、扩容等问题
  10. python pandas 日期格式_python+pandas+时间、日期以及时间序列处理方法