Azure 上的 IoT:AzureBot
使用 Azure 服务控制您的第一个 IoT 设备的简单且经济高效的项目
引言
本项目旨在为您介绍如何在 Azure 中托管服务。最终目标是能够从 PC 控制一个价格便宜但功能相对齐全的机械臂,并且机械臂可能位于世界各地。我知道您在想,“如果机械臂在别的地方,我为什么要控制它?”我没有很好的答案,除了因为它能够做到,并且从计算机极客的角度来看,这很酷。话虽如此,这希望能为物联网领域的新手/初学者提供一个可以构建的平台,以实现更伟大的成就,同时展示您也可以相对轻松地完成这项工作。
背景
此项目的架构保持非常简单。下图显示了所有相关组件以及它们如何相互连接。
开始之前所需的零件
1 - 机械臂(我选择了 OWI Robotic Arm Edge。它价格便宜,组装起来只花了几个小时)
1 - OWI 机械臂 USB 接口套件(我相信它的用途不言而喻)
1 - 树莓派 2(RPi,对于它带来的功能来说,也非常便宜)
使用技术
Azure 云服务 - 用于托管一个简单的服务,以提供控制应用程序和机械臂之间的通信。
SignalR - 通信库,可以轻松地启动通知服务,代码量最少。
Mono - .Net 框架移植到,在此情况下,是树莓派上使用的 Linux 发行版(Raspbian)。
LibUsbDotNet - 出色的开源库,用于通过 USB 在树莓派和机械臂之间进行通信。
安装
在使用代码之前,您需要在 RPi 上安装 Mono 运行时。在 RPi 的命令行上键入以下命令来执行此操作:
sudo apt-get update
sudo apt-get install mono
使用代码
AzureBotSvc
此项目仅包含将 AzureBotSvc 发布到 Azure 所需的配置文件等。我没有以任何方式修改此项目,它开箱即用。
AzureBotWebRole
此项目用于实现用于在 WPF 远程控制应用程序和 RPi 上运行的、监听机器人命令的应用程序之间进行通信的服务。
Startup
类由 SignalR 库用于使服务器端启动并运行。它看起来像这样:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
AzureBotHub
类定义了客户端和服务器用于机器人命令和状态更新的通信方法。
public class AzureBotHub : Hub
{
private ArmConnectionStatuses _armStatus = ArmConnectionStatuses.Disconnected;
public void SendCommand(RobotCommand command)
{
Clients.All.SendCommand(command);
}
public void SendArmConnectionStatus(ArmConnectionStatuses armConnectionStatus)
{
_armStatus = armConnectionStatus;
UpdateArmConnectionStatus(_armStatus);
}
public void RequestArmStatus()
{
UpdateArmConnectionStatus(_armStatus);
}
public void UpdateArmConnectionStatus(ArmConnectionStatuses armConnectionStatus)
{
Clients.All.UpdateArmConnectionStatus(armConnectionStatus);
}
}
MonoBotControl
这是运行在 RPi 上的项目。它通过 USB 连接到机械臂。它使用 LibUsbDotNet 库查找、连接和通信机械臂。它还连接到 Azure 中托管的 AzureBotSvc 以接收和执行机械臂命令,并发布机械臂连接状态更新到该服务。
Program
类负责连接到 AzureBotSvc 和机械臂。连接到服务是通过以下方式实现的:
_hubConnection = new HubConnection("http://azurebot.cloudapp.net/");
_hubProxy = _hubConnection.CreateHubProxy("AzureBotHub");
_hubProxy.On<RobotCommand>("SendCommand", rc => SendCommand(rc.ArmAction, rc.BaseRotateAction, rc.LightAction));
ServicePointManager.DefaultConnectionLimit = 10;
Console.Write("Connecting to AzureBot service...");
_hubConnection.Start().Wait(Timeout.Infinite);
Console.WriteLine("Connected");
ConnectToArm
和 SendCommand
方法用于与机械臂交互。
private static bool ConnectToArm() { _sessionHandle = new MonoUsbSessionHandle(); if (_sessionHandle.IsInvalid) throw new Exception("Invalid session handle."); _device = MonoUsbApi.OpenDeviceWithVidPid(_sessionHandle, VendorId, ProductId); _armConnectionStatus = _device == null ? ArmConnectionStatuses.Disconnected : ArmConnectionStatuses.Connected; Console.WriteLine("Arm connection status:{0}", _armConnectionStatus); return _armConnectionStatus == ArmConnectionStatuses.Connected; } private static int SendCommand(ArmActions firstByte, BaseRotateActions secondByte, LightActions thirdByte) { if (_device != null) { var cmd = new byte[3]; cmd[0] = (byte)firstByte; cmd[1] = (byte)secondByte; cmd[2] = (byte)thirdByte; var pntr = Marshal.AllocHGlobal(cmd.Length); Marshal.Copy(cmd, 0, pntr, cmd.Length); return MonoUsbApi.ControlTransfer(_device, (byte)UsbRequestType.TypeVendor, 6, 0x100, 0, pntr, (short)cmd.Length, 0); } return -1; }
LibUsbDotNet 库有一个 DeviceNotifier 类,能够通知您设备的连接和断开。事件的处理程序设置如下:
_deviceNotify = DeviceNotifier.OpenDeviceNotifier();
_deviceNotify.OnDeviceNotify += (sender, e) =>
{
if (e.Device.IdVendor != VendorId || e.Device.IdProduct != ProductId) return;
switch (e.EventType)
{
case EventType.DeviceArrival:
Console.WriteLine("Device detected");
ConnectToArm();
TestArm();
break;
case EventType.DeviceRemoveComplete:
_armConnectionStatus = ArmConnectionStatuses.Disconnected;
Console.WriteLine("Device removed from the system");
break;
}
Console.WriteLine("Arm connection status: {0}", _armConnectionStatus);
_hubProxy.Invoke("SendArmConnectionStatus", _armConnectionStatus);
};
这是 Mono 应用程序在 RPi 上运行的屏幕截图(您可以看到几次连接和断开机械臂的记录)。
RobotArmRemoteControl
此项目包含用于连接远程控制机器人的 WPF 应用程序。此应用程序只需要有互联网连接,以便能够访问 Azure 中的 AzureBotSvc。
用于处理所有按钮按下的 SendArmCommand 属性一次只能发送一个命令。机械臂可以处理多个命令,但不幸的是,我没有时间实现某种拇指摇杆控制来发送多个命令(也许这是您可以做的事情)。我还尝试使用 Xbox 控制器作为输入机制,但这只部分成功,可惜我也没时间完成。
public RelayCommand<object> SendArmCommand
{
get
{
return _sendArmCommand ?? (_sendArmCommand = new RelayCommand<object>(
cmd =>
{
var newRobotCommand = new RobotCommand
{
ArmAction = ArmActions.NoAction,
BaseRotateAction = BaseRotateActions.NoAction,
LightAction = _toggleLightCommand ? LightActions.LightOn : LightActions.LightOff
};
if (cmd is ArmActions)
{
newRobotCommand.ArmAction = (ArmActions)cmd;
}
else if (cmd is BaseRotateActions)
{
newRobotCommand.BaseRotateAction = (BaseRotateActions)cmd;
}
else if (cmd is LightActions)
{
newRobotCommand.LightAction = (LightActions)cmd;
}
_hubProxy.Invoke<RobotCommand>("SendCommand", newRobotCommand);
}));
}
}
这里有几张应用程序的截图,显示了机械臂处于离线和在线状态。
SharedTypes
最后,此项目用于存储通用枚举和用于将机械臂命令从 RobotArmRemoteControl 项目传递到 RPi 上的 MonoBotControl 项目的结构。
ArmActions、BaseRotationActions 和 LigthActions 枚举用于定义发送到机械臂以实现所需操作的字节。它们定义如下:
[Flags]
public enum ArmActions
{
NoAction = 0x00,
GripClose = 0x01,
GripOpen = 0x02,
WristBack = 0x04,
WristForward = 0x08,
ElbowBack = 0x10,
ElbowForward = 0x20,
ShoulderBack = 0x40,
ShoulderForward = 0x80
}
[Flags]
public enum BaseRotateActions
{
NoAction = 0x00,
RotateRight = 0x01,
RotateLeft = 0x02
}
[Flags]
public enum LightActions
{
LightOff = 0x00,
LightOn = 0x01
}
我不能声称自己逆向工程了 OWI Robotic Arm Edge 的 USB 协议。我发现了 这篇 有用的文章,其中工作已经完成。
RobotCommand
结构定义如下:
public struct RobotCommand
{
public ArmActions ArmAction;
public BaseRotateActions BaseRotateAction;
public LightActions LightAction;
}
结论
这里有一个 链接 到一个简短的视频,展示了机械臂的实际运行情况。视频不是最好的,但它应该能让您对它的响应速度有一个大概的了解。
就这样。我希望您能从中看到,设置一个可以从任何地方控制的设备是多么简单。我有一些扩展此项目的想法,并将在项目成熟时尝试发布更新。一、我想升级机械臂,使用带有位置传感器的机械臂,以实现更精确的定位,并且伺服速度更快。二、我想为控制应用程序制作一个 Web 前端,可能将机械臂的控制权交给大众,也许可以进行一些集体艺术创作,或者举办在线叠叠乐比赛。
历史
2015 年 3 月 31 日 - 初始修订