当前位置: 首页 > article >正文

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

快速使用

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.kler.cn/a/288391.html

相关文章:

  • C# WPF FontDialog字体对话框,ColorDialog颜色对话框 引用
  • C++STL容器——map和set
  • 安全见闻1-5
  • 如何使用 Web Scraper API 高效采集 Facebook 用户帖子信息
  • 鸿蒙学习基本概念
  • Diffusion Policy——斯坦福机器人UMI所用的扩散策略:从原理到其编码实现(含Diff-Control、ControlNet详解)
  • 中兴-ZSRV2路由器-任意文件读取
  • arcgisjs4.0 内网部署字体不显示问题处理
  • 【技术详解】Java泛型:全面解析与实战应用(进阶版)
  • sqli-labs靶场通关攻略(六十一关到六十五关)
  • ARM/Linux嵌入式面经(三十):腾讯 C++开发工程师
  • 【Linux学习】Linux开发工具——vim
  • html+css+js网页设计 博物馆 亚历山大美术馆6个页面
  • Flask中的g的作用
  • Linux学习笔记(4)----Debian压力测试方法
  • 日本IT编程语言对比分析-Python /Ruby /C++ /Java
  • 【加密社】马后炮视角来看以太坊二层战略
  • LLM大模型:不要怪大模型回答质量不行了,那是你不会问~
  • 计算机视觉之 SE 注意力模块
  • 微信小程序接入客服功能
  • 逆向工程核心原理 Chapter23 | DLL注入
  • 【舍入,取整,取小数,取余数丨Excel 函数】
  • 探索四川财谷通信息技术有限公司抖音小店的独特魅力
  • 收银系统源码-收银台UI自定义
  • 51单片机-第九节-AT24C02存储器(I2C总线)
  • 代码随想录算法训练营第35天 | 416.分割等和子集