树莓派4B-Python-使用PCA9685控制舵机云台+跟随人脸转动

系列文章

  1. 树莓派4B-Python-控制舵机
  2. 树莓派-Pico控制舵机
  3. 树莓派4B-Python-使用PCA9685控制舵机云台+跟随人脸转动(本文章)

目录

  • 系列文章
  • 前言
  • 一、SG90s舵机是什么?
  • 二、PCA9685与舵机信号线的接线图
  • 三、控制SG90s云台(也可用来测试舵机转动的范围)
  • 四、给树莓派注入灵魂(代码)
  • 五、给电脑注入灵魂(代码)
  • 总结


前言

先说明一下哈,本人用的是树莓派4B,Python的版本为3.7,OpenCV的版本为3.2.0

计划了好久的舵机云台终于有机会做出来了!原先一开始用的是塑料的云台+SG90舵机,但效果有点怪,云台会乱抽搐,于是换了金属的云台+SG90s舵机,嘿嘿,现在就没啥问题。
后来想了一下,还是觉得是以下的问题导致云台抽搐的:

  1. 舵机线靠近其他正在通电的电源线,受到电磁的干扰,导致舵机信号被干扰;
  2. PCA9685舵机控制板的问题。
    1、与舵机的接线接触不良;
    2、控制板与树莓派的接线接触不良。一般为通信线接触不良。
    3、控制板的5V供电有问题。输出电压不够5V或输出电流小于1A。
  3. 云台顺滑度的问题。本人的就是云台的上下转动的舵机那里卡了,导致变换不了舵机的角度,最后舵机发热严重,排查了好久才发现这个问题。

一、SG90s舵机是什么?

简单的说,就是SG90舵机的齿轮换成了金属齿轮而已,扭力增加了而已。可以理解为SG90舵机的升级版吧。
SG90S
还是不知道的可以参考之前写的关于舵机的内容:
树莓派4B-Python-控制舵机
树莓派-Pico控制舵机


二、PCA9685与舵机信号线的接线图

树莓派上的接线图
PCA9685上的接线图
简单说明一下接线:

PCA9685树莓派4B
GNDGND
DE空着
SCLSCL
SDASDA
VCC3.3V
V+5V(5V1A)

一定要接V+到5V,不然板子达不到控制舵机的效果。
若想外接5V1A的电源,那最好是将那个电源的GND与树莓派的GND连上,也就是共地。


提示:使用下面的代码前,记得先测试好舵机的转动角度哦,若转动的角度会卡死的话,舵机轻则发热,重则爆炸,所以切记切记,一定要先确定好舵机转动的范围。

三、控制SG90s云台(也可用来测试舵机转动的范围)

import time  # 引入time库
from adafruit_servokit import ServoKit  # 引入刚刚安装的PCA9685库并将名字缩写成ServoKit方便后续调用# from Adafruit_PCA9685 import ServoKit
kit = ServoKit(channels=16)  # 明确PCA9685的舵机控制数Servo1 = kit.servo[0]  # 左右转向的舵机
Servo2 = kit.servo[1]  # 上下移动的舵机Servo3 = kit.servo[2]  # 前后伸缩的舵机
Servo4 = kit.servo[3]  # 夹子舵机Servo5 = kit.servo[14]
Servo6 = kit.servo[15]Servo = [Servo1, Servo2, Servo3, Servo4, Servo5, Servo6]def servo_start(servo_number, start_angle, stop_angle, angle_sleep, time_sleep):"""servo_number:设置想要控制的舵机通道start_angle:设置起始的角度stop_angle:设置终止的角度angle_sleep:设置角度变换的步长  一般设为10time_sleep:设置角度变换的间隔   数字越小越快,但最好不要设置太快,舵机会反应不过来然后抽搐。最快0.005s,但是到达指定角度位置后需要等待1s左右让程序反应过来,否则会抽搐。"""for speed in range(start_angle, stop_angle, angle_sleep):servo_number.angle = speedprint(speed)time.sleep(time_sleep)def servo_dongzuo1():"""先让机械臂向后缩,然后向前伸,再向下移动,最后向上移动,这就回到了原点"""time_sleep = 0.01servo_start(Servo3, 110, 41, -1, time_sleep)servo_start(Servo3, 40, 111, 1, time_sleep)time.sleep(0.1)servo_start(Servo2, 130, 31, -1, time_sleep)servo_start(Servo2, 30, 131, 1, time_sleep)time.sleep(0.1)def aa():time_sleep = 0.01servo_start(Servo3, 40, 181, 1, time_sleep)servo_start(Servo3, 180, 41, -1, time_sleep)servo_start(Servo1, 90, 0, -1, 0.01)
servo_start(Servo1, 0, 180, 1, 0.01)
servo_start(Servo1, 180, 0, -1, 0.01)
servo_start(Servo1, 0, 90, 1, 0.01)
servo_start(Servo2, 80, 170, 1, 0.01)
servo_start(Servo2, 170, 80, -1, 0.01)

四、给树莓派注入灵魂(代码)

注入的灵魂如下(示例):

# -*- coding: utf-8 -*-import cv2
import time  # 引入time库
from adafruit_servokit import ServoKit  # 引入刚刚安装的PCA9685库并将名字缩写成ServoKit方便后续调用class Follow_the_face:def __init__(self):kit = ServoKit(channels=16)  # 明确PCA9685的舵机控制数self.Servo1 = kit.servo[0]  # 左右转向的舵机self.Servo2 = kit.servo[1]  # 上下移动的舵机self.Servo3 = kit.servo[2]  # 前后伸缩的舵机self.Servo4 = kit.servo[3]  # 夹子舵机self.Servo5 = kit.servo[14]self.Servo6 = kit.servo[15]self.Servo = [self.Servo1, self.Servo2, self.Servo3, self.Servo4, self.Servo5, self.Servo6]self.l_r_number_of_degrees = 110  # 左右电机初始化的角度self.u_d_number_of_degrees = 150  # 上下电机初始化的角度self.range_value = 30  # 在人脸检测中,设置人脸框的中心坐标与屏幕中心坐标的可接受偏移的范围def servo_start(self, servo_number, start_angle, stop_angle, angle_sleep, time_sleep):"""servo_number:设置想要控制的舵机通道start_angle:设置起始的角度stop_angle:设置终止的角度angle_sleep:设置角度变换的步长  一般设为10time_sleep:设置角度变换的间隔   数字越小越快,但最好不要设置太快,舵机会反应不过来然后抽搐。最快0.005s,但是到达指定角度位置后需要等待1s左右让程序反应过来,否则会抽搐。"""for speed in range(start_angle, stop_angle, angle_sleep):servo_number.angle = speedprint(speed)time.sleep(time_sleep)def Inittalize(self):"""初始化云台的各个位置,也算是一个启动前的角度自检和归位"""self.servo_start(self.Servo1, 111, 110, -1, 0.01)  # 可转动的角度为:0~180self.servo_start(self.Servo2, 180, 40, -1, 0.01)  # 可转动的角度为:40~180.40度时为垂直向上看self.servo_start(self.Servo2, 40, 150, 1, 0.01)def Auto_Angle(self, direction, offset_number):  # , auto_angle"""自动偏移指定度数direction:选择方向offset_number:偏移数auto_angle:自动角度"""if direction == "左":self.l_r_number_of_degrees -= offset_numberif 1 <= self.l_r_number_of_degrees <= 180:self.servo_start(self.Servo1, self.l_r_number_of_degrees, self.l_r_number_of_degrees - 1, -1, 0.01)else:self.l_r_number_of_degrees = self.l_r_number_of_degreesif direction == "右":self.l_r_number_of_degrees += offset_numberif 1 <= self.l_r_number_of_degrees <= 180:self.servo_start(self.Servo1, self.l_r_number_of_degrees, self.l_r_number_of_degrees - 1, -1, 0.01)else:self.l_r_number_of_degrees = self.l_r_number_of_degreesif direction == "上":self.u_d_number_of_degrees -= offset_numberprint('上被触发', self.u_d_number_of_degrees)if 41 <= self.u_d_number_of_degrees <= 179:self.servo_start(self.Servo2, self.u_d_number_of_degrees, self.u_d_number_of_degrees - 1, -1, 0.01)elif self.u_d_number_of_degrees > 179:u_d_number_of_degrees = 179self.servo_start(self.Servo2, u_d_number_of_degrees, u_d_number_of_degrees - 1, -1, 0.01)elif self.u_d_number_of_degrees < 40:self.u_d_number_of_degrees = 40self.servo_start(self.Servo2, self.u_d_number_of_degrees, self.u_d_number_of_degrees - 1, -1, 0.01)else:print('上出现意外', self.u_d_number_of_degrees)if direction == "下":self.u_d_number_of_degrees += offset_numberprint('下被触发', self.u_d_number_of_degrees)if 41 <= self.u_d_number_of_degrees <= 179:self.servo_start(self.Servo2, self.u_d_number_of_degrees, self.u_d_number_of_degrees - 1, -1, 0.01)elif self.u_d_number_of_degrees > 179:self.u_d_number_of_degrees = 179self.servo_start(self.Servo2, self.u_d_number_of_degrees, self.u_d_number_of_degrees - 1, -1, 0.01)elif self.u_d_number_of_degrees < 40:self.u_d_number_of_degrees = 40self.servo_start(self.Servo2, self.u_d_number_of_degrees, self.u_d_number_of_degrees - 1, -1, 0.01)else:print('下出现意外', self.u_d_number_of_degrees)def determine_face_position(self, difference_value_x, difference_value_y):"""检测人脸位置:param difference_value_x:x偏移的距离值:param difference_value_y:y偏移的距离值:return:"""if -self.range_value <= difference_value_x <= self.range_value and -self.range_value <= difference_value_y <= self.range_value:print("x坐标、y坐标都不用移动")elif difference_value_x > self.range_value:print("摄像头向右转动一点")self.Auto_Angle('右', 1)if difference_value_y > self.range_value:print("摄像头向下转动一点1")self.Auto_Angle('下', 2)if difference_value_y < -self.range_value:print("摄像头向上转动一点1")self.Auto_Angle('上', 2)elif difference_value_x < -self.range_value:print("摄像头向左转动一点")self.Auto_Angle('左', 1)if difference_value_y > self.range_value:print("摄像头向下转动一点2")self.Auto_Angle('下', 2)if difference_value_y < -self.range_value:print("摄像头向上转动一点2")self.Auto_Angle('上', 2)elif difference_value_y > self.range_value:print("摄像头向下转动一点")self.Auto_Angle('下', 2)elif difference_value_y < -self.range_value:print("摄像头向上转动一点")self.Auto_Angle('上', 2)def turn_on_face_detection(self):"""使用人脸检测:return:"""# 加载预训练的人脸检测器。直接选择树莓派中人脸模型(haarcascade_frontalface_default.xml)的路径即可face_cascade = cv2.CascadeClassifier('/home/pi/Desktop/12_29/haarcascade_frontalface_default.xml')# face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')# 创建用于显示相机画面的窗口cv2.namedWindow("Camera Feed")# 打开摄像头cap = cv2.VideoCapture(0)r, f = cap.read()# 获取画面的宽度和高度h, w, ch = f.shapeprint("输出画面的高度和宽度", h, w)center_x1 = int(w / 2)center_y1 = int(h / 2)print("输出中心坐标为:", center_x1, center_y1)# 记录人脸中心坐标f_x = f_y = f_w = f_h = Nonewhile True:# 读取相机画面ret, frame = cap.read()# 画面水平翻转flipped_frame = cv2.flip(frame, 1)  # 0时为垂直翻转# 获取画面的宽度和高度height, width, channels = flipped_frame.shape# 计算中心点的坐标center_x = int(width / 2)center_y = int(height / 2)# 正方形的边长length = 120# 左上角的xy坐标left_up_x = int(center_x - (length / 2))  # 因为原点在正方形的中心,处于一半的位置,所以左上角的x坐标需要边长除以2left_up_y = int(center_y - (length / 2))# 画红色的正方形cv2.rectangle(flipped_frame, (left_up_x, left_up_y), (left_up_x + length, left_up_y + length), (0, 0, 255),3)# 将帧转换为灰度图像gray = cv2.cvtColor(flipped_frame, cv2.COLOR_BGR2GRAY)# 进行人脸检测# 检测设置,将图片放大1.1倍(一般设1.1倍,看效果而定)# 重复检测的次数为6次(检测次数越多,速度越慢,检测也越严格,准确率可能有所提升)# 最小的检测框为100*100的正方形,以上的会显示,以下的被屏蔽faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=6, minSize=(100, 100))# 在帧中标记人脸for (x, y, w, h) in faces:f_x, f_y, f_w, f_h = x, y, w, hcv2.rectangle(flipped_frame, (x, y), (x + w, y + h), (0, 255, 0), 3)# 计算人脸正方形的中心点坐标if len(faces) > 0:  # 判断人脸的数量是否>0f_z_x = int(f_x + (f_w / 2))f_z_y = int(f_y + (f_h / 2))# print("脸部中心x:", f_z_x, "脸部中心y:",  f_z_y)else:f_z_x, f_z_y = center_x, center_y  # 没有人脸时就默认人脸位置居中,防止后续使用舵机云台时乱动# print("脸部中心x:", f_z_x, "脸部中心y:",  f_z_y)# 计算人脸中心与画面中心相差的坐标difference_value_x = f_z_x - center_xdifference_value_y = f_z_y - center_y# print("x坐标偏移量为:", difference_value_x, "y坐标偏移量为:", difference_value_y)# 检测人脸位置self.determine_face_position(difference_value_x, difference_value_y)# 显示相机画面cv2.imshow("Face following...", flipped_frame)# 检测按键,如果按下q键则退出循环if cv2.waitKey(1) & 0xFF == ord('q'):break# 释放摄像头资源cap.release()# 关闭窗口cv2.destroyAllWindows()face = Follow_the_face()
face.Inittalize()  # 初始化舵机位置
face.turn_on_face_detection()  # 开启人脸检测

五、给电脑注入灵魂(代码)

import cv2def detect_faces():# 加载预训练的人脸检测器face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')# 打开默认摄像头cap = cv2.VideoCapture(0)r, f = cap.read()# 获取画面的宽度和高度h, w, ch = f.shapeprint("输出画面的高度和宽度", h, w, ch)center_x1 = int(w / 2)center_y1 = int(h / 2)print("输出中心坐标为:", center_x1, center_y1)# 记录人脸中心坐标f_x = Nonef_y = Nonef_w = Nonef_h = Nonewhile True:# 读取帧ret, frame = cap.read()# 水平翻转帧flipped_frame = cv2.flip(frame, 1)  # 0时为垂直翻转# 获取画面的宽度和高度height, width, channels = flipped_frame.shape# 计算中心点的坐标center_x = int(width / 2)center_y = int(height / 2)# 在中心点画一个红色圆形# radius = 20# color = (0, 0, 255)  # 红色# thickness = 0  # 填充圆形# cv2.circle(flipped_frame, (center_x, center_y), radius, color, thickness)# 画个红色正方形length = 120# 左上left_up_x = int(center_x - (length / 2))     # 因为原点在正方形的中心,处于一半的位置,所以左上角的x坐标需要边长除以2left_up_y = int(center_y - (length / 2))# # 左下# left_down_x = int(center_x - (length / 2))# left_down_y = int(center_y + (length / 2))# # 右上# right_up_x = int(center_x + (length / 2))# right_up_y = int(center_y - (length / 2))# # 右下# right_down_x = int(center_x + (length / 2))# right_down_y = int(center_y + (length / 2))cv2.rectangle(flipped_frame, (left_up_x, left_up_y), (left_up_x + length, left_up_y + length), (0, 0, 255), 3)# 将帧转换为灰度图像gray = cv2.cvtColor(flipped_frame, cv2.COLOR_BGR2GRAY)# 进行人脸检测faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))# 在帧中标记人脸for (x, y, w, h) in faces:f_x, f_y, f_w, f_h = x, y, w, hcv2.rectangle(flipped_frame, (x, y), (x + w, y + h), (0, 255, 0), 3)# 计算人脸正方形的中心点坐标if len(faces) > 0:  # 判断人脸的数量是否>0f_z_x = int(f_x + (f_w / 2))f_z_y = int(f_y + (f_h / 2))# print("脸部中心x:", f_z_x, "脸部中心y:",  f_z_y)else:f_z_x, f_z_y = center_x, center_y   # 没有人脸时就默认人脸位置居中,防止后续使用舵机云台时乱动# print("脸部中心x:", f_z_x, "脸部中心y:",  f_z_y)# 计算人脸中心与画面中心相差的坐标difference_value_x = f_z_x - center_xdifference_value_y = f_z_y - center_y# print("x坐标偏移量为:", difference_value_x, "y坐标偏移量为:", difference_value_y)range_value = 10    # 设置可接受偏移的范围if -range_value <= difference_value_x <= range_value and -range_value <= difference_value_y <= range_value:print("x坐标、y坐标都不用移动")elif difference_value_x > range_value:print("摄像头向右转动一点")if difference_value_y > range_value:print("摄像头向下转动一点")if difference_value_y < -range_value:print("摄像头向上转动一点")elif difference_value_x < -range_value:print("摄像头向左转动一点")if difference_value_y > range_value:print("摄像头向下转动一点")if difference_value_y < -range_value:print("摄像头向上转动一点")elif difference_value_y > range_value:print("摄像头向下转动一点")elif difference_value_y < -range_value:print("摄像头向上转动一点")# 显示带有人脸标记的帧cv2.imshow("Faces Detected", flipped_frame)# 按下 'q' 键退出循环if cv2.waitKey(1) & 0xFF == ord('q'):break# 释放摄像头并关闭窗口cap.release()cv2.destroyAllWindows()# 调用实时人脸检测函数
detect_faces()

总结

目前的云台呢确实是可以跟随人脸转动的,但以下问题点:

  1. 实时显示相机中的画面并进行人脸检测时,有些流畅度不足,因为本人是显示彩色画面的,若改成灰度图的话应该会更快一点;
  2. 检测人脸的准确度不足,这里使用的是OpenCV自带的人脸检测模型,存在较多的误判,这样会干扰云台的自动人脸定位,所以可以的话,自己训练一个更优的人脸检测模型来使用。或者是调整重复检测次数之类的增加一些精准度吧,但重复检测次数增加得越多,画面越卡顿;
  3. 目前的程序只能针对画面中有一个人脸的时候进行跟随,若出现多个人脸,云台就会乱套了,各位大佬可以根据自身需求优化或利用这个问题;
  4. 使用模拟信号的SG90s舵机会存在一些精度的问题,表现的样子就是定位有些不准,然后出现抖动。可以的话,将模拟舵机换成数字舵机,当然价格会贵上一倍。数字舵机的精准度会高上许多,之后本人就打算买回来试试;

该文章若对你有帮助,不妨点个赞噢~


2024.01.15写
有写得不好、写得不对的地方还请各位指出,谢谢!

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

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

相关文章

MongoDB Compass 的教程

第一步&#xff1a;建立连接 点击Save&Connect 增加数据库&#xff1a; 填写数据库名字和文档名字并点击Create Database 删除文档&#xff1a; 创建文档&#xff1a; 插入文档数据 {Id:1001,name:"cyl",age:21} 插入成功&#xff1a; 更改原有数据 删除原有数据…

ACM论文LaTeX模板解析(二)| documentclass设置

本文收录于专栏&#xff1a;ACM 论文 LaTeX模板解析&#xff0c;本专栏将会围绕ACM 论文 LaTeX模板解析持续更新。欢迎点赞收藏关注&#xff01; 文章目录 要使用documentclass类&#xff0c;请在文件的开头中写明&#xff1a; \documentclass[⟨options⟩]{acmart}有几个选项…

SpringBoot-项目部署

SpringBoot项目部署可以通过将项目打成可执行的jar包或war包来实现&#xff0c;也可以使用容器化技术如Docker将项目部署到云平台中。在部署时需要注意配置文件的位置和启动参数的设置&#xff0c;同时确保目标环境中的Java版本与项目所需的Java版本一致。部署完成后&#xff0…

【Nuxt3】Nuxt3脚手架nuxi安装项目和项目目录介绍

简言 最近学了Nuxt3,并使用它创建了自己的小网站。记录下学习到的nuxt3内容。 Nuxt3官网 Nuxt 是一个免费的开源框架&#xff0c;可通过直观、可扩展的方式使用 Vue.js 创建类型安全、高性能、生产级的全栈 Web 应用程序和网站。 支持SSR、SPA、建立静态网站&#xff0c;也可以…

如何在BTC生态中创造独特的数字资产?bitget教程

BRC-20通证是什么&#xff1f; 听说了吗&#xff1f;BRC-20通证在比特币上搞事情啦&#xff01;它们不依赖智能合约&#xff0c;直接在比特币的最小单位上动手脚。这就像在用最小的积木搭房子&#xff0c;太神奇了&#xff01;虽然现在还在实验阶段&#xff0c;但已经有很多人…

12AOP面向切面编程/GoF之代理模式

先看一个例子&#xff1a; 声明一个接口&#xff1a; // - * / 运算的标准接口! public interface Calculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j); }实现该接口&#xff1a; package com.sunsplanter.prox…

流星全自动网页生成系统重构版源码

流星全自动网页生成系统重构版源码分享&#xff0c;所有模板经过精心审核与修改&#xff0c;完美兼容小屏手机大屏手机&#xff0c;以及各种平板端、电脑端和360浏览器、谷歌浏览器、火狐浏览器等等各大浏览器显示。 为用户使用方便考虑&#xff0c;全自动网页制作系统无需繁琐…

错误处理(基于ESP-IDF)

主要参考资料 B站Up主孤独的二进制《错误处理 - 乐鑫 ESP32 物联网开发框架 ESP-IDF 开发入门》 ESP-IDF编程指南>API指南>错误消息: https://docs.espressif.com/projects/esp-idf/zh_CN/v5.1/esp32s3/api-guides/error-handling.html ESP-IDF编程指南>API指南>严…

二、QT下载、安装及问题解决(windows系统)

本章节最重要的一点&#xff1a;安装时&#xff0c;路径中不能有中文&#xff0c;切记&#xff0c;否则QT不能正常运行。 下载两种途径&#xff1a; 1、官网下载&#xff0c;慢且不好访问&#xff1b; 2、国内一些大学网站的镜像&#xff0c;下载比较快&#xff0c;但是可能…

Android中的anr定位指导与建议

1.背景 8月份安卓出现了一次直播间卡死(ANR)问题&#xff0c;且由于排查难度较大&#xff0c;持续了较长时间。本文针对如何快速定位安卓端出现ANR问题进行总结和探讨. 这里大致补充一下当时的情况,当时看到情景的是从某一个特定的场景下进入直播间后整个直播间界面立刻就卡住…

lenovo联想笔记本电脑拯救者Legion Y7000 2019 PG0(81T0)原装出厂Windows10系统

链接&#xff1a;https://pan.baidu.com/s/1fn0aStc4sfAfgyOKtMiCCA?pwdas1l 提取码&#xff1a;as1l 联想拯救者原厂Win10系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;…

更换为mainwindow.ui更新工程架构

文章目录 前言一、新建带mainwindow.ui的工程1.新建工程2. 添加工程模块添加opencv的库3.添加资源3.1工程上添加资源3.2引用资源 4.添加曲线文件4.1 复制关键文件到新工程4.2 新进显示曲线的ui带.h的为了方面名字取一样4.3添加曲线显示控件4.4 添加工具 5. 添加曲线.h文件内容6…

MySQL之单表查询

素材&#xff1a; 表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker ( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL, 工资 float(8,2) NOT NULL, 政治面貌 varchar(10) NO…

数据库结构文档生成(通过PDMReader)

将数据库的表结构生成数据库结构文档有三种方法&#xff1a; 1、通过 PDMReader生成文档&#xff1b; 2、使用EZDML 工具生成&#xff08;下载地址&#xff1a;EZDML - 下载&#xff09;&#xff1b; 3、使用SCREW 插件&#xff0c;通过java代码生成。 本文章先介绍通过PDM…

Python入门0基础学习笔记

1.编程之前 在编写代码之前&#xff0c;还有两件事需要做&#xff1a; 安装 Python 解释器&#xff1a;计算机是没法直接读懂 Python 代码的&#xff0c;需要一个解释器作为中间的翻译&#xff0c;把代码转换成字节码之后再执行。 Python 是翻译一行执行一行。一般说的安装 …

【开源】基于JAVA语言的康复中心管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 普通用户模块2.2 护工模块2.3 管理员模块 三、系统展示四、核心代码4.1 查询康复护理4.2 新增康复训练4.3 查询房间4.4 查询来访4.5 新增用药 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的康复中…

css3 2D与3D转换

css3 2D与3D转换 前言2D变形旋转变形 rotate()transform-origin属性 缩放变形 scale()斜切变形 skew()位移变形 translate() 3D变形3D旋转 rotateX() | rotateY()perspective属性 空间移动 制作一个正方体结语 前言 网页设计不再局限于平面&#xff0c;而是充满了立体感和动态…

Learning Vision from Models Rivals Learning Vision from Data

Learning Vision from Models Rivals Learning Vision from Data 论文&#xff1a;https://arxiv.org/abs/2312.17742 TL; DR&#xff1a;只使用机造数据进行训练达到了与真实数据训练相当的效果。本文提出了 SynCLR。首先使用 LLM 来根据视觉概念词生成图像描述&#xff0c;再…

WEB 3D技术 three.js 点光源

本文的话 我们来设置一下点光源 点光源其实最直观的就是可以做萤火虫 也可以做星光 点点的效果 我们可以直接在官网中搜索 Pointlight 大家可以在官网这里看一下 其实 SpotLight 聚关灯中的属性 Pointlight 点光源也有的 我们先编写代码如下 import ./style.css import * a…

Codeforces Round 768 (Div. 1) D. Flipping Range(思维题 等价类性质 dp)

题目 思路来源 官方题解 洛谷题解 题解 可操作的最短区间长度肯定是gcd&#xff0c;记为g&#xff0c;然后考虑如何dp 考虑g个等价类&#xff0c;每个等价类i,ig,i2*g,... 每次翻转长度为g的区间&#xff0c;会同时影响到g个等价类总的翻转的奇偶性&#xff0c; 性质一&…