Пару дней назад мне в руки попала забавная игрушка для гиков, любящих роботов и кодировать. Последние пару ночей я провела изучая игрушку и применяя к ней свой опыт Windows 8 разработки. Так как занятие оказалось довольно увлекательным, спешу с вами им поделиться.
Вашему вниманию предлагается…
Sphero (www.gosphero.com) – маленький робот шарик сделанный компанией Orbotix.
В комплекте поставляется зарядное устройство, инструкция по эксплуатации и, непосредственно, сам шарик.
Sphero выглядит как отличная платформа для постижения азов программирования роботов, а еще, это веселая игрушка для гиков, их друзей, детей и домашних животных.
Sphero умеет перемещаться практически по любой поверхности в заданном направлении, в том числе плавать, а так же загораться разными цветами.
Производитель рекомендует использовать робота в следующих сценариях.
Используя фантазию и SDK вы можете создавать собственные игры или пользоваться уже готовыми приложениями.
Готовые приложения для управления Sphero и SDK для разработчиков доступны на многих популярных платформах:
Цена вопроса
Стоит такое удовольствие в России от 4 000 до 7 000 тыс. руб. на Яндекс.Маркет. За границей его можно купить за сумму от 80 до 130$. Да, это не самая дешевая игрушка.
Немного теории
В робот встроен акселерометр, а еще гироскоп и лампочка. Для того, чтобы Sphero двигался, необходимо понимать где у него перед, где зад и как устроена его система координат. Система координат представлена на рисунке ниже.
Ось Y отвечает за движение вперед и назад (положительное значение – вперед, отрицательное — назад). Ось X — за движение в стороны. Ось Z — за перемещение вверх или вниз.
Sphero можно калибровать. Перемешать тот самый зад и перед куда следует. Заднюю часть робота можно опознать по синему огоньку, который горит, когда робот едет.
Разработка
Наконец мы дошли до самого интересного. До программного управления этой штуковиной. В качестве примера, мы создадим простую игру на реакцию для небольшой компании. Назовем её Sphero Party Game.
Правила игры
Игра начинается после нажатия на кнопку в приложении. Sphero будет крутиться вокруг своей оси и гореть разными цветами в случайном порядке. Игрокам необходимо дождаться, когда Sphero загорится зеленым и схватить его. Кто схватит, тот и победил.
Инструменты
Для разработки я буду использовать:
- Планшет Microsoft Surface 2 Pro
- OS Windows 8.1
- Среду разработки: Visual Studio 2013
- Язык: C#
- Sphero SDK для Windows 8.1
Приступаем к работе
1. Первое что нам надо будет сделать – это скачать Sphero SDK. Оно будет представлять собой сборку, которую мы подключим к проекту и будем использовать.
2. Sphero SDK использует Bluetooth RFCOMM для работы с устройством. Для того, чтобы мы программно могли работать с устройством, нам необходимо включить Bluetooth и установить с ним соединение.
3. Далее открываем Visual Studio 2013 и создаём проект File / New / Project / Blank App.
4. Подключаем сборку из SDK RobotKit.dll
5. Добавляем поддержку 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>
6. На основной экран приложения кладем:
- переключатель состояния робота (подключен\не подключен);
- текстовое поле для отображения состояния;
- кнопку “Start” для запуска игры.
Откроем файл MainPage.xaml и добавим туда всё это.
Основной экран приложения
<Grid Background="#0094D0">
<TextBlock x:Name="SpheroName" HorizontalAlignment="Left" Margin="82,97,0,0" TextWrapping="Wrap" Text="No Sphero Connected" VerticalAlignment="Top" FontSize="24"/>
<ToggleSwitch x:Name="ConnectionToggle" HorizontalAlignment="Left" Margin="76,122,0,0" VerticalAlignment="Top" Toggled="ConnectionToggle_Toggled" />
<Grid HorizontalAlignment="Center" Height="512" Margin="0,0,0,0" VerticalAlignment="Center" Width="512">
<Button Name="startGameBtn" Click="startGameBtn_Click" Content="Start" Height="113" Width="334" />
</Grid>
</Grid>
7. Перейдем в MainPage.xaml.cs и опишем логику игры. Для общения со Sphero используем объектную модель, предоставленную SDK.
Логика игры
public sealed partial class MainPage : Page
{
private Sphero m_robot = null;
private long m_lastCommandSentTimeMs;
private bool m_gameIsStarted = 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;
}
//Завершение работы. Переход с основного экрана
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
ShutdownRobotConnection();
Application app = Application.Current;
app.Suspending -= OnSuspending;
}
//Переход приложения в фоновый режим
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.SensorControl.StopAll();
m_robot.Sleep();
m_robot.Disconnect();
ConnectionToggle.OffContent = "Disconnected";
SpheroName.Text = c_noSpheroConnected;
m_robot.SensorControl.AccelerometerUpdatedEvent -= OnAccelerometerUpdated;
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);
SetRobotDefault();
m_robot.SensorControl.Hz = 10;
m_robot.SensorControl.AccelerometerUpdatedEvent += OnAccelerometerUpdated;
}
//Изменение местоположения робота
private void OnAccelerometerUpdated(object sender, AccelerometerReading reading)
{
if (m_gameIsStarted)
MoveRobot(reading.X, reading.Y);
}
//Начальное состояние робота. Он зеленый и неподвижно стоит
private void SetRobotDefault()
{
m_robot.SetHeading(0);
m_robot.SetRGBLED(0, 255, 0);
m_robot.Roll(0, 0);
}
//Герератор случайных цветов робота и интервала их смены
public async void ChangeRobotColor()
{
int colorsCount = 20;
Random r = new Random();
List<Color> colors = new List<Color> { Color.FromArgb(100, 0, 255, 0) };
for (int c = 0; c < colorsCount; c++)
colors.Add(Color.FromArgb(100, (byte)r.Next(255), 0, (byte)r.Next(255)));
List<int> miliseconds = new List<int>();
for (int m = 0; m <= 5; m++)
miliseconds.Add(r.Next(1000));
while (true)
{
int colorNumber = r.Next(colorsCount);
Color color = colors[colorNumber];
int milisecond = miliseconds[r.Next(5)];
m_robot.SetRGBLED(color.R, color.G, color.B);
await Task.Delay(TimeSpan.FromMilliseconds(milisecond));
}
}
//Вращение робота по кругу. На 10 градусов в милисекунду
public void MoveRobot(float x, float y)
{
m_robot.SetHeading(0);
int angleDegrees = 10;
long milliseconds = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
if ((milliseconds - m_lastCommandSentTimeMs) > 1)
{
m_robot.Roll(angleDegrees, 0);
m_lastCommandSentTimeMs = milliseconds;
}
}
//Кнопка для запуска и остановки игры
private void startGameBtn_Click(object sender, RoutedEventArgs e)
{
m_gameIsStarted = !m_gameIsStarted;
if (m_gameIsStarted)
{
startGameBtn.Content = "Stop";
ChangeRobotColor();
}
else
{
startGameBtn.Content = "Start";
SetRobotDefault();
}
}
//Кнопка для отключения робота
private void ConnectionToggle_Toggled(object sender, RoutedEventArgs e)
{
ConnectionToggle.OnContent = "Connecting...";
if (ConnectionToggle.IsOn)
{
if (m_robot == null)
{
SetupRobotConnection();
}
}
else
{
ShutdownRobotConnection();
}
}
}
8. Запускаем приложение. Моё приложение выглядит следующим образом:
Вопрос про разрешение подключиться приложению к роботу по Bluetooth- обязательная часть программы, появляться будет у всех при первом открытии установленного приложения.
9. Нажимаем на кнопку Start и проверяем игру на деле.
Скачать исходники можно тут: http://1drv.ms/MXwR6w
Заключение
На этом сегодня всё. Если кто-то заинтересовался или имеет такой девайс, делитесь своими забавными играми, приложениями, сценариями использования и открытиями. Я планирую продолжить данный цикл статей и через некоторое время вернусь к вам с еще одной статьей про Sphero и использование её API.
Полезные ссылки
Официальный сайт Sphero
Центр разработчика Sphero
Windows 8.1 Sphero SDK
Пример использования Sphero SDK для Windows 8.1
Канал разработчиков Sphero на youtube
Скачать Visual Studio 2013
Подробнее о Visual Studio Online
Зарегистрироваться на Visual Studio Online