STM32平衡车开发实战教程:从零基础到项目精通
STM32平衡车开发实战教程:从零基础到项目精通
一、项目概述与基本原理
1.1 平衡车工作原理
平衡车是一种基于倒立摆原理的两轮自平衡小车,其核心控制原理类似于人类保持平衡的过程。当人站立不稳时,会通过腿部肌肉的快速调整来维持平衡。平衡车同样通过传感器检测车身倾斜角度,利用电机驱动实现动态平衡。
核心控制原理:
- 姿态感知:通过MPU6050六轴传感器(三轴加速度计+三轴陀螺仪)实时检测小车的倾斜角度和角速度
- 控制算法:使用PID算法计算电机控制量,其中:
- 比例项§快速响应角度偏差
- 积分项(I)消除静态误差
- 微分项(D)抑制振荡
- 执行机构:电机根据控制量调整转速,保持平衡
1.2 系统组成与功能
基本功能模块:
- 主控制器:STM32F103C8T6(性价比高,资源丰富)
- 姿态传感器:MPU6050(检测倾角)
- 电机驱动:TB6612(高效驱动电机)
- 编码器:测量电机转速(反馈控制)
- 电源管理:锂电池供电系统
- 通信模块:蓝牙/WiFi(无线控制)
- 显示模块:OLED(状态显示)
进阶功能扩展:
- 超声波避障
- 红外循迹
- 手机APP遥控
- 语音控制
二、硬件设计与组装
2.1 硬件选型指南
| 部件 | 推荐型号 | 关键参数 | 注意事项 |
|---|---|---|---|
| 主控芯片 | STM32F103C8T6 | Cortex-M3内核,72MHz主频,64KB Flash | 确保有足够定时器资源 |
| 姿态传感器 | MPU6050 | ±2g加速度,±2000°/s陀螺仪 | 注意安装方向与位置 |
| 电机 | MG315带编码器 | 减速比1:30,编码器11线 | 需配对使用 |
| 电机驱动 | TB6612FNG | 双通道,1.2A连续电流 | 比L298N效率高 |
| 电源 | 18650锂电池×3 | 12V供电 | 需配保护板 |
| 蓝牙模块 | HC-06 | 蓝牙4.0,串口通信 | 注意主从模式 |
| 显示模块 | 0.96寸OLED | I2C接口,128×64分辨率 | 可选SPI接口版本 |
2.2 详细电路连接
STM32最小系统连接:
- 8MHz晶振连接OSC_IN/OSC_OUT
- 复位电路连接NRST
- Boot0通过10K电阻接地
- 3.3V稳压电路(AMS1117-3.3)
MPU6050连接:
- VCC → 3.3V
- GND → GND
- SCL → PB6
- SDA → PB7
- INT → PA0(外部中断)
TB6612电机驱动连接:
- PWMA → PA8(TIM1_CH1)
- PWMB → PA11(TIM1_CH4)
- AIN1 → PC13, AIN2 → PC14
- BIN1 → PC15, BIN2 → PD2
- STBY → 3.3V(常使能)
编码器连接:
- 左编码器A相 → PA0(TIM2_CH1)
- 左编码器B相 → PA1(TIM2_CH2)
- 右编码器A相 → PB6(TIM4_CH1)
- 右编码器B相 → PB7(TIM4_CH2)
电源系统设计:
- 主电源:3节18650锂电池(12V)
- 5V降压:MP1584EN模块(为传感器供电)
- 3.3V稳压:AMS1117-3.3(为MCU和传感器供电)
2.3 PCB设计与制作
设计要点:
- 电机驱动部分走线加宽(至少1mm)
- 模拟部分(传感器)与数字部分分开布局
- 添加电源滤波电容(10uF电解+0.1uF陶瓷)
- MPU6050尽量靠近MCU放置
常见问题解决:
- 电机干扰导致复位:加强电源滤波,缩短电机线长度
- 传感器数据跳动:确保I2C线上拉电阻(4.7K)正确连接
- 电机不转:检查STBY引脚电平,确认PWM信号正常
三、软件架构与核心算法
3.1 系统软件架构
分层设计:
-
硬件抽象层(HAL):
- 传感器驱动(MPU6050)
- 电机驱动(TB6612)
- 编码器接口
- 通信接口(蓝牙/串口)
-
算法层:
- 姿态解算(互补滤波/卡尔曼滤波)
- PID控制算法
- 数据滤波处理
-
应用层:
- 任务调度
- 人机交互(按键/显示)
- 遥控处理
-
通信层:
- 蓝牙协议处理
- 串口调试接口
3.2 姿态解算算法
互补滤波实现:
// 互补滤波函数
float ComplementaryFilter(float accelAngle, float gyroRate, float dt, float alpha) {static float angle = 0;angle = alpha * (angle + gyroRate * dt) + (1 - alpha) * accelAngle;return angle;
}// 调用示例
void IMU_Update() {// 读取传感器原始数据MPU6050_ReadData(&accelX, &accelY, &accelZ, &gyroX, &gyroY, &gyroZ);// 计算加速度角度float accelAngle = atan2(accelY, accelZ) * 180.0 / PI;// 获取陀螺仪角速度(转换为度/秒)float gyroRate = gyroX / 16.4f;// 互补滤波float dt = 0.005f; // 5ms采样周期float alpha = 0.98f; // 滤波系数currentAngle = ComplementaryFilter(accelAngle, gyroRate, dt, alpha);
}
卡尔曼滤波实现:
typedef struct {float Q_angle; // 过程噪声协方差float Q_bias; // 过程噪声协方差float R_measure; // 测量噪声协方差float angle; // 计算得到的最优角度float bias; // 陀螺仪偏置float P[2][2]; // 误差协方差矩阵
} KalmanFilter;float Kalman_Update(KalmanFilter *kf, float newAngle, float newRate, float dt) {// 预测步骤kf->angle += dt * (newRate - kf->bias);kf->P[0][0] += dt * (dt*kf->P[1][1] - kf->P[0][1] - kf->P[1][0] + kf->Q_angle);kf->P[0][1] -= dt * kf->P[1][1];kf->P[1][0] -= dt * kf->P[1][1];kf->P[1][1] += kf->Q_bias * dt;// 更新步骤float y = newAngle - kf->angle;float S = kf->P[0][0] + kf->R_measure;float K[2];K[0] = kf->P[0][0] / S;K[1] = kf->P[1][0] / S;// 更新估计值kf->angle += K[0] * y;kf->bias += K[1] * y;// 更新协方差矩阵float P00_temp = kf->P[0][0];float P01_temp = kf->P[0][1];kf->P[0][0] -= K[0] * P00_temp;kf->P[0][1] -= K[0] * P01_temp;kf->P[1][0] -= K[1] * P00_temp;kf->P[1][1] -= K[1] * P01_temp;return kf->angle;
}
3.3 PID控制算法
串级PID实现:
typedef struct {float Kp, Ki, Kd;float integral;float prevError;float integralLimit;
} PID_Controller;float PID_Update(PID_Controller *pid, float error, float dt) {// 比例项float proportional = pid->Kp * error;// 积分项pid->integral += error * dt;// 积分限幅if(pid->integral > pid->integralLimit) pid->integral = pid->integralLimit;else if(pid->integral < -pid->integralLimit) pid->integral = -pid->integralLimit;float integral = pid->Ki * pid->integral;// 微分项float derivative = pid->Kd * (error - pid->prevError) / dt;pid->prevError = error;return proportional + integral + derivative;
}// 直立环PD控制
float Balance_PID(float angle, float targetAngle, float gyroRate) {static PID_Controller pid = {20.0f, 0.0f, 0.5f, 0, 0, 1000};float error = angle - targetAngle;return PID_Update(&pid, error, 0.005f) + pid.Kd * gyroRate;
}// 速度环PI控制
float Velocity_PID(int encoderLeft, int encoderRight) {static PID_Controller pid = {0.3f, 0.001f, 0.0f, 0, 0, 10000};int speed = (encoderLeft + encoderRight) / 2; // 平均速度return PID_Update(&pid, -speed, 0.005f); // 目标速度为0
}
四、系统实现与调试
4.1 主程序框架
int main(void) {// 系统初始化HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_TIM1_Init(); // PWM定时器MX_TIM2_Init(); // 编码器1MX_TIM4_Init(); // 编码器2MX_I2C1_Init(); // MPU6050MX_USART1_UART_Init(); // 调试串口// 外设初始化MPU6050_Init();Motor_Init();Encoder_Init();OLED_Init();Bluetooth_Init();// 主循环while (1) {// 1. 读取传感器数据MPU6050_ReadData(&imuData);// 2. 姿态解算currentAngle = ComplementaryFilter(atan2(imuData.Accel_Y, imuData.Accel_Z) * RAD_TO_DEG,imuData.Gyro_X,0.005f, // 5ms0.98f);// 3. 读取编码器速度int speedLeft = Read_Encoder(TIM_ENCODER_LEFT);int speedRight = Read_Encoder(TIM_ENCODER_RIGHT);// 4. PID控制计算float balanceOut = Balance_PID(currentAngle, TARGET_ANGLE, imuData.Gyro_X);float speedOut = Velocity_PID(speedLeft, speedRight);// 5. 综合控制输出int motorOut = balanceOut + speedOut;// 6. 电机控制Motor_SetPWM(MOTOR_LEFT, motorOut);Motor_SetPWM(MOTOR_RIGHT, motorOut);// 7. 状态显示与通信if(HAL_GetTick() - lastDisplayTime >= 100) { // 100ms更新一次显示OLED_ShowAngle(currentAngle);Bluetooth_SendData(currentAngle);lastDisplayTime = HAL_GetTick();}HAL_Delay(5); // 5ms控制周期}
}
4.2 关键模块实现
MPU6050初始化与数据读取:
void MPU6050_Init(void) {// 复位设备MPU6050_WriteByte(MPU6050_RA_PWR_MGMT_1, 0x80);HAL_Delay(100);// 唤醒设备,选择时钟源MPU6050_WriteByte(MPU6050_RA_PWR_MGMT_1, 0x01);// 设置陀螺仪量程 ±2000°/sMPU6050_WriteByte(MPU6050_RA_GYRO_CONFIG, 0x18);// 设置加速度计量程 ±2gMPU6050_WriteByte(MPU6050_RA_ACCEL_CONFIG, 0x00);// 设置低通滤波器带宽 44HzMPU6050_WriteByte(MPU6050_RA_CONFIG, 0x03);// 设置采样率 1kHzMPU6050_WriteByte(MPU6050_RA_SMPLRT_DIV, 0x00);
}void MPU6050_ReadData(MPU6050_Data *data) {uint8_t buf[14];MPU6050_ReadBytes(MPU6050_RA_ACCEL_XOUT_H, 14, buf);data->Accel_X = (int16_t)(buf[0] << 8 | buf[1]);data->Accel_Y = (int16_t)(buf[2] << 8 | buf[3]);data->Accel_Z = (int16_t)(buf[4] << 8 | buf[5]);data->Temp = (int16_t)(buf[6] << 8 | buf[7]);data->Gyro_X = (int16_t)(buf[8] << 8 | buf[9]);data->Gyro_Y = (int16_t)(buf[10] << 8 | buf[11]);data->Gyro_Z = (int16_t)(buf[12] << 8 | buf[13]);
}
编码器接口配置:
void Encoder_Init(void) {TIM_Encoder_InitTypeDef sConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};// 编码器1配置(TIM2)htim2.Instance = TIM2;htim2.Init.Prescaler = 0;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 65535;htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;sConfig.EncoderMode = TIM_ENCODERMODE_TI12;sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;sConfig.IC1Prescaler = TIM_ICPSC_DIV1;sConfig.IC1Filter = 0;sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;sConfig.IC2Prescaler = TIM_ICPSC_DIV1;sConfig.IC2Filter = 0;HAL_TIM_Encoder_Init(&htim2, &sConfig);sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);// 编码器2配置(TIM4)类似...
}
4.3 PID参数整定方法
调参步骤:
-
确定机械中值
- 将小车放在地面上,寻找能够自然平衡的角度
- 记录这个角度作为TARGET_ANGLE(通常在-3°到3°之间)
-
直立环调参
- 先调Kp(比例项):
- 从较小值开始(如10)
- 逐渐增大直到小车出现低频振荡
- 典型值范围:20-50
- 再调Kd(微分项):
- 从0.1开始
- 逐渐增大抑制振荡
- 过大则会出现高频抖动
- 典型值范围:0.3-0.8
- 先调Kp(比例项):
-
速度环调参
- 先调Kp:
- 从0.1开始
- 增大使小车能抵抗外力
- 过大则会出现前后摆动
- 典型值范围:0.2-0.5
- Ki与Kp保持比例关系(Ki ≈ Kp/200):
- 消除静态误差
- 过大则积分饱和
- 先调Kp:
-
转向环调参(可选)
- 使用单独的Kp控制
- 根据转向灵敏度调整
- 典型值范围:0.1-1.0
调试技巧:
- 使用蓝牙或串口实时调整参数
- 记录数据并分析响应曲线
- 采用"试凑法"结合理论分析
- 先调内环再调外环
五、进阶优化与功能扩展
5.1 系统优化策略
实时性优化:
-
使用定时器中断确保控制周期精确
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {if(htim->Instance == TIM6) { // 5ms定时器// 执行控制算法Control_Task();} } -
优化传感器数据读取速度
- 使用DMA传输减少CPU开销
- 提高I2C时钟频率(400kHz)
-
关键代码使用汇编优化
- PID计算等关键算法
稳定性优化:
- 增加软件看门狗
- 异常状态检测与保护
- 角度过大时切断电机
- 通信异常处理
5.2 功能扩展实现
蓝牙遥控功能:
void Bluetooth_Process(void) {if(UART_Receive(&huart3, &bluetoothData, 1) == HAL_OK) {switch(bluetoothData) {case 'F': targetSpeed += 10; break; // 前进case 'B': targetSpeed -= 10; break; // 后退case 'L': turnOffset = -5; break; // 左转case 'R': turnOffset = 5; break; // 右转case 'S': targetSpeed = 0; break; // 停止}}
}
超声波避障功能:
float Ultrasonic_GetDistance(void) {// 触发信号HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);HAL_Delay(0.01);HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);// 等待回波while(HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) == GPIO_PIN_RESET);uint32_t start = HAL_GetTick();while(HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) == GPIO_PIN_SET);uint32_t end = HAL_GetTick();// 计算距离(cm)return (end - start) * 0.034 / 2;
}
数据记录与分析:
- 使用SD卡模块记录运行数据
- 通过无线模块上传到云端
- 使用MATLAB/Python分析数据
六、项目总结与进阶学习
6.1 常见问题解决
问题1:小车无法保持平衡
- 检查传感器数据是否正确
- 确认PID参数极性是否正确
- 检查电机转向是否正确
问题2:小车出现高频振荡
- 减小微分项Kd
- 检查机械结构是否牢固
- 增加传感器数据滤波
问题3:小车向一边偏移
- 检查机械结构对称性
- 校准传感器
- 调整机械中值
6.2 学习资源推荐
开源项目参考:
- 平衡小车之家开源项目
- Cleanflight/Betaflight飞控代码
- 小马哥四轴开源项目
推荐书籍:
- 《STM32库开发实战指南》
- 《自动控制原理》
- 《嵌入式实时操作系统》
进阶方向:
- 改用RTOS实现多任务
- 加入机器学习算法
- 实现集群控制
- 开发手机APP控制界面
6.3 项目展示与分享
博客撰写要点:
- 项目背景与意义
- 系统设计与实现
- 关键技术难点与解决方案
- 效果展示(视频/图片)
- 经验总结与未来改进
面试项目介绍要点:
- 突出技术难点和解决方案
- 展示对系统原理的深入理解
- 说明个人贡献和收获
- 准备技术细节的深入讨论
通过本教程,您已经掌握了STM32平衡车从硬件设计到软件实现的完整开发流程。建议按照步骤实际动手实践,在实践中深化理解。平衡车项目是学习嵌入式系统和控制算法的绝佳平台,希望您能在此基础上不断探索和创新!
相关文章:
STM32平衡车开发实战教程:从零基础到项目精通
STM32平衡车开发实战教程:从零基础到项目精通 一、项目概述与基本原理 1.1 平衡车工作原理 平衡车是一种基于倒立摆原理的两轮自平衡小车,其核心控制原理类似于人类保持平衡的过程。当人站立不稳时,会通过腿部肌肉的快速调整来维持平衡。平…...
使用DeepSeek AI高效降低论文重复率
一、论文查重原理与DeepSeek降重机制 1.1 主流查重系统工作原理 文本比对算法:连续字符匹配(通常13-15字符)语义识别技术:检测同义替换和结构调整参考文献识别:区分合理引用与不当抄袭跨语言检测:中英文互译内容识别1.2 DeepSeek降重核心技术 深度语义理解:分析句子核心…...
linux多线(进)程编程——(7)消息队列
前言 现在修真界大家的沟通手段已经越来越丰富了,有了匿名管道,命名管道,共享内存等多种方式。但是随着深入使用人们逐渐发现了这些传音术的局限性。 匿名管道:只能在有血缘关系的修真者(进程)间使用&…...
WinForm真入门(14)——ListView控件详解
一、ListView 控件核心概念与功能 ListView 是 WinForm 中用于展示结构化数据的多功能列表控件,支持多列、多视图模式及复杂交互,常用于文件资源管理器、数据报表等场景。 核心特点: 支持 5种视图模式:Details&…...
Python + Playwright:规避常见的UI自动化测试反模式
Python + Playwright:规避常见的UI自动化测试反模式 前言反模式一:整体式页面对象(POM)反模式二:具有逻辑的页面对象 - POM 的“越界”行为反模式三:基于 UI 的测试设置 - 缓慢且脆弱的“舞台搭建”反模式四:功能测试过载 - “试图覆盖一切”的测试反模式之间的关联与核…...
从服务器多线程批量下载文件到本地
1、客户端安装 aria2 下载地址:aria2 解压文件,然后将文件目录添加到系统环境变量Path中,然后打开cmd,输入:aria2c 文件地址,就可以下载文件了 2、服务端配置nginx文件服务器 server {listen 8080…...
循环神经网络 - 深层循环神经网络
如果将深度定义为网络中信息传递路径长度的话,循环神经网络可以看作既“深”又“浅”的网络。 一方面来说,如果我们把循环网络按时间展开,长时间间隔的状态之间的路径很长,循环网络可以看作一个非常深的网络。 从另一方面来 说&…...
linux运维篇-Ubuntu(debian)系操作系统创建源仓库
适用范围 适用于Ubuntu(Debian)及其衍生版本的linux系统 例如,国产化操作系统kylin-desktop-v10 简介 先来看下我们需要创建出来的仓库目录结构 Deb_conf_test apt源的主目录 conf 配置文件存放目录 conf目录下存放两个配置文件&…...
深度学习之微积分
2.4.1 导数和微分 2.4.2 偏导数 安装步骤 参考 ubuntu2018 安装 vcs2018 安装该…...
Express中间件(Middleware)详解:从零开始掌握(3)
实用中间件模式25例 1. 基础增强模式 请求属性扩展 function extendRequest() {return (req, res, next) > {req.getClientLanguage () > {return req.headers[accept-language]?.split(,)[0] || en;};next();}; } 响应时间头 function responseTime() {return (r…...
深入理解微信小程序开发:架构、组件化与进阶实战
📘博文正文: 深入理解微信小程序开发:架构、组件化与进阶实战 微信小程序已成为移动互联网的重要入口。随着业务复杂度提升,仅靠入门知识已无法应对日常开发需求。本文将深入剖析小程序开发架构、组件化模式、状态管理、网络封装…...
逆向|中国产业政策大数据平台|请求体加密
2025-04-11 逆向地址:aHR0cDovL3poZW5nY2UuMmIuY24v 打开开发者工具出现debugger,直接注入脚本过掉无限debugger let aaa Function.prototype.constructor; Function.prototype.constructor function (params) { if(params ‘debugger’){ console.log(params); return null…...
在SpringBoot中访问 static 与 templates 目录下的内容
目录 步骤一:添加 Thymeleaf 依赖 (处理 Templates 目录)步骤二:配置静态资源路径 (可选但建议了解)步骤三:访问不同目录下的 HTML 文件访问 static 目录下的 HTML 文件访问 templates 目录下的 HTML 文件 总结 在使用 Spring Boot 开发 Web …...
游戏引擎学习第226天
引言,计划 我们目前的目标是开始构建“元游戏”结构。所谓元游戏,指的是不直接属于核心玩法本身,但又是游戏体验不可或缺的一部分,比如主菜单、标题画面、存档选择、选项设置、过场动画等。我们正在慢慢将这些系统结构搭建起来。…...
青少年编程与数学 02-016 Python数据结构与算法 22课题、并行算法
青少年编程与数学 02-016 Python数据结构与算法 22课题、并行算法 一、GPU并行计算矩阵乘法示例 二、MPI并行计算allgather操作示例 三、Python中的并行计算多线程并行计算多进程并行计算 四、SIMD并行计算SIMD并行计算示例 总结 课题摘要: 并行算法是通过同时执行多个任务或操…...
Ubuntu系统18.04更新驱动解决方法
原始是:ubuntu18.04里面的驱动是470,对应cuda11.4 现在需要更新为525,对应cuda为12.0 实现: 1、打开终端 Ctrl Alt T2、使用 lspci 命令(快速查看显卡型号) lspci | grep -i vga3、终端输入 ubuntu-d…...
Notepad++安装Markdown实时预览插件
具体操作 打开notepad -> 插件 -> 插件管理 -> 可用 -> “Markdown Panel” -> 安装,安装完成后工具栏点击"Markdown Panel"按钮。 注意:由于网络等原因可能安装失败 导致工具栏没出现""Markdown Panel"按钮&am…...
Mysql-视图和存储过程
视图 1.介绍 视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条…...
FreeRTOS入门与工程实践-基于STM32F103(二)(互斥量,事件组,任务通知,软件定时器,中断管理,资源管理,调试与优化)
互斥量 一、互斥量(Mutex):解决多任务 “抢资源” 的问题 1. 是什么? 互斥量是一种 “任务间互斥访问资源” 的工具,本质是一个 只能被锁定(0)或释放(1)的二进制信号量…...
stm32面试
数据结构相关问题 stm32面试 数据结构相关问题 目录基础数据结构树与图排序与查找算法 Linux相关问题Linux系统基础Linux命令与脚本Linux网络与服务 操作系统相关问题操作系统基础概念操作系统调度算法操作系统同步与通信 STM32相关问题STM32硬件基础STM32编程与开发STM32应用与…...
202524 | 分布式事务
分布式事务(Distributed Transaction) 分布式事务是指跨多个数据库、服务或系统节点的事务操作,要求所有参与方要么全部成功提交,要么全部回滚,保证数据一致性。 1. 为什么需要分布式事务? 在单体应用中&…...
Python 企业级架构实战(上篇)
深入企业级系统设计与高可用架构,掌握构建可扩展 Python 系统的核心技能。 41. 微服务架构设计与 FastAPI 实现 多服务协同开发示例 # 用户服务 (user_service/main.py) from fastapi import FastAPI app = FastAPI() users_db = { 1: {"id": 1, "name&…...
在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级配置的详细步骤
在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级配置。以下是详细步骤: 在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级…...
Python 文本和字节序列(字符问题)
本章将讨论下述话题: 字符、码位和字节表述 bytes、bytearray 和 memoryview 等二进制序列的独特特性 全部 Unicode 和陈旧字符集的编解码器 避免和处理编码错误 处理文本文件的最佳实践 默认编码的陷阱和标准 I/O 的问题 规范化 Unicode 文本,进行安全的…...
通过Arduino IDE向闪存文件系统上传文件
注意:适用于Arduino IDE 2.0版本以上。对于Arduino IDE版本在2.0以下的请参考太极创客的教程:http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/。 1. 下载脚本文件 下载地址:https://github.com/earl…...
leetcode 121. Best Time to Buy and Sell Stock
题目描述 本题属于动态规划类问题。 dp数组的含义 dp[i][0]表示从第0天到第i天为止,处于持有股票的状态下,账户里的最大金额。 dp[i][1]表示从第0天到第i天为止,处于不持有股票的状态下,账户里的最大金额。 按照这个定义dp[n-…...
