Рука-робот своими руками: Робототехника для начинающих

IT-копирайтер
Время чтения: 8 минут
Недавно у нашего PHP-отдела родилась идея развлечь коллег — провести в компании конкурс на самого умелого «пилота»… руки-робота, управляемой через интернет. Конкурс вызвал восторг у всех сотрудников, поэтому мы решили поделиться рецептом на случай, если вы захотите удивить своих друзей и команду.
Что и как делает робот?
Такая рука-робот может совершать простые действия, получая команды через интернет: доставать лотерейные билеты, передвигать предметы, даже заваривать чай — все зависит от вашей фантазии и изобретательности.
В нашем случаем с помощью руки нужно было переносить предметы с игрового поля в специальный контейнер. Все сотрудники компании получили доступ к веб-странице, с которой по очереди могли управлять роботом и наблюдать за действиями робота через веб-камеру.
Вам понадобятся
- Микроконтроллер TI Stellaris Launchpad;
- Два сервопривода (один поворотный, один на подъем руки) Hitec HS-322;
- Сервопривод для клешни TowerPro SG90.
Процесс работы
Проект состоял из нескольких этапов:
- Заказ микроконтроллеров и сервоприводов.
- Сборка деталей:рука, подставка и др.
- Написание и тестирование прошивки (Energia IDE) и серверной части (RubyOnRails + CofeeScript).
- Тестирование и поиск ошибок.
Этап 1. Заказ микроконтроллеров и сервоприводов
Все лучше заказать заранее на ebay.com или aliexpress.com, т. к. в ближайшем магазине нужных деталей может не оказаться. В Новосибирске могу порекомендовать devicter.ru и hobbymarket.ru.
Этап 2. Сборка
Когда все необходимые детали подготовлены, можно собирать основную часть устройства — руку. Нам ее удалось собрать только со второй попытки: первый раз — из ДВП, но рука не прошла испытание, второй раз — уже из ПВХ.
Этап 3. Прошивка
Прошивка была разработана в Energia IDE. В первой версии прошивки рука могла перемещаться на 5 градусов. Позже было принято решение, что рука будет просто принимать новое значение положения и переходить к нему немедленно.
Тестирование
Существует множество инструментов для того, что бы понять, где именно происходит ошибка и исправить ее. Например, для проверки вы можете использовать лог файлов приложения. RubyOnRails будет автоматически отображать сведения отладки. Можно также заносить свои собственные сообщения непосредственно в файл журнала из кода с помощью класса Ruby logger.
Серверная часть
После того как механика и электроника были протестированы, осталась самая сложная часть — серверная.
1. Авторизация
При входе пользователю предлагалось пройти авторизацию:
class HomeController < ApplicationController before_filter :need_auth def index define_mode end end
Авторизация пользователей по доменным учетным записям (ldap):
class UsersController < ApplicationController before_filter :check_request_method, :except => [ :sign_up, :logout ] def sign_up username = params[:username] password = params[:password] ldap_user = get_ldap_user(username, password) return do_respond(:wrong_credentials) if ldap_user.empty? user = User.get_by_names ldap_user[:first_name], ldap_user[:last_name] unless user.nil? authorize_user user return render_need_email if ldap_user[:email].nil? and user.email.nil? return do_respond(:choose_queue) end user = User.new user.email = ldap_user[:email] unless ldap_user[:email].nil? user.first_name = ldap_user[:first_name] user.last_name = ldap_user[:last_name] if user.save authorize_user user return render_need_email if user.email.nil? return do_respond :choose_queue else return do_respond :wrong_credentials end end def update_email if request.method == 'GET' return render_sign_up end if params[:email].empty? return render_need_email end email = params[:email] user = self.current_user return do_respond :wrong_credentials if user.nil? user.update_attribute('email', email) return do_respond :choose_queue end def logout session[:user] = nil respond_to do |f| f.html { redirect_to root_url } end end private def get_ldap_user(username, password) ldap = Net::LDAP.new( :host => 'example.com', :auth => { :method => :simple, :username => 'TEST\\' + username, :password => password } ) @ldap_user = {} if ldap.bind ldap_base = "cn=users,dc=test,dc=test,dc=com" ldap_filter = Net::LDAP::Filter.eq( "samaccountname", username ) ldap.search(:base => ldap_base, :filter => ldap_filter, :return_result => true ) do |entry| @ldap_user[:first_name] = entry[:givenname][0] unless entry[:givenname].nil? and entry[:givenname][0].nil? @ldap_user[:last_name] = entry[:sn][0] unless entry[:sn].nil? and entry[:sn][0].nil? @ldap_user[:email] = entry[:mail][0] unless entry[:mail].nil? and entry[:mail][0].nil? end end @ldap_user end def check_request_method if request.method == 'GET' respond_to do |f| f.html { redirect_to get_out_and_come_in_correctly and return } end end end end
2. Доступ к управлению
Далее пишем часть кода, которая непосредственно отвечает за подачу команд руке:
require 'fileutils' class PlayController < ApplicationController #receive request from client (web-browser) def do_command # if current user can't play we send false response wich will be handled on client-side return false_json_response unless can_play? write_command(params) respond_to do |f| f.json { render :json => { :status => 'Ok' } } # it is not required to set anything in json block, you can pass just empty hash like this: {} end end # check if user can still play # return true or false (what to do in each of these cases is your own deal) def check_can_play respond_to do |f| f.json { render :json => can_play? } end end private # this method sends command to the hand def write_command(params) # defining which of serves should be moved serva_num = params[:serva_num] command = params[:command].rjust(3, '0') # hand is device actually; we are going to write into it like into file device = '/dev/ttyACM0' File.open(device, 'w') { |f| f.write(serva_num + command + "\n") } end end
Самой сложной задачей было определить промежуток времени, когда очередной пользователь должен получать доступ к управлению, т.к. доступ к управлению роботом в определенный отрезок времени должен был быть только у одного пользователя. Однако, вопреки ожиданиям, задача была решена быстро.
class ApplicationController < ActionController::Base protect_from_forgery def current_user return session[:user] end # check if current user signed in def signed_in? !self.current_user.nil? end # check if current user is admin def is_admin? signed_in? and current_user.role == User::ROLE_ADMIN end # check if it is time to play for current user def its_time? is_queued = false queues = UserQueue.all now = ((Time.now + Time.now.gmt_offset).utc.to_f * 1000).to_i queues.each do |queue| time_start = (queue.time_start.to_f * 1000).to_i time_end = (queue.time_end.to_f * 1000).to_i if !queue.user.nil? and queue.user.id == self.current_user.id and (time_start..time_end).include?(now) is_queued = true break end end is_queued end # check if current user still can play def can_play? return true if is_admin? return true if its_time? false end helper_method :current_user, :signed_in?, :is_admin?, :its_time? end
Этап 4. Тестирование робота
Когда все материалы собраны, а код написан, можно переходить к тестированию продукта.
Дебаггер Ruby позволяет останавливать выполнение процесса в любой точке кода, исправлять ошибку, а затем продолжать выполнение. С предметной областью приложения можно работать из консоли: проверять домен, совершать изменения и сохранять их в базе данных.
Подводя итоги
В первый день конкурса наш робот выдержал более 40 испытаний. За весь день произошла всего одна поломка, но и она была решена в течении 5 минут, не повлияв на ход конкурса. Надеемся, что наш опыт пробудит в вас интерес к робототехнике и положит начало вашим собственным экспериментам.
Комментарии