前阵子设计机器人趣味项目时,一直想做一个“能根据比赛名次现场生成奖杯”的小系统。灵感来自大雄的金牌梦:比赛结束之后,机器人不只是举一下牌子,而是能随机生成一个独一无二的奖杯名称、奖牌材质、颁奖词,甚至生成一张奖杯图片,再通过机器人展示出来。这个项目听起来像哆啦A梦的道具,但实现难度其实并不高,非常适合用来串起随机算法、图片生成、机器人控制和 Web 接口开发这些知识点。
接下来我会从整体设计讲起,逐步拆解每个功能模块,最后给出可直接运行的完整代码示例。无论你是刚开始学机器人的新手,还是已经有 Python 基础、想尝试机器人趣味项目的开发者,都能跟着文章把流程跑通。
1. 背景与核心概念
1.1 什么是随机生成奖杯机器人
随机生成奖杯机器人,是指机器人根据外部输入的比赛信息(例如名次、比赛类型),通过程序从预设的奖池中随机组合出一套赛事奖项,再通过图片渲染、屏幕展示或机械动作将奖项呈现出来。
它和传统实体奖杯的区别在于:
- 传统奖杯是提前定制好的,千篇一律。
- 随机生成奖杯是每次都不一样,带有很强的趣味性和纪念意义。
- 生成结果可以包括奖项名称、奖牌材质、颁奖词、唯一编号等结构化信息。
- 机器人可以执行“移动、展示、庆祝”等动作,让颁奖过程更有仪式感。
从专业角度来说,这个系统属于“机器人 + 随机内容生成 + 展示交互”的复合项目。它不涉及复杂的机器人运动学问题,但会用到机器人的基础控制逻辑,适合作为入门和进阶之间的过渡项目。
1.2 它能解决什么问题
在实际场景中,很多比赛或活动都存在“奖项复用”“奖杯没有个性”的问题。比如:
- 校园科技节需要给大量参赛者颁发不同称号的奖项,但实体奖杯数量有限。
- 公司年会需要给员工随机抽奖,但奖项名称和奖品展示比较单调。
- 直播间或线上比赛需要快速生成颁奖页面,但人工制作图片效率太低。
- 机器人课程设计需要体现“机器人 + 应用创新”,但普通巡线或避障项目已经不够有亮点。
而随机生成奖杯机器人可以把这些流程自动化。它既能生成随机且唯一的结果,又能通过机器人表演增加现场氛围,是一个既有技术含量又有观赏性的项目。
1.3 为什么值得动手做一个
这个项目虽然偏趣味,但涉及的技术点非常完整:
- 随机生成算法:如何从奖池中抽取组合,并生成唯一编号。
- 图片生成:使用 Pillow 库动态绘制奖杯图片。
- 机器人控制:通过抽象接口控制机器人移动、展示和播放庆祝动画。
- Web 接口:使用 Flask 将随机生成能力封装成 HTTP 接口,方便其他系统调用。
- 扩展能力:后续可以接 ROS 2,让机器人在真实场地中导航到颁奖点位。
换句话说,做完这个项目,你等于同时练了一遍“算法 + 图像处理 + 机器人控制 + 后端接口”的综合流程。这对之后的机器人竞赛、课程设计或者求职项目都有帮助。
2. 系统整体设计与功能拆分
2.1 系统架构总览
整个系统可以分成三层:
第一层是输入层。调用方通过 HTTP 请求或命令行参数传入比赛名次、比赛类型,甚至指定随机种子。
第二层是服务层。核心是随机生成模块,它会从奖池中随机选出奖杯类型、金属材质、头衔、理由等字段,组合成一个完整的奖项对象,同时生成唯一编号。
第三层是展示层。奖项对象会被送进图片生成模块,渲染成一张奖杯图片,同时送进机器人控制模块,让机器人执行移动、展示、庆祝等动作。
为了便于理解,整个数据流可以简化为:
输入比赛名次 ↓ 随机生成奖项对象 ↓ 生成奖杯图片 + 生成机器人动作指令 ↓ 现场展示 / HTTP 返回 JSON2.2 功能模块划分
根据上述架构,我将项目拆成四个核心模块:
| 模块 | 职责 |
|---|---|
| 奖项生成模块 | 负责随机组合奖项名称、编号、颁奖词 |
| 图片生成模块 | 根据奖项对象绘制奖杯图片 |
| 机器人控制模块 | 控制机器人移动、展示、庆祝,并提供硬件接口 |
| Web 服务模块 | 提供 HTTP API,串联前三个模块 |
这样拆分的好处是模块之间相互独立。即使没有实体机器人,你也能先用模拟机器人把整条流程跑通;即便不想用 Web 接口,也可以直接在命令行中调用奖项生成模块。
2.3 技术选型与说明
本项目的核心语言使用 Python 3。原因有三点:
- Python 的随机库和字符串处理非常方便。
- Pillow 库可以快速完成图片绘制。
- Flask 框架可以轻量地提供 Web 接口,不需要额外引入大型后端框架。
机器人的控制部分,我做了一个分层设计:
- 默认为 ConsoleRobotController,只在控制台打印动作,方便没有硬件的读者调试。
- 提供 SerialRobotController 示例,演示如何通过串口向下位机发送指令。
- 如果使用 ROS 2 控制真实机器人,可以单独实现一个 Ros2RobotController,在
move_to_center、show_trophy等方法中发布话题消息。
这样的设计能保证项目在没有实体机器人时也可以完整运行,同时保留了接入真实硬件的扩展空间。
3. 环境准备与项目结构
3.1 硬件环境说明
如果只是验证软件流程,你只需要一台普通电脑即可,不需要准备真实机器人。
如果想接入实体机器人,可以按以下方向准备:
- 桌面级小车或机械臂,带电机驱动和串口/Wi-Fi 通信能力。
- 一个显示屏或扩展板上的 LED 灯阵,用于展示奖杯信息。
- 可选:语音播报模块,用来朗读颁奖词。
如果完全没接触过硬件,建议先使用 ConsoleRobotController 把流程跑通,再逐步替换成真实控制逻辑。
3.2 软件环境与依赖
推荐使用 Python 3.8 或更高版本。需要安装的依赖包括:
pillow:用于绘制奖杯图片。flask:用于搭建 Web 接口。pyserial:仅在连接串口设备时需要,纯软件调试时可不安装。
在开始之前,先创建一个虚拟环境:
python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate然后安装依赖:
pip install pillow flask pyserial如果你的项目中不需要串口控制,也可以不安装 pyserial,后续代码在导入时会做异常兜底。
3.3 项目目录结构
推荐按下面的结构组织项目:
trophy_robot/ ├── app.py ├── random_award.py ├── generate_trophy_image.py ├── robot_controller.py ├── requirements.txt ├── output/ └── README.md其中:
random_award.py负责随机奖项生成。generate_trophy_image.py负责奖杯图片渲染。robot_controller.py负责机器人控制抽象与实现。app.py是 Web 服务主入口。output目录用于存放生成的奖杯图片。
4. 核心模块实现
4.1 随机奖项生成模块
先来实现最核心的随机奖项生成逻辑。这个模块需要解决两个问题:
- 随机性:每次生成的结果不能完全一样。
- 唯一性:即使名称相同,也要有唯一编号区分。
为了解决随机性,我会建立几个奖池列表,然后使用random.choice从各列表中抽取元素进行组合。
为了解决唯一性,我会把时间戳和 UUID 结合起来,生成一个短编号。这样即使同一秒内多次调用,也能得到不同编号。
下面是完整代码:
# random_award.py import random import uuid import datetime TROPHY_TYPES = ["金杯", "银杯", "水晶杯", "奖牌", "奖状", "勋章"] RANKS = ["冠军", "亚军", "季军", "最佳创意奖", "最佳人气奖", "最佳进步奖", "最佳团队奖"] METALS = ["黄金", "白银", "青铜", "铂金", "钛合金", "哑光黑"] QUALIFIERS = ["闪耀", "荣耀", "未来之星", "王者", "追梦", "无畏", "先锋"] REASONS = [ "在本场比赛中表现惊艳,以微弱优势夺得胜利", "凭借坚持不懈的拼搏精神,赢得了评委一致好评", "以独特的创意和稳定的发挥征服了全场", "在关键时刻挺身而出,完成了逆转", "展现了完美的团队协作能力,值得嘉奖", "用行动诠释了永不放弃的体育精神", ] def generate_award(rank=None, seed=None): """生成随机奖项对象。 参数: rank: 指定名次,不传则从 RANKS 中随机选取。 seed: 随机种子,方便复现结果。 返回: 包含奖项信息的字典。 """ if seed is not None: random.seed(seed) final_rank = rank if rank else random.choice(RANKS) trophy_type = random.choice(TROPHY_TYPES) metal = random.choice(METALS) qualifier = random.choice(QUALIFIERS) reason = random.choice(REASONS) # 生成唯一编号:时间戳 + UUID 前 8 位 award_id = uuid.uuid4().hex[:8].upper() now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") unique_code = f"{now}-{award_id}" return { "unique_code": unique_code, "rank": final_rank, "trophy_type": trophy_type, "metal": metal, "qualifier": qualifier, "title": f"{final_rank}·{qualifier}{metal}{trophy_type}", "reason": reason, }核心逻辑是第 91 行到第 95 行的组合过程。举个例子,如果随机抽取到"冠军"、"黄金"、"金杯"、"闪耀",那么最终标题就是:
冠军·闪耀黄金金杯虽然组合词看起来有点夸张,但正是这种随机搭配带来了趣味性。
这里需要注意的是,如果调用方传入了seed,每次生成结果都会一致。这在测试和故障复现时非常有用。生产环境中一般不建议固定 seed,而是让系统每次生成不同的结果。
4.2 奖杯图片生成模块
仅返回文本和 JSON 还不够直观。为了让机器人展示效果更好,我用 Pillow 动态绘制一张奖杯图片。
图片生成模块需要做到:
- 根据奖项对象绘制“冠军”“亚军”等文字。
- 在图片中绘制一个简单奖杯形状。
- 把唯一编号写到图片底部,让每张奖杯图片都有身份标识。
- 处理中文字体问题,避免生成乱码。
代码如下:
# generate_trophy_image.py from PIL import Image, ImageDraw, ImageFont def load_font(size): """加载中文字体,找不到时回退到默认字体。""" font_paths = [ "C:/Windows/Fonts/msyh.ttc", "C:/Windows/Fonts/simhei.ttf", "/System/Library/Fonts/PingFang.ttc", "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", ] for path in font_paths: try: return ImageFont.truetype(path, size=size) except Exception: continue return ImageFont.load_default() def generate_trophy_image(award, path="trophy.png"): """根据奖项信息生成一张奖杯图片并保存。""" width, height = 600, 450 image = Image.new("RGB", (width, height), "#1E1E2E") draw = ImageDraw.Draw(image) # 绘制从上到下的深色渐变背景 for y in range(height): color = (30 + int(10 * y / height), 30 + int(20 * y / height), 46) draw.line([(0, y), (width, y)], fill=color) # 绘制奖杯主体 cx = width // 2 draw.ellipse([cx - 90, 100, cx + 90, 280], fill="#F5C542", outline="#D4A017", width=4) draw.rectangle([cx - 30, 280, cx + 30, 340], fill="#F5C542") draw.rectangle([cx - 70, 340, cx + 70, 365], fill="#D4A017") # 绘制两侧把手 draw.arc([cx - 140, 140, cx - 40, 260], start=90, end=270, fill="#F5C542", width=12) draw.arc([cx + 40, 140, cx + 140, 260], start=270, end=450, fill="#F5C542", width=12) # 加载字体 title_font = load_font(36) code_font = load_font(20) reason_font = load_font(18) # 绘制标题、颁奖词和编号 title_text = award["title"] draw.text((cx, 80), title_text, font=title_font, fill="#FFFFFF", anchor="mm") reason_text = award["reason"] draw.text((cx, 50), reason_text, font=reason_font, fill="#DDDDDD", anchor="mm") code_text = award["unique_code"] draw.text((cx, 410), f"编号: {code_text}", font=code_font, fill="#BBBBBB", anchor="mm") image.save(path) return path这里有一点要特别提醒:Pillow 的draw.text如果使用默认字体,那么中文字符会显示成方框。因此我在load_font函数中按系统常见路径寻找中文字体。不同操作系统字体路径不同,你可以根据自己的环境调整路径。
如果你的电脑上没有任何可用中文字体,可以考虑下载开源字体,例如思源黑体,然后把字体文件放到项目目录中,再修改load_font函数去加载本地字体文件。
4.3 机器人控制模块
机器人控制模块是这个项目的“动作层”。为了让代码在没有实体机器人时也能调试,我设计了抽象基类和多个子类。
# robot_controller.py class RobotController: """机器人控制抽象基类。""" def __init__(self, robot_type="console"): self.robot_type = robot_type def move_to_center(self): raise NotImplementedError def show_trophy(self, award): raise NotImplementedError def play_celebration(self): raise NotImplementedError class ConsoleRobotController(RobotController): """模拟机器人,将动作打印到控制台,方便在没有硬件时测试。""" def move_to_center(self): print("[动作] 机器人移动到舞台中央") def show_trophy(self, award): print(f"[展示] 举起奖杯: {award['title']}") print(f"[展示] 展示编号: {award['unique_code']}") def play_celebration(self): print("[庆祝] 播放颁奖音乐,LED 灯亮起") class SerialRobotController(RobotController): """通过串口发送指令给单片机或机器人底盘的示例实现。""" def __init__(self, port="/dev/ttyUSB0", baudrate=115200): super().__init__(robot_type="serial") self.port = port self.baudrate = baudrate self.ser = None self._connect() def _connect(self): try: import serial self.ser = serial.Serial(self.port, self.baudrate, timeout=1) except ImportError: print("未安装 pyserial,无法连接硬件") except Exception as e: print(f"串口连接失败: {e}") def _send(self, command): if self.ser: self.ser.write((command + "\n").encode("utf-8")) def move_to_center(self): self._send("MOVE_CENTER") def show_trophy(self, award): message = f"SHOW:{award['unique_code']}:{award['title']}" self._send(message) def play_celebration(self): self._send("PLAY_CELEBRATION")抽象基类的意义在于:所有具体的机器人实现都必须提供move_to_center、show_trophy、play_celebration三个动作,这样上层调用时不需要关心具体硬件差异。
如果你使用的是 ROS 2 机器人,可以在show_trophy方法中发布一个std_msgs/msg/String消息,将奖项 JSON 字符串发送给机器人端的订阅节点。例如:
# ros2 控制器代码片段(示意) import rclpy from std_msgs.msg import String class Ros2RobotController(RobotController): def __init__(self, node, topic="/trophy_command"): super().__init__(robot_type="ros2") self.publisher = node.create_publisher(String, topic, 10) def show_trophy(self, award): msg = String() msg.data = f"SHOW:{award['unique_code']}:{award['title']}" self.publisher.publish(msg)这里只是一个示例思路,实际使用时需要根据你的 ROS 2 版本和消息类型做调整。机器人端负责接收指令,然后执行对应动作。如果机器人需要在地图中自主移动,还可以结合机器人导航与定位模块,在收到指令后先导航到颁奖点,再执行展示动作。
4.4 Web 服务模块
为了更方便地演示,我使用 Flask 封装一个 HTTP 接口。
接口设计如下:
- 请求方式:GET 或 POST。
- 请求参数:
rank指定名次,seed指定随机种子,generate_image决定是否生成图片。 - 返回结果:奖项 JSON 数据,包含唯一编号、标题、颁奖词和图片路径。
# app.py from flask import Flask, jsonify, request from random_award import generate_award from generate_trophy_image import generate_trophy_image from robot_controller import ConsoleRobotController app = Flask(__name__) robot = ConsoleRobotController() @app.route("/api/random_award", methods=["GET", "POST"]) def random_award(): if request.method == "POST": data = request.get_json(silent=True) or {} rank = data.get("rank") seed = data.get("seed") generate_image = data.get("generate_image", True) else: rank = request.args.get("rank") seed = request.args.get("seed") generate_image = request.args.get("generate_image", "true").lower() == "true" award = generate_award(rank=rank, seed=seed) # 控制机器人执行动作 robot.move_to_center() robot.show_trophy(award) robot.play_celebration() result = dict(award) if generate_image: image_path = generate_trophy_image( award, path=f"output/{award['unique_code']}.png" ) result["image_path"] = image_path return jsonify(result) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=False)这里使用了generate_image参数来控制是否生成图片。如果现场展示时已经接了大屏幕,可以关闭图片生成以减少磁盘写入。如果只是软件验证,建议开启图片生成,这样可以直观地看到奖杯效果。
5. 完整实战流程
5.1 创建项目并安装依赖
先创建项目目录:
mkdir trophy_robot cd trophy_robot python -m venv venv source venv/bin/activate然后安装依赖:
pip install pillow flask pyserial将上面四个代码文件分别保存到项目目录中,并创建output文件夹:
mkdir output5.2 运行 Web 服务
在项目根目录执行:
python app.py看到类似下面的输出,说明服务启动成功:
* Running on all addresses (0.0.0.0) * Running on http://0.0.0.0:5000此时不要关闭命令行窗口,重新打开一个新的终端,用curl调用接口。
5.3 调用接口生成随机奖项
在项目根目录执行:
curl "http://127.0.0.1:5000/api/random_award"控制台会先输出机器人动作:
[动作] 机器人移动到舞台中央 [展示] 举起奖杯: 最佳创意奖·无畏白银奖牌 [展示] 展示编号: 20250610123015-8F3A2B1C [庆祝] 播放颁奖音乐,LED 灯亮起HTTP 接口返回的 JSON 类似:
{ "unique_code": "20250610123015-8F3A2B1C", "rank": "最佳创意奖", "trophy_type": "奖牌", "metal": "白银", "qualifier": "无畏", "title": "最佳创意奖·无畏白银奖牌", "reason": "以独特的创意和稳定的发挥征服了全场", "image_path": "output/20250610123015-8F3A2B1C.png" }打开output目录,可以看到生成的奖杯图片。
5.4 指定名次并固定随机种子
有时候我们需要测试固定结果,比如希望复现某一个奖项,可以传seed:
curl "http://127.0.0.1:5000/api/random_award?rank=冠军&seed=42"传了seed之后,只要奖池不变化,每次生成结果都相同。这个特性非常适合做自动化测试。
如果想用 POST 方式:
curl -X POST http://127.0.0.1:5000/api/random_award \ -H "Content-Type: application/json" \ -d '{"rank": "团队冠军", "seed": 2024}'注意,这里的rank如果不传,系统会直接从预设名次中随机抽取。如果传入自定义名次,就直接使用自定义名次,不会从奖池中再选。
6. 常见问题与排查思路
在实际运行过程中,可能会遇到以下常见问题。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 生成图片时中文显示为方框 | 系统没有可用中文字体,Pillow 使用默认字体 | 下载开源中文字体放入项目目录,修改load_font路径 |
| 调用接口后没有图片生成 | generate_image参数为 false | 确认请求参数,或检查output目录是否存在 |
| 随机结果重复 | 每次生成都传了相同的seed | 不传 seed,或在生成后重新设置随机种子 |
| 串口连接失败 | 串口号不正确、权限不足或未安装 pyserial | 检查port参数,使用ls /dev/ttyUSB*查看设备,必要时加权限 |
| ROS 2 节点收不到指令 | 话题名不一致或消息类型不匹配 | 确认发布的话题名和机器人端订阅话题名一致 |
| Flask 端口被占用 | 5000 端口已被其他进程使用 | 修改app.run中的port参数,或换一个端口 |
下面针对几个重点问题展开说明。
6.1 中文乱码问题
Pillow 默认字体不支持中文,所以load_font函数中必须指定中文字体路径。如果你在 Windows 上运行,一般C:/Windows/Fonts/msyh.ttc和simhei.ttf是存在的。如果在 Linux 服务器上运行,请确认是否安装了中文字体包。
Linux 下推荐下载 Noto Sans CJK 字体,放到项目目录,然后把路径改成:
font_paths = [ "./fonts/NotoSansCJK-Regular.ttc", ]6.2 随机重复问题
随机重复通常有两种原因:
- 调用方固定传了同一个
seed。 - 奖池数量太少,即使不固定 seed,也容易出现相同的组合。
解决方法是扩大奖池,或者在组合时加入更多维度字段。例如把奖杯底座材质、奖杯形状、奖杯颜色也纳入随机范围。
另一个做法是加入历史记录,每次生成前先和最近 N 条记录比对,如果重复就重新生成。这在并发不高的场景下比较好实现。
6.3 串口控制不稳定
如果机器人真实连接串口,可能会出现指令丢失或乱码。常见原因包括波特率不匹配、下位机串口缓冲区溢出、指令末尾没有换行符等。
解决办法:
- 统一上下位机波特率,常见 115200 或 9600。
- 指令末尾统一加
\n。 - 下位机解析时做容错,忽略空行和非法字符。
- 控制指令设计成带校验和的形式,例如
SHOW:编号:标题:CRC。
7. 最佳实践与工程建议
7.1 随机生成唯一值是核心
随机生成不仅仅是random.choice那么简单。在实际项目中,还要考虑唯一性。
比如同一场比赛中,如果连续生成两个“冠军·闪耀黄金金杯”,虽然理论上概率很低,但依然可能发生。为了避免这个问题,我在generate_award中使用了 UUID 和时间戳组合,生成唯一编号。更严谨的做法是加一层数据库存储,在每次生成后把唯一编号写入数据库,并建立唯一索引,这样即使并发调用,也能保证编号不重复。
7.2 奖池设计要有业务含义
奖池中的词汇不能太随意。比如“金杯”“银杯”是通用奖项;“最佳创意奖”“最佳团队奖”更适合团队比赛;“黄金”“铂金”等材质词可以增加奖项的“价值感”。
建议把奖池设计成可配置的外部文件,例如 JSON 或 YAML,而不是硬编码在 Python 中。这样运营人员不需要改代码,就能调整奖池内容。
7.3 机器人控制要注重安全
如果接入真实机器人,安全永远是第一优先级。尤其是机械臂或移动底盘,在正式颁奖前一定要在测试环境中验证动作范围。
建议做到:
- 设置机器人的最大运动速度上限。
- 在机器人运动路径上增加障碍物检测。
- 保留急停按钮或远程急停指令。
- 测试时安排人工看护,避免机器人撞到人或物。
- 在窄小场景中,优先使用机器人导航与定位,而不是盲目走固定路线。
对于工业级项目,还可以参考 ISO 10218 等机器人安全标准,设计安全防护逻辑。这里不展开标准内容,但提醒大家:任何机器人在非受控环境中运行,都必须在有人监护的前提下进行。
7.4 接口设计要幂等和可观测
当 Web 接口被多个客户端调用时,建议增加以下设计:
- 请求 ID:每次请求传入请求 ID,服务端用请求 ID 做幂等控制。
- 日志记录:记录接口入参、生成结果、耗时,方便事后排查。
- 限流:防止短时间内大量请求导致图片生成模块频繁写磁盘。
例如,可以为generate_award增加一个日志装饰器:
import functools import logging logging.basicConfig(level=logging.INFO) def log_award(func): @functools.wraps(func) def wrapper(*args, **kwargs): award = func(*args, **kwargs) logging.info("生成奖项: %s", award["unique_code"]) return award return wrapper7.5 图片生成要注意性能与缓存
每调用一次接口都生成一张图片,如果并发量大,磁盘 IO 会成为瓶颈。常见的优化思路是:
- 图片结果按唯一编号缓存,重复请求直接返回已有图片。
- 图片尺寸不要过大,600x450 已经足够展示。
- 使用内存缓存或 Redis 保存最近生成的图片路径。
- 定期清理
output目录,避免磁盘被占满。
8. 总结与学习路线
通过这个项目,你其实已经完成了一条比较完整的机器人趣味应用开发流程:
- 掌握了随机生成算法的基本用法,包括随机组合、随机种子和唯一编号生成。
- 学会了使用 Pillow 动态绘制中文图片。
- 理解了机器人控制模块的抽象设计,能在没有硬件时用模拟控制器调试。
- 学会了用 Flask 将功能封装成 HTTP API,方便其他系统调用。
- 被引入了 ROS 2 机器人导航与定位的扩展方向。
如果接下来想继续深入,建议按这个顺序学习:
先去把随机生成和图片生成模块做扎实,增加更多奖池维度和图片样式。然后学习 ROS 2 的基础通信机制,写一个机器人端订阅节点,接收 Web 服务发布的奖项指令。接着可以尝试在仿真环境中让机器人移动到颁奖点,结合机器人导航与定位功能完成完整颁奖流程。最后再考虑语音播报、表情屏显示等交互增强功能。
做这一类项目时,不要急着堆硬件。先把软件流程跑通,再逐步替换真实模块,这样既安全又高效。
如果本文对你有帮助,可以收藏备用。也欢迎直接动手改造这套代码,做一台属于自己的“随机生成比赛奖杯机器人”,让大雄的金牌梦在你的手里真正达成。