65.9K
CodeProject 正在变化。 阅读更多。
Home

Azure 上的 IoT:AzureBot

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (4投票s)

2015年4月1日

CPOL

5分钟阅读

viewsIcon

14633

downloadIcon

62

使用 Azure 服务控制您的第一个 IoT 设备的简单且经济高效的项目

引言

本项目旨在为您介绍如何在 Azure 中托管服务。最终目标是能够从 PC 控制一个价格便宜但功能相对齐全的机械臂,并且机械臂可能位于世界各地。我知道您在想,“如果机械臂在别的地方,我为什么要控制它?”我没有很好的答案,除了因为它能够做到,并且从计算机极客的角度来看,这很酷。话虽如此,这希望能为物联网领域的新手/初学者提供一个可以构建的平台,以实现更伟大的成就,同时展示您也可以相对轻松地完成这项工作。

下载 AzureBotSvc.zip

背景

此项目的架构保持非常简单。下图显示了所有相关组件以及它们如何相互连接。

 

                   

 

开始之前所需的零件

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");

ConnectToArmSendCommand 方法用于与机械臂交互。

       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 日 - 初始修订

© . All rights reserved.