- PVSM.RU - https://www.pvsm.ru -

Программируем роботов на Windows 8. Управляем Sphero с планшета используя акселерометр и датчик столкновений

image

Продолжаем исследовать возможности Sphero [1] и его SDK [2].
На повестке дня управление роботом с помощью акселерометра планшета и распознавание столкновения с препятствием.

Немного теории

В прошлой статье [3] мы уже рассматривали кто такой Sphero [1] и как начать управлять им программно на Windows 8.1 используя Visual Studio 2013 [4].
Теперь мы займемся более сложными вещами. Научимся «рулить» роботом наклоняя планшет в разные стороны и определять столкновение с препятствием. Для этого мы будем использовать акселерометр моего планшета и датчик столкновения робота с внешним миром.

Акселерометр

Операционная система Windows 8.1 и объектная модель WinRT поддерживают работу со следующими сенсорами, если, конечно, они есть у устройства:

  • Акселерометр;
  • Компас;
  • Гирометр;
  • Датчик освещения;
  • Инклинометр;
  • Геолокация.

Акселерометр – это модуль, измеряющий движение в трех направлениях. Этим модулем обустроены многие современные устройства. Чтобы получить доступ к данным акселерометра, необходимо обратиться к объекту Accelerometer [5], который предоставляет доступ к объекту AccelerometerReading [6] со следующими свойствами:

  • AccelerationX: отображает ускорение вдоль оси Х;
  • AccelerationY: отображает ускорение вдоль оси Y;
  • AccelerationZ: отображает ускорение вдоль оси Z;
  • Timestamp: определяет время снятия данных.

Мы подпишемся на событие изменения показаний акселерометра и будем передвигать робота, ускоряя, замедляя или разворачивая его в зависимости от показаний.

image

Подписаться на событие изменения показаний акселерометра можно так.

Подписка на событие акселерометра

protected override void OnNavigatedTo(NavigationEventArgs e)
{
…
Accelerometer _accelerometer = Accelerometer.GetDefault();  
_accelerometer.ReadingChanged += new TypedEventHandler<Accelerometer, AccelerometerReadingChangedEventArgs>(ReadingChanged);
…
}
async private void ReadingChanged(object sender, AccelerometerReadingChangedEventArgs e)
{
…
}

Перемещение

Предположим, мы получили данные акселерометра с планшета. Теперь надо их использовать для перемещения Sphero в пространстве.
В Sphero SDK для Windows 8.1 есть метод Roll, котoрый занимается перемещением Sphero и принимает два параметра:

  • Heading – угол перемещения.
    Рассчитывается как:
    image
    Принимает значения в диапазоне от 0 до 359.
  • Speed – скорость перемещения.
    0 – не двигается.
    255 – максимальная скорость.

По умолчанию, Sphero начинает свою работу с показателей x = 0, y = 0, heading = 0, speed = 0.

Детектор столкновения

Sphero поддерживает такую забавную вещь, как датчик столкновения (Collision Detection [7]). Он работает весьма примитивно [7], на разнице координат.

В Sphero SDK для Windows 8.1 уже есть готовое событие CollisionDetectedEvent, на которое вы можете подписаться и отлавливать столкновения с объектами реального мира.

Описание события

private void OnRobotConnected(object sender, Robot robot)
{
…
robot.CollisionControl.StartDetectionForWallCollisions();
robot.CollisionControl.CollisionDetectedEvent += OnCollisionDetectedEvent; 
…
}

Событие будет срабатывать, если вы предварительно не забудете включить датчик столкновений:

robot.CollisionControl.StartDetectionForWallCollisions();

Когда датчик вам перестанет быть нужен – выключите его:

robot.CollisionControl.StopDetection();

Некоторые полезные функции Sphero SDK

Для работы с роботом, нам так же понадобятся следующие методы Sphero SDK для Windows 8.1:

SetBackLED(intensity) – включить подсветку задней части робота, чтобы можно было понимать куда он поедет. Значения:
1.0f – включает на полную мощность.
0.0f — отключает.

SetHeading(heading) – повернуть робота. Принимает значения от 0 до 359.

SetRGBLED(red, green, blue) – установить цвет шара.

Roll(heading, speed) – задать направление движения и скорость движения:
heading – угол перемещения. Принимает значения в диапазоне от 0 до 359.
speed – скорость перемещения. 0 – не двигается. 255 – максимальная скорость.

Sleep() – усыпить робота.

Paзработка

Задача

Передвигать робота используя акселерометр. При столкновении с препятствием – красить робота красным.

Инструменты

  • Планшет Microsoft Surface 2 Pro
  • OS Windows 8.1
  • Среду разработки: Visual Studio 2013 [8]
  • Язык: C#
  • Sphero SDK [2] для Windows 8.1

Процесс

1. Открываем Visual Studio 2013 и создаём проект File / New / Project / Blank App.

image

2. Подключаем сборку RobotKit.dll из Sphero SDK [2] для Windows 8.1

image

3. Добавляем поддержку Bluetooth RFCOMM для приложения. Открываем Package.appxmanifest в режиме правки кода и добавляем в секцию Capabilities следующий код.

Поддержка Bluetooth RFCOMM для приложения

<wb:DeviceCapability Name="bluetooth.rfcomm"> <wb:Device Id="any"> <wb:Function Type="serviceId:00001101-0000-1000-8000-00805F9B34FB" /> </wb:Device> </wb:DeviceCapability>

4. На основной экран приложения кладем:

  • переключатель состояния робота (подключенне подключен);
  • текстовое поле для отображения состояния;
  • кнопки Start и Stop для запуска и остановки игры.

Откроем файл MainPage.xaml и добавим туда всё это.

Основной экран приложения

<Page
    x:Class="SpheroRace.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SpheroRace"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"   >
    <Grid Margin="100" >        
        <Grid.RowDefinitions >
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBlock x:Name="SpheroName" Grid.Row="0" Grid.Column="0" Text="No Sphero Connected" FontSize="24"/>
        <ToggleSwitch x:Name="ConnectionToggle" Grid.Row="1" Grid.Column="0" Toggled="ConnectionToggle_Toggled" />
        <Button Name="StartBtn" Click="StartBtn_Click" Grid.Row="2" Grid.Column="0" Content="Start" VerticalAlignment="Bottom" HorizontalAlignment="Left" Padding="100" ></Button>
        <Button Name="StopBtn" Click="StopBtn_Click" Grid.Row="2" Grid.Column="1" Content="Stop" VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="100" IsEnabled="False" ></Button>
    </Grid>
</Page>

5. Перейдем в MainPage.xaml.cs и опишем логику игры. Для общения со Sphero используем объектную модель, предоставленную SDK.

Логика игры

public sealed partial class MainPage : Page
    {
        private Sphero m_robot = null;
        private long   m_lastTimeMs;
        private double m_currentX = 0;
        private double m_currentY = 0;

        private bool m_isStarted = false;

        private const string c_noSpheroConnected = "No Sphero Connected";
        private const string c_connectingToSphero = "Connecting to {0}";
        private const string c_spheroConnected = "Connected to {0}";

        public MainPage()
        {
            this.InitializeComponent();
        }
         
        //Начало работы. Переход на основной экран
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            SetupRobotConnection();
            Application app = Application.Current;
            app.Suspending += OnSuspending;
            
            Accelerometer _accelerometer = Accelerometer.GetDefault();    
            _accelerometer.ReadingChanged += new TypedEventHandler<Accelerometer, AccelerometerReadingChangedEventArgs>(ReadingChanged);

        }

        //Завершение работы. Переход с основного экрана
        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);

            ShutdownRobotConnection();

            Application app = Application.Current;
            app.Suspending -= OnSuspending;

            Accelerometer _accelerometer = Accelerometer.GetDefault();
            _accelerometer.ReadingChanged -= new TypedEventHandler<Accelerometer, AccelerometerReadingChangedEventArgs>(ReadingChanged);
        }

        //handle the application entering the background
        private void OnSuspending(object sender, SuspendingEventArgs args)
        {
            ShutdownRobotConnection();
        }

        //Ищем робота и подключаемся к нему
        private void SetupRobotConnection()
        {
            SpheroName.Text = c_noSpheroConnected;

            RobotProvider provider = RobotProvider.GetSharedProvider();
            provider.DiscoveredRobotEvent += OnRobotDiscovered;
            provider.NoRobotsEvent += OnNoRobotsEvent;
            provider.ConnectedRobotEvent += OnRobotConnected;
            provider.FindRobots();
        }            

        //Завершаем работу с роботом
        private void ShutdownRobotConnection()
        {
            if (m_robot != null && m_robot.ConnectionState == ConnectionState.Connected)
            {
                m_robot.SensorControl.StopAll();
                m_robot.CollisionControl.StopDetection();
                m_robot.Sleep();                
                m_robot.Disconnect();

                ConnectionToggle.OffContent = "Disconnected";
                SpheroName.Text = c_noSpheroConnected;
                SetRedColor();
    
                RobotProvider provider = RobotProvider.GetSharedProvider();
                provider.DiscoveredRobotEvent -= OnRobotDiscovered;
                provider.NoRobotsEvent -= OnNoRobotsEvent;
                provider.ConnectedRobotEvent -= OnRobotConnected;
            }
        }
       
        //Робот найден!
        private void OnRobotDiscovered(object sender, Robot robot)
        {          
            if (m_robot == null)
            {                
                RobotProvider provider = RobotProvider.GetSharedProvider();
                provider.ConnectRobot(robot);
                ConnectionToggle.OnContent = "Connecting...";
                m_robot = (Sphero)robot;

                SpheroName.Text = string.Format(c_connectingToSphero, robot.BluetoothName);
            }
        }

        //Робот не найден :(
        private void OnNoRobotsEvent(object sender, EventArgs e)
        {
            MessageDialog dialog = new MessageDialog(c_noSpheroConnected);
            dialog.DefaultCommandIndex = 0;
            dialog.CancelCommandIndex = 1;
            dialog.ShowAsync();
        }
    
        //Робот готов исполнять команды
        private void OnRobotConnected(object sender, Robot robot)
        {
            ConnectionToggle.IsOn = true;
            ConnectionToggle.OnContent = "Connected";

            SpheroName.Text = string.Format(c_spheroConnected, robot.BluetoothName);

            if (m_robot != null && m_robot.ConnectionState == ConnectionState.Connected)
            {
                m_robot.CollisionControl.StartDetectionForWallCollisions();
                m_robot.CollisionControl.CollisionDetectedEvent += OnCollisionDetectedEvent;

                SetRedColor();
            }
        }

        //Событие изменения показаний акселерометра
        async private void ReadingChanged(object sender, AccelerometerReadingChangedEventArgs e)
        {
            if (!m_isStarted)
                return;

            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                Windows.Devices.Sensors.AccelerometerReading reading = e.Reading;
                SendRollCommand(reading.AccelerationX, reading.AccelerationY, reading.AccelerationZ);
            });
        }

        //Событие столкновения с препятствием
        private void OnCollisionDetectedEvent(object sender, RobotKit.CollisionData data)
        {
            SetRedColor();
            m_robot.Roll(0, 0);
        }
        
        //Кнопка для отключения робота
        private void ConnectionToggle_Toggled(object sender, RoutedEventArgs e)
        {
            ConnectionToggle.OnContent = "Connecting...";
            if (ConnectionToggle.IsOn)
            {
                if (m_robot == null || m_robot.ConnectionState != ConnectionState.Connected)
                {
                    SetupRobotConnection();
                }
            }
            else
            {
                ShutdownRobotConnection();
            }
        }

        //Запустить робота
        private void StartBtn_Click(object sender, RoutedEventArgs e)
        {
            m_isStarted = true;
            StartBtn.IsEnabled = false;
            StopBtn.IsEnabled = true; 
            
            SetGreenColor();
            m_robot.Roll(0, 0);           
        }

        //Остановить робота
        private void StopBtn_Click(object sender, RoutedEventArgs e)
        {
            m_isStarted = false;
            StartBtn.IsEnabled = true;
            StopBtn.IsEnabled = false;

            SetRedColor();
            m_robot.Roll(0, 0);
        }

        //Начальное состояние робота. Он зеленый и неподвижно стоит
        private void SetGreenColor()
        {
            m_robot.SetHeading(0);
            m_robot.SetBackLED(1.0f);
            m_robot.SetRGBLED(0, 255, 0);
        }

        //Состояние остановки или столкновения в препятствием
        private void SetRedColor()
        {
            m_robot.SetHeading(0);
            m_robot.SetBackLED(1.0f);
            m_robot.SetRGBLED(255, 0, 0);
        }

        //Логика движения робота при изменении показаний акселерометра
        private async void SendRollCommand(double newX, double newY, double newZ)
        {
            float x = (float)newX;
            float y = (float)newY;
            float z = (float)newZ;

            float speed = Math.Abs(z);
            speed = (speed == 0) ? 0 : (float)Math.Sqrt(speed);
            if (speed > 1f)
                speed = 0.01f;

            int heading = Convert.ToInt32(Math.PI / 2.0 - Math.Atan2((double)y - m_currentY, (double)x - m_currentX));

            long milliseconds = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
            if ((milliseconds - m_lastTimeMs) > 1000)
            {
                SetGreenColor();
                m_robot.Roll(heading, speed);

                m_lastTimeMs = milliseconds;
                m_currentX = x;
                m_currentY = y;
            }
        }
       
}

6. Подключаемся к Sphero по Bluetooth

image

7. Запускаем приложение.

image

8. Нажимаем на кнопку Start и проверяем игру на деле.

image

Результат

У нас получилась простая игрушка. Её можно применять, устраивая многопользовательские гонки роботов, или для игры с домашним животным.

Результат работы можно посмотреть на видео.

Исходники доступны тут: github.com/MissUFO/SpheroRace [9]

Полезные ссылки

Программируем роботов на Windows 8. Магический шар Sphero [10]
Официальный сайт Sphero [1]
Центр разработчика Sphero [11]
Sphero SDK для Windows 8.1 [2]
Пример использования Sphero SDK для Windows 8.1 [12]
Пример использования акселерометра в Windows 8.1 [13]
Канал разработчиков Sphero на youtube [14]
Скачать Visual Studio 2013 [4]
Подробнее о Visual Studio Online [15]
Зарегистрироваться на Visual Studio Online [16]

Автор: MissUFO

Источник [17]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/windows/56505

Ссылки в тексте:

[1] Sphero: http://www.gosphero.com/

[2] SDK: https://github.com/orbotix/Sphero-Win-SDK

[3] прошлой статье: http://habrahabr.ru/company/microsoft/blog/213797/

[4] Visual Studio 2013: http://www.visualstudio.com/downloads/download-visual-studio-vs

[5] Accelerometer: http://msdn.microsoft.com/ru-ru/library/windows/apps/windows.devices.sensors.accelerometer

[6] AccelerometerReading: http://msdn.microsoft.com/ru-ru/library/windows/apps/windows.devices.sensors.accelerometerreading.aspx

[7] Collision Detection: http://orbotixinc.github.io/Sphero-Docs/docs/collision-detection/index.html

[8] Visual Studio 2013: http://www.visualstudio.com/

[9] github.com/MissUFO/SpheroRace: https://github.com/MissUFO/SpheroRace

[10] Программируем роботов на Windows 8. Магический шар Sphero: http://habrahabr.ru/users/missufo/topics/

[11] Центр разработчика Sphero: https://developer.gosphero.com/

[12] Пример использования Sphero SDK для Windows 8.1: http://code.msdn.microsoft.com/windowsapps/Sphero-SDK-Sample-2b18913c

[13] Пример использования акселерометра в Windows 8.1: http://code.msdn.microsoft.com/windowsapps/Accelerometer-Sensor-Sample-22982671

[14] Канал разработчиков Sphero на youtube: http://www.youtube.com/playlist?list=PLECE4BD55445BDA51&feature=plcp

[15] Подробнее о Visual Studio Online: http://www.visualstudio.com/products/visual-studio-online-overview-vs

[16] Зарегистрироваться на Visual Studio Online: http://go.microsoft.com/fwlink/?LinkId=307137&clcid=0x419

[17] Источник: http://habrahabr.ru/post/214759/