此项目托管在 Amazon Lambda 上的代码需要更新,目前无法运行。
乐高一直致力于通过游戏发现和学习。使用这款 LEGO Mindstorms 小工具成为一名太空探险家,并将 Alexa 变成您的宇宙私人向导。Sky Finder 会向您展示天空中最有趣的恒星、行星甚至星系,而 Alexa 会告诉您有关它们的信息。
无需昂贵的望远镜即可在天空中看到许多令人兴奋的事物,但如果没有一些帮助则很难找到它们。该项目是一个乐高小工具,您可以使用标准的 Mindstorms EV3 套装 (31313) 轻松拼搭,并发现从您的窗户或后院可以看到的东西。
该项目参加了 hackster.io 上的 LEGO Mindstorms 语音挑战赛。我想构建一些有趣且原创的东西,而这些东西无法单独使用 Alexa 或 LEGOMindstorms 完成。
Alexa 和乐高的结合非常适合这个项目。Alexa 提供了一个友好的界面来探索天空,Sky Finder 小工具会告诉你在哪里看。观看屏幕后,您的眼睛需要一段时间才能适应夜空的黑暗,因此语音界面是在深空和我们自己的太阳系中定位数百个不同物体的完美方式。
Sky Finder 知道从北半球和南半球可见的所有星座;天空中许多最有趣的星星;我们太阳系中的所有行星;以及许多肉眼可见的深空天体,例如仙女座星系和昴宿星团。
它还会建议从您的位置可见的天空中有趣的事物,并为您提供有关您正在查看的内容的信息。
得益于亚马逊 AWS 服务中可用的处理能力,Sky Finder 可以执行定位恒星、行星和其他天体所需的复杂计算,而仅靠 EV3 brick 很难完成这些计算。
我想让人们尽可能轻松地构建项目,从中学习并根据需要扩展项目。
下面的演练包含有关如何制作您自己的 Skyfinder 的说明及其工作原理的信息。如果您想在不了解其工作原理的情况下构建它,只需按照编号说明进行操作即可。
1. 使用本页底部示意图部分中的完整搭建说明搭建乐高小工具。
2. 小工具完成后,使用 4 根电缆将电机和传感器连接到积木上。我建议如下:
使用 50 厘米电缆将颜色传感器连接到 EV3 程序块上的插座 1,确保电缆在 EV3 程序块上方运行。
使用 25 厘米的电缆将触摸传感器连接到插座 4。您可能需要暂时将传感器与小工具分离,以便更容易插入。
使用 25 厘米的电缆将大型电机连接到插座 D,小心地在 EV3 程序块和电机之间铺设电缆。
使用 35 厘米电缆将中型电机连接到插座 C。
3. 您需要将 ev3dev 操作系统安装到您的 micro SD 卡上,从卡上启动您的 EV3 程序块,并将 EV3 连接到 Visual Studio Code 编辑器。LEGO MINDSTORMS 语音挑战团队在此处提供了一些分步说明。(除非您想了解更多信息,否则无需继续执行任何任务)。
4. 接下来您需要在 Alexa 开发者控制台中注册您的小工具。LEGO MINDSTORMS 语音挑战团队再次提供了说明。按照此处的步骤 1-10进行操作,并记下Amazon ID和Alexa Gadget Secret。
Sky Finder 由物理乐高硬件和两个用 Python 编写的软件程序组成。第一个(“Alexa 技能”)在亚马逊的云服务器上运行并处理来自 Echo 设备的语音命令,计算星星位置并创建一个有效载荷/命令,该有效载荷/命令被发送到 Sky Finder 小工具(通过 Echo)以移动电机.
第二个软件程序也是用 Python 编写的,在 EV3 程序块上运行。它通过蓝牙将 Sky Finder 小工具连接到 Echo 设备,然后收听来自 Alexa 技能的命令,通过 Echo 转发,并相应地移动电机。
在下一节中,我将介绍 Alexa skill 的一些主要功能,并解释如何在 Alexa 开发人员控制台中进行设置。
5. 要构建新的 Alexa 技能,请转到Alexa Developer Console ,根据需要登录并单击“创建技能”按钮。
6. 在下一个屏幕上,输入您的技能名称(Sky Finder 或您选择的其他名称)并选择与您的 Echo 设备上的设置相匹配的语言/地区。接下来选择Custom skill model 和Alexa-Hosted (Python) ,然后单击Create Skill按钮。
7. 在最后一个屏幕上,选择Hello World模板并单击“选择”按钮以创建新技能。设置需要几分钟时间。
自定义 Alexa 技能包括一个语音交互模型,它将用户的口头输入映射到Python 软件中的类可以处理的意图。
Sky Finder 接受两个主要意图:
语音交互模型还指定槽(或参数),其中包含上面两个示例中以粗体显示的罗盘方向或对象。
8. 首先我们需要启用技能来使用我们的小工具,因此在“构建”选项卡上,从左侧菜单中选择界面并启用自定义界面控制器。
9. 单击页面顶部的保存接口按钮。
10. 接下来,从左侧菜单中选择JSON 编辑器。将本教程底部提供的 JSON 交互模型代码下载到编辑器中,然后将文件拖放到编辑器上方的拖放区域。
11. 单击屏幕顶部的保存模型按钮,然后单击构建模型按钮。构建需要一分钟左右的时间。您应该会看到意图(LocateIntent 和 ScanIntent)及其相应的槽出现在屏幕左侧。
单击“意图”以查看示例话语,这些话语是用户可能会说出的触发该意图的示例。Alexa 的 AI 根据用户所说的内容选择最合适的话语 - 他们不必说出示例中列出的确切单词。
单击插槽类型以查看用户可能会说的罗盘方向或(天体)物体的名称,以及同义词。插槽值还包含一个 ID,该 ID 将传递给唯一标识对象的 Python 代码。例如,如果您是美国人,您可能会询问“找到北斗七星”的技能,如果您是英国人,则可能会询问“找到犁”的技能,但除了这些信息之外,还有“UMa”(大熊座)的官方名称作为 ID 传递。
在本例中用 Python 编写的 Lambda 函数包含 Alexa 技能的所有主要处理逻辑。它由许多处理意图的方法组成。@ 语法根据 Alexa SDK 的要求将装饰器应用于类。所有技能都遵循相似的结构,因此您可以轻松调整此技能,或亚马逊团队提供的适合您目的的技能,而不是完全从头开始编写。
12. 单击开发人员控制台的“代码”选项卡。
13.将lambda_function.py 、utils.py和requirements.txt的内容替换为本页底部Code部分对应文件的内容。
注意:目前,请忽略代码部分中标记为“API 代码”的同名文件
14. 右键单击 utils.py 下面的空间并选择创建文件。
15.在 lambda 文件夹中创建一个名为data.py的新文件。
16.将本页底部代码部分的data.py文件内容复制到新文件中。
17. 单击保存按钮保存您创建的文件,如果您想查看下面解释的代码,则单击 lambda_function.py 选项卡。
启动请求
除了我们在交互模型中定义的两个意图(ScanIntent 和 LocateIntent)之外,还有一些内置的意图需要处理。其中最重要的是 LaunchRequest,它在用户打开技能时调用(“Alexa,打开 Sky Finder”)。
@skill_builder.request_handler(can_handle_func=is_request_type("LaunchRequest"))
def launch_request_handler(handler_input: HandlerInput):
LaunchRequest 处理程序方法首先检查 Alexa 小工具是否已连接到 Echo 设备。
system = handler_input.request_envelope.context.system
api_access_token = system.api_access_token
api_endpoint = system.api_endpoint
# Get connected gadget endpoint ID.
endpoints = get_connected_endpoints(api_endpoint, api_access_token)
如果小工具已连接,端点 ID 将存储在会话属性中,以便我们将来可以将位置发送到小工具。
# Store endpoint ID for using it to send custom directives later.
logger.debug("Received endpoints. Storing Endpoint Id: %s", endpoint_id)
session_attr = handler_input.attributes_manager.session_attributes
session_attr["endpoint_id"] = endpoint_id
一般来说,每次用户说话时都会再次调用 lambda 函数,因此我们需要在对话期间保留的任何变量都应存储在会话属性中。
定位意图
LocateIntent 是我们定义的自定义意图,当用户要求 Sky Finder 在天空中定位某物时调用它。当我们收到 YesIntent 时,也会调用此处理程序方法。(稍后对此进行更多解释)。
@skill_builder.request_handler(can_handle_func=lambda handler_input:
is_intent_name("AMAZON.YesIntent")(handler_input) or
is_intent_name("LocateIntent")(handler_input))
def locate_intent_handler(handler_input: HandlerInput):
首先,请求处理程序尝试获取我们在交互模型的插槽类型部分中指定的(天体)对象的唯一 ID,例如 UMa(大熊座)。
# Get the name of the object the user is trying to locate from the slot
slots = handler_input.request_envelope.request.intent.slots
try:
# See if it's been matched to one of the specified values for this slot type in the interaction model
object = slots["object"].resolutions.resolutions_per_authority[0].values[0].value.id
下一段代码在我们定位对象时向用户发送一个临时响应(“定位大熊座......”)我不会在这里详细解释它,除了说 SpeakDirective 是一种发送在 lambda 函数完成执行之前响应 Echo 设备。
# Locate the object in space
az,alt,distance = utils.locate(object)
物体在天空中的位置使用方位角、高度坐标指定。方位角在 0 到 359 度之间,对应于罗盘方向。高度表示物体在天空中的高度,从地平线上的 0 度到头顶正上方的 90 度。
接下来,处理程序准备来自 Alexa 的语音响应,告诉用户对象已被定位,并提供一些有趣的事实(如果我们有)。
# object found and visible with a defined distance
compass = utils.degrees_to_dir(az)
speak_output = "{} Located. {} is currently {} kilometers from your location. {} {}".format(radar_ping, name, distance, description, data.ASK_MESSAGE)
如果小工具已连接,我们会准备一个负载(命令),其中包含 Echo 设备中继到小工具的位置,指定我们保存到 LaunchIntent 处理程序中的会话属性的端点 ID。
if ("endpoint_id" in session_attr and object_located):
payload = {
"type": "move",
"az": az,
"alt": alt,
}
endpoint_id = session_attr["endpoint_id"]
directive = build_send_directive(NAMESPACE, NAME_CONTROL, endpoint_id, payload)
最后,我们使用 Alexa SDK 中响应生成器类的方法来构建发送回 Echo 设备的数据结构。
return (handler_input.response_builder
.speak(speak_output)
.ask("What do you want me to find?")
.add_directive(directive)
.response)
扫描意图
当用户询问有关他们在特定天空区域中可以看到的内容的信息时,将调用 ScanIntent。
@skill_builder.request_handler(can_handle_func=is_intent_name("ScanIntent"))
def scan_intent_handler(handler_input: HandlerInput):
首先,技能从插槽中获取用户指定的罗盘方向。
# Get the compass direction the user has specified
slots = handler_input.request_envelope.request.intent.slots
try:
direction = slots["compass_direction"].resolutions.resolutions_per_authority[0].values[0].value.id
再一次,下一段代码在我们进行查找时向用户发送一个临时响应,然后我们调用一个函数返回该部分天空中突出物体的列表。
# Find a list of objects in our specified part of the sky
logger.info("Scanning sky in {}.".format(direction))
scan_list = utils.scan(direction)
接下来,我们创建一个以逗号分隔的对象列表,供 Alexa 与用户对话。为此,我们在 data.py 文件中的 Python 字典中维护对象的常用名称列表。
for object in scan_list:
# Get list of proper names of objects from database
spoken_list.append(data.OBJECT_DATA[object['name']]['name'])
speak_output += utils.comma(spoken_list)
如果只找到一件物品,Alexa 会问,“你想让我告诉你它在哪里吗?”。对象名称保存在会话属性中。如果用户回答“是”,则下次将触发YesIntent ,这实际上是由LocateIntent处理程序处理的,该处理程序将定位已保存在会话属性中的对象。
如果有多个对象,Alexa 会询问“您希望我定位哪一个?”。
if (len(scan_list) > 1):
speak_output += ". Which one would you like me to locate? "
else:
speak_output += ". Do you want me to show you where it is?"
# save object ready for a possible 'yes' response from the user
object_of_interest = scan_list[0]['name']
session_attr = handler_input.attributes_manager.session_attributes
session_attr["object_of_interest"] = object_of_interest
最后,构建响应并将其发送到 Echo 设备。
return (handler_input.response_builder
.speak(speak_output)
.ask("Do you want me to show you where they are?")
.response)
Lambda 函数导入另外两个文件:
utils.py包含许多实用函数,包括查找恒星和行星位置的代码。目前,这会调用外部查找 API 以避免必须将依赖项编译到 Lambda 函数中。API 的代码包含在本页底部以供参考,但您不需要使用它。
data.py包含一个 Python 字典,其中包含有关恒星、星座、行星和其他物体的信息,以及 Alexa 可能会向用户发出的消息列表,例如用户在技能启动时听到的欢迎消息。
18、目前gadget的地理位置是在data.py文件中指定的。
# My latitude and longitude - NB no spaces
LAT = '51.5842N'
LON = '2.9977W'
要将其替换为您的位置,您可以使用 Google 查找附近城市的纬度/经度。
在将其粘贴到代码中之前,请删除空格和度数符号。
19. 完成此操作后,单击保存按钮,然后单击编辑器窗口上方的部署按钮。
20. 要测试技能,请转到开发人员控制台中的“测试”选项卡。
21. 在Alexa 模拟器的输入窗口中输入Open Sky Finder (或您之前选择的技能名称)。您应该会收到以下响应。
在下一节中,我们将了解 EV3 程序块的软件。
22. 在您的 PC 上,创建一个名为 SkyFinder 的新文件夹。从本页底部的代码部分下载skyfinder.py和skyfinder.ini文件,并将它们移动到您刚刚创建的新文件夹中。
23. 打开 Visual Studio 代码编辑器,点击 File -> Open Folder 并打开刚刚创建的 SkyFinder 文件夹,
24. 在编辑器中打开skyfinder.ini文件,添加第 4 步中的Amazon ID和Alexa Gadget Secret ,然后点击 File -> Save
25. 将 Visual Studio 连接到 EV3 程序块。
26. 设备连接后,圆圈将变为绿色。单击 ev3dev 设备浏览器旁边的图标,将工作区发送到设备。
与 Alexa 技能类相比,EV3 brick 上的代码相对简单。SkyFinder主类扩展了 AlexaGadget并调用超类来初始化小工具。
class SkyFinder(AlexaGadget):
"""
A Mindstorms gadget that points to a specified position in the sky
For use with the Sky Finder skill
"""
def __init__(self):
"""
Performs Alexa Gadget initialization routines and ev3dev resource allocation.
"""
super().__init__()
当小工具与 Echo 设备连接或断开连接时,将调用OnConnected和OnDisconnected方法。这些只是将前面的 LED 分别设置为绿色或关闭作为状态指示。
on_custom_mindstorms_gadget_control方法在小工具通过 Echo 设备从技能接收有效负载时被调用。我们支持两种有效载荷,一种将小工具重置为其起始位置(目前仅在启动时使用),另一种将指针移动到指定的方位角、高度坐标。
def on_custom_mindstorms_gadget_control(self, directive):
"""
Handles the Custom.Mindstorms.Gadget control directive.
:param directive: the custom directive with the matching namespace and name
"""
try:
payload = json.loads(directive.payload.decode("utf-8"))
print("Control payload: {}".format(payload), file=sys.stderr)
control_type = payload["type"]
if control_type == "move":
# Expected params: [az, alt]
self.go_to(payload["az"], int(payload["alt"]))
if control_type == "reset":
# Expected params: none
self.reset_position()
except KeyError:
print("Missing expected parameters: {}".format(directive), file=sys.stderr)
reset_position方法首先转动控制转盘的大电机,直到触摸传感器被按下,然后将转盘旋转指定距离回到起始位置。接下来它转动移动手臂的中型电机,直到它被颜色传感器检测到。然后以与起始位置相反的方向旋转。
def reset_position(self):
"""
Move arm to starting position
"""
logger.info(""Resetting arm position)
self.lm.stop_action = 'brake'
self.lm.reset()
self.lm.position = 0
# Rotate arm until it pushes touch sensor switch
# and then move specified distance to start position
self.lm.on_for_rotations(speed = SpeedDPS(72), rotations = -3, block=False)
self.ts.wait_for_pressed()
self.lm.off()
self.lm.on_for_degrees(SpeedDPS(72), START_AZ, block = True)
# Rotate arm in other axis until it's seen by the colour sensor
# and then move specified distance to start position
self.mm.stop_action = 'brake'
self.mm.on_for_rotations(speed = 5, rotations = -3, block=False)
# wait until sensor sees (red) arm
while self.cs.color != 5:
continue
self.mm.off()
self.mm.reset()
self.mm.position = 0
self.mm.on_for_degrees(5, START_ALT, block = True)
# turn off the distracting LED on the colour sensor
self.cs.mode='COL-AMBIENT'
# Reset the position counter to 0
self.mm.off()
self.mm.reset()
self.mm.position = 0
self.lm.off()
self.lm.reset()
self.lm.position = 0
重置完成后,指针应水平并指向与 EV3 程序块成直角的位置。如果不是这种情况,请尝试调整代码第 23 和 24 行中的变量。
# Calibration constants for initial position
# Tweak these if required to ensure starting pos is North horizon (0 az, 0 alt)
START_AZ = 388
START_ALT = 210
go_to方法处理“移动”有效负载并简单地将转盘和指针移动到指定位置。
为避免电缆缠结,我们只将转盘向任一方向移动 180 度(北向各 90 度)。要指向南方的某个位置,我们将转盘移动到指向北方,并将指针移到天顶(垂直向上的位置)以外以指向相反的方向。
电机位置已经以度为单位指定,但由于它们连接到齿轮,我们将数字乘以包含齿轮比的GEAR_LM和GEAR_MM 。
def go_to(self, new_az, new_alt):
"""
Move pointer to a specified position
:param new_az: new azimouth position (in degrees)
:param new_alt: new altitude position (in degrees)
"""
self.leds.set_color("LEFT", "YELLOW")
self.leds.set_color("RIGHT", "YELLOW")
# Normalise rotation to prevent cable tangle
if (new_az > 270):
az1 = new_az - 360
alt1 = new_alt
elif (new_az > 90):
az1 = new_az - 180
alt1 = 180 - new_alt
else:
az1 = new_az
alt1 = new_alt
# Move arm
self.lm.on_to_position(SpeedDPS(ARM_SPEED), az1 * GEAR_LM, block = False, brake = True)
self.mm.on_to_position(SpeedDPS(ARM_SPEED), alt1 * GEAR_MM, block = True, brake = True)
self.leds.set_color("LEFT", "GREEN")
self.leds.set_color("RIGHT", "GREEN")
最后一段代码在启动时运行并调用主入口点。
27. 要在 EV3 程序块上运行代码,请在 VS 代码中的 ev3dev 设备浏览器中导航到skyfinder.py文件,右键单击该文件并选择运行。
EV3 程序块上的绿灯将开始闪烁。
28. 如果小工具尚未连接到 Echo 设备,它将进入蓝牙配对模式,您将在 EV3 砖屏和 VS Code 中看到类似的消息。
。许可证:CC BY ( http://creativecommons.org/licenses/by/4.0/ )图表中使用的图标: Ben Davis 的 Amazon echo dot 来自 Noun Projectchat 来自 Noun Project 的 I cons 来自 Noun Project 的 naim 的 Signal 来自 Noun Project 的 Deemak DaksinaWoman 来自 Noun Projectlego 来自 Gerardo Martín Martínez 来自 Noun Projectpython 格式来自 ICONZ来自名词项目
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !