LCOV - code coverage report
Current view: top level - src/Kinematics - RobotController.cpp (source / functions) Hit Total Coverage
Test: HLR Lines: 1 266 0.4 %
Date: 2018-01-24 12:42:21 Functions: 2 25 8.0 %

          Line data    Source code
       1             : #include "RobotController.hpp"
       2             : 
       3             : #include "HLR/ObjectMoveStart.h"
       4             : #include "Planners/InterpolatingAnglePlanner.hpp"
       5             : #include "Planners/InterpolatingEuclideanPlanner.hpp"
       6             : 
       7             : namespace HLR
       8             : {
       9             : namespace Kinematics
      10             : {
      11           0 : std::string robot_controller_state_to_string(RobotControllerState state)
      12             : {
      13           0 :     switch (state)
      14             :     {
      15           0 :         case RobotControllerState::idle:
      16           0 :             return "idle";
      17           0 :         case RobotControllerState::moving_to_ready:
      18           0 :             return "moving_to_ready";
      19           0 :         case RobotControllerState::calculating_path:
      20           0 :             return "calculating_path";
      21           0 :         case RobotControllerState::giving_cup:
      22           0 :             return "giving_cup";
      23           0 :         case RobotControllerState::cancelling:
      24           0 :             return "cancelling";
      25           0 :         case RobotControllerState::stopped:
      26           0 :             return "stopped";
      27             :     }
      28             : 
      29           0 :     return "Invalid state.";
      30             : }
      31             : 
      32           0 : std::string arm_error_to_string(ArmError error)
      33             : {
      34           0 :     switch (error)
      35             :     {
      36           0 :         case ArmError::safety_concern:
      37           0 :             return "safety_concern";
      38           0 :         case ArmError::no_path_found:
      39           0 :             return "no_path_found";
      40           0 :         case ArmError::driver_error:
      41           0 :             return "driver_error";
      42             :     }
      43             : 
      44           0 :     return "Invalid error.";
      45             : }
      46             : 
      47           0 : RobotController::RobotController(RobotDriverHandler& driver) : driver(driver)
      48             : {
      49           0 :     this->driver.set_move_complete_handler(
      50           0 :         [this]() { this->position_reached(); });
      51             : 
      52           0 :     this->events.push(Event::move_to_ready);
      53           0 :     this->worker = std::thread(&RobotController::event_handler, this);
      54           0 : }
      55             : 
      56           0 : RobotController::~RobotController()
      57             : {
      58           0 :     this->events.push(Event::terminate);
      59           0 :     this->worker.join();
      60           0 : }
      61             : 
      62           0 : RobotControllerState RobotController::get_state() const
      63             : {
      64           0 :     std::lock_guard<std::mutex> l(this->state_m);
      65           0 :     return this->state;
      66             : }
      67             : 
      68           0 : void RobotController::pick_up_cup(std::uint32_t id)
      69             : {
      70           0 :     std::lock_guard<std::mutex> l(this->state_m);
      71           0 :     if (this->state != RobotControllerState::idle)
      72             :     {
      73           0 :         throw std::logic_error("Robot is not currently idle.");
      74             :     }
      75             : 
      76             :     {
      77           0 :         std::lock_guard<std::recursive_mutex> l(this->objects_m);
      78           0 :         const auto it = this->objects.find(id);
      79           0 :         if (it == this->objects.end())
      80             :         {
      81             :             throw std::invalid_argument(
      82           0 :                 "No object found with the specified id.");
      83             :         }
      84             : 
      85           0 :         if (!it->second.is_cup)
      86             :         {
      87             :             throw std::invalid_argument(
      88           0 :                 "Specifed object exists but is not a cup.");
      89             :         }
      90           0 :         this->cup_position = it->second.position;
      91             :     }
      92           0 :     this->set_state(RobotControllerState::calculating_path);
      93           0 :     this->events.push(Event::pick_up_cup);
      94           0 : }
      95             : 
      96           0 : void RobotController::move_stop()
      97             : {
      98           0 :     std::lock_guard<std::mutex> l(this->state_m);
      99           0 :     switch (this->state)
     100             :     {
     101           0 :         case RobotControllerState::moving_to_ready:
     102             :         case RobotControllerState::calculating_path:
     103             :         case RobotControllerState::giving_cup:
     104           0 :             this->set_state(RobotControllerState::stopped);
     105           0 :             this->events.push(Event::stop);
     106           0 :             break;
     107           0 :         default:
     108             :             throw std::logic_error(
     109           0 :                 "Robot is not currently moving or movement is cancelling.");
     110             :     }
     111           0 : }
     112             : 
     113           0 : void RobotController::move_continue()
     114             : {
     115           0 :     std::lock_guard<std::mutex> l(this->state_m);
     116           0 :     switch (this->state)
     117             :     {
     118           0 :         case RobotControllerState::stopped:
     119           0 :             this->set_state(this->prev_state);
     120           0 :             this->events.push(Event::resume);
     121           0 :             break;
     122           0 :         default:
     123           0 :             throw std::logic_error("Robot is not currently stopped.");
     124             :     }
     125           0 : }
     126             : 
     127           0 : void RobotController::move_cancel()
     128             : {
     129           0 :     std::lock_guard<std::mutex> l(this->state_m);
     130             :     // Can't cancel a move to ready.
     131           0 :     if ((this->state == RobotControllerState::stopped
     132           0 :          && this->prev_state == RobotControllerState::moving_to_ready)
     133           0 :         || this->state == RobotControllerState::moving_to_ready)
     134             :     {
     135           0 :         throw std::logic_error("Can't cancel a move to ready.");
     136             :     }
     137             : 
     138           0 :     switch (this->state)
     139             :     {
     140           0 :         case RobotControllerState::stopped:
     141             :         case RobotControllerState::giving_cup:
     142             :         case RobotControllerState::calculating_path:
     143           0 :             this->set_state(RobotControllerState::cancelling);
     144           0 :             this->events.push(Event::cancel);
     145           0 :             break;
     146           0 :         default:
     147             :             throw std::logic_error(
     148           0 :                 "Robot is not currently stopped or moving to a position.");
     149             :     }
     150           0 : }
     151             : 
     152           0 : void RobotController::remove_object(const EnvironmentObject& obj)
     153             : {
     154           0 :     std::lock_guard<std::recursive_mutex> l(this->objects_m);
     155           0 :     const auto it = this->objects.find(obj.id);
     156           0 :     if (it != this->objects.end())
     157             :     {
     158           0 :         this->objects.erase(it);
     159             :     }
     160           0 : }
     161             : 
     162           0 : void RobotController::add_object(EnvironmentObject&& obj)
     163             : {
     164           0 :     std::lock_guard<std::recursive_mutex> l(this->objects_m);
     165           0 :     this->remove_object(obj);
     166           0 :     const auto id = obj.id;
     167           0 :     this->objects.insert(std::make_pair(id, std::move(obj)));
     168           0 : }
     169             : 
     170           0 : void RobotController::set_state(RobotControllerState state)
     171             : {
     172           0 :     this->prev_state = this->state;
     173           0 :     this->state = state;
     174           0 :     if (this->state_callback)
     175             :     {
     176           0 :         this->state_callback(state);
     177             :     }
     178           0 : }
     179             : 
     180           0 : void RobotController::set_error_callback(
     181             :     const std::function<void(ArmError)>& cb)
     182             : {
     183           0 :     this->error_callback = cb;
     184           0 : }
     185             : 
     186           0 : void RobotController::set_state_change_callback(
     187             :     const std::function<void(RobotControllerState)>& cb)
     188             : {
     189           0 :     this->state_callback = cb;
     190           0 : }
     191             : 
     192           0 : void RobotController::position_reached()
     193             : {
     194           0 :     std::lock_guard<std::mutex> l(this->state_m);
     195           0 :     switch (this->state)
     196             :     {
     197           0 :         case RobotControllerState::moving_to_ready:
     198           0 :             this->set_state(RobotControllerState::idle);
     199           0 :             break;
     200           0 :         case RobotControllerState::giving_cup:
     201           0 :             this->set_state(RobotControllerState::moving_to_ready);
     202           0 :             this->events.push(Event::move_to_ready);
     203           0 :             break;
     204           0 :         default:
     205           0 :             return;
     206             :     }
     207             : }
     208             : 
     209           0 : void RobotController::event_handler()
     210             : {
     211           0 :     bool term = false;
     212           0 :     while (!term)
     213             :     {
     214           0 :         auto ev = this->events.pop();
     215           0 :         switch (ev)
     216             :         {
     217           0 :             case Event::move_to_ready:
     218           0 :                 this->move_to_ready_handler();
     219           0 :                 break;
     220           0 :             case Event::pick_up_cup:
     221           0 :                 this->pick_up_cup_handler();
     222           0 :                 break;
     223           0 :             case Event::stop:
     224           0 :                 this->stop_handler();
     225           0 :                 break;
     226           0 :             case Event::resume:
     227           0 :                 this->resume_handler();
     228           0 :                 break;
     229           0 :             case Event::cancel:
     230           0 :                 this->cancel_handler();
     231           0 :                 break;
     232           0 :             case Event::terminate:
     233           0 :                 term = true;
     234           0 :                 break;
     235             :         }
     236             :     }
     237           0 : }
     238             : 
     239           0 : void RobotController::move_to_ready_handler()
     240             : {
     241           0 :     const Planners::InterpolatingAnglePlanner planner(this->angle_step_size);
     242             :     auto path_opt =
     243           0 :         planner.get_path(this->driver.get_arm_state(), this->ready_pos);
     244           0 :     if (!path_opt.has_value())
     245             :     {
     246           0 :         if (this->error_callback)
     247             :         {
     248           0 :             this->error_callback(ArmError::no_path_found);
     249             :         }
     250           0 :         this->set_state(RobotControllerState::idle);
     251           0 :         return;
     252             :     }
     253             : 
     254             :     try
     255             :     {
     256           0 :         this->driver.move(path_opt.value(), this->speed);
     257             :     }
     258           0 :     catch (const std::runtime_error& e)
     259             :     {
     260           0 :         if (this->error_callback)
     261             :         {
     262           0 :             this->error_callback(ArmError::driver_error);
     263             :         }
     264             :     }
     265             : }
     266             : 
     267           0 : void RobotController::pick_up_cup_handler()
     268             : {
     269             :     // The different planners that are used.
     270             :     const Planners::InterpolatingAnglePlanner angle_planner(
     271           0 :         this->angle_step_size);
     272             :     const Planners::InterpolatingEuclideanPlanner euclidean_planner(
     273           0 :         this->euclidean_step_size);
     274             : 
     275             :     /**
     276             :      * The intermediate positions. The following positons are computed:
     277             :      *      1. Just prior to pickup up the cup.
     278             :      *      2. Move gripper around cup.
     279             :      *      3. Move gripper upwards to lock in cup.
     280             :      *      4. Move gripper with cup above the target position.
     281             :      *      5. Move gripper down so cup will stand on the target position.
     282             :      *      6. Move gripper back a little bit so the gripper no longer surrounds
     283             :      * the cup.
     284             :      */
     285           0 :     Matrix<double, 3, 1> ignore_y(this->cup_position);
     286           0 :     ignore_y[1][0] = 0;
     287           0 :     auto magnitude = ignore_y.get_magnitude();
     288           0 :     auto scalefactor = (magnitude - this->distance_from_cup) / magnitude;
     289             : 
     290           0 :     Matrix<double, 3, 1> pre_cup_position(this->cup_position);
     291           0 :     pre_cup_position[0][0] *= scalefactor;
     292           0 :     pre_cup_position[2][0] *= scalefactor;
     293             : 
     294           0 :     Matrix<double, 3, 1> lift_cup_postion(this->cup_position);
     295           0 :     lift_cup_postion[1][0] += lift_distance;
     296             : 
     297           0 :     Matrix<double, 3, 1> target_lift_position(this->target_position);
     298           0 :     target_lift_position[1][0] += lift_distance;
     299             : 
     300           0 :     ignore_y = this->target_position;
     301           0 :     ignore_y[1][0] = 0;
     302           0 :     magnitude = ignore_y.get_magnitude();
     303           0 :     scalefactor = (magnitude - this->distance_from_cup) / magnitude;
     304           0 :     Matrix<double, 3, 1> post_target_position(this->target_position);
     305           0 :     post_target_position[0][0] *= scalefactor;
     306           0 :     post_target_position[2][0] *= scalefactor;
     307             : 
     308             :     // All path parts.
     309             :     const auto to_pre_cup_opt =
     310           0 :         angle_planner.get_path(this->driver.get_arm_state(), pre_cup_position);
     311           0 :     if (!to_pre_cup_opt.has_value())
     312             :     {
     313           0 :         if (this->error_callback)
     314             :         {
     315           0 :             this->error_callback(ArmError::no_path_found);
     316             :         }
     317           0 :         this->set_state(RobotControllerState::idle);
     318           0 :         return;
     319             :     }
     320             : 
     321             :     const auto to_cup_opt = euclidean_planner.get_path(
     322           0 :         to_pre_cup_opt.value().back(), this->cup_position);
     323           0 :     if (!to_cup_opt.has_value())
     324             :     {
     325           0 :         if (this->error_callback)
     326             :         {
     327           0 :             this->error_callback(ArmError::no_path_found);
     328             :         }
     329           0 :         this->set_state(RobotControllerState::idle);
     330           0 :         return;
     331             :     }
     332             : 
     333             :     const auto lift_cup_opt =
     334           0 :         euclidean_planner.get_path(to_cup_opt.value().back(), lift_cup_postion);
     335           0 :     if (!lift_cup_opt.has_value())
     336             :     {
     337           0 :         if (this->error_callback)
     338             :         {
     339           0 :             this->error_callback(ArmError::no_path_found);
     340             :         }
     341           0 :         this->set_state(RobotControllerState::idle);
     342           0 :         return;
     343             :     }
     344             : 
     345             :     const auto target_lift_opt = angle_planner.get_path(
     346           0 :         lift_cup_opt.value().back(), target_lift_position);
     347           0 :     if (!target_lift_opt.has_value())
     348             :     {
     349           0 :         if (this->error_callback)
     350             :         {
     351           0 :             this->error_callback(ArmError::no_path_found);
     352             :         }
     353           0 :         this->set_state(RobotControllerState::idle);
     354           0 :         return;
     355             :     }
     356             : 
     357             :     const auto target_opt = euclidean_planner.get_path(
     358           0 :         target_lift_opt.value().back(), this->target_position);
     359           0 :     if (!target_opt.has_value())
     360             :     {
     361           0 :         if (this->error_callback)
     362             :         {
     363           0 :             this->error_callback(ArmError::no_path_found);
     364             :         }
     365           0 :         this->set_state(RobotControllerState::idle);
     366           0 :         return;
     367             :     }
     368             : 
     369             :     const auto post_target_opt = euclidean_planner.get_path(
     370           0 :         target_opt.value().back(), post_target_position);
     371           0 :     if (!post_target_opt.has_value())
     372             :     {
     373           0 :         if (this->error_callback)
     374             :         {
     375           0 :             this->error_callback(ArmError::no_path_found);
     376             :         }
     377           0 :         this->set_state(RobotControllerState::idle);
     378           0 :         return;
     379             :     }
     380             : 
     381             :     // Move all parts into single vector.
     382           0 :     std::vector<Matrix<double, 5, 1>> path;
     383           0 :     path.reserve(to_pre_cup_opt.value().size() + to_cup_opt.value().size()
     384           0 :                  + lift_cup_opt.value().size() + target_lift_opt.value().size()
     385           0 :                  + target_opt.value().size() + post_target_opt.value().size());
     386           0 :     std::move(to_pre_cup_opt.value().begin(),
     387           0 :               to_pre_cup_opt.value().end(),
     388           0 :               std::back_inserter(path));
     389           0 :     std::move(to_cup_opt.value().begin(),
     390           0 :               to_cup_opt.value().end(),
     391           0 :               std::back_inserter(path));
     392           0 :     std::move(lift_cup_opt.value().begin(),
     393           0 :               lift_cup_opt.value().end(),
     394           0 :               std::back_inserter(path));
     395           0 :     std::move(target_lift_opt.value().begin(),
     396           0 :               target_lift_opt.value().end(),
     397           0 :               std::back_inserter(path));
     398           0 :     std::move(target_opt.value().begin(),
     399           0 :               target_opt.value().end(),
     400           0 :               std::back_inserter(path));
     401           0 :     std::move(post_target_opt.value().begin(),
     402           0 :               post_target_opt.value().end(),
     403           0 :               std::back_inserter(path));
     404             : 
     405             :     {
     406           0 :         std::lock_guard<std::mutex> l(this->state_m);
     407           0 :         this->set_state(RobotControllerState::giving_cup);
     408             :     }
     409             : 
     410             :     try
     411             :     {
     412           0 :         this->driver.move(path, this->speed);
     413             :     }
     414           0 :     catch (const std::runtime_error& e)
     415             :     {
     416           0 :         if (this->error_callback)
     417             :         {
     418           0 :             this->error_callback(ArmError::driver_error);
     419             :         }
     420             :     }
     421             : }
     422             : 
     423           0 : void RobotController::stop_handler()
     424             : {
     425             :     try
     426             :     {
     427           0 :         this->driver.stop();
     428             :     }
     429           0 :     catch (const std::runtime_error& e)
     430             :     {
     431             :     }
     432           0 : }
     433             : 
     434           0 : void RobotController::resume_handler()
     435             : {
     436           0 :     std::vector<Matrix<double, 5, 1>> remaining_path = this->driver.get_path();
     437           0 :     const auto size = this->driver.get_offset_current_move();
     438             : 
     439           0 :     remaining_path.erase(remaining_path.begin() + size, remaining_path.end());
     440             :     try
     441             :     {
     442           0 :         this->driver.move(remaining_path, this->speed);
     443             :     }
     444           0 :     catch (const std::runtime_error& e)
     445             :     {
     446           0 :         if (this->error_callback)
     447             :         {
     448           0 :             this->error_callback(ArmError::driver_error);
     449             :         }
     450             :     }
     451           0 : }
     452             : 
     453           0 : void RobotController::cancel_handler()
     454             : {
     455           0 :     std::vector<Matrix<double, 5, 1>> remaining_path = this->driver.get_path();
     456           0 :     const auto size = this->driver.get_offset_current_move();
     457             : 
     458           0 :     remaining_path.erase(remaining_path.end() - size, remaining_path.end());
     459           0 :     std::reverse(remaining_path.begin(), remaining_path.end());
     460             :     try
     461             :     {
     462           0 :         this->driver.move(remaining_path, this->speed);
     463             :     }
     464           0 :     catch (const std::runtime_error& e)
     465             :     {
     466           0 :         if (this->error_callback)
     467             :         {
     468           0 :             this->error_callback(ArmError::driver_error);
     469             :         }
     470             :     }
     471           0 : }
     472             : 
     473             : } // namespace Kinematics
     474           3 : } // namespace HLR

Generated by: LCOV version 1.12