三子棋装置(电赛24E题)K230/STM32全开源

三子棋装置(电赛24E题)K230/STM32全开源,后续有具体代码参数讲解,帮助大家移植

k230代码

import time, os, sysfrom media.sensor import *
from media.display import *
from media.media import *from machine import UART
from machine import FPIOA
from machine import TimerUartFlag=False#串口数据接收标志# 配置引脚
fpioa = FPIOA()
fpioa.set_function(11, FPIOA.UART2_TXD)
fpioa.set_function(12, FPIOA.UART2_RXD)# 初始化UART2,波特率115200,8位数据位,无校验,1位停止位
uart = UART(UART.UART2, baudrate=115200, bits=UART.EIGHTBITS, parity=UART.PARITY_NONE, stop=UART.STOPBITS_ONE)sensor_id = 2
sensor = None# 构造一个具有默认配置的摄像头对象
sensor = Sensor(id=sensor_id,width=1920, height=1080)
# 重置摄像头sensor
sensor.reset()
# 无需进行镜像和翻转
# 设置不要水平镜像
sensor.set_hmirror(False)
# 设置不要垂直翻转
sensor.set_vflip(False)
sensor.set_framesize(width=800, height=480, chn=CAM_CHN_ID_0)
# 设置通道0的输出像素格式为RGB565,要注意有些案例只支持GRAYSCALE格式
sensor.set_pixformat(Sensor.RGB565, chn=CAM_CHN_ID_0)Display.init(Display.ST7701, width=800, height=480, to_ide=True)# 初始化媒体管理器
MediaManager.init()
# 启动传感器
sensor.run()
# 丢掉前面50帧数据,防止摄像头还不稳定
for i in range(50):sensor.snapshot()fps = time.clock()correction_points = [[268, 72], [635, 64],[708, 469], [226, 476]]
taruge_rect= [[57, 412],[411, 411],[411, 29],[55,29]]
# 棋盘数组
# 黑子:X
# 白子:O
# 没有棋子:空字符串
board = [[" "," "," "],[" "," "," "],[" "," "," "],
]#标准灰度值
std_grayscale_values = [[0,0,0],[0,0,0],[0,0,0],
]'''
#实际坐标系九宫格中心位置
中心点顺序为从左到右、从上到下:
第一行:中心点1(左)、中心点2(中)、中心点3(右)
第二行:中心点4(左)、中心点5(中)、中心点6(右)
第三行:中心点7(左)、中心点8(中)、中心点9(右)
'''
centers_real=[]
#白色棋子位置
white_positions=[[25,0],[50,0],[75,0],[100,0],[125,0]]
white_cnt=0
#黑色棋子位置
black_positions=[[25,155],[50,155],[75,155],[100,155],[125,155]]
black_cnt=0
'''
获得九宫格灰度
'''
def get_grid_region_grayscale():# 1. 对四个顶点排序(tl, tr, br, bl) = [[0, 0],[480, 0],[480, 480],[0,480]]# 2. 计算大正方形的宽度和高度width = max(abs(tr[0]-tl[0]), abs(br[0]-bl[0]))height = max(abs(bl[1]-tl[1]), abs(br[1]-tr[1]))# 3. 计算每个小格子的宽度和高度cell_width = width / 3cell_height = height / 3# 4. 计算中心区域的大小(按比例)region_w = int(cell_width * 0.3)region_h = int(cell_height * 0.3)# 5. 存储9个格子的灰度值grayscale_values = [[0,0,0],[0,0,0],[0,0,0]]for i in range(3):  # 行 (y方向)for j in range(3):  # 列 (x方向)# 计算当前格子的左上角坐标cell_x = tl[0] + j * cell_widthcell_y = tl[1] + i * cell_height# 计算中心区域的坐标center_x = cell_x + cell_width / 2center_y = cell_y + cell_height / 2# 提取中心区域 (region_w × region_h)x1 = int(center_x - region_w / 2)y1 = int(center_y - region_h / 2)x2 = int(center_x + region_w / 2)y2 = int(center_y + region_h / 2)# 防止越界x1 = max(0, x1)y1 = max(0, y1)x2 = min(img.width()-1, x2)y2 = min(img.height()-1, y2)# 计算该区域的平均灰度img.draw_rectangle(x1, y1, x2-x1, y2-y1,color=(255,255,255))region = img.get_statistics(roi=(x1, y1, x2-x1, y2-y1))grayscale_values[i][j]=region.l_mean()return grayscale_values'''
更新棋盘信息
'''
def update_board():global boardimg = sensor.snapshot() # Take a picture and return the image.img.gamma_corr(0.8)img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑#for n in range(4):#img.draw_cross(int(taruge_rect[n][0]),int(taruge_rect[n][1]))img.rotation_corr(corners = (taruge_rect[3],taruge_rect[2],taruge_rect[1],taruge_rect[0]))   #画面梯形校正img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑grayscale_values=get_grid_region_grayscale()for i in range(3):  # 行 (y方向)for j in range(3):  # 列 (x方向)if grayscale_values[i][j]-std_grayscale_values[i][j]>10:board[i][j]='O'elif grayscale_values[i][j]-std_grayscale_values[i][j]<-10:board[i][j]='X'else:board[i][j]=" "print(board[i])#print(grayscale_values[i])print('......')'''
初始化标准灰度
'''
def init_std_grayscale_values():global std_grayscale_valuesimg = sensor.snapshot() # Take a picture and return the image.img.gamma_corr(0.8)img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑#for n in range(4):#img.draw_cross(int(taruge_rect[n][0]),int(taruge_rect[n][1]))img.rotation_corr(corners = (taruge_rect[3],taruge_rect[2],taruge_rect[1],taruge_rect[0]))   #画面梯形校正img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑std_grayscale_values=get_grid_region_grayscale()"""
计算四边形内 9 宫格的 9 个中心点坐标,同步更新真实坐标系下的坐标
参数:corners: 四边形的 4 个角点,顺序为 [左下, 右下,右上 , 左上]
返回: 9 个中心点的列表,顺序为从左到右、从上到下中心点顺序为从左到右、从上到下:第一行:中心点1(左)、中心点2(中)、中心点3(右)第二行:中心点4(左)、中心点5(中)、中心点6(右)第三行:中心点7(左)、中心点8(中)、中心点9(右)
"""
def get_nine_grid_centers(corners):global centers_real# 提取四个角点lb = corners[0]  # 左下 (u=0, v=0)rb = corners[1]  # 右下 (u=1, v=0)rt = corners[2]  # 右上 (u=1, v=1)lt = corners[3]  # 左上 (u=0, v=1)# 双线性插值函数def bilinear_interp(u, v):x = (1-u)*(1-v)*lb[0] + u*(1-v)*rb[0] + u*v*rt[0] + (1-u)*v*lt[0]y = (1-u)*(1-v)*lb[1] + u*(1-v)*rb[1] + u*v*rt[1] + (1-u)*v*lt[1]return (x, y)# 计算9个中心点(从左到右,从上到下)tmp=[]centers = []for v in [5/6, 3/6, 1/6]:  # 从上到下(v=1是顶部,v=0是底部)for u in [1/6, 3/6, 5/6]:  # 从左到右(u=0是左侧,u=1是右侧)center = bilinear_interp(u, v)centers.append(center)center_real =(center[1]*13/48+11, center[0]*13/48+11)#x,y反转tmp.append(center_real)centers_real=tmpreturn centers"""
输入当前棋盘和执棋颜色,返回最佳下棋位置 (row, col)参数:board: 3x3 的二维列表,表示当前棋盘,例如:[["X", " ", "O"],[" ", "X", " "],["O", " ", " "]]player_color: 0 表示白棋 (O),1 表示黑棋 (X)返回:(row, col): 最佳下棋位置,行列范围 0-2
"""
def get_best_move(board, player_color):# 预计算所有可能的赢法(8 种:3行 + 3列 + 2对角线)win_patterns = [[(0, 0), (0, 1), (0, 2)],  # 第一行[(1, 0), (1, 1), (1, 2)],  # 第二行[(2, 0), (2, 1), (2, 2)],  # 第三行[(0, 0), (1, 0), (2, 0)],  # 第一列[(0, 1), (1, 1), (2, 1)],  # 第二列[(0, 2), (1, 2), (2, 2)],  # 第三列[(0, 0), (1, 1), (2, 2)],  # 主对角线[(0, 2), (1, 1), (2, 0)]   # 副对角线]def evaluate(b):"""快速评估当前棋盘是否有玩家获胜"""for pattern in win_patterns:cells = [b[i][j] for (i, j) in pattern]if cells[0] == cells[1] == cells[2] != " ":return 1 if cells[0] == "X" else -1return 0  # 无胜负或平局def minimax(b, depth, alpha, beta, is_maximizing):"""带 Alpha-Beta 剪枝的极小化极大算法"""score = evaluate(b)if score != 0:  # 有玩家获胜return scoreif all(cell != " " for row in b for cell in row):  # 平局return 0if is_maximizing:max_score = -float("inf")for i in range(3):for j in range(3):if b[i][j] == " ":b[i][j] = "X"current_score = minimax(b, depth + 1, alpha, beta, False)b[i][j] = " "max_score = max(max_score, current_score)alpha = max(alpha, current_score)if beta <= alpha:  # Alpha-Beta 剪枝breakreturn max_scoreelse:min_score = float("inf")for i in range(3):for j in range(3):if b[i][j] == " ":b[i][j] = "O"current_score = minimax(b, depth + 1, alpha, beta, True)b[i][j] = " "min_score = min(min_score, current_score)beta = min(beta, current_score)if beta <= alpha:  # Alpha-Beta 剪枝breakreturn min_scoreplayer = "X" if player_color == 1 else "O"best_score = -float("inf") if player == "X" else float("inf")best_move = (-1, -1)alpha = -float("inf")beta = float("inf")for i in range(3):for j in range(3):if board[i][j] == " ":board[i][j] = playerscore = minimax(board, 0, alpha, beta, player == "O")board[i][j] = " "if (player == "X" and score > best_score) or (player == "O" and score < best_score):best_score = scorebest_move = (i, j)# 更新 Alpha/Betaif player == "X":alpha = max(alpha, best_score)else:beta = min(beta, best_score)return best_move'''
找矩形外框
'''
loop=True#False#
last_taruge_rect = [[0,0],[0,0],[0,0],[0,0]]  #记录上一次识别到的定点数据,用来判断数据是否稳定
matching_counts = 0#记录识别稳定不变的次数,达到一定数量则判断识别成功
while(loop):img = sensor.snapshot() # Take a picture and return the image.#img.gamma_corr(0.8)img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例img.draw_rectangle(480,0,320,480,color=(0,0,0),fill=True)    #右侧空白处涂黑img.midpoint(2, bias=0.9, threshold=True, offset=10, invert=True)    #凸显黑线rr = img.find_rects(threshold=500000)   #找矩形if rr:  #如果有目标for r in rr:img.draw_rectangle(r.rect(), color = (255, 0, 0))   #在屏幕绘制标识框taruge_rect = r.corners()  #存储方框顶点坐标for n in range(4):#对比方框定点坐标数据的变动情况,判断是否获取成功for n2 in range(2):#注意,此处判断阈值为3,如果画面不稳定,可能程序无法向后进行。可以修改阈值。另外还可以增加低通滤波。if abs(taruge_rect[n][n2] - last_taruge_rect[n][n2]) < 30:matching_counts += 1print(matching_counts)else:matching_counts = 0print('识别失败')last_taruge_rect = taruge_rectif matching_counts > 10:loop = Falseprint('识别成功')#print(taruge_rect)#img.draw_string_advanced(50,50,80,"fps:{}".format(fps.fps()*10000),color=(255,0,0))Display.show_image(img)
init_std_grayscale_values()
centers=get_nine_grid_centers(taruge_rect)
UartFlag=True#开始接收串口数据
#print(centers)
#print(centers_real)
'''
while(True):img = sensor.snapshot() # Take a picture and return the image.img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑for i in range(9):img.draw_cross(int(centers[i][0]),int(centers[i][1]))Display.show_image(img)
'''
def TimCallBack(timer):global white_positions,black_positions,white_cnt,black_cnt,UartFlag,boardif UartFlag:data = uart.read()if data:#通过CanMV IDE K230中的串行终端控制台打印出来hex_str = data.hex()  # 转为16进制字符串print('收到数字:',hex_str)# 提取第一个字节的高低4位high_nibble = int(hex_str[0], 16)low_nibble = int(hex_str[1], 16)print(high_nibble,low_nibble)if high_nibble==0:uart.write(f"X{white_positions[white_cnt][0]:.2f}Y{white_positions[white_cnt][1]:.2f}P")white_cnt+=1time.sleep_ms(4000)uart.write(f"X{centers_real[low_nibble-1][0]:.2f}Y{centers_real[low_nibble-1][1]:.2f}L")#print('.....')#print(f"X{white_positions[white_cnt][0]:.2f}Y{white_positions[white_cnt][1]:.2f}P")#print(f"X{centers_real[low_nibble][0]:.2f}Y{centers_real[low_nibble][1]:.2f}L")elif high_nibble==1:uart.write(f"X{black_positions[black_cnt][0]:.2f}Y{black_positions[black_cnt][1]:.2f}P")black_cnt+=1time.sleep_ms(4000)uart.write(f"X{centers_real[low_nibble-1][0]:.2f}Y{centers_real[low_nibble-1][1]:.2f}L")elif high_nibble==5:#执白棋update_board()x,y=get_best_move(board,0)uart.write(f"X{white_positions[white_cnt][0]:.2f}Y{white_positions[white_cnt][1]:.2f}P")white_cnt+=1time.sleep_ms(4000)uart.write(f"X{centers_real[3*x+y][0]:.2f}Y{centers_real[3*x+y][1]:.2f}L")print('.....')print(x,y)#print(f"X{centers_real[3*x+y][0]:.2f}Y{centers_real[3*x+y][1]:.2f}L")elif high_nibble==6:#执黑棋update_board()x,y=get_best_move(board,1)uart.write(f"X{black_positions[black_cnt][0]:.2f}Y{black_positions[black_cnt][1]:.2f}P")black_cnt+=1time.sleep_ms(4000)uart.write(f"X{centers_real[3*x+y][0]:.2f}Y{centers_real[3*x+y][1]:.2f}L")print('.....')print(x,y)#print(f"X{centers_real[3*x+y][0]:.2f}Y{centers_real[3*x+y][1]:.2f}L")
# 创建一个软件定时器实例,-1 表示使用软件定时器
tim = Timer(-1)# 配置定时器,定时2000毫秒
tim.init(period=2000, mode=Timer.PERIODIC, callback=TimCallBack)while(True):img = sensor.snapshot() # Take a picture and return the image.img.gamma_corr(0.8)img.rotation_corr(corners = (correction_points[:4]))   #画面梯形校正img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑img.rotation_corr(corners = (taruge_rect[3],taruge_rect[2],taruge_rect[1],taruge_rect[0]))   #画面梯形校正img.draw_image(img,0,0,x_size=480,y_size=480)   #缩放画面至正常比例img.draw_rectangle(480,0,320,480,color=(255,255,255),fill=True)    #右侧空白处涂黑'''update_board()(y,x)=get_best_move(board,1)img.draw_cross(80+160*x,80+160*y)'''Display.show_image(img)time.sleep_ms(1000)

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

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

相关文章

终端安全检测与防御

1. 终端安全风险主要问题&#xff1a;企业网络中80%的安全事件源于终端&#xff0c;终端成为黑客攻击的重要目标。攻击手段&#xff1a;勒索病毒&#xff1a;直接勒索用户。横向渗透&#xff1a;通过受控终端攻击内部服务器。僵尸网络危害&#xff1a;信息窃取、钓鱼网站引导、…

Video_AVI_Packet(2)

博主声明&#xff1a;内容来自网络&#xff0c;仅供参考&#xff0c;仅适用于浅了解&#xff0c;如有错误&#xff0c;自行甄别&#xff0c;由此引起的后果概不负责 Video_AVI_Packet&#xff08;2&#xff09;一、Video Picture Aspect Ratio 与 Active Format Aspect Ratio1.…

八月补丁星期二:微软修复 111 个漏洞

微软将在2025 年 8 月补丁星期二修复 111 个漏洞&#xff0c;这一数量与近期平均水平大致相同。 与上个月的情况类似&#xff0c;微软知道今天发布的漏洞中只有一个已被公开披露&#xff0c;但声称没有证据表明存在野外利用。同样&#xff0c;截至发布时&#xff0c;唯一的补丁…

《C++进阶之继承多态》【普通类/模板类的继承 + 父类子类的转换 + 继承的作用域 + 子类的默认成员函数】

【普通类/模板类的继承 父类&子类的转换 继承的作用域 子类的默认构造函数】目录前言&#xff1a;------------------------一、继承的定义和使用1. 什么使继承&#xff1f;2. 为什么要引入继承&#xff1f;3. 怎么使用继承&#xff1f;① 父类&#xff08;基类&#xf…

Ubuntu22.04安装OBS Studio

OBS官网的最新的虽然支持Ubuntu系统&#xff0c;但是只支持最新的24.2版本的&#xff0c;而我的电脑上的Ubuntu的版本是22.04&#xff0c;所以在网上寻求解决办法&#xff0c;看到了这一片博客&#xff0c;作为参考来实现ubuntu22.04安装OBS&#xff0c;这里提示一下&#xff0…

Ansible 基本使用

Ansible 清单 静态主机清单 主机清单支持多种格式&#xff0c;例如ini、yaml、脚本等。 本次课程使用 ini 格式。 #创建主机清单[lykcontroller ~ 13:36:01]# vim inventory#vim添加controllernode1node2node3node4​#测试连接单个服务器[lykcontroller ~ 14:08:18]$ ansibl…

网络资源模板--基于Android Studio 实现的九寨沟App

目录 一、测试环境说明 二、项目简介 三、项目演示 四、部设计详情&#xff08;部分) 首页 购票页面 五、项目源码 一、测试环境说明 电脑环境 Windows 11 编写语言 JAVA 开发软件 Android Studio (2020) 开发软件只要大于等于测试版本即可(近几年官网直接下载也…

系统架构设计师备考之架构设计实践知识

1.信息系统架构设计理论与实践1.1.基本概念信息系统架构定义目前关于信息系统架构较为权威的定义有&#xff1a; &#xff08;1&#xff09;信息系统架构是系统的结构&#xff0c;由软件元素、元素外部可见属性和元素间关系组成。 &#xff08;2&#xff09;信息系统架构是软件…

【IgH EtherCAT】如何利用 RTAI 提供的实时任务和调度机制来构建一个高精度、确定性的工业控制应用

SVG图展示了系统的分层架构&#xff1a;RTAI实时层&#xff1a;包含RT_TASK、信号量和定时器EtherCAT Master层&#xff1a;主站、域、从站配置和PDO映射EtherCAT网络层&#xff1a;与实际硬件设备&#xff08;EL3162模拟输入、EL2004数字输出&#xff09;通信关键特点&#xf…

7款热门智能电视文件管理器横向评测

7款智能电视文件管理器横向评测 在智能电视和电视盒子日益普及的今天&#xff0c;一款好用的文件管理器能让您的数字生活更加便捷。本文为您评测了7款广受欢迎的TV版文件管理器&#xff0c;助您找到最适合自己的工具。 1. ES文件浏览器TV版 ES文件浏览器是一款广受欢迎的多功能…

Python 类元编程(导入时和运行时比较)

导入时和运行时比较 为了正确地做元编程&#xff0c;你必须知道 Python 解释器什么时候计算各个代码 块。Python 程序员会区分“导入时”和“运行时”&#xff0c;不过这两个术语没有严 格的定义&#xff0c;而且二者之间存在着灰色地带。在导入时&#xff0c;解释器会从上到 下…

[git diff] 对比检查变更 | 提交前复审 | 版本回退

git diff git diff 是 Git 版本控制系统中用于比较文件差异的核心命令&#xff0c;可以显示工作目录、暂存区&#xff08;Index&#xff09;和仓库历史之间的变化。 通过对比不同版本或状态的文件内容&#xff0c;帮助开发者理解代码变更。 比较工作目录与暂存区 运行以下命令查…

【数据可视化-85】海底捞门店数据分析与可视化:Python + pyecharts打造炫酷暗黑主题大屏

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

物联网之小白调试网关设备

小伙伴们&#xff0c;你们好呀&#xff01;我是老寇&#xff01;跟我一起学习调试网关设备 相信搞过物联网的朋友&#xff0c;对网关设备非常熟悉&#xff0c;本人以小白的视角&#xff0c;手把手教你调试网关设备&#xff01; 工作中使用的是Ubuntu操作系统&#xff0c;因此&a…

Node.js特训专栏-实战进阶:22. Docker容器化部署

🔥 欢迎来到 Node.js 实战专栏!在这里,每一行代码都是解锁高性能应用的钥匙,让我们一起开启 Node.js 的奇妙开发之旅! Node.js 特训专栏主页 专栏内容规划详情 我将从Docker容器化部署的基础概念入手,介绍Node.js应用容器化的步骤,包括创建Dockerfile、构建镜像、运行…

eclipse嵌入式编译速度慢

eclipse 嵌入式 编译 速度慢 同一个项目&#xff0c;eclipse编译速度越来越慢&#xff0c;一开始几秒钟编译完&#xff0c;后面要10分钟。只需要将以下两个程序卸载重新安装即可。

编译Android版本可用的高版本iproute2

背景&#xff1a; Android自带的iproute2 太老&#xff0c;很多指令格式不支持 直接基于Android源码&#xff0c;替换源码下iproute2 代码编译新版&#xff0c;报错太多&#xff0c;于是改用Android NDK工具编译 环境&#xff1a; android-ndk-r25c-linux.zip 下载链接&am…

JavaScript的fetch函数的用法

基本语法fetch函数用于发起网络请求&#xff0c;返回一个Promise对象。基本语法如下&#xff1a;fetch(url, options).then(response > response.json()).then(data > console.log(data)).catch(error > console.error(Error:, error));GET请求发起一个简单的GET请求&…

Json和XML文件相互转化

目录 一.XML转Json文件 示例&#xff1a;将XML转换为JSON 依赖准备 Java代码示例 代码详细讲解 二.Json转XML文件 示例&#xff1a;将JSON转换为XML 依赖准备 Java代码示例 代码详细讲解 关键代码解析 将JSON转换为XML 写入文件 示例输入与输出 三.具有相同功能的…

Python科学计算与可视化领域工具TVTK、Mayavi、Mlab、Traits(附视频教程)

概述 TVTK、Mayavi、Mlab 和 Traits 都是 Python 科学计算与可视化领域中紧密相关的工具&#xff0c;它们常被结合使用来处理和展示三维数据。视频教程&#xff1a;https://pan.quark.cn/s/f73e875225ca 1. TVTK TVTK&#xff08;Traits-based Visualization Toolkit&#xff0…