使用 Python 和 OpenCV 制作反应游戏

news2024/11/15 17:52:41

e77077fd059e07a749b5dc11bfea02ac.jpeg

在本文中,将向你展示如何使用 OpenCV 在 Python 中制作一个反应游戏,你可以动手来玩。

你可能已经熟悉 OpenCV,OpenCV 基本上允许进行各种图像处理。

你可以在下面的视频中看到最终结果,并且可以在此处获取文件:https://github.com/Goncalo-Chambel/ReactionGame

尽管这可能看起来很复杂(取决于你的专业知识),但在我们看来,这是一个相当简单但很有趣的项目。你基本上可以用 200 行代码创建一个游戏(这代码量很少了!)。

我们将把任务分成几个部分:设置+手部检测主要游戏机制创建实际游戏最后润色

第 1 步:设置 + 手部检测

这个项目的主要目标是创建一个反应游戏,其中圆圈会随机出现在屏幕上,你必须用你的手尽可能快地“触摸”它们。

因此,第一个步骤是让程序访问你的网络摄像头。

为此,我们将使用 OpenCV 库,为此我们只需添加一行import cv2。就这么简单,但如果你还没有安装,你必须先安装它。

在此处添加了此项目的要求:https://github.com/Goncalo-Chambel/ReactionGame/blob/main/requirements.txt

因此你可以通过在命令行中键入pip install -r requirements.txt来安装所有这些要求。

cv2 库有很多功能,但让我们一步一步来第一个目标是告诉 Python 从网络摄像头读取数据并将其显示在屏幕上。这可以通过使用函数cv2.VideoCapture()来完成

import cv2
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)

现在cap变量包含对你的网络摄像头的引用。然后在我们的主文件中,我们可以创建一个无限循环,每次迭代都会显示网络摄像头捕获的当前图像。

import cv2
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)  # set width of window
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)  # set height of window

while True:
    ret, frame = cap.read()
    cv2.imshow("Reaction Game", frame)
    k = cv2.waitKey(1) & 0xFF
    if k == ord('q'):
        break

有了这几行代码,我们应该有一个程序,这个程序可以简单地打开一个新窗口(名为“反应游戏”),大小为 1280 x 720 像素,带有网络摄像头的视频源。我还添加了最后几行代码,以便你可以关闭窗口并按“q”退出程序。

现在我们可以继续检测手了。创建一个算法来检测视频中的手是一项复杂的任务,但幸运的是我们不必重新发明轮子。有一个非常好的库可以为我们做这件事,叫做 CV Zone。

CV Zone 允许各种与对象检测相关的项目,但我们主要对 HandDetector 模型感兴趣。该模型使我们能够获得有关视频中被跟踪手的重要信息,例如它们的中心位置或边界框。我们初始化模型的方式如下:

from cvzone.HandTrackingModule import HandDetector
detector = HandDetector(detectionCon=0.8, maxHands=2)

其中detectionCon是置信区间(从0到1),表明你希望模型跟踪手的精度。更高的值意味着模型更确信被跟踪的手就是手,但也可能使模型“错过手”,因为它没有信心相信它们是手。maxHands参数仅限制模型一次可以跟踪的手的数量。

然后在我们的主循环中,我们只调用函数detector.findHands()来获取有关被跟踪手的信息。

while True:
    ret, frame = cap.read()
    hands, frame = detector.findHands(frame, flipType=False)
    cv2.imshow("Reaction Game", frame)

输出:

80bfdedd12b33cc12851b49e2c41de09.png

如你所见,该算法非常擅长跟踪手部(包括手指位置)。请注意,根据你的规格,该程序可能看起来有点“不稳定”。此外,根据你使用的相机,你可能不需要翻转图像,只需检查标签“左手”是否真的对应于你的左手。

最后,在屏幕上显示所有这些信息可能会太混乱,因此我们可以告诉检测器不要通过书写来绘制这些信息

hands = detector.findHands(frame, flipType=False, draw=False)

请注意,当我们包含参数draw=False时,detector仅返回手信息(与返回帧相反)。

第 2 步:主要游戏机制

我们现在可以研究我们游戏的基本机制。基本想法是在屏幕上随机生成圆圈并检测手是否在触摸它。

这里有两个主要部分,画圈检查碰撞

画圈

这是一个相对简单的步骤,因为cv2有一个内置函数cv2.circle(),可以在屏幕上绘制一个圆形,该函数将我们正在绘制的图像、屏幕上的位置(以像素为单位)、圆的半径(以像素为单位)、颜色(以BGR) 和线条粗细作为输入。所以假设我们想在屏幕中间放置一个红色圆圈,厚度为2,半径为 50 像素,我们会这样做:

while True:
    ret, frame = cap.read()
    cv2.circle(frame,(int(width/2),int(heigh/2)), 50, (0,0,255), 2)
    cv2.imshow("Reaction Game", frame)

请注意,此函数不返回任何内容,它会自动更新frame变量。此外,如果你想要一个实心圆而不仅仅是轮廓,你可以将粗细设置为 -1。

在继续在屏幕上创建随机圆圈之前,让我们先创建自己的Circle类。如果我们想在只处理一个变量的同时访问圆的多个属性,这将很方便。这是一个非常简单的类,现在让我们添加一个构造函数和一个draw()方法

class Circle:

    def __init__(self, coordinates, radius, color, thickness):
        self.coordinates = coordinates
        self.radius = radius
        self.color = color
        self.thickness = thickness

    def draw(self, _frame):
        cv2.circle(_frame, self.coordinates, self.radius, self.color, self.thickness)

现在如果我们想像之前那样画一个圆圈,我们会这样做:

target = Circle((int(width/2),int(heigh/2)), 50, (0, 0,255), 2)
while True:
    ret, frame = cap.read()
    target.draw(frame)
    cv2.imshow("Reaction Game", frame)

现在可能看起来不是很有用,但以后会有所帮助。

在继续讨论交集方法本身之前,我们首先需要一些东西来检查交集。我们已经有了一种在屏幕上创建目标的方法,但我们需要将其与某些东西进行比较。很明显,有些东西会是我们的手,但具体是手的哪一部分?

我们可以使用边界框,但我觉得这可能太容易了。我们还可以使用手指的位置,例如食指,但这似乎不太直观。我发现我认为效果最好的方法是使用手的中心位置。

我们可以在手的中心位置创建另一个圆圈,这样我们只需要检查一个圆圈是否与另一个圆圈相交。

首先我们需要一种方法在手的中心位置创建一个圆圈。变量handsdetector.findHands()函数的输出)是一个列表,其中每个项目都是一个字典,其中包含有关被跟踪的手的信息。这个字典有 4 个键:

  • lmList:21 个地标的位置列表(以像素为单位)

  • bbox : 边界框的坐标和大小(以像素为单位)

  • center : 中心位置的坐标,以像素为单位

  • type:左手或右手

从这 4 个键中,我们感兴趣的是中心,所以为了得到中心位置,我们这么做:

while True:
    ret, frame = cap.read()
    hands = detector.findHands(frame, flipType=False, draw=False)
    if hands:
        for i in range(len(hands)):
            hand_position = hands[i]["center"]

我们首先检查是否检测到任何手,如果是,我们可以访问中心位置,但只需指定键“center”。

现在我们有了创建一个圆圈的方法,我们可以在每只手的中心创建一个圆圈并通过添加这两条线来绘制它

hand_circle = Circle(hand_position, hand_radius, (0, 0, 255), 1)
hand_circle.draw(frame)

这会在每个被跟踪手的中心位置绘制一个红色圆圈(未填充)。注意,手的中心位置不是手掌中心,而是所有地标位置的平均位置。你可以通过合上手来测试一下,你可以看到中心位置向你手的下部移动。

检查碰撞

我们需要一种方法来检查玩家是否击中了目标。这将是我们游戏的主要机制。

Cv2 没有任何检查两个对象是否相交的函数,但由于我们处理的是圆,所以这个任务变得非常简单。我们只需要检查圆心之间的距离是否小于或等于半径之和。让我们看一下下面的例子

e4034f6f28d1fc35e19ce1047df57680.png

相交算法

希望这说明了相交算法。由于我们已经知道目标半径和手圆半径,我们只需要计算距离d。并且有一个非常简单的数学公式,给定两个点,计算它们之间的距离。

该距离公式是:

000d40be9c4679af4168417a5b3f5720.png

我们所要做的就是在代码中创建该函数。为了方便起见,我在Circle类中创建了这个函数,如下所示

def check_intersection(self, other_coordinates, other_radius):
    distance = math.sqrt(math.pow(other_coordinates[0] - self.coordinates[0], 2) + math.pow(
        other_coordinates[1] - self.coordinates[1], 2))

    if distance <= self.radius + other_radius:
        return True
    else:
        return False

该函数考虑了我们上面讨论的所有内容并返回一个布尔值,指示一个圆是否与另一个圆相交。

我们可以通过在主循环中添加几行来测试它

if hands:
    for i in range(len(hands)):
        hand_position = hands[i]["center"]
        hand_circle = Circle(hand_position,hand_radius,(0,0,255),1)
        if target.check_intersection(hand_circle.coordinates, hand_circle.radius):
           
            # is intersecting
            hand_circle.color = (0, 255, 0)
        else:
            # not intersection
            hand_circle.color = (0, 0, 255)
        hand_circle.draw(frame)

你现在可以看到,如果我“触摸”目标,我的手圈会变成绿色,否则就是红色

第 3 步:创建实际游戏

因此,创建我们实际游戏的第一步是,一旦我们击中当前目标,就能够在随机位置选择一个新目标。为此,我们可以创建一个这样的函数

def create_random_target(current_target_pos=[]):
    if current_target_pos:
        possible_x = []

        x_limit = [target_radius + border_size + 15, width - target_radius - border_size - 15]
        y_limit = [target_radius + border_size + 15, height - target_radius - border_size - 15]

        for i in range(x_limit[0], x_limit[1]):
            if i + 200 < current_target_pos[0] or i - 200 > current_target_pos[0]:
                possible_x.append(i)

        possible_y = []
        for i in range(y_limit[0], y_limit[1]):
            if i + 200 < current_target_pos[1] or i - 200 > current_target_pos[1]:
                possible_y.append(i)

        if not possible_x:
            possible_x = range(x_limit[0], x_limit[1])

        if not possible_y:
            possible_y = range(y_limit[0], y_limit[1])

    else:
        possible_x = range(target_radius + border_size, width - target_radius - border_size)
        possible_y = range(target_radius + border_size, height - target_radius - border_size)
    # pick a random coordinate
    random_x = random.choice(possible_x)
    random_y = random.choice(possible_y)
    # pick a random color
    random_color = [random.randint(0, 255), random.randint(0, 255), random.randint(0, 256)]
    _target = Circle([random_x, random_y], target_radius, random_color, -1)

    return _target

这个函数有很多事情要做,所以让我们分解一下。

第一部分是我们设置新目标可以采用的宽度和高度的可能值。我们通过首先设置变量x_limity_limit来做到这一点,顾名思义,这些变量限制了可以放置目标的位置。这是为了避免我们最终得到一个部分在屏幕外的目标。

你可能已经注意到有一个新变量,border_size我们还没有讨论过,但我稍后会讨论它。

然后,对于每个维度(宽度和高度),我们运行一个 for 循环,用可能的位置填充数组possible_xpossible_y。请注意,我加入了一个限制,为了让游戏更具挑战性,新目标必须与当前目标相距至少 282 像素(宽度为 200 像素,高度为 200 像素))

之后,只需从possible_xpossible_y中选择一个随机值,分配一个随机颜色,然后返回新的圆圈。

现在我们可以在主循环中使用这个函数

if hands:
    for i in range(len(hands)):
        hand_position = hands[i]["center"]
        hand_circle = Circle(hand_position,hand_radius,(0,0,255),1)
        hand_circle.draw(frame)
        if target.check_intersection(hand_circle.coordinates, hand_circle.radius):
           
            # is intersecting
            hit_target = True
            break;
if hit_target:
    target_count += 1
    target = create_random_target(target.coordinates)
    hit_target = False

如你所见,此代码可能非常耗时,因为每次我们要创建新目标时,我们都会遍历屏幕的几乎每个像素。如果这个函数会减慢你的游戏速度,只需注释掉你有 for 循环的行并使用以下代码来代替

possible_x = range(target_radius + border_size, width -        
target_radius - border_size)

possible_y = range(target_radius + border_size, height - target_radius - border_size)

这将产生相同的影响,除了我们不再有创建远离当前目标的新目标的限制。

有了这段代码,游戏就完成了!你现在可以不停地玩游戏。但当然,我们会改进它。

可以改进这款游戏的众多方法之一是赋予它一种感觉或紧迫感。基本上,我们需要一种方法来激励玩家尽快达到目标。一个很好的方法是添加一个计时器。

为了使用计时器,我们首先必须有一种方法来跟踪经过的时间。我们可以通过time库做到这一点

import time

t_start = time.time()
while True:
    elapsed_time = time.time() - t_start
    print(elapsed_time)

这应该输出我们自循环开始以来经过的时间。有了这个,我们现在可以限制玩家玩游戏的时间,迫使玩家尽可能快地获得更好的分数。我们还需要一种方法让玩家知道他打得有多好,所以我们将在图像中添加一条消息,使用函数cv2.putText()显示得分

if elapsed_time >= max_time:
    is_playing = False
    final_message = "Time's up! You hit " + str(target_count) + " targets in " + str(max_time) + " seconds"
    frame = cv2.putText(frame, final_message, object_title_pos, cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0, 255), 2)

我还添加了标志is_playing以在计时器结束后阻止目标出现。现在我们可以玩游戏并尝试每次提高我们的分数,我们甚至可以与其他人竞争!

我们还可以用两种不同的方式玩我们的游戏。事实上,现在我们已经设置了运行时间的游戏,但我们也可以让游戏运行目标。我的意思是,与其尝试在给定时间内击中尽可能多的目标,我们可以看到我们可以多快击中给定数量的目标。

我们只需要添加一些东西,即控制我们正在玩的游戏类型的变量,我们还需要一种方法来检查目标计数何时达到最大值

play_for_time = True
play_for_targets = not play_for_time
...
while True:
    if hit_target:
        target_count += 1
        target = create_random_target(target.coordinates)
        if play_for_time and target_count == max_targets:
            is_playing = False
            final_message = "Congrats! You hit " + str(target_count) + " targets in " + "{:.2f}".format(elapsed_time) + " seconds"

现在我们可以用两种不同的方式玩我们的游戏了!

第 4 步:最后润色

我们现在拥有的游戏非常基础,有无数种方法可以改进它,但是在本节中,我将与你分享一些我也实现的其他功能。

保存/加载高分

不必记住上次获得的分数,你可以创建一个读取(和写入)当前高分的方法。

为此,我们需要两个函数来加载和保存我们的高分

import pickle
def load_highscore(is_time):
    try:
        if is_time:
            with open('high_score_time.dat', 'rb') as file:
                score = pickle.load(file)
        else:
            with open('high_score_targets.dat', 'rb') as file:
                score = pickle.load(file)
    except:
        score = 0
    return score


def save_highscore(score, is_time):
    if is_time:
        with open('high_score_time.dat', 'wb') as file:
            pickle.dump(score, file)
    else:
        with open('high_score_targets.dat', 'wb') as file:
            pickle.dump(score, file)

我们正在使用pickle将当前的高分保存并加载到文件中。要将其合并到我们当前的代码中,我们只需要检查我们得到的当前分数是否比我们的高分更好,如果是,则更新高分

highscore_targets = load_highscore(False)
while True:
    if hit_target:
        target_count += 1
        target = create_random_target(target.coordinates)
        if play_for_time and target_count == max_targets:
            is_playing = False
            final_message = "Congrats! You hit " + str(target_count) + " targets in " + "{:.2f}".format(elapsed_time) + " seconds"
        if target_count > highscore_targets or highscore_targets==0:
            save_highscore(target_count, False)
            highscore_message = "New highscore!!"
        else:
             highscore_message = "Best score: " + str(highscore_targets)

添加当前时间和分数

现在我们无法知道我们击中了多少个目标,或者我们玩了多长时间,所以为了解决这个问题,我们可以在顶部角落添加两个文本框来显示当前目标计数和当前时间

score_text_pos = (width - 150, border_size + 30)
time_text_pos = (border_size + 15, border_size + 30)
while True:
    ...
    frame = cv2.putText(frame, "Total: " + str(target_count), score_text_pos, cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0, 255), 2)
    frame = cv2.putText(frame, "Time left: " + "{:.2f}".format(elapsed_time), time_text_pos,               cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0, 255), 2)

添加边框

最后,我之前提到过border_size这个变量,这个变量表示我们希望边框的像素数。我们可以使用函数cv2.copyMakeBorder()创建边框

while True:
    frame = cv2.copyMakeBorder(frame, border_size, border_size, border_size, border_size, cv2.BORDER_CONSTANT,                       value=[0, 0, 0])

GitHub 存储库上提供的文件还有更多功能:https://github.com/Goncalo-Chambel/ReactionGame

☆ END ☆

如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「uncle_pn」,欢迎添加小编微信「 woshicver」,每日朋友圈更新一篇高质量博文。

扫描二维码添加小编↓

e60b786f91830b36f8d0e3e6d5e25fb2.jpeg

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

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

相关文章

jsp4S店管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 4S店管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开 发&#xff0c;数据库为Mysql&#xff0c;使用ja…

海带软件分享——日常办公学习软件下载安装(百度网盘 | 收藏)

>>>深度学习Tricks&#xff0c;第一时间送达<<< &#x1f680; 写在前面 &#x1f431;‍&#x1f3cd; 本期开始&#xff0c;小海带会定期推荐一些日常办公学习软件及趣味网址&#xff08;内含安装教程&#xff09;&#xff0c;供大家学习交流参考 ~ 小伙…

三层交换技术

数据来源 一、三层交换技术出现背景&#xff1a; 解决之前给网段划分VLAN控制端口&#xff0c;然后使用路由器设置单臂路由的让不同VLAN可以通信的缺点&#xff0c; 单臂路由缺点&#xff1a; 1&#xff09;容易出现网络瓶颈 2&#xff09;容易发生单点物理故障 3&a…

[附源码]Python计算机毕业设计Django海南与东北的美食文化差异及做法的研究展示平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;我…

设计一个互联网交换设备的SNMP MIB库“X-MIB”

设计一个互联网交换设备的SNMP MIB库“X-MIB”&#xff1a; 问题描述 设备型号&#xff1a;字符串类型 设备生产厂商&#xff1a;字符串类型 设备名称&#xff1a;字符串类型 设备位置&#xff1a;字符串类型 设备运行时间&#xff1a;Timeticks 类型 设备队列数&#xff…

Find My资讯|苹果Find My帮助美警察逮捕连环盗车嫌犯

报道称&#xff0c;美国Shelby County Sheriff警方于11月13日搜查了Shady Ridge的5300街区的一处住宅&#xff0c;成功逮捕了现年20岁的嫌疑人Michael Walker。根据警方通告&#xff0c;该地区近期发生了多起盗车案件。而案件的突破口是发生在Pleasantwood 6800街区的一件盗车事…

【JUC】循环屏障 CyclicBarrier 详解

前言 jdk 中提供了许多的并发工具类&#xff0c;大家可能比较熟悉的有CountDownLatch&#xff0c;主要用来阻塞一个线程运行&#xff0c;直到其他线程运行完毕。而 jdk 还有一个功能类似并发工具类CyclicBarrier&#xff0c;你知道它的作用吗&#xff1f;和CountDownLatch有什…

MySQL学习记录(6)索引02

2.6、索引的使用 2.6.1、最左前缀法则 如果索引了多列&#xff08;联合索引&#xff09;&#xff0c;要遵循最左前缀法则。最左前缀法则指的是查询从索引的最左列开始&#xff0c;并且不跳过索引的列&#xff0c;如果跳跃某一列&#xff0c;索引将会部分失效&#xff08;后面…

大二Web课程设计:HTML+CSS学校静态网页设计——南京师范大学泰州学院(11页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

[附源码]Python计算机毕业设计SSM开心鲜花系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java外卖小程序管理系统源码带小程序前端+后端搭建教程

这套系统已经完成了线下配送的大部分功能 技术架构 技术框架&#xff1a;springboot ssm mysql redis 运行环境&#xff1a;IntelliJ IDEA 2022 jdk1.8 Mysql5.7.4 maven nginx 宝塔面板 后端搭建教程 1.下载源码后打开小皮面板&#xff0c;安装mysql5.7数据库&#x…

LIO-SAM源码解析(二):代码结构

1. 代码整体框架 首先看看工程目录结构&#xff0c;主要有五个文件&#xff0c;分别是utility.h&#xff0c;featureExtraction.cpp&#xff0c; imageProjection.cpp&#xff0c;imuPreintegration.cpp&#xff0c;mapOptmization.cpp LIO-SAM/config/params.yaml …

如何借助低代码开发平台 YonBuilder 填补应用开发 “产能缺口”?

低代码平台本身是各类模型、引擎的重新组合&#xff0c;目的是为了填补应用开发需求远超开发者产能这一缺口&#xff0c;那如何理解填补应用开发需求和开发者产能的缺口&#xff1f; 完成一个企业级的应用复杂度随着技术的进步、需求的细化、业务要求的变化并不是逐渐降低而是…

原创|对接三方服务商回调鉴权的程序代码设计

文章目录一、背景二、详细设计1、UML设计2、程序设计2.1、AuthenticateActionEnum2.2、AuthenticateDispatcher2.3、BaseAuthenticateContext<Request>2.3.1、ActivityStatusChangeAuthenticateContext2.3.2、VodEventNotifyAuthenticateContext2.4、AbstractAuthenticat…

【Python+Appium】自动化测试(十一)location与size获取元素坐标

目录 前言 一&#xff0c;获取元素坐标的方法 1&#xff0c;size获取元素的宽、高 2&#xff0c;location获取元素左上角坐标 3&#xff0c;由此可以计算出元素其他的坐标 二&#xff0c;使用场景 结语 前言 appium做app自动化测试过程中&#xff0c;有时需要获取控件元…

git clean 命令详解

1. git clean 介绍 2. git clean 使用 3. clean 和 reset 命令 1. git clean 介绍 git clean 命令用于删除工作目录中没有被 tracked 的文件 这个命令很多人都不知道&#xff0c;也不去用它&#xff0c;而是通过手动去删除这些文件 这个命令一定要慎用&#xff0c;当你对这…

智源社区AI周刊No.108:Meta发布玩外交游戏的Cicero,登Science;Neuralink实现猴子意念打字...

汇聚每周AI热点&#xff0c;不错过重要资讯&#xff01;欢迎扫码&#xff0c;关注并订阅智源社区AI周刊。Meta发布外交谈判策略系统Cicero&#xff1a;模拟人类参与战略决策&#xff0c;已登《Science》期刊近日&#xff0c;Meta发布人工智能系统Cicero&#xff0c;该系统结合神…

【云原生】k8s 管理平台 rancher

文章目录一、概述二、Rancher 架构三、安装 Rancher1&#xff09;安装Helm2&#xff09;安装ingress-controller3&#xff09;为 Rancher 创建命名空间4&#xff09;选择 SSL 配置5&#xff09;安装 cert-manager6&#xff09;通过 Helm 安装 Rancher2&#xff09;添加 Helm Ch…

电子签名-为你的数据签字画押

博主&#xff1a;爱码叔 个人博客站点&#xff1a; icodebook 公众号&#xff1a;漫话软件设计 专注于软件设计与架构、技术管理。擅长用通俗易懂的语言讲解技术。对技术管理工作有自己的一定见解。文章会第一时间首发在个站上&#xff0c;欢迎大家关注访问&#xff01; 更多密…

[附源码]Python计算机毕业设计SSM绝味鸭脖连锁店信息系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…