娱乐小项目-树莓派履带小车

news2024/12/28 20:36:14

快速使用

1.小车上电,开关在电源插口旁边

2.上电之后用电脑查看局域网WIFI,密码是12345678,固定IP是192.168.50.1

3.安装VNC软件:20240324_树莓派履带车\工具

4.打开VNC软件

5.在这个界面下

按ctrl+alt+t,弹出终端

6.输入python pi.py

7.再按按ctrl+alt+t,弹出终端,输入python camer1.py(如果没有弹出摄像头界面,就试试python camer.py;python camer2.py;python camer3.py)

8.打开软件20240324_树莓派履带车\dist\mycar

9.正常操作软件

1.设备连接

小车上电,开关在电源接头的旁边,上电之后等待1分钟,发现前一分钟有不稳定的情况

小车自己有一个WIFI,密码是12345678,固定IP是192.168.50.1

连接这个网络

在电脑上打开命令行,输入命令ping 192.168.50.1,确认网络联通性,

通过命令行SSH登录设备

这个方式是电脑自带的,不需要安装什么软件,相对来说比较简单

如果在这方面有什么需求,可以自行用vscode或者xshell这类软件登录

还有一个是VNC登录设备,用于摄像头控制,安装包和配套资料一起给过来了

需要换源,不然国内网络可能会导致更新有问题(目前板子里面东西太多了,没法完成更多软件的安装)

修改/etc/apt/source.list的内容

# 官方软件源
deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main contrib non-free rpi
deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ buster main contrib non-free rpi

# 更新固件的软件源
deb http://mirrors.tuna.tsinghua.edu.cn/raspberrypi/ buster main ui

执行apt-get update

2.官方例程的使用

控制小车的运动

把下面的命令,一条条敲到终端运行(可以复制,不必一个字母一个字母的敲)

cd /home/pi/SmartCar
./CarRun
执行完车子会在原地运动,执行ctrl+c,结束程序

控制灯光

cd /home/pi/SmartCar ./ColorLED 执行ctrl+c,结束程序

配套的硬件程序都在/home/pi/SmartCar目录下

3.控制端程序

目前硬件几个关键的传感器或者部分有损坏,所以做了一个桌面的应用程序,不然车里面没什么好说的了

下面简单说一下程序的功能,需要自己安装python的开发环境,这个参考网上教程,就不多说了

soket连接部分

引用相关的库

import tkinter as tk
import time
import socket
import threading

实现socket连接,写收发的相关代码

HOST = ''  # 主机IP地址
PORT = 1234  # 端口号
MESSAGE_SIZE = 1024  # 消息大小
bind_ip = "192.168.50.73"

# 创建UDP套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

sock.bind((bind_ip, 1234))

def send_message(message):
    sock.sendto(message.encode(), ('192.168.50.1', 1234))

def receive_message():
    while True:
        data, address = sock.recvfrom(MESSAGE_SIZE)

        print(f"Received message: {data.decode()} from {address}")
        # 在此处处理接收到的消息

# 创建接收消息的线程
receive_thread = threading.Thread(target=receive_message)
receive_thread.daemon = True
receive_thread.start()

这段代码是一个简单的UDP网络通信的示例。它使用Python的socket模块来创建一个UDP套接字并进行数据的发送和接收。

代码中的变量解释如下:

  • HOST: 主机的IP地址(为空字符串表示绑定到所有可用的网络接口)
  • PORT: 端口号
  • MESSAGE_SIZE: 消息的最大大小
  • bind_ip: 用于绑定套接字的IP地址

接下来的代码执行以下操作:

  1. 创建一个UDP套接字对象,使用socket.socket(socket.AF_INET, socket.SOCK_DGRAM)语句,指定地址族为IPv4,套接字类型为数据报套接字。
  2. 设置套接字选项,使用sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)和sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)语句,分别启用套接字地址重用和广播功能。
  3. 使用sock.bind((bind_ip, 1234))将套接字绑定到指定的IP地址和端口号。

接下来定义了两个函数:

  • send_message(message)函数用于发送消息。它使用sock.sendto(message.encode(), ('192.168.50.1', 1234))语句将消息编码为字节流并发送给目标地址。
  • receive_message()函数用于接收消息。它通过循环不断接收数据,使用sock.recvfrom(MESSAGE_SIZE)语句接收数据和发送方的地址。然后打印接收到的消息和发送方的地址。

最后,代码创建了一个线程receive_thread,并将其设置为守护线程(daemon),然后启动线程。这个线程用于在后台不断接收消息,并在接收到消息时处理消息。

总的来说,这段代码创建了一个UDP套接字,绑定到指定的IP地址和端口号,然后通过一个线程来接收消息,并在接收到消息时进行处理。另外还提供了一个函数用于发送消息。

键盘消息响应

# 创建运动按键的回调函数
def move_forward_mouse():
    log_text.insert(tk.END, "前进\n")
    log_text.see(tk.END)
    send_message("move_forward")
    # 在此处编写前进的代码

def stop_forward_mouse():
    log_text.insert(tk.END, "停止前进\n")
    log_text.see(tk.END)
    send_message("stop_forward")
    # 在此处编写停止前进的代码

def move_backward_mouse():
    log_text.insert(tk.END, "后退\n")
    log_text.see(tk.END)
    send_message("move_backward")
    # 在此处编写后退的代码

def stop_backward_mouse():
    log_text.insert(tk.END, "停止后退\n")
    log_text.see(tk.END)
    send_message("stop_backward")
    # 在此处编写停止后退的代码

def turn_left_mouse():
    log_text.insert(tk.END, "左转\n")
    log_text.see(tk.END)
    send_message("turn_left")
    # 在此处编写左转的代码

def stop_left_mouse():
    log_text.insert(tk.END, "停止左转\n")
    log_text.see(tk.END)
    send_message("stop_left")
    # 在此处编写停止左转的代码

def turn_right_mouse():
    log_text.insert(tk.END, "右转\n")
    log_text.see(tk.END)
    send_message("turn_right")
    # 在此处编写右转的代码

def stop_right_mouse():
    log_text.insert(tk.END, "停止右转\n")
    log_text.see(tk.END)
    send_message("stop_right")
    # 在此处编写停止右转的代码
    
root.bind("<KeyPress-Up>", move_forward)
root.bind("<KeyRelease-Up>", stop_forward)
root.bind("<KeyPress-Down>", move_backward)
root.bind("<KeyRelease-Down>", stop_backward)
root.bind("<KeyPress-Left>", turn_left)
root.bind("<KeyRelease-Left>", stop_left)
root.bind("<KeyPress-Right>", turn_right)
root.bind("<KeyRelease-Right>", stop_right)

这段代码定义了一系列函数,用作按键事件的回调函数。它们用于处理运动控制按键的按下和释放事件,并在每个事件中执行相应的操作。

每个回调函数的功能如下:

  • move_forward_mouse()

: 当按下前进按键时,向文本框(log_text)插入"前进\n"的文本,并将其滚动到末尾可见位置。然后,通过调用send_message("move_forward")发送消息"move_forward"。在函数内部的注释处,你可以编写关于前进操作的代码。

  • stop_forward_mouse()

: 当释放前进按键时,向文本框(log_text)插入"停止前进\n"的文本,并将其滚动到末尾可见位置。然后,通过调用send_message("stop_forward")发送消息"stop_forward"。在函数内部的注释处,你可以编写关于停止前进操作的代码。

最后的代码段将这些回调函数与特定的按键事件绑定。它使用root.bind(, )的形式,将按键事件和相应的回调函数进行绑定。具体来说:

  • 和事件与move_forward_mouse()和stop_forward_mouse()回调函数绑定,用于前进操作。
  • 和事件与move_backward_mouse()和stop_backward_mouse()回调函数绑定,用于后退操作。
  • 和事件与turn_left_mouse()和stop_left_mouse()回调函数绑定,用于左转操作。
  • 和事件与turn_right_mouse()和stop_right_mouse()回调函数绑定,用于右转操作。

这些绑定意味着当用户按下或释放相应的按键时,与之对应的回调函数将被调用。例如,当用户按下前进按键时,move_forward_mouse()函数将被调用,文本框将显示"前进\n",并向指定的地址发送"move_forward"的消息。

这段代码定义了一组按键事件的回调函数,用于处理运动控制按键的按下和释放事件。每个回调函数负责更新日志文本框的内容、发送相应的消息,并在函数内部的注释处编写实际的运动控制代码。最后,通过绑定按键事件和回调函数,将它们与特定的按键操作关联起来。

4.设备端程序

soket连接部分

引用相关的库

# -*- coding: utf-8 -*-
import signal
import sys
import socket
import time
  • signal模块用于处理信号,例如用于进程间通信或中断处理。
  • sys模块提供了与Python解释器和运行时环境交互的函数。
  • socket模块提供了网络编程相关的功能。
  • time模块提供了时间相关的函数。
  • 树莓派里面,使用中文的注释,需要设置代码的编码,# -*- coding: utf-8 -*-就是用于编码格式的设置

基本程序部分

# 定义信号处理函数
def signal_handler(sig, frame):
    print("Received KeyboardInterrupt, exiting...")
    sys.exit(0)

# 注册信号处理函数
signal.signal(signal.SIGINT, signal_handler)

# 创建 socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 设置超时时间
sock.settimeout(0.1)  # 设置超时时间为10秒

# 绑定到本地端口
port = 1234
address = ('', port)
sock.bind(address)

try:
    motor_init()
    while True:
        try:
            # 接收数据
            data, address = sock.recvfrom(1024)
            message = data.decode()

            print(message)
            if message.strip() == "open beep":
                GPIO.output(buzzer, GPIO.LOW)   
            elif message.strip() == "close beep":
                GPIO.output(buzzer, GPIO.HIGH)
            elif message.strip() == "red":
                GPIO.output(red, GPIO.HIGH)
            elif message.strip() == "green":
                GPIO.output(green, GPIO.HIGH)
            elif message.strip() == "blue":
                GPIO.output(blue, GPIO.HIGH)
            elif message.strip() == "open video":
                pass
            elif message.strip() == "close video":
                pass
            elif message.strip() == "photo":
                pass    
            elif message.strip() == "move_forward":
                run(0.5)    
            elif message.strip() == "stop_forward":
                brake(0.5)        
            elif message.strip() == "move_backward":
                back(0.5)        
            elif message.strip() == "stop_backward":
                brake(0.5)    
            elif message.strip() == "turn_left":
                left(0.5)     
            elif message.strip() == "stop_left":
                brake(0.5)    
            elif message.strip() == "turn_right":
                right(0.5)    
            elif message.strip() == "stop_right":
                brake(0.5)    
            else:
                response = "命令识别失败"

            sock.sendto(message.encode(), address)
            # print(f"Received message: {data.decode()} from {address}")
        except socket.timeout:
            pass
except Exception as e:
    print("Error occurred:", e)
finally:
    sock.close()
    sys.exit()

实现了一个基于UDP协议的服务器端程序。它包含了信号处理函数、网络通信、GPIO控制等功能。

首先,定义了一个信号处理函数signal_handler,用于处理SIGINT信号(即键盘中断信号,通常由Ctrl+C触发)。在信号处理函数中,打印一条退出消息并调用sys.exit(0)退出程序。

然后,使用socket模块创建了一个UDP socket对象sock,并设置了超时时间为0.1秒。

接下来,通过sock.bind(address)将socket绑定到本地端口1234。

在一个主循环中,不断接收来自客户端的数据。使用sock.recvfrom(1024)接收数据,并将接收到的数据解码为字符串。根据接收到的消息内容,执行相应的操作,如打开/关闭蜂鸣器、控制GPIO引脚输出等。如果接收到未知的消息内容,则发送一个"命令识别失败"的响应消息。

最后,使用sock.sendto(message.encode(), address)将响应消息发送回客户端。

异常处理部分捕获任何异常,并打印出错误信息。

最后,在finally块中关闭socket并调用sys.exit()退出程序。

5.使用步骤

电脑上用python打开程序

python mycar.py

树莓派上登录VNC之后,用命令行打开程序,按ctrl+alt+t

python pi.py

通过鼠标点击软件或者用方向键可以控制程序

6.摄像头使用

代码解释

# -*- coding: utf-8 -*-
import cv2

# 创建摄像头对象
cap = cv2.VideoCapture(1)  # 0 表示默认摄像头,如果有多个摄像头可以尝试不同的索引值

# 检查摄像头是否成功打开
if not cap.isOpened():
    print("无法打开摄像头")
    exit()

# 循环读取摄像头数据
while True:
    # 逐帧捕获图像
    ret, frame = cap.read()

    # 检查图像是否成功获取
    if not ret:
        print("无法获取图像")
        break

    # 在窗口中显示图像
    cv2.imshow('Camera', frame)

    # 按下 'q' 键退出循环
    if cv2.waitKey(1) == ord('q'):
        break

# 释放摄像头资源
cap.release()

# 关闭窗口
cv2.destroyAllWindows()

使用OpenCV库进行摄像头图像捕获和显示的简单示例。

首先,导入了cv2模块,这是OpenCV库的Python接口。

接下来,通过cv2.VideoCapture()函数创建了一个摄像头对象cap,并指定摄像头的索引值为1。如果有多个摄像头可用,可以尝试不同的索引值。(硬件不稳定,有时候要自己尝试用哪个,输入命令ls /dev/video*,从数值最小的那个往上加)

然后,通过调用cap.isOpened()方法检查摄像头是否成功打开。如果摄像头无法打开,则打印一条错误消息并退出程序。

在一个主循环中,使用cap.read()方法逐帧读取摄像头数据。ret是一个布尔值,表示是否成功获取到图像帧,frame是捕获到的图像帧。

然后,检查图像是否成功获取。如果未成功获取到图像帧,则打印一条错误消息并跳出循环。

接下来,使用cv2.imshow()方法在一个名为"Camera"的窗口中显示图像帧。

通过调用cv2.waitKey(1)等待用户按键输入,如果按下键盘上的'q'键,则退出主循环。

循环结束后,使用cap.release()释放摄像头资源。

最后,调用cv2.destroyAllWindows()关闭显示图像的窗口。

7.二值化程序

程序

#--coding:utf-8 --
import cv2

# 创建摄像头对象
cap = cv2.VideoCapture(1)  # 0 表示默认摄像头,如果有多个摄像头可以尝试不同的索引值

# 检查摄像头是否成功打开
if not cap.isOpened():
    print("无法打开摄像头")
    exit()

# 循环读取摄像头数据
while True:
    # 逐帧捕获图像
    ret, frame = cap.read()

    # 检查图像是否成功获取
    if not ret:
        print("无法获取图像")
        break

    # 将图像转换为灰度图
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 进行二值化处理
    _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

    # 在窗口中显示二值化后的图像
    cv2.imshow('Camera', binary)

    # 按下 'q' 键退出循环
    if cv2.waitKey(1) == ord('q'):
        break

# 释放摄像头资源
cap.release()

# 关闭窗口
cv2.destroyAllWindows()

8.颜色识别

#--coding:utf-8 --
import cv2
import numpy as np

# 定义要识别的颜色范围(以HSV颜色空间为基准)
lower_red = np.array([0, 100, 100])
upper_red = np.array([10, 255, 255])
lower_green = np.array([35, 100, 100])
upper_green = np.array([85, 255, 255])
lower_blue = np.array([100, 100, 100])
upper_blue = np.array([130, 255, 255])
lower_white = np.array([0, 0, 200])
upper_white = np.array([180, 30, 255])

# 创建摄像头对象
cap = cv2.VideoCapture(0)  # 0 表示默认摄像头,如果有多个摄像头可以尝试不同的索引值

# 检查摄像头是否成功打开
if not cap.isOpened():
    print("无法打开摄像头")
    exit()

# 循环读取摄像头数据
while True:
    # 逐帧捕获图像
    ret, frame = cap.read()

    # 检查图像是否成功获取
    if not ret:
        print("无法获取图像")
        break

    # 将图像转换为HSV颜色空间
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    # 根据颜色范围创建掩码
    mask_red = cv2.inRange(hsv, lower_red, upper_red)
    mask_green = cv2.inRange(hsv, lower_green, upper_green)
    mask_blue = cv2.inRange(hsv, lower_blue, upper_blue)
    mask_white = cv2.inRange(hsv, lower_white, upper_white)

    # 对原始图像和掩码进行按位与操作
    result_red = cv2.bitwise_and(frame, frame, mask=mask_red)
    result_green = cv2.bitwise_and(frame, frame, mask=mask_green)
    result_blue = cv2.bitwise_and(frame, frame, mask=mask_blue)
    result_white = cv2.bitwise_and(frame, frame, mask=mask_white)

    # 在窗口中显示识别结果
    cv2.imshow('Red', result_red)
    cv2.imshow('Green', result_green)
    cv2.imshow('Blue', result_blue)
    cv2.imshow('White', result_white)

    # 按下 'q' 键退出循环
    if cv2.waitKey(1) == ord('q'):
        break

# 释放摄像头资源
cap.release()

# 关闭窗口
cv2.destroyAllWindows()

首先定义了要识别的颜色范围,这里以HSV颜色空间为基准,使用np.array()创建了一个包含最低和最高颜色值的数组。

然后,我们将图像转换为HSV颜色空间,使用cv2.cvtColor()函数将BGR图像转换为HSV图像。

接下来,我们根据颜色范围创建了一个掩码,使用cv2.inRange()函数根据最低和最高颜色值在HSV图像中创建一个二值掩码。

然后,我们使用cv2.bitwise_and()函数对原始图像和掩码进行按位与操作,得到识别结果。

最后,我们在窗口中显示识别结果,并通过按下键盘上的'q'键来退出循环。

9.WEB服务控制

在电脑的浏览器输入下面的网址,会进入树莓派的WEB控制

http://yahboom4wd:8889

如要实现RGB灯的控制

点击运行就可以了

10.小车循迹

采样python调用可执行文件的方式,了解linux下程序进程的开启和关闭

实现了两个shell脚本

run.sh

#!/bin/bash
/home/pi/SmartCar/tracking &

stop.sh

#!/bin/bash
pkill -f tracking

通过subprocess实现对外部程序的调用

            elif message.strip() == "tracking":
                subprocess.Popen("/home/pi/run.sh", shell=True)
            elif message.strip() == "stop":
                brake(0.5)  
                subprocess.Popen("/home/pi/stop.sh", shell=True)

循迹逻辑

void main()
{
  //wiringPi初始化
  wiringPiSetup();
  
  //初始化电机驱动IO口为输出方式
  pinMode(Left_motor_go, OUTPUT);
  pinMode(Left_motor_back, OUTPUT);
  pinMode(Right_motor_go, OUTPUT);
  pinMode(Right_motor_back, OUTPUT);

  //创建两个软件控制的PWM脚
  softPwmCreate(Left_motor_pwm,0,255); 
  softPwmCreate(Right_motor_pwm,0,255);
  
  //定义按键接口为输入接口
  pinMode(key, INPUT);

  //定义四路循迹红外传感器为输入接口
  pinMode(TrackSensorLeftPin1, INPUT);
  pinMode(TrackSensorLeftPin2, INPUT);
  pinMode(TrackSensorRightPin1, INPUT);
  pinMode(TrackSensorRightPin2, INPUT);

  //调用按键扫描函数
//  key_scan();
  
  while(1)
  {
   //检测到黑线时循迹模块相应的指示灯亮,端口电平为LOW
   //未检测到黑线时循迹模块相应的指示灯灭,端口电平为HIGH
   TrackSensorLeftValue1  = digitalRead(TrackSensorLeftPin1);
   TrackSensorLeftValue2  = digitalRead(TrackSensorLeftPin2);
   TrackSensorRightValue1 = digitalRead(TrackSensorRightPin1);
   TrackSensorRightValue2 = digitalRead(TrackSensorRightPin2);

   //四路循迹引脚电平状态
   // 0 0 X 0
   // 1 0 X 0
   // 0 1 X 0
   //以上6种电平状态时小车原地右转,速度为250,延时80ms
   //处理右锐角和右直角的转动
   if ( (TrackSensorLeftValue1 == LOW || TrackSensorLeftValue2 == LOW) &&  TrackSensorRightValue2 == LOW)
   {
     spin_right(150, 150);
     delay(80);
   }
   //四路循迹引脚电平状态
   // 0 X 0 0       
   // 0 X 0 1 
   // 0 X 1 0       
   //处理左锐角和左直角的转动
   else if ( TrackSensorLeftValue1 == LOW && (TrackSensorRightValue1 == LOW ||  TrackSensorRightValue2 == LOW))
   {
     spin_left(150, 150);
     delay(80);
   }
   // 0 X X X
   //最左边检测到
   else if ( TrackSensorLeftValue1 == LOW)
   {
     spin_left(150, 150);
    // delay(10);
   }
   // X X X 0
   //最右边检测到
   else if ( TrackSensorRightValue2 == LOW )
   {
     spin_right(150, 150);
    // delay(10);
   }
   //四路循迹引脚电平状态
   // X 0 1 X
   //处理左小弯
   else if ( TrackSensorLeftValue2 == LOW && TrackSensorRightValue1 == HIGH)
   {
     left(0, 150);
   }
   //四路循迹引脚电平状态
   // X 1 0 X  
   //处理右小弯
   else if (TrackSensorLeftValue2 == HIGH && TrackSensorRightValue1 == LOW)
   {
     right(150, 0);
   }
   //四路循迹引脚电平状态
   // X 0 0 X
   //处理直线
   else if (TrackSensorLeftValue2 == LOW && TrackSensorRightValue1 == LOW)
   {
     run(150, 150);
   }
   //当为1 1 1 1时小车保持上一个小车运行状态
 }
 return;
}

这段代码是一个示例的C语言程序,用于控制基于树莓派的循迹小车。下面是对代码的解释:

  1. wiringPiSetup():这是WiringPi库的初始化函数,用于初始化树莓派的GPIO引脚。
  2. pinMode():这些函数用于设置引脚的输入或输出模式。例如,pinMode(Left_motor_go, OUTPUT)将Left_motor_go引脚设置为输出模式。
  3. softPwmCreate():这些函数用于创建软件控制的PWM脚。在这里,创建了Left_motor_pwm和Right_motor_pwm两个软件PWM引脚。
  4. pinMode(key, INPUT):将key引脚设置为输入模式,用于接收按键输入。
  5. pinMode(TrackSensorLeftPin1, INPUT)等:将四个循迹红外传感器引脚设置为输入模式。
  6. while(1):进入主循环,程序将在这里一直运行。
  7. 检测循迹传感器状态:通过digitalRead()函数读取循迹传感器引脚的电平状态,将结果存储在相应的变量中(例如TrackSensorLeftValue1)。
  8. 根据传感器状态执行相应的动作:使用一系列的条件语句(if和else if)来判断传感器的状态,并根据情况执行相应的动作,如转弯、直行或停止。
  9. delay():延时函数,用于控制小车的动作持续时间。
  10. return:函数结束。

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

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

相关文章

内存管理篇-20 Linux虚拟内存管理

1.虚拟地址的经典布局 这里的内容比较少。只要就是内核用户空间的划分。内核空间又有自己的划分。也需要注意一下每个区域的性能。理论上线性映射是最简单的&#xff0c;所以性能最高。同时&#xff0c;注意内核空间是可以配置的&#xff0c;并不是都3:1。 2.ARM32下的内存…

使用cURL探索WebSocket连接的奥秘

更多内容访问个人网站&#xff1a;孔乙己大叔 在现代Web开发中&#xff0c;实时通信已经成为不可或缺的一部分。WebSocket协议因其能够提供低延迟、全双工的通信能力&#xff0c;而被广泛应用于各种实时应用场景中&#xff0c;如在线聊天、实时通知、游戏等。虽然WebSocket主要…

React 实现PDF预览(数据源使用文件流而不是url)

一 前提 应公司要求&#xff0c;需要进行上传文件&#xff08;pdf&#xff09;的预览功能&#xff0c;网上大部分都是使用url作为预览数据源&#xff0c;但是现在后端那边只返回了pdf文件流&#xff0c;所以本文主要是用文件流来预览pdf。 二 首先需要获取pdf文件流&#xff…

四款经典的防泄密软件,企业防泄密必备软件

防泄密软件有哪些呢&#xff1f;以下是四款经典的防泄密软件介绍&#xff0c;每款软件都将从其主要功能、特点以及适用场景等方面进行详细阐述。 1. 安企神 主要功能&#xff1a; 文件加密&#xff1a;提供全面的文件加密解决方案&#xff0c;支持对敏感文件进行加密处理&…

IP地址在TikTok运营中为何重要?

TikTok作为外贸人宣传推广的重要平台&#xff0c;其运营成效与产品的实际转化率息息相关。然而&#xff0c;在TikTok的运营过程中&#xff0c;一个看似微不足道的元素—IP地址&#xff0c;却扮演着至关重要的角色。本文将深入探讨TikTok运营中IP地址的重要性&#xff0c;揭示其…

ETL数据集成丨SQLServer到Doris的无缝数据同步策略

在数据驱动的新时代&#xff0c;企业对数据的需求日益增加&#xff0c;尤其是数据同步的速度和准确性。随着数据源和数据目标的多样化&#xff0c;如何实现高效、无缝的数据同步成为了许多企业的关注焦点。ETLCloud正是这一领域的先锋&#xff0c;为用户提供了从 SQLServer 到 …

面向GPU计算平台的归约算法的性能优化研究

1 GPU归约算法的实现与优化 图3-1为本文提出的GPU归约算法总图&#xff0c;GPU归约求和算法的实现可以定义为三个层次&#xff1a; 线程内归约&#xff1a;线程从global memory中读取一个或多个数据进行归约操作&#xff0c;再把归约结果写入至LDS&#xff1b;work-group内归…

告警管理大师:深入解析Alertmanager的配置与实战应用

目录 一、前言 二、Alertmanager 简介 三、Alertmanager核心内容介绍 &#xff08;1&#xff09;告警分组&#xff08;Alert Grouping&#xff09; 分组原理 配置示例 &#xff08;2&#xff09;告警路由&#xff08;Alert Routing&#xff09; 路由原理 配置示例 &a…

中资优配:白马股跌出性价比 基金经理公开唱多

近年来走势欠安的一些白马股&#xff0c;其时现已跌出了性价比。 在刚刚宣布的二季报中&#xff0c;就有多名基金司理旗帜鲜明地标明看好此类财物。有基金司理认为&#xff0c;这些个股的股息率已靠近或高于无风险利率&#xff0c;其隐含的长期酬谢水平或许已明显高于其时获商…

VScode 的下载安装及常见插件 + Git的下载和安装

目录 一、VScode 的下载安装及常见插件 1、VSCode下载 2、VSCode安装 3、VSCode常见扩展插件及介绍 二、Git的下载和安装 1、Github 和 Gitee的区别 2、Git下载&#xff08;以Win为例&#xff09; 3、Git安装 一、VScode 的下载安装及常见插件 1、VSCode下载 &#x…

VBA字典与数组第十八讲:VBA中静态数组的定义及创建

《VBA数组与字典方案》教程&#xff08;10144533&#xff09;是我推出的第三套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;字典是VBA的精华&#xff0c;我要求学员必学。7.1.3.9教程和手册掌握后&#xff0c;可以解决大多数工作中遇到的实际问题。…

ArcGIS小技巧:批量加载文件夹下的所有SHP数据到当前地图框

欢迎关注同名微信公众号&#xff0c;更多文章推送&#xff1a; 一般情况下&#xff0c;如果要加载SHP数据&#xff0c;只要在工程目录栏中将其拖到当前地图框中即可。 假设这样一个场景&#xff0c;一个文件夹下分布着很多个SHP数据&#xff0c;甚至有的SHP数据位于子文件夹中…

python进阶篇-day04-闭包与装饰器

day04闭包装饰器 函数参数 函数名作为对象 细节 Python是一门以 面向对象为基础的语言, 一切皆对象, 所以: 函数名也是对象. 直接打印函数名, 打印的是函数的地址. 函数名()则是在调用函数. 函数名可以作为对象使用, 所以它可以像变量一样赋值, 且赋值后的 变量名() 和 调用…

用 BigQuery ML 和 Google Sheets 数据预测电商网站访客趋势

看看如何使用 BigQuery ML 与 Google Sheets 构建时间预测模型&#xff0c;为商业分析提供助力~ 电子表格无处不在&#xff01;作为最实用的生产力工具之一&#xff0c;Google Workspace 的 Sheets 电子表格工具拥有超过 20 亿用户&#xff0c;可让数据的组织、计算和呈现变得轻…

如何完整删除rancher中已接入的rancher集群并重新导入

前提&#xff1a;如果手动删除kubectl delete all --all --namespace<namespace>删除不了的情况下可以使用此方案 一&#xff1a;查找rancher接入集群的所有namespace 接入rancher的k8s集群namespace都是以cattle命名的 rootA800-gpu-node01:~# kubectl get namespaces |…

32位Win7+64位Win10双系统教程来袭,真香!

前言 前段时间整了很多关于Windows双系统的教程&#xff0c;但基本都是UEFI引导启动的方式&#xff0c;安装的系统要求必须是64位Windows。 各种双系统方案&#xff08;点我跳转&#xff09; 今天咱们就来玩一玩32位 Windows 764位 Windows 10的装机方案&#xff01; 开始之…

逆向工程核心原理 Chapter23 | DLL注入

前面学的只是简单的Hook&#xff0c;现在正式开始DLL注入的学习。 0x01 DLL注入概念 DLL注入指的是向运行中的其它进程强制插入特点的DLL文件。 从技术细节上来说&#xff0c;DLL注入就是命令其它进程自行调用LoadLibrary() API&#xff0c;加载用户指定的DLL文件。 概念示…

PMP–一、二、三模、冲刺、必刷–分类–2.项目运行环境–治理

文章目录 技巧一模2.项目运行环境--4.组织系统--治理--项目组合、项目集和项目治理--项目治理是指用于指导项目管理活动的框架、功能和过程&#xff0c;从而创造独特的产品、服务或结果以满足组织、战略和运营目标。不存在一种治理框架适用于所有组织。组织应根据组织文化、项目…

【Godot4.1】自定义纯绘图函数版进度条控件——RectProgress

概述 一个纯粹基于CanvasItem绘图函数&#xff0c;重叠绘制矩形思路实现的简单进度条控件。2023年7月编写。 之所以将它作为单独的示例发出来&#xff0c;是因为它代表了一种可能性&#xff0c;就是不基于Godot内置的控件&#xff0c;而是完全用绘图函数或其他底层API形式来创…

第二百一十二节 Java反射 - Java构造函数反射

Java反射 - Java构造函数反射 以下四种方法来自 Class 类获取有关构造函数的信息: Constructor[] getConstructors() Constructor[] getDeclaredConstructors() Constructor<T> getConstructor(Class... parameterTypes) Constructor<T> getDeclaredConstructor(…