Повод, чтобы начать
Я хочу рассказать про хакатон, который проводила компания Microsoft и Intel в Нижнем Новгороде в рамках Технологической экспедиции Microsoft Developer Tour. Так сказать из первых уст. Как участник. Думаю, так будет наиболее интересно.
Тема хакатона, который должен был проходить в Нижнем Новгороде, IoT — Internet of Things (интернет вещей). Честно говоря, для меня этот термин был в новинку и пришлось погуглить, чтобы понять основные принципы. Оказалось всё достаточно просто, есть устройство, которое собирает информацию с некоторых датчиков и отправляет ее в интернет для доступа и обработки.
О том, что в рамках тура будет проходить хакатон, я узнал незадолго от мероприятия. Было написано, что на хакатоне надо будет создать что-нибудь с использованием платы Intel Galileo.
И как хорошо совпало, что около месяца назад я посетил конференцию Intel, посвященный Intel Galileo и Intel Edison. Где я получил плату Intel Galileo Gen 2. Спасибо! Но руки до неё совершенно не доходили, и она просто лежала на столе. Я, конечно, заказал на всем известном китайском сайте сенсоры и шилды для неё, но они всё еще не дошли.
Пара слов об Intel Galileo и Intel Edison. Это платы, которые совместимы по контактам с Arduino Uno R3. В них можно вставлять стандартные сенсоры и шилды. Есть средства разработки, чтобы запускать скетчи от ардуино. И тут может возникнуть вопрос, зачем нужны эти платы, если они и дороже и потребляют больше. Я, признаться, тоже так думал изначально. Но поразмышляв, пришел к выводу, что назначение этих плат совершенно разное. Сравните Arduino с 16 МГц и Intel Galileo Gen 2 с 400 МГц. Память ОЗУ 2 КБ против 256 MГб DDR3. Еще на платах от Intel есть встроенный сетевой интерфейс на 100 Мбит и работающий Linux. Их не надо сравнивать, они для разных задач.
Подробнее тут: habrahabr.ru/company/intel/blog/248279
В общем, у меня была плата Intel Galileo Gen 2, которая просто так лежала в коробке. Опыта работы с этой платой как и с Arduino у меня не было. И тут как раз хакатон.
За несколько дней до хакатона я решил поставить на свою плату Linux по этой инструкции: habrahabr.ru/company/intel/blog/248893
Подключался к плате по SSH, через локальную сеть. Там обычный Linux, приятно. Всё отлично, всё работает. Написал простой пример на C++ из интернета, который мигает встроенным светодиодом. Заработало не сразу (забыл указать -lmraa в параметрах компиляции). Ура! Всё работает, можно идти спать.
Раз плата работает, можно попытаться что-нибудь подключить. Пошел на радиорынок, чтобы посмотреть можно ли что-нибудь купить для arduino. Купил макетную плату, провода. Что-то по мелочам, какие-то датчики. Но ничего не потестил, опять руки не дошли.
Идея
В последние несколько дней у меня был мозговой штурм, что же можно реализовать на этой плате. Не хотелось чего-то простого, типа мигания светодиодом, измерения температуры в комнате. Всё это уже было раньше, да и Intel Galileo слишком мощный для таких задач. Хотелось чего-то с большими вычислениями, обработкой данных, пересылкой их на сервер для последующей обработки или анализа.
И вот постепенно пришла в голову следующая идея. Как-то много лет назад в журнале Компьютерра была новость про экспериментальную технологию для определения положения источника громкого звука, например, выстрела. На улице расположены микрофоны. Они воспринимают звук, и из-за того, что звук достигает всех микрофонов не одновременно, а с задержкой, можно найти положение источника звука. Вот такую систему я и решил создать.
Идея есть, можно идти на радиорынок покупать сенсоры. Погуглив, я нашел, что есть два типа звуковых сенсоров. Первый с цифровым выходом, реагирует на превышение порога и имеет, соответственно, три контакта (земля, +5, сигнал-порог). Второй сенсор дополнительно имеет аналоговый выход и, соответственно, четыре контакта (земля, +5, сигнал-порог, аналоговый сигнал). Я хотел второй, который выдает аналоговый сигнал. Получив сигнал с нескольких датчиков можно будет наложить их и найти совпадение для точного определения задержки. Ну, либо динамически искать порог срабатывания. Еще к датчикам были нужны провода. Метров по 10 на каждый. Так, на всякий случай. На рынке я нашел звуковые датчики, но только с тремя выводами. Других не было. Продавец заверил, что выход у них аналоговый. Ладно, поверим, хотя зачем тут подстроечный резистор? Я взял 5 датчиков. Теперь провода. 50 метров красно-черного звукового провода 0.25 мм и 30 метров светлого (0.25 мм, двойной). Я рассчитывал на каждый датчик пустить красно-черный для питания и один из пары светлых для сигнала.
Окей, всё есть. Это всё было в четверг, а в субботу уже конференция.
Дома попробовал подключить датчики. Первый раз страшновато что-то втыкать в плату, вдруг сгорит. Но ничего, воткнул, дыма нет, консоль работает. Пробую получать аналоговые значения с датчика. И тут сюрприз, датчик оказывается бинарным, т.е. только реагирует на превышение уровня сигнала. И это уровень задается настроечным резистором на плате. А-а-а! Бежать на следующий день, в пятницу, на рынок уже смысла нет. Неизвестно будут ли там правильные датчики. Да и некогда, дела всякие, еще и Visual Studio на ноут ставить и сетку настраивать. Рынок ещё полдня займет. А в субботу конференция с 9 утра, ещё бы поспать успеть.
Вечером в пятницу, придя домой часов в 10, я начал всё настраивать.
Для хакатона я решил на всякий случай запастись всем необходимым. Удлинитель, так как участников будет много, и розеток может не хватить. Роутер, т.к. плату надо подключать по сетевому проводу. Роутер также нужен для подключения к интернету через USB модем. В общем, полная автономность. Думал уложиться за пару часов. Поставил на установку Visual Studio 2013 Community. От установки VC 2015 отказался. Итак, подготовка и сбор всех железок, которые надо взять на хакатон, вместо планируемых пары часов, растянулось до 4 утра. И да, в 8 вставать.
В итоге с собой взял: удлинитель, скотч, синюю изоленту (да, та самая), длинные провода, соединительные провода для макетной платы и сенсоров, ножницы, роутер, 5 звуковых сенсоров, USB модем, вибродатчик, рулетку. Всё, пора спать, там будем разбираться.
Magical Mystery Tour
На конференции было много докладов. Разнообразные, интересные.
Был доклад про WebGL и библиотеку Babylon JS, с помощью которой можно легко рисовать в браузере в 3D. Идея хорошая, можно позднее на хакатоне попробовать прикрутить к своему проекту.
Конaеренция заканчивалась в 19:00, хакатон начинался в 20:00 в здании Политеха. Дойти можно пешком. По дороге купил пиццу, чтобы немного перекусить.
Хакатон
Итак 20:00, я на месте. Зал большой. Еще с университета привык сидеть на первом ряду, поэтому занимаю первый стол по центру. Для моих железок как раз целый стол и понадобится.
Фото со страницы хакатона events.techdays.ru/msdevtour/news#fe58625b-bcd9-498a-94e8-161ccc286f11
С 20:00 до 00:00 лекции про IoT и Azure от Microsoft и Intel.
Одна лекция была от Intel про Galileo и Edison. Одна про использование Azure. Что он умеет и как им пользоваться. Это было самое главное, так как до этого момента я с Azure ни разу не работал.
Дмитрий Сошников рассказал, как Microsoft видят IoT. Это небольшое устройство, которое собирает какие-то данные с датчиков, передает их в облако и позволяет их получать и анализировать. Мой проект как раз удовлетворял всем этим требованиям, оставалось его реализовать.
Пока шли лекции, чтобы не тратить время зря, решил вывести формулы, которые понадобятся для определения положения источника сигнала.
Наша задача найти точку в пространстве, которая наиболее хорошо подходит к полученным временам задержек с датчиков. Т. е. оптимизационная задача, в которой возможно переменных будет больше, чем неизвестных. Может что-то вроде метода наименьших квадратов.
Написал зависимости, стал выводить. Две страницы исписал, и понял, что решать уравнения 4-степени мне лень и я на это могут потратить всё своё время, а там еще и ошибки могут быть. И стало как-то грустно.
В итоге решил действовать универсальным методом, методом полного перебора. Прикинул размеры аудитории, где-то 10 на 10 метров, пусть это куб 10х10х10, пусть точность 10 см. Получаем 1 миллион точек. Много, но может процессор справится. В случае чего, точность можно будет уменьшить.
Итак, лекции закончились, все должны придумать проекты и собрать команды. В зале было человек 60.
Началось представление команд. Я вышел третьим. Вкратце объяснил о чем мой проект и сказал, что плата у меня уже есть, и проект я планирую делать один. Название Audio sprut, потому что звуковые датчики как щупальца.
Все рассказали про свои проекты. Народ начал ходить и присоединяться к проектам.
Ну что ж, начнем. Первым делом надо было точно определять время. Первый вариант, это считать сколько шагов цикла выполнено. Но тут надо определить сколько времени занимает каждый шаг цикла, а это не очень точно. Надо было найти высокоточный таймер. Я вспомнил, что есть что-то в С++11. Поиски по интернету дали решение .
Итак алгоритм работы с датчиками следующий. Опрашиваем все датчики, пока на одном из них не появится сигнала. Его время принимаем за ноль. Дальше опрашиваем оставшиеся датчики и как только на каком-то появляется сигнал, записываем для него текущее время. Выполняем этот опрос до тех пор, пока все датчики не выдадут сигнал. По полученным временам для каждого датчика находим его дельту расстояния, умножив скорость звука на время.
Измерения показали, что цифровые датчики опрашиваются со скоростью 83 кГц. Это дает для пяти датчиков точность около 2 см. Очень неплохо. Ради интереса померил скорость аналогового считывания. Около 4 кГц.
Похоже, что датчики, которые я купил, пусть и не те, что хотелось, но они упрощают мне задачу. Во первых не придется искать соответствие между аналоговыми сигналами, а во-вторых точность выше.
Но надо еще учитывать, что Linux это не операционная система реального времени. Поэтому не гарантируется, что система будет занята только нашим кодом, а не переключится на что-то другое. Тут бы конечно и помог Intel Edison с двумя ядрами. Но, что есть, то есть.
Для тестов я брал два датчика расположенных на небольшом расстоянии, примерно сантиметров 15. И щелкал мальцами то со стороны одного датчика, то со стороны другого. При этом было четко видно, что сигнал запаздывал и вычисленная дельта расстояния согласовывалась с реальностью.
Так как у меня была своя плата и настроенный роутер, от работы WiFi я не зависел. Но была и большая проблема, мой интернет иногда был ужасен. Временами скорость падала до 100 кБит/сек.
Должно быть облачно сегодня
Итак, программа положение источника звука определяет, осталось передать эту информацию в облако.
Нам рассказали, что наиболее простой вариант отсылать данные через события. Пытаюсь разобраться в Python скрипте, чтобы послать что-нибудь на сервер (питоном раньше не занимался). Сначала скрипт выдает ошибки в ответе от сервера. Соседняя команда тоже пытается сделать отправку данных и тоже безрезультатно. Долго ковыряюсь, и о чудо, ответ 201, значит нет ошибки и данные переданы. Захожу на Azure, а там никаких данных не видно. Есть обращения, а данных я не вижу. Долго пытаюсь разобраться, спрашиваю организаторов и оказывается, что события мне не подходят. Они одноразовые, если его считать оно удаляется. Сюрприз! И что же использовать? Таблицы.
Отлично, всё с начала. С таблицами не всё так просто. Интерфейс в Azure не слишком интуитивно понятный. Подробностей уже не помню, но сервер создал, таблицу создал. Теперь бы записать туда данные. А это уже как оказалось не такая тривиальная задача. Код, аналогичный сообщениям не работает. После общения с Татьяной Сметаниной, которая удивительным образом успевала бегать между командами и всем помогать, становится понятно, что база данных стандартная и с ней можно работать как с какой-нибудь MySQL. Осталось подключиться к базе и сделать INSERT. Осталось немного. Как же подключиться? Из C++ у меня желания нет. Python я плохо знаю. Но на Gagileo есть perl. Отлично! Пробую написать небольшой тестовый скрипт. Но DBI модуль не установлен. Запускаю cpan. Интернет у меня тормозит, но что-то качается, уже приятно. Но в конце установки минут через 20, оказывается, что не хватает какой-то библиотеки или модули. Пробую поставить эту библиотеку, аналогично. Пробую поставить что-то еще. Время уходит, ничего не работает. Обидно. Как я понимаю, у соседей аналогично. Всё как-то сложно, а на презентации было просто, всего несколько строчек. И тут оказывается, что к базе данных можно обратиться через REST протокол, для этого надо создать в Azure мобильный клиент. Хорошо пробую. Опять куча поисков по интернету. Найден пример на питоне. И о чудо, он пишет в базу! На сайте Azure видно данные, которые присылает Galileo. Отлично, добавляю вызов из С++ функции system, которая запускает python скрипт с параметрами. Всё. Можно про эту часть забыть. Теперь надо научиться считывать данные из базы через JavaScript.
База, отдай данные!
Если в браузере вписать в строку адрес таблицы, то браузер показывает JSON с данными. Все отлично. Осталось разобрать данные. Пишу небольшую локальную html-ку с JavaScript, который должен загрузить табличку и показать ее на странице. Не работает. Данных нет. Что-то приходит, но непонятно что. Начинаю разбираться. Отладчик в браузере пишет что-то про какие-то права. При чем тут права? По обычному адресу всё показывается. Ковыряюсь, ищу, ковыряюсь, ищу. Ничего не понятно почему не работает. После уж не знаю какой попытки, решаю по шагам сделать то, что написано на странице Azure по работе с мобильным интрефейсом. Там предлагается запустить локальный web сервер. Делов на пару строк, но как-то не нравится мне сервер локально. Ладно, если ничего не помогает прочтите инструкцию. Делаю всё как написано, и всё работает. Локальная страница со скриптом получает данные и отображает их на экране. Ура! Хотелось конечно веб сервера, который на Azure в интернете, но уже хорошо.
Вавилон, но не 5, а JS
Данные есть, надо прикрутить 3D визуализацию на основе Babylon JS.
Меряю размер стола, за которым сижу, чтобы сделать правильную 3D модель комнаты. 106 сантиметров в длину одна секция. Т. е. весь стол 2 метра. Отлично. Три блока со столами плюс проход, получается 10 метров. Нормально. Добавляю на сцену переднюю стену, столы первого ряда, столы организаторов. кафедры. В браузере всё выглядит нормально. Думаю куда поставить датчики. Пока делаю прикидки оказывается, что что-то не сходится. Столы, оказывается состоят не из двух секций по 1 метру, а из четырех. И три блока из столов не умещаются в 10 метров. Иду с рулеткой в коридор, измерять размер аудитории снаружи. 16 метров. Надо все перерисовывать.
Обед. Нет, спасибо, я могу потерпеть и без обеда. Хоть полчаса, но и это может помочь.
Всё работает. Данные с положением точек зачитываются, можно еще помучиться с получением данных с сайта, а не с локальной страницы. Ищу, ищу, долго ищу. Потом где-то замечаю в настройках безопасности, что доступ к сайту разрешен только с localhost. Ну да, точно, вспоминаю, что нечно подобное есть и у MySQL. Добавляю в разрешенные хосты адрес сайта и всё сразу заработало. Ура. Всё успел. У меня еще есть больше часа.
(Надо заметить, что пока я экспериментировал с добавлением и получением данных, доступ был настроен полный для всех. Поэтому в скрипте добавления данных не указаны никакие ключи доступа. Сейчас я это выключил.)
Последние приготовления, время навалом
Всё работает, датчики получают данные, плата вычисляет положение, отсылает в облако, сайт делает визуализацию. Оставалось только прикрепить длинные провода к датчикам, чтобы разместить их далеко друг от друга в аудитории и задать их абсолютное положение в пространстве в файле настроек. Я рассчитывал разместить два датчика на краях своего стола, два датчика на кафедрах для доклада и один по центру за главным столом. Оставался целый час для этого. Времени навалом, подумал я, и стал разрывать двойные провода вдоль. Провода до первых двух датчиков должны были быть трёхметровые.
Провод разорвал. Надо сложить парный провод с одиночным, чтобы их стало три. Начинаю скручивать их вместе, через каждые полметра фиксируя скотчем. На всё это ушло 10 минут. Понятно, что такими темпами я не успею. Провод готов, но теперь надо удлинить провод от датчика, включив новые провода в разрыв. Зачищаю, скручиваю, заматываю скотчем. Подключаю к плате, датчик работает. Отлично, переходим к следующему. Так как время точно на всё не хватит, то решаю оставить только 3 датчика. С проводами 3 метра, 3 метра и 8 метров. Последний восьмиметровый уже и не фиксирую скотчем, просто немного скручиваю. Тороплюсь, а это очень плохо. Зачищаю провод, он естественно рвется. Поправил. Сделал. Подключаю датчик. А на нем светится светодиод, хотя не должен. Смотрю провода, а я +5 подал на выход сенсора, сразу на светодиод. Отлично, думаю, возможно сжег. Меняю провода правильно, датчик всё равно светится красным. Точно сжег! Беру еще один. хорошо есть запасные. Подключаю. Он тоже светится красным. Настроечный резистор не помогает. В чем дело? Разбираюсь. Оказывается неправильно подключил к плате. Ну вот, может и на плате порты сжег. Это конец, демо не будет. Еще раз всё проверяю, подключаю все правильно. И о чудо, всё работает. И как раз 16:00, начало докладов. Мой второй.
Доклады
Хорошо, что мне удалось запустить проект на Azure сайте, не надо ноут подключать для презентации. Тем более он подключен к плате через роутер и было бы сложно его отключить. Рассказываю о проекте. Показываю 3D модель аудитории с шариками, предыдущих отладочных положений источников сигнала. Хлопаю в ладоши. В логах с консоли видно, что данные пошли, перезагружаю браузер. Должна была появится новая точка, но я, честно говоря, уже не помню какие были, а какие нет. Всё.
Очень приятно выступать одним из первых, потом можно сидеть и спокойно слушать другие доклады. Доклады были разные.
Вот собственно и всё. Ах да, я стал одним из победителей хакатона и получил приз.
Общие впечатления
Понравилось. Такое большое скопление людей, которым интересно. Я пообщался с некоторыми из них.
Спасибо хакатону, а то неизвестно когда бы я занялся Intel Galileo. А уж добраться до Azure, это вообще что-то невозможное. Много всего изучено.
В общем, спасибо Microsoft и Intel.
И в завершение привожу без изменений код, который был у меня на хакатоне.
Файлы:
dist1.cpp
#include "mraa.h"
#include <ctime>
#include <ratio>
#include <chrono>
#include <vector>
#include <iostream>
#include <stdlib.h>
#include "funcs.h"
int main()
{
init();
int waitId = 0;
const int numMics = NUM;
// std::cout<<"Num sensors="<<numMics<<std::endl;
// return 0;
mraa_gpio_context gpio[numMics];
bool on[numMics];
int dist[numMics];
// std::chrono::high_resolution_clock::time_point times[numMics];
std::chrono::duration<double> times[numMics];
for( int k = 0; k < numMics; k++ )
{
gpio[k] = mraa_gpio_init(k);
mraa_gpio_dir(gpio[k],MRAA_GPIO_IN);
}
do
{
std::cout<<"Wait..."<<waitId<<std::endl;
waitId++;
for( int k = 0; k < numMics; k++ )
{
on[k] = false;
dist[k] = 0;
}
auto numOn = 0;
int currStep = 0;
std::chrono::high_resolution_clock::time_point startTime;
do
{
for( int k = 0; k < numMics; k++ )
{
if( ! on[k] )
{
int v = mraa_gpio_read(gpio[k]);
if( v == 0 )
{
std::chrono::high_resolution_clock::time_point now =
std::chrono::high_resolution_clock::now();
if( numOn == 0 )
{
currStep = 0;
startTime = now;
}
on[k] = true;
dist[k] = currStep;
times[k] = std::chrono::duration_cast<std::chrono::duration<double>
>(now-startTime);
numOn++;
}
}
}
currStep++;
} while (numOn < numMics && (currStep < 8000 || numOn < 1) );
if( currStep >= 8000 ) continue;
for( int k = 0; k < numMics; k++ )
{
printf("%d ",dist[k]);
}
printf(" === time(s): ");
for( int k = 0; k < numMics; k++ )
{
printf("%f ",times[k].count());
}
printf(" === dist(m): ");
std::vector<float> L;
for( int k = 0; k < numMics; k++ )
{
float d = 300*times[k].count();
L.push_back(d);
printf("%f ",d);
}
Point3 event = getPosition(L);
std::cout<<"Event=("<<event.x<<","<<event.y<<","<<event.z<<")"<<std::endl;
char str[300];
sprintf(str,"./test_rest_add.py %f %f %f",event.x,event.y,event.z);
std::cout<<"send data"<<std::endl;
system(str);
std::cout<<"send ok"<<std::endl;
// create dists
sleep(1);
} while (true);
for( int k = 0; k < numMics; k++ )
{
mraa_gpio_close(gpio[k]);
}
return 0;
}
funcs.h
#include <vector>
struct Point3
{
float x,y,z;
};
struct DistPos
{
Point3 p;
float dist;
};
struct Box
{
Point3 p1,p2;
float step;
};
void init();
Point3 getPosition(std::vector<float> &dists);
extern int NUM;
funcs.cpp
#include <vector>
#include <fstream>
#include <iostream>
#include "funcs.h"
#include <math.h>
std::vector<Point3> sensorPos;
int NUM = 0;
void readSensorPos()
{
if( sensorPos.size() > 0 )
{
return;
}
std::ifstream file("positions.txt");
if( ! file )
{
return;
}
int num = 0;
file>>num;
std::cout<<"Num="<<num<<std::endl;
for( int k = 0; k < num; k++ )
{
float x,y,z;
file>>x>>y>>z;
Point3 point;
point.x = x;
point.y = y;
point.z = z;
sensorPos.push_back( point );
std::cout<<"x="<<x<<" ";
std::cout<<"y="<<y<<" ";
std::cout<<"z="<<z<<std::endl;
}
NUM = num;
}
void init()
{
readSensorPos();
}
Point3 getPosition( std::vector<DistPos> dists, Box box)
{
std::cout<<"Box=(";
std::cout<<box.p1.x<<","<<box.p1.y<<","<<box.p1.z<<")-(";
std::cout<<box.p2.x<<","<<box.p2.y<<","<<box.p2.z<<")"<<std::endl;
std::cout<<"dists.size="<<dists.size()<<std::endl;
Point3 bestPoint;
bestPoint.x = 0;
bestPoint.y = 0;
bestPoint.z = 0;
float minErr = 10e20;
float normDist[NUM];
for( float tx = box.p1.x; tx <= box.p2.x; tx+= box.step )
{
for( float ty = box.p1.y; ty <= box.p2.y; ty+= box.step )
{
for( float tz = box.p1.z; tz <= box.p2.z; tz+= box.step )
{
float currErr = 0;
//std::vector<float> normDist(dists.size());
int kk = 0;
for( auto iter = dists.begin(); iter != dists.end(); iter++, kk++ )
{
float dx = iter->p.x - tx;
float dy = iter->p.y - ty;
float dz = iter->p.z - tz;
float d = sqrt(dx*dx + dy*dy + dz*dz);
normDist[kk] = d;
// float err = d - (iter->dist * iter->dist);
// currErr += err*err;
}
float minVal = normDist[0];
for( int k = 1; k < NUM; k++ )
{
if( normDist[k] < minVal )
minVal = normDist[k];
}
for( int k = 0; k < NUM; k++ )
{
normDist[k] -= minVal;
}
currErr = 0;
for( int k = 0; k < NUM; k++ )
{
float delta = normDist[k] - dists[k].dist;
currErr = delta*delta;
}
if( currErr < minErr )
{
minErr = currErr;
Point3 p;
p.x = tx;
p.y = ty;
p.z = tz;
bestPoint = p;
}
}
}
}
return bestPoint;
}
Point3 getPosition(std::vector<float> &dists)
{
Point3 p1,p2;
p1.x = -5;
p1.y = 0;
p1.z = -5;
p2.x = 5;
p2.y = 3;
p2.z = 5;
float step = 0.1;
Box box;
box.p1 = p1;
box.p2 = p2;
box.step = step;
std::cout<<"DistPos="<<std::endl;
std::vector<DistPos> distPos;
for( int k = 0; k < dists.size(); k++ )
{
DistPos dp;
dp.p = sensorPos[k];
dp.dist = dists[k];
distPos.push_back(dp);
std::cout<<"("<<dp.p.x<<","<<dp.p.y<<","<<dp.p.z<<") d="<<dp.dist<<std::endl;
}
Point3 bestPoint = getPosition( distPos, box);
return bestPoint;
}
//const int NUM = 3;
//float dist[NUM];
positions.txt
3
2 1 3
-2 1 3
0 1 5
test_rest_add.py
#!/usr/bin/python
import urllib
import urllib2
import sys
x = sys.argv[1]
y = sys.argv[2]
z = sys.argv[3]
#print x," ",y," ",z,"\n"
url = 'https://audiosprut.azure-mobile.net/tables/pos'
params = urllib.urlencode({
"x": x,
"y": y,
"z": z,
"present" : "true"
})
response = urllib2.urlopen(url, params).read()
Ссылка: audiosprut.azurewebsites.net
polybook
Потешный трейлер Майкрософта — это прекрасно! А вот то, что он убил свою Microsoft Robotics Developer Studio — очень жаль.