项目分享| 自制巡线机器人

本次分享机器人项目由myyerrol制作,是他的第一个基于RISC-V的机器人项目。以下文字详细记录了机器人制作的软硬件组成、核心算法等,对机器人感兴趣的朋友可收藏,也可关注他的知乎账号(myyerrol)或GitHub(https://github.com/myyerrol/line_tracking_robot),与他交流。文中提及的代码可以在“嵌入式Linux”回复:巡线机器人,获取。先来看看机器人演示效果:

项目概述

巡线解迷宫机器人是一款能够通过红外反射传感器实现自主巡线以及解迷宫(暂未实现)等功能的小型三轮机器人。硬件核心采用的是芯来基于GD32VF103处理器所自主设计的RV-STAR开发板,主要负责处理红外反射传感器采所集到的模拟数据,并将处理后的数据后向下发送给电机驱动模块来精确控制两个直流电机的转向和转速。由于机器人为原理验证机且为了提高项目整体的开发速度,机器人的所有元器件均采用TB上的现有模块,并使用洞洞板直接焊接的方式来构建其机械结构和电气连接。

1a5e67165e4cdbdeaa96031fa0f327e3.png

在软件层面上,该机器人内部集成有经典的PID闭环控制算法,可根据模拟输入数据自动调整整个系统的动态平衡,使得机器人最终能够快速且平滑地沿着黑线移动。除此之外,该项目最大的亮点是我根据GD32VF103处理器的库函数手册等相关资料为RV-STAR开发板封装了一个类Arduino语法的静态链接库,取名为RVStarArduino,这样不仅我可以将自己之前基于Arduino所做的机器人项目中的一些代码无缝地移植过来,从而大大提高了项目的开发效率,还使得一些刚接触GD32嵌入式开发的小白用户能够快速上手实践,打消他们对传统嵌入式应用开发的畏难心理,毕竟不是所有人都喜欢研究底层技术嘛。

最后,该项目的代码可以从我的GitHub仓库上获得,感兴趣的朋友可以下载到本地玩一玩,当然如果你在使用的过程中遇到了一些问题,欢迎在GitHub上给我提交Issues或者在文章评论区里留言,有空的话我肯定会及时回复的。

9fea867c9cbf95401919a3cba645b405.png

巡线解迷宫机器人图1

机器人硬件

✔ 电机电源层

电机电源层是整个机器人硬件架构中最底层且最基础的单元,它主要包含直流减速电机、电机驱动模块以及电源管理模块共三大部分,其中关于直流减速电机这块儿,我本次采用的是目前市场上应用广泛且体积较小的N20减速电机,考虑到机器人总装之后的负载情况以及自己对机器人巡线速度的需求,我最终购买的N20电机减速比为30:1,且额定电压为6V时,其空载转速为300RPM(即每分钟300转),这样就能保证动力系统在大负载情况下也能提供较为稳定的扭矩和速度输出,从而使得机器人的巡线过程变得更加高效。

bab77a6265e6e417d0fde23e0a9aaeff.png

电机电源层正面

电机驱动模块我选用的是智能小车里面最常用的TB6612FNG,它相较于其他模块的最大优点在于其在驱动两路大电流电机的同时还能保持自身体积非常小巧,因为我这次做的巡线解迷宫机器人的尺寸非常小,为了节省宝贵的内部空间,TB6612FNG模块几乎可以说是我的唯一选择。至于该模块具体该如何使用,网上有很多教程可供参考,这里我就不展开讲解了,不过有两点大家需要注意一下:一是TB6612FNG的3个GND引脚是互通的;二是其STBY引脚必须接高电平才能正常驱动电机。

最后,电源管理部分我使用的是TB上的一款低成本的锂电池充放电一体模块,这个模块最厉害的地方在于它不仅能够通过外置USB接口给锂电池充电,还能在锂电池放电的时候将其电压从3.7V升到5V,功能相当于原先的充电模块+升压模块,这对于寸土寸金的机器人内部空间来说简直就是一个梦幻般的存在。此外,该模块还带有智能省电功能,即当外接负载所需电流小于50mA达到30秒时会自动关闭电源输出,从而有效降低电池电量的损耗。

0658e95580fe29e62b01e2f988085adb.png

电机电源层背面

✔ 主控处理层

主控处理层作为上层决策单元,是整个机器人硬件架构中的核心。在本次的巡线解迷宫机器人中,它主要用于采样红外反射传感器回传的模拟数据并根据PID算法进行数学运算,最后将计算结果以逻辑电平信号的形式反馈给电机控制板,从而实现对直流电机转向与转速的精确控制。当然除了最核心的功能之外,主控处理层这块儿将来还会涉及到与带有IIC接口的OLED显示屏以及基于ESP8266的Wi-Fi模块间的数据交互等内容,也许到时候主控的性能才能被完全地发挥出来。

f9b319083b91b667755b4fa27de22dae.png

RV-STAR开发板示意图(引用自官方论坛)

接下来为了让大家对主控有一个基本的认识,这里先简要介绍一下芯来主推的这款RV-STAR开发板(以下内容引用自官网论坛):

RV-STAR是一款基于GD32VF103 MCU的RISC-V评估开发板,提供了板载调试器、Reset和Wakeup用户按键、RGB LED、USB OTG,以及EXMC、Arduino和PMOD扩展接口等资源。

相较于开发板本身的不俗性能以及所搭载的丰富外设资源,其实我更看重的是它在硬件上完全兼容Arduino这一点。玩过Arduino的同学们都知道,Arduino之所以一经推出便火遍全世界,就是在于它在软硬件层面上的易用性,而RV-STAR在硬件上支持Arduino扩展,这无疑可以让其复用很多现有的Arduino扩展板,如果后期能打通软件语法方面的壁垒(感兴趣的可以关注一下我写的RVStarArduino库),就可以真正实现将RV-STAR无缝接入到庞大且成熟的Arduino生态中,我想这也许是未来国产处理器生态环境建设可以走的捷径之一吧。

 外设模块层

外设模块层位于机器人硬件架构的最顶层,主要用于与外部环境进行数据交互,在本项目最早的规划中这部分应该是由红外反射传感器、OLED显示屏与Wi-Fi透传模块等三个核心外设组成,不过由于时间缘故后两个我并没有添加到机器人上,所以这次我就先详细介绍一下红外反射传感器的原理与使用,而OLED和Wi-Fi模块我打算留到下篇的时候再讲。

如下图所示,机器人底盘前端布置的一排黑色物体便是红外反射传感器,考虑到机器人的体积以及主控的运算能力,在器件选型方面我采用的是DIP封装的红外反射管TCRT5000,为了尽可能提高机器人巡线的精度,我总共使用了4个红外反射传感器,后期实际测试效果相当不错。在数据采集方面,我参考了稚晖君文章里所提到的方法,即直接使用主控的ADC功能来读取传感器的模拟数据,这样做的好处是不仅可以在代码中动态调整传感器的阈值,更重要的是它还能够让我们获得额外的道路信息,这对于提高机器人巡线的鲁棒性是非常有益的,具体内容我会在后文的算法部分进行讲解。

33181f32d5606f0b60524d636896c9e4.png

电机电源层背面的四路红外反射传感器

软件开发

 环境配置

古人曾经说过:工欲善其事,必先利其器,广大开发者们想要高效地开发嵌入式应用,简单易用且性能强大的开发工具肯定是必不可少的。根据论坛快速入门板块的介绍,RV-STAR开发板支持芯来的Nuclei Studio、Segger的Embedded Studio以及基于VSCode的PlatformIO等多种开发平台,当然如果你追求极致的开发效率,直接在命令行中用Make来编译、调试与上传代码也是没有问题的,不过这里我还是推荐小白用户首选IDE,这样后期在配置环境的过程中遇到的问题会少一些。

有关具体如何配置IDE以及设备驱动的方法,官方手册和论坛里都介绍得很详细了,这里就不再赘述了,接下来我主要想讲讲自己是如何在Ubuntu 16.04等这类依赖低版本Python 3的GNU/Linux系统中安装最新版本的PlatformIO扩展插件,以及如何在PlatformIO中安装和使用我编写的RVStarArduino库进行二次开发。

由于新版的PlatformIO最低仅支持Python 3.6,所以对于像我一样使用旧版GNU/Linux系统的人来说是无法使用PlatformIO的,唯一的解决办法就是通过第三方软件源来安装新版本的Python 3,具体操作如下:

$> sudo add-apt-repository ppa:deadsnakes/ppa
$> sudo apt update
$> sudo apt install python3.8 python3.8-distutils

安装完成后需要手动将系统的Python 3环境变量指向最新安装的这个版本:

$> sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 1
$> sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 2
$> sudo update-alternatives --config python3

配置成功后即可按照正常流程在VSCode中安装PlatformIO扩展插件,但是注意如果你的系统使用的是类GNOME桌面环境的话,那切换到高版本Python 3很有可能会导致GNOME命令行终端软件无法使用,所以在装完PlatformIO后需要再次使用下面这个命令将Python 3的版本切换回去:

$> sudo update-alternatives --config python3

至此,如果你能在重启VSCode后看到如下界面即表明PlatformIO扩展插件已成功安装。

bd0cb26f16963333312d6c17a5327efe.png

PlatformIO的主界面

接下来我介绍一下如何在PlatformIO中安装RVStarArduino库,其实这个步骤非常简单,如下图所示,各位只需要在PlatformIO的库管理器中搜索RVStarArduino关键词,并在其介绍界面中点击【Add to Project】即可将库文件添加到自己的项目中。除此之外,你也可以直接在platformio.ini配置文件中添加库依赖参数,这样PlatformIO在编译项目代码的时候会自动下载对应版本的库文件。

1095b196c98719777f6fb60b0051a32c.png

RVStarArduino库的主界面1

a6174ebab8fa6a2a2f574b93c59c45cf.png

RVStarArduino库的主界面2

核心算法

巡线解迷宫机器人的软件部分主要涉及传感器读取以及PID控制等核心算法,其中传感器读取部分我采用的是模拟式输入,即通过主控的ADC功能直接获取红外反射传感器的模拟电压值,数值范围为0~1023,估计看到这儿很多人都会问为什么我不使用更简单的数字式输入,其实这主要是因为现实场景中的很多事物并不是只有0和1两种状态,在本项目中红外反射传感器相对于黑线的位置是实时变化的,如果采用模拟量的话,系统可以表示两个边界之间的所有位置信息,精度相较于数字量的0和1会有比较大的提升。除此之外,得益于精度的显著提升,模拟量所表示的信息还能通过加权求和的方式更好地表征系统误差的大小,这对于后面的PID运算过程至关重要。

// 获取每个红外反射传感器的模拟值
// Get the analog value of each infrared reflection sensor
uint16_t a0 = analogRead(A0);
uint16_t a1 = analogRead(A1);
uint16_t a2 = analogRead(A2);
uint16_t a3 = analogRead(A3);        // 使用加权求和的方式来表示系统误差
// Use weighted summation to represent systematic errors
float turn_error = 0.0;
turn_error = turn_error + g_turn_error_k0 * a0;
turn_error = turn_error + g_turn_error_k1 * a1;
turn_error = turn_error - g_turn_error_k1 * a2;
turn_error = turn_error - g_turn_error_k0 * a3;

接下来就到了最经典的PID部分了,所谓PID就是比例、积分和微分的英文缩写,主要应用于各种闭环控制系统中。根据反馈器件的类型,PID中常见的控制环有:速度环、角度环和转向环,那大家可以猜猜本项目中使用的是哪种控制环吗?没错,因为我选择的是可以返回位置信息的红外反射传感器,所以机器人实际应用的是转向环PID算法。关于PID算法的原理网上有很多详细的资料可供参考,这里我就不再过多介绍了,大家可以尝试直接阅读下面的代码来理解其中的精髓。

// 计算转向环PID
// Calculate turn PID
g_turn_p = g_turn_kp * turn_error;
g_turn_i = g_turn_ki * (g_turn_i + turn_error);
g_turn_d = g_turn_kd * (turn_error - g_turn_error_last);
float turn_sum = (g_turn_p + g_turn_i + g_turn_d) / 100;// 根据计算结果与当前所处状态控制电机的转速或转向
// Control the speed or direction of the motor according to the
// calculation result and the current state
int16_t motor_speed_a = MOTOR_SPEED_REF;
int16_t motor_speed_b = MOTOR_SPEED_REF;// 规定机器人水平向左为负向右为正,此时如果PID计算结果为正,则机器人需要向右偏移,
// 右侧电机应降低一定的速度,速度变化值由PID计算结果的绝对值表示,反方向同理
// It is stipulated that the horizontal left of the robot is negative
// and the right is positive. At this time, if the PID calculation
// result is positive, the robot needs to shift to the right, and the
// right motor should reduce a certain speed. The speed change value is
// represented by the absolute value of the PID calculation result.
// The same goes in the opposite direction
if (turn_sum > 0) {motor_speed_b = motor_speed_b - turn_sum;
}
else {motor_speed_a = motor_speed_a - abs(turn_sum);
}setMotorSpeed(MOTOR_A, motor_speed_a);
setMotorSpeed(MOTOR_B, motor_speed_b);核心算法完整代码如下所示:
float g_turn_kp = 20.0;
float g_turn_ki = 0.0;
float g_turn_kd = 5.0;
float g_turn_p  = 0.0;
float g_turn_i  = 0.0;
float g_turn_d  = 0.0;float g_turn_error_k0   = 2.0;
float g_turn_error_k1   = 1.0;
float g_turn_error_last = 0.0;uint32_t g_timer_turn_pid = 0;void cacluteTurnPID(void) {uint32_t ms = millis();if (ms - g_timer_turn_pid >= 50) {
#ifdef DEBUG_PID_CYCLEprintf("Time turn: %lums\n", ms);
#endif// 获取每个红外反射传感器的模拟值// Get the analog value of each infrared reflection sensoruint16_t a0 = analogRead(A0);uint16_t a1 = analogRead(A1);uint16_t a2 = analogRead(A2);uint16_t a3 = analogRead(A3);
#ifdef DEBUG_ADC_VALUEprintf("A0: %u, A1: %u, A2: %u, A3: %u\n", a0, a1, a2, a3);
#endif// 使用加权求和的方式来表示系统误差// Use weighted summation to represent systematic errorsfloat turn_error = 0.0;turn_error = turn_error + g_turn_error_k0 * a0;turn_error = turn_error + g_turn_error_k1 * a1;turn_error = turn_error - g_turn_error_k1 * a2;turn_error = turn_error - g_turn_error_k0 * a3;// 计算转向环PID// Calculate turn PIDg_turn_p = g_turn_kp * turn_error;g_turn_i = g_turn_ki * (g_turn_i + turn_error);g_turn_d = g_turn_kd * (turn_error - g_turn_error_last);float turn_sum = (g_turn_p + g_turn_i + g_turn_d) / 100;#ifdef DEBUG_PID_VALUEprintf("Turn E: %.2f, Turn P: %.2f, Turn I: %.2f, Turn D: %.2f, ""Turn Sum: %.2f\n",turn_error, g_turn_p, g_turn_i, g_turn_d, turn_sum);
#endif// 根据计算结果与当前所处状态控制电机的转速或转向// Control the speed or direction of the motor according to the// calculation result and the current stateint16_t motor_speed_a = MOTOR_SPEED_REF;int16_t motor_speed_b = MOTOR_SPEED_REF;// 规定机器人水平向左为负向右为正,此时如果PID计算结果为正,则机器人需要向右偏移,// 右侧电机应降低一定的速度,速度变化值由PID计算结果的绝对值表示,反方向同理// It is stipulated that the horizontal left of the robot is negative// and the right is positive. At this time, if the PID calculation// result is positive, the robot needs to shift to the right, and the// right motor should reduce a certain speed. The speed change value is// represented by the absolute value of the PID calculation result.// The same goes in the opposite directionif (turn_sum > 0) {motor_speed_b = motor_speed_b - turn_sum;}else {motor_speed_a = motor_speed_a - abs(turn_sum);}#ifdef DEBUG_MTR_VALUEprintf("Motor A: %d, Motor B: %d\n\n", motor_speed_a, motor_speed_b);
#endifsetMotorSpeed(MOTOR_A, motor_speed_a);setMotorSpeed(MOTOR_B, motor_speed_b);g_turn_error_last = turn_error;g_timer_turn_pid = ms;}
}
遇到的问题1)我在测试TB6612FNG驱动板的时候,发现无论自己怎么设置电机始终不转,在排查硬件问题的过程中,我先后用万用表测试了电源管理模块输出和TB6612FNG驱动板的逻辑输入引脚,发现电源供电电压与信号高低电平均正常,所以首先可以排除电源供电不足和逻辑信号错误所导致电机不转的可能,之后我对驱动板的相邻引脚做了短路测试,偶然发现其BIN0、BIN1和GND引脚短接了,而短接出现的原因比较特殊:因为我是将两个电机直接固定在双面敷铜的洞洞板上,此时电机的外壳与正面的焊盘紧密接触,而背面的BIN和GND信号正好走在电机外壳的区域内,焊接时焊锡过孔导致两个信号与电机外壳联通,从而出现短接。但神奇的是解决短路问题后电机依旧不转,最后我是在面包板上复现TB6612FNG驱动电路的时候才发现自己竟然犯了一个非常低级的错误——忘记在代码中设置电机的PWM信号,我当初下意识认为PWM只用于调速,只要AIN或BIN信号设置对就能让电机转起来,可惜事与愿违。这个经历给我的启示在于做项目时要以官方提供的芯片或模块手册为准,不要想当然地按照自己的想法去做,否则后期出现问题的概率会比较高。
2)在测试四路红外反射传感器的时候,我发现板子串口输出的数值竟然全部一样,经过单步调试后最终确定是自己编写的RVStarArduino库里的analogRead函数存在Bug,使得函数每次只能返回第一次设置的ADC通道的数值。解决方法是根据网上STM32 ADC多通道数据采集范例,将通道配置库函数从初始化阶段移动到analogRead函数中,这样系统就能根据不同的引脚编号来读取相对应的ADC通道数值了。
3)转向环PID算法中存在浮点变量和与之相关的运算过程,但是令人费解的是,程序在执行过程中始终无法在串口里打印出浮点数值,后来经过仔细排查发现系统自带的newlib为了优化编译后的体积,默认并没有开启浮点数运算功能,所以程序一遇到浮点变量就直接跳过执行了。解决方法其实很简单,只需要在编译器的链接参数中指定【-u _printf_float】即可。

总结

巡线解迷宫机器人是我基于国产处理器来开发嵌入式应用的首次尝试,通过一个月左右的理论学习与动手实践,我学会了如何在项目正式开始前做需求分析,如何根据机器人所要实现的功能来做软硬件方案的选型,如何在遇到Bug的时候通过调试机制来定位并解决问题,如何规划机器人内部空间使硬件布局更加合理与美观等等。虽然最后因为其他项目节点的缘故,我并没有给机器人添加OLED显示和Wi-Fi传输模块,同时也没有实现解迷宫功能,但是我至少验证了采用RISC-V架构的国产嵌入式处理器无论是在运行时性能还是在开发便捷性等方面均不输于目前主流的STM32,所以本项目成果从原理验证的角度上来看还是达到了自己的预期。

最后我一直坚信学习某个理论知识最好的办法就是将其应用到某个具体的项目中,特别是对于嵌入式开发来说,在开始上手前应该先给自己选择一个比较有挑战性的小项目,然后在后期学习的过程中通过不断地搜集资料与解决问题,尽自己最大的努力将其实现出来,这样到时候你就会发现很多原本晦涩难懂的理论知识都是那么的合理与自然。本次巡线解迷宫机器人项目就是一个最好的范本,对此感兴趣的同学也可以自己尝试做一个,然后去实现一些文中提到却没实现的功能,当然相比最后的成果,享受过程才是最重要的,预祝大家玩得开心。

END

44a2d5eb85c19a129ba72ccf72b173c1.png

327c3a37864a1880ea09b96b800f6ad5.png

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CUDA线程、线程块、线程束、流多处理器、流处理器、网格概念的深入理解

一.与CUDA相关的几个概念:thread,block,grid,warp,sp,sm。 sp: 最基本的处理单元,streaming processor 最后具体的指令和任务都是在sp上处理的。GPU进行并行计算,也就是很多个sp同时…

.Net环境下基于Ajax的MVC方案

现在,越来越多人开始尝试基于Ajax进行无刷新的Web开发,不过,在.Net环境下,应用Ajax并不是非常方便,这主要可能是由以下一些原因造成的: •由于Ajax基于javascript的本质,使得开发者必须对javasc…

一道C语言指针的题目

早上看到的题目,但是现在去找没看到了。我当时还把图片放在小号里面了,大家可以看看。大家看看这个代码输出什么?如果想让它输出77要怎么用指针写呢?凭记忆写一下#include "stdio.h"#include "stdint.h" int …

Linux IO多路复用之epoll网络编程,高并发的使用例子 (含源码)

本章节是用基本的Linux基本函数加上epoll调用编写一个完整的服务器和客户端例子,可在Linux上运行,客户端和服务端的功能如下: 客户端从标准输入读入一行,发送到服务端 服务端从网络读取一行,然后输出到客户端 客户端收到服务端…

c#中Excel数据的导入、导出

/**//// <summary>/// 导出到 Excel 文件/// </summary>/// <param name"fileName">含完整路径</param>/// <param name"dataTable">含字段标题名</param>publicvoidExpExcel(stringfileName ,DataTable dataTable){ …

你对Linux下的实时性应该多点了解

作者简介顺刚(网名:沐多)&#xff0c;一线码农&#xff0c;从事工控行业&#xff0c;目前在一家工业自动化公司从事工业实时现场总线开发工作&#xff0c;喜欢钻研Linux内核及xenomai&#xff0c;个人博客 wsg1100&#xff0c;欢迎大家关注&#xff01;本文讲述一些有利于提高x…

DHCP机制

DHCP概念&#xff1a;局域网的网络协议&#xff0c;使用UDP协议工作&#xff0c;在工作过程中&#xff0c;它有两个对象&#xff0c;DHCP客户端和DHCP服务端&#xff0c;DHCP服务运行在&#xff16;&#xff17;端口和&#xff16;&#xff18;端口。 用途&#xff1a;&#xf…

一号团队-团队任务3:每日立会(2018-12-01)

一.基本信息 团队序号&#xff1a;一号 开发的软件名称&#xff1a;Java教学官网 撰写人&#xff1a;张浩洋 学号:2016035107283 职务:项目经理 二.团队汇报 1.汇报文字版 一号团队全体成员与2018年12月01日在第一组项目群中针对2018年11月30日工作任务进行汇报&#xff0c;汇总…

40 岁的中年失业

大家好&#xff0c;我是写代码的篮球球痴&#xff0c;昨天发了一篇文章&#xff0c;有一个朋友转发了&#xff0c;然后看到有人评论&#xff0c;如下&#xff1a;前两天有朋友问我&#xff0c;说腾讯裁员好严重&#xff0c;还有如何如何&#xff0c;我是这样回答的。很多事情我…

Linux/Windows系统内核性能调优

做过Linux平台性能测试的童鞋平时可能会遇到如下问题&#xff1a; 1、 TCP端口号不够用导致并发上不去&#xff08;即与服务器端建立新连接失败&#xff09; 2、 TIME_WAIT状态连接过多导致应用服务器&#xff08;Nginx、Haproxy、Redis、Tomcat等&#xff09;性能下降或假死…

MCU复位和程序启动那些事

大家好&#xff0c;这篇文章转自电源漫谈&#xff0c;文章从专业的角度说明了MCU复位经过的过程&#xff0c;对大家学习很有帮助。MCU通常会在工作之前&#xff0c;先经历复位和启动的一个过程&#xff0c;在用户使用过程中往往在这个阶段的工作不是那么的清楚&#xff0c;这里…

若有所思

今天出来​逛了下&#xff0c;没有准备文章&#xff0c;刚好一个朋友有一段思考&#xff0c;给大家分享下。深圳动物园是一个值得去的地方​-----佚名海边的夏天&#xff0c;太阳公公当空照&#xff0c;照的大地滚烫&#xff0c;海水湛蓝。人们都躲到树荫下&#xff0c;享受舶风…

第三章 阴阳的工作机制(1)

一、道生一&#xff0c;一生二&#xff0c;二生三&#xff0c;三生万物1.易有太极&#xff0c;是生两仪上面&#xff0c;对阴阳已经讨论了很多&#xff0c;对这个问题已经有所了解。现在我们来讨论"阴阳的工作机制"&#xff0c;弄清楚这个以后对理解《伤寒论》的许多…

jquery 操作日期、星期、元素的追加

主要实现日期的显示&#xff0c;获取年月日&#xff0c;时分秒、星期、判断闰年<script language"javascript" >$(document).ready(function(){function show(){var mydatenew Date();var str "" mydate.getFullYear() "年"; …

简单易懂的芯片科普漫画,帮你打开高深的新技术大门

大家好&#xff0c;我是写代码的篮球球痴&#xff0c;今天给大家推荐一本新书&#xff0c;是华为麒麟团队出版的&#xff0c;主要是让大家了解芯片的结构&#xff0c;这对很多人理解计算机是非常有帮助的。希望大家喜欢。喜欢书籍的同学在文章下面评论&#xff0c;我们会选出评…

在STM32上模拟Linux自动初始化过程

Linux中有很多编程思想可以学习&#xff0c;很多大佬把这些思想、机制运用到单片机的编程上&#xff0c;STM32 模拟Linux kernel自动初始化流程。通常我们写程序都是按照这个套路&#xff0c;一个函数一个函数按照顺序逻辑一个一个的执行下去。如果逻辑非常复杂&#xff0c;涉及…

日志配置(springboot、mybatis、Lombok)

Spring Boot在所有内部日志中使用Commons Logging&#xff0c;但是默认配置也提供了对常用日志的支持&#xff0c;如&#xff1a;Java Util Logging&#xff0c;Log4J, Log4J2和Logback。每种Logger都可以通过配置使用控制台或者文件输出日志内容 SLF4J——Simple Logging Faca…

1.6元的蓝牙芯片,你能想得到?

前不久喵了个咪在群里发了一个1.6元的SOP8蓝牙芯片的信息&#xff0c;引起了大家的热烈讨论&#xff0c;我当然也按捺不住鸡冻的心情&#xff0c;立马下单买了几片。淘宝连接&#xff1a;KT6368Ahttps://item.taobao.com/item.htm?spma1z09.2.0.0.74a52e8dTpp1my&id630430…

ADC采样效应及相关影响解剖

在前述文章&#xff0c;BUCK电路模拟补偿器的数字化过程 &#xff0c;我们讨论了模拟补偿器的数字化&#xff0c;事实上&#xff0c;数字化过程的第一个重要的环节就是ADC对反馈量的采样&#xff0c;本文就重点探讨一下由于ADC采样频率带来的一些问题&#xff0c;进而讨论一下相…

2012.2.9日晚亚马逊的一个错误链接截图

1现在时间 2 问题链接 http://www.amazon.cn/%E5%81%A5%E8%BA%AB%E5%99%A8%E6%A2%B0/b/refsa_menu_sef7?ieUTF8&node42787071 3 点击后结果 http://www.amazon.cn/gp/product/B0073RWUQA/refs9_hps_bw_g200_ir05?pf_rd_mA1AJ19PSB66TGU&pf_rd_scenter-5&pf_rd_r0…