Movement feedback mechanism
Our goal is to allow vehicles to control movement (arcs, lines, and rotates) using data from a variety of sensors. A vehicle should be able to control the distance of travel and change in angle. Therefore the minimum sensor requirement for this is the ability to measure distance traveled and changes in direction. If a vehicle has no sensor that can measure direction or distance, it will be unable to execute a move.
There are a multitude of strategies that can be used for move-control feedback. This Wiki page attempts to examine some of those.
It is worth noting that in applications using classes higher than just Pilot, the movement requests will always come directly from the PoseController. The PoseController, however, can receive paths from the Path planner, which is the ultimate builder of path data in our API. For this reason it is worth examining the data produced by a PathPlanner.
Imagine an application in which the user is on vacation in Florida and wants to look out the front window of their home back in Oregon. They have a map of their home (see image below) and are connected to a server via the Internet that controls the robot at home. In this scenario, some obstacles (red) are in the path between the starting pose and the desired final pose. When the vehicle is done moving, it must be facing an open window.
The PathPlanner comes up with a path tailored to moves a vehicle can perform. The PathPlanner consults the MoveController class (via the PoseController which has an instance of MoveController) to see which moves are available, specifically the minimum turn radius the robot can achieve. For a differential vehicle, the data it produces and passes to PoseController is a series of moves and waypoints that looks like this:
A steering vehicle does not have a tight enough turn a radius to go through the middle, so the PathPlanner generates path data that looks like this:
The PathPlanner will want to allow for a margin of error so that inaccuracies with vehicle movement and sensors allow it to avoid walls/obstacles. This error should be gettable from sensors and actuators and propagate up the chain to the higher level classes to compute a margin of error to avoid solid objects.
Note: The PathPlanner will likely take a long time to do a space-search in order to find the most efficient path. This precludes it from computing new paths while a vehicle is on the move, unless the application decides to computer a path from some future point in a path that it is driving towards (in other words it won't reach said point before PathPlanner is able to compute that path). I don't think we want to attempt this until we are more comfortable with 1.0 functionality, so for now we should conclude PathPlanner is too slow.
PoseController interaction with MoveController
Now that the PathPlanner has given a suggested path to the PoseController, its job is done and PoseController takes over, unless it finds itself in trouble.
PoseController has no access to Map data. It only has access to Pose data via a PoseProvider. It also has an instance of MoveController, which allows it to command the vehicle to make moves.
The basic problem is to allow the vehicle to steer through each of the discrete moves shown in the diagrams above, so that it gets from one point to the next. Specific move segments are marked on the diagram above at A and B:
Here are several scenarios for how these two classes (PoseController and MoveController) interact to provide move-feedback control.
Follow the Line
With this scheme, the PoseProvider passes a whole move segment to the MoveController. The MoveController attempts to perform virtual line following with the move segment.
Data passed from PoseController to MoveController: current or proposed Move object
- Very little work for the PoseController to perform
- Simple communication between PoseController and MoveController
- Each segment generated by PathPlanner could be very long (miles)
- MoveController attempts the move in one go, which would lead to huge inaccuracies after long distances
- There is no opportunity to correct the path using positional data from a PoseProvider
Follow the Breadcrumbs
This scheme uses Pose-Based Move Control at the MoveController level. Basically the PoseController breaks up each move into a number of waypoints that are closer together. It then passes all these waypoints to the MoveController, and it attempts to steer towards each waypoint in the path. For this to method, the MoveController requires an instance of PoseProvider, which is a radical departure from our previous architecture.
Data passed from PoseController to MoveController: Pose objects (waypoints)
- Homing in on points means it is constantly correcting along the path, ensuring it arrives close to the final pose.
- MoveController requires a PoseProvider, so it is no longer as independent to use in applications on its own for driving around.
- MoveController is now only able to navigate waypoints, not really drive discrete moves like travel(100);
- The spacing between waypoints is rather arbitrary. Should they be 10 cm apart? What if the vehicle is huge like a car?
One breadcrumb at a time
With this scheme the PoseController looks at the current Pose via its PoseProvider, then it looks at the next waypoint, and calculates the Move required to get approximately to the next waypoint. PoseController could update MoveController after an increment of distance (every 10 cm) or an increment of time (every second).
Data passed from PoseController to MoveController: current Move object
- MoveController is allowed to be a MoveController again, and make discrete movements.
- Communication between PoseController and MoveController is frequent but simple.
- To maintain continuous movement, it's somewhat tricky to determine when it should calculate the next move and pass it to the MoveController.
- The spacing between waypoints is still pretty arbitrary.
- Alternately if it updates the MoveController with a new Move periodically (say every second) then it is still arbitrary.
Steering Ratio Move Control
This scheme is based on the idea Roger had that path should be controlled only by direction, which in practical terms is dictated by the vehicles steering ratio. Both differential and steering vehicles can use steering ratio, and steering ratio can produce rotates on the spot for differential vehicles. The PoseController monitors Pose using PoseProvider, and as frequently as it can it updates the steering ratio to get it back on track towards the target, perhaps using PID control.
Data passed from PoseController to MoveController: steering ratio value
- No arbitrariness in distances or times. PoseProvider just tells vehicle to start and corrects steering ratio as it moves.
- Communication between PoseController and MoveController is frequent but simple.
- Steering vehicles might require less kinematic information, which could simplify Steering constructors
- PoseProvider performs virtual line following? This is new territory for us.
- A differential vehicle making rotates can't do continuous movement. DifferentialMoveController would need to call stop() on itself first..
- When a Differential vehicle in a maze reaches the end of a tight corridor, PoseController might not react quickly enough to stop it before it collides.
How does PathPlanner interact with ObstacleDetector? There could be two different actions taken, indicating a sort of yellow alert and red alert mode. PathPlanner passes the path once at the start with all the data to get to the destination (poses and moves). The vehicle starts following the path in green mode, until it encounters something unexpected that wasn't in the map data. Then it goes into yellow alert mode and does some sort of shallow-thinking low level attempt to get around the obstacle and rejoin the previous path. It could wall-follow the obstacle until it rejoins the path on the other side, at which point it goes back to green mode and continues on. If it fails to reconnect with the path and ends up wandering against a barrier in one direction for a long time, it realizes it is screwed and goes to red alert, which means it stops, tells the PathPlanner it needs a new path calculated from the current position (taking into account the previous barrier it encountered) and off it goes to try again.