Checkpoint a whole of of new commenting on the planner. Not done yet.

This commit is contained in:
Alden Hart
2016-11-30 18:54:18 -05:00
parent d0c236dc10
commit 5214a7e2a1
8 changed files with 327 additions and 85 deletions
Executable → Regular
-3
View File
@@ -3,9 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "g2core", "g2core\g2core.cppproj", "{44EA8FEC-55D7-4149-8A78-A574FC26BF51}"
ProjectSection(ProjectDependencies) = postProject
{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0} = {D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}
EndProjectSection
EndProject
Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "Motate", "Motate\Motate.cppproj", "{D7779B24-5CD6-4A1B-893C-2CAE8CF7A3A0}"
EndProject
+7 -7
View File
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProjectVersion>7.0</ProjectVersion>
<ToolchainName>com.Atmel.ARMGCC.CPP</ToolchainName>
<ProjectGuid>{44ea8fec-55d7-4149-8a78-a574fc26bf51}</ProjectGuid>
<avrdevice>ATSAMS70N19</avrdevice>
<avrdevice>ATSAM3X8C</avrdevice>
<avrdeviceseries>none</avrdeviceseries>
<OutputType>Executable</OutputType>
<Language>CPP</Language>
@@ -68,12 +68,12 @@
<com_atmel_avrdbg_tool_atmelice>
<ToolOptions>
<InterfaceProperties>
<SwdClock>10000000</SwdClock>
<SwdClock>2000000</SwdClock>
</InterfaceProperties>
<InterfaceName>SWD</InterfaceName>
</ToolOptions>
<ToolType>com.atmel.avrdbg.tool.atmelice</ToolType>
<ToolNumber>J41800006366</ToolNumber>
<ToolNumber>J41800030015</ToolNumber>
<ToolName>Atmel-ICE</ToolName>
</com_atmel_avrdbg_tool_atmelice>
<UseGdb>True</UseGdb>
@@ -100,9 +100,9 @@
<HWProgramCounterSampling>True</HWProgramCounterSampling>
</PercepioTrace>
<preserveEEPROM>true</preserveEEPROM>
<avrtoolserialnumber>J41800006366</avrtoolserialnumber>
<avrdeviceexpectedsignature>0xA11D0A00</avrdeviceexpectedsignature>
<avrtoolinterfaceclock>10000000</avrtoolinterfaceclock>
<avrtoolserialnumber>J41800030015</avrtoolserialnumber>
<avrdeviceexpectedsignature>0x284E0A60</avrdeviceexpectedsignature>
<avrtoolinterfaceclock>2000000</avrtoolinterfaceclock>
<custom>
<ToolOptions xmlns="">
<InterfaceProperties>
+1 -1
View File
@@ -21,7 +21,7 @@
#ifndef G2CORE_INFO_H_ONCE
#define G2CORE_INFO_H_ONCE
#define G2CORE_FIRMWARE_BUILD 100.12 // Fix UART flow control
#define G2CORE_FIRMWARE_BUILD 100.12 // Changes to USB serial for SAMS70 and SAM8X targets
#ifdef GIT_VERSION
#define G2CORE_FIRMWARE_BUILD_STRING GIT_VERSION
#else
+171 -49
View File
@@ -49,86 +49,186 @@ static void _init_forward_diffs(float v_0, float v_1);
/*************************************************************************
* mp_plan_move() - call ramping function to plan moves ahead of the exec
*
* This should NOT normally be called directly! Instead call st_request_plan_move().
* mp_plan_move() performs just-in-time forward planning immediately before
* lines and commands are queued to the move execution runtime (exec).
* It examines the currently running buffer and its adjacent buffers to:
* - Stop the system from re-planning or planning something that's not prepped
* - Plan the next available ALINE (movement) block past the COMMAND blocks
* - Skip past/ or pre-plan COMMAND blocks while labeling them as PLANNED
*
* Returns STAT_OK if exec should be called to start (or continue) movement,
* or exit with no action (STAT_NOOP) if exec does not need to be called.
*
**** WARNING ****
**** This function should NOT be called directly! Instead call
**** st_request_plan_move(), which mediates access. Mp_plan_move() is called
**** aggressively from multiple places and multiple interrupt levels,
**** and has a relatively low interrupt level to call its own.
*/
/*
* Forward Planning Background
*
* - Forward planning only occurs once, JIT just ahead of the exec
Forward planning only ever originates from the run buffer (mb.r), only occurs once for each block
* -
* - 'Bootstrap' refers to startup condition w/buffers arriving before movement starts
* - see planner.h / bufferState enum for shorthand used
* - The
*/
/*
* Forward Planning Cases
*
*
* CASE:
* 0. NOT_PREPPED. No lines or commands in planner buffer. Exit with no action
* This case also handles case before a run buffer can be assigned
*
* 1. Bootstrap cases, lines only, N lines in buffer, 2 blocks (JIT planning queue only has plan and run)
* run_buffer next_buffer
* a. NOT_PREPPED don't care Action: exit no action (backplanner is still running)
* b. PREPPED don't care Action: plan line, exit OK (prepped means backplanned)
* c. PLANNED don't care Action: exit no action (cannot plan next buffer beyond current PLANNED buffer)
*
* 1'. Bootstrap cases, lines only, N lines in buffer, N blocks (deeper JIT planning queue)
* run_buffer next_buffer
* a. NOT_PREPPED don't care Action: exit no action (backplanner is still running)
* b. PREPPED don't care Action: plan line, exit OK (prepped means backplanned)
* c. PLANNED, NOT_PREPPED Action: exit no action (cannot plan next buffer beyond current PLANNED buffer)
* d. PLANNED, PREPPED Action: plan the next block into the next planner block if the
* next planner block is NOT the run BLOCK. This can iterate to more blocks
*
* 2. Running cases, lines only, N lines in buffer, 2 blocks
* run_buffer next_buffer
* a. RUNNING NOT_PREPPED Action: exit no action (backplanner is still running)
* b. RUNNING PREPPED Action: plan buffer, exit OK (do a JIT plan)
* c. RUNNING PLANNED Action: exit no action
* 3. Bootstrap cases, lines and commands mixed, N lines in buffer, 2 blocks
(Note: NOT_PREPPED can be either line or command)
* run_buffer next/last buffer
* a. NOT_PREPPED (any state) Action: exit no action
* b. PREPPED-LINE (any state) Action: plan line, exit OK
* c. PLANNED-LINE NOT_PREPPED Action: exit no action (cannot plan next buffer beyond current PLANNED buffer)
* d. PLANNED-LINE PREPPED Action: plan the next block into the next planner block if the
* a. PREPPED-CMD(s) NOT_PREPPED Action: set contiguous PREPPED-COMMANDs to PLANNED, exit OK
* b. PREPPED-CMD(s) PREPPED-LINE Action: set contiguous PREPPED-COMMANDs to PLANNED,
* carry forward initial exit_velocity (see note *)
* iterate to PREPPED-LINE, plan line, exit OK.
*
* (*) Use mr.entry_velocity for the run velocity, which is almost always 0, but could be non-0 in a race condition.
*
* 4. Running cases, lines and commands mixed, N lines in buffer, 2 blocks
* All running cases start with first block is running (Note: NOT_PREPPED can be either line or command)
* run_buffer next N buffers terminating buffer
* a. RUNNING-LINE PREPPED-COMAND(s) NOT_PREPPED Action: Call mp_plan_command(), exit OK
* b. RUNNING-LINE PREPPED-COMAND(s) PREPPED-LINE Action: Mark all commands as PLANNED and exit OK
* Plan PREPPED-LINE using exit_velocity of run block
* (We trust exit velocity here because the backplanner has already handled this)
* c. RUNNING-LINE PREPPED-LINE (identical to 2b) Action: plan buffer, exit OK (do a JIT plan)
* d. RUNNING-LINE PLANNED-LINE (identical to 2c) Action: exit no action
*
* e. RUNNING-LINE PLANNED-COMMAND9s) Action: iterate over the PLANNED and PREPPED commands, skipping PLANNED
* f. PLANNED-COMMAND (anything)
*
* g. RUNNING-COMMAND (anything) as cases 4a - 4e, but use mr.entry_velocity
*/
/* PSEUDOCODE
*
*
* Test the running buffer for early exit conditions.
* (In the motion startup (bootstrap) condition the "running" block is not actually running yet)
* If nothing is actually running, ext immediately (prevents a race condition)
*
* Test the planning buffer
* Explanation: There are 2 cases where this can occur:
(1) nothing is actually running
(2) something is running
The case changes which buffer is passed to calculate _ramps(), and what the entry_velocity is.
* Code: See if the running buffer is -in-fact- running
* If so, move off the running buffer to the next buffer
* Set the entry velocity for be the exit velocity of the running block
*
*/
static stat_t _plan_command(mpBuf_t *bf)
{
bf->buffer_state = MP_BUFFER_PLANNED; // report that we "planned" something...
return (STAT_OK);
}
stat_t mp_plan_move()
{
mpBuf_t *bf;
// NULL means nothing's running - this is OK
if ((bf = mp_get_run_buffer()) == NULL) {
// Examine current running buffer for early exit conditions
if ((bf = mp_get_run_buffer()) == NULL) { // NULL means nothing's running - this is OK
st_prep_null();
return (STAT_NOOP);
}
if (bf->buffer_state < MP_BUFFER_PREPPED) {
// Get outta here.
// We did nothing
return (STAT_NOOP);
return (STAT_NOOP); // get outta here - we did nothing
}
if (bf->block_type != BLOCK_TYPE_ALINE) {
// Nothing to see here...
bf->buffer_state = MP_BUFFER_PLANNED;
// report that we "planned" something...
///*
if (bf->block_type != BLOCK_TYPE_ALINE) { // nothing to see here...
bf->buffer_state = MP_BUFFER_PLANNED; // report that we "planned" something...
return (STAT_OK);
}
//*/
// Examine the next plannable block(s) NB: at this point bf == bf.r
float entry_velocity = mr.entry_velocity; // used for
// We default to the planning block
mpBlockRuntimeBuf_t* block = mr.p;
// Default to the planning buffer
float entry_velocity = mr.entry_velocity;
// At this point, bf == bf.r
if (bf->buffer_state == MP_BUFFER_RUNNING) {
// Update bf to bf->nx, set entry_* to mr.r->exit_*.
bf = bf->nx;
entry_velocity = mr.r->exit_velocity;
entry_velocity = mr.r->exit_velocity; // set entry_* to mr.r->exit_*
if (bf->buffer_state < MP_BUFFER_PREPPED) {
// Get outta here.
// We did nothing
return (STAT_NOOP);
return (STAT_NOOP); // get outta here - we did nothing
}
/*
while (bf->buffer_state < MP_BUFFER_PLANNED) {
if (mp_get_next_buffer(bf)->buffer_state < MP_BUFFER_PREPPED) { // i.e. EMPTY
return (STAT_OK); // ...and invoke an EXEC call
}
return (STAT_NOOP); // get outta here - we did nothing
}
*/
if (bf->block_type != BLOCK_TYPE_ALINE) {
// Nothing to see here...
bf->buffer_state = MP_BUFFER_PLANNED;
// report that we "planned" something...
return (STAT_OK);
return (_plan_command(bf));
}
}
if (bf->buffer_state == MP_BUFFER_PLANNED) {
// Get outta here.
// We did nothing
return (STAT_NOOP);
return (STAT_NOOP); // get outta here - we did nothing
}
// Pass in the bf buffer that will "link" with the planned block
// The block and the buffer are implicitly linked for exec_aline()
// Calculate ramps for the current planning block and the next PREPPED buffer
// The PREPPED buffer will be set to PLANNED later...
//
// Note that that can only be one PLANNED move at a time.
// This is to help sync mr.p to point to the next planned mr.bf
// mr.p is only advanced in mp_exec_aline, after mp.r = mr.p.
// mr.p is only advanced in mp_exec_aline(), after mp.r = mr.p.
// This code aligns the buffers and the blocks for exec_aline().
mpBlockRuntimeBuf_t* block = mr.p; // set a local planning block to the current planning block
mp_calculate_ramps(block, bf, entry_velocity);
// mp_calculate_ramps(mr.p, bf, entry_velocity);
if (block->exit_velocity > block->cruise_velocity) {
__asm__("BKPT"); // exit > cruise after calculate_block
__asm__("BKPT"); // exit > cruise after calculate_block
}
if (block->head_length < 0.00001 && block->body_length < 0.00001 && block->tail_length < 0.00001) {
__asm__("BKPT"); // zero or negative length block
__asm__("BKPT"); // zero or negative length block
}
bf->buffer_state = MP_BUFFER_PLANNED;
bf->buffer_state = MP_BUFFER_PLANNED; //...here
bf->plannable = false;
// report that we planned something...
return (STAT_OK);
return (STAT_OK); // report that we planned something...
}
/*************************************************************************
@@ -246,12 +346,33 @@ stat_t mp_exec_move()
* _NEW - trigger initialization
* _RUN1 - run the first part
* _RUN2 - run the second part
*
* Important distinction to note:
* - mp_plan move() is called for every type of move
* - mp_exec_move() is called for every type of move
* - mp_exec_aline() is only called for alines
*/
/* Note:
* For a version of these routines that execute using the original equation-of-motion
* math (as opposed to the forward difference math) please refer to build 357.xx or earlier.
* Builds 358 onward have only forward difference code. ALso, the Kahan corrections for the
* forward differencing were also been removed shortly after as they were not needed.
/* Synchronization of run BUFFER and run BLOCK
*
* The runtime uses 2 structures for the current move or commend, the run BUFFER
* from the planner queue (mb), and the run BLOCK from the runtime singleton (mr).
* These are synchronized implicitly, but not explicitly referenced, as pointers
* can lead to race conditions. See plan_zoid.cpp / mp_calculate_ramps() for more details
*
* Mp_exec_aline() makes a huge assumption: When it comes time to get a new run block
* (mr.r) it assumes the planner block (mr.p) has been fully planned (JIT planning),
* and is ready for use as the new run block.
*
* When mp_exec_aline() needs to grab a new planner buffer for a new move or command
* (i.e. block state is inactive) it swaps (rolls) the run and planner BLOCKS so that
* mr.p (planner block) is now the mr.r (run block), and the old mr.r block becomes
* available for planning; it becomes mr.p block.
*
* At the same time, it's when finished with its current run buffer (mb.r), it has already
* advanced to the next buffer. mp_exec_move() does this at the end of previous move.
* Or in the bootstrap case, there never was a previous mb.r, so the current one is OK.
*
* As if by magic, the new mb.r aligns with the run block that was just moved in from the planning block
*/
/**** NOTICE ** NOTICE ** NOTICE ****
@@ -290,8 +411,9 @@ stat_t mp_exec_aline(mpBuf_t *bf)
mr.section = SECTION_HEAD;
mr.section_state = SECTION_NEW;
mr.r = mr.p;
mr.p = mr.p->nx;
// This is the only place in the system where mr.r and mr.p are allowed to be changed
mr.r = mr.p; // we are now going to run the planning block
mr.p = mr.p->nx; // re-use the old running block as the new planning block
// Assumptions that are required for this to work:
// entry velocity <= cruise velocity && cruise velocity >= exit velocity
+6 -5
View File
@@ -194,6 +194,7 @@ stat_t mp_aline(GCodeState_t* gm_in)
if (fp_ZERO(length)) {
sr_request_status_report(SR_REQUEST_TIMED_FULL); // Was SR_REQUEST_IMMEDIATE_FULL
return (STAT_MINIMUM_LENGTH_MOVE);
// return (STAT_OK); //+++++ test this
}
// get a cleared buffer and copy in the Gcode model state
@@ -285,13 +286,13 @@ static mpBuf_t* _plan_block(mpBuf_t* bf)
}
}
_calculate_override(bf); // adjust cruise_vmax for feed/traverse override
// bf->plannable_time = bf->pv->plannable_time; // set plannable time - excluding current move
// bf->plannable_time = bf->pv->plannable_time; // set plannable time - excluding current move
bf->buffer_state = MP_BUFFER_IN_PROCESS;
// +++++ Why do we have to do this here?
// bf->pv_group = bf->pv;
bf->hint = NO_HINT; // ensure we've cleared the hints
bf->hint = NO_HINT; // ensure we've cleared the hints
// Time: 12us-41us
if (bf->nx->plannable) { // read in new buffers until EMPTY
return (bf->nx);
@@ -319,15 +320,15 @@ static mpBuf_t* _plan_block(mpBuf_t* bf)
bf->iterations++;
bf->plannable = bf->plannable && !optimal; // Don't accidentally enable plannable!
// Let's be mindful that for ward planning may change exit_vmax, and our exit velocity may be lowered
// Let's be mindful that forward planning may change exit_vmax, and our exit velocity may be lowered
braking_velocity = min(braking_velocity, bf->exit_vmax);
// We *must* set cruise before exit, and keep it at least as high as exit.
bf->cruise_velocity = max(braking_velocity, bf->cruise_velocity);
bf->exit_velocity = braking_velocity;
// We have two places where it could be a mixed decel or an asymetric bump,
// dpending on if the pv->exit_vmax is the same as bf.cruise_vmax
// We have two places where it could be a mixed decel or an asymmetric bump,
// depending on if the pv->exit_vmax is the same as bf.cruise_vmax
bool test_decel_or_bump = false;
// command blocks
Executable → Regular
+5 -5
View File
@@ -80,7 +80,7 @@ static float _get_meet_velocity(const float v_0,
*
* Note we use three data structures: mr, bf, and block.
*
* bf holds the data from aline and back-planning. For the most part it is immuatable.
* bf holds the data from aline and back-planning. For the most part it is immutable.
*
* block is the data for forward-planning. There are only two block structures, and they
* are for the current block and the next block.
@@ -94,8 +94,8 @@ static float _get_meet_velocity(const float v_0,
*
* All values of block are expected to be setup by mp_calculate_ramps.
*
* Quick cheat-sheet on which is in bf and whcih is in block:
* bf:
* Quick cheat-sheet on which is in buffer (bf) and which is in block:
* buffer (bf):
* block_type
* hint
* {cruise,exit}_vmax
@@ -290,9 +290,9 @@ void mp_calculate_ramps(mpBlockRuntimeBuf_t* block, mpBuf_t* bf, const float ent
// PERFECT_DECELERATION
// MIXED_DECELERATION
// All that remians is ASYMMETRIC_BUMP and SYMMETRIC_BUMP.
// All that remains is ASYMMETRIC_BUMP and SYMMETRIC_BUMP.
// We don't really care if it's symmetric, since the first test that _get_meet_velocity
// does is for a symmetic move. It's cheaper to just let it do that then to try and prevent it.
// does is for a symmetric move. It's cheaper to just let it do that then to try and prevent it.
// *** Requested-Fit cases (2) ***
Executable → Regular
+8 -11
View File
@@ -44,7 +44,7 @@
* functions. Data from the Gcode model is transferred to the motion planner by the mp_xxxx()
* functions called by the canonical machine.
*
* The planner should only use data in the planner model. When a move (block) is ready for
* The planner should only use data in the planner model. When a move (buffer) is ready for
* execution the relevant data from the planner is transferred to the runtime model,
* which should also be isolated.
*
@@ -470,8 +470,8 @@ bool mp_is_phat_city_time()
* mp_planner_callback()'s job is to invoke backward planning intelligently.
* The flow of control and division of responsibilities for planning is:
*
* - mp_aline() receives new Gcode blocks and initializes the local variables
* for the new block.
* - mp_aline() receives new Gcode moves and initializes the local variables
* for the new buffer.
*
* - mp_planner_callback() is called regularly from the main loop.
* It's job is to determine whether or not to call mp_plan_block_list(),
@@ -480,9 +480,9 @@ bool mp_is_phat_city_time()
* mp_planner_callback() also manages planner state - whether the planner
* is IDLE, in STARTUP or in one of the running states.
*
* - _plan_block_list() / _planblock() is the backward planning function.
* - _plan_block() is the backward planning function for a single buffer.
*
* - Forward planning is just-in-time by the execution runtime
* - Just-in-time forward planning is performed by mp_plan_move() in the plan_exec.cpp runtime executive
*
* Some Items to note:
*
@@ -542,12 +542,9 @@ void mp_replan_queue(mpBuf_t *bf)
{
do {
if (bf->buffer_state >= MP_BUFFER_PLANNED) {
// revert from PLANNED state
bf->buffer_state = MP_BUFFER_PREPPED;
} else {
// If it's not "planned" then it's either PREPPED or earlier.
// We don't need to adjust it.
break;
bf->buffer_state = MP_BUFFER_PREPPED; // revert from PLANNED state
} else { // If it's not "planned" then it's either PREPPED or earlier.
break; // We don't need to adjust it.
}
} while ((bf = mp_get_next_buffer(bf)) != mb.r);
Executable → Regular
+129 -4
View File
@@ -25,6 +25,126 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* --- Background on the Planner ---
*
* The planner is a complicated beast that takes a lot of things into account.
* Planner documentation is scattered about and co-located with the functions
* that perform the actions. Key files are:
*
* - planner.h - This file has defines, structures and prototypes. What you would expect
* - planner.cpp - Core and common functions, queue handling, JSON and command handlers
* - plan_line.cpp - Move planning and queuing, backward planning functions
* - plan_zoid.cpp - Move forward planning, velocity contour calculations and crazy math
* - plan_exec.cpp - Runtime execution functions, calls zoid's forward planning functions
* - stepper.cpp/h - Real-time step generation, segment loading, pulls from plan_exec
* - plan_arc.cpp/h- Arc calculation and runtime functions - layer above the rest of this
*
* --- Overview of Operation ---
*
* At high level the planner's job is to reconstruct smooth motion from a set of linear
* approximations while observing and operating within the physical constraints of the
* machine and the physics of motion. Gcode - which consists of a series of into linear
* motion segments - is interpreted, queued to the planner, and joined together to produce
* continuous, synchronized motion. Non-motion commands such as pauses (dwells) and
* peripheral controls such as spindles can also be synchronized in the queue. Arcs are
* just a special case consisting of many linear moves. Arcs are not interpreted directly.
*
* The planner sits in the middle of three system layers:
* - The Gcode interpreter and canonical machine (the 'model'), which feeds...
* - The planner - taking generic commands from the model and queuing them for...
* - The runtime layer - pulling from the planner and driving stepper motors or other devices
*
* The planner queue is the heart of the planner. It's a circular list of ~48 complex structures
* that carry the state of the system needs to execute a linear motion, run a pre-planned command,
* like turning on a spindle, or executing an arbitrary JSON command such as an active comment.
*
* The queue can be viewed as a list of instructions that will execute in exact sequence.
* Some instructions control motion and need to be joined to their forward and backwards
* neighbors so that position, velocity, acceleration, and jerk constraints are not
* violated when moving from one motion to the next.
*
* Others are "commands" that are actually just function callbacks that happen to execute
* at a particular point in time (synchronized with motion commands). Commands can control
* anything you can reasonably program, such as digital IO, serial communications, or
* interpreted commands encoded in JSON.
*
* The buffers in the planner queue are treated as a 'closure' - with all state needed for
* proper execution carried in the planner structure. This is important as it keeps
* model state coherent in a heavily pipelined system. The local copy of the Gcode
* model is carried in the gm structure that is part of each planner buffer.
* See header notes in planner.cpp for more details.
*
* The planner is entered by calling one of:
* - mp_aline() - plan and queue a move with acceleration management
* - mp_dwell() - plan and queue a pause (dwell) to the planner queue
* - mp_queue_command() - queue a canned command
* - mp_json_command() - queue a JSON command for run-time interpretation and execution (M100)
* - mp_json_wait() - queue a JSON wait for run-time interpretation and execution (M101)
* -
* In addition, cm_arc_feed() valaidates and sets up a arc paramewters and calls mp_aline()
* repeatedly to spool out the arc segments into the planner queue.
*
* All the above queueing commands other than mp_aline() are relatively trivial; they just
* post callbacks into the next available planner buffer. Command functions are in 2 parts:
* the part that posts to the queue, and the callback that is executed when the command is
* finally reached in the queue - the _exec().
*
* All mp_aline() does is some preliminary math and then posts an initialized buffer to
* the planner queue. The rest of the move planning operations takes place in background;
* via mp_planner_callback() called from the main loop, and as 'pulls' from the runtime
* stepper operations.
*
* Motion planning is separated into backward planning and forward planning stages.
* Backward planning is initiated by mp_planner_callback() which is called repeatedly
* from the main loop. Backwards planning is performed by mp_plan_block_list() and
* _plan_block(). It starts at the most recently arrived Gcode block. Backward
* planning can occur multiple times for a given buffer, as new moves arriving
* can make the motion profile more optimal.
*
* Backward planning uses velocity and jerk constraints to set maximum entry,
* travel (cruise) and exit velocities for the moves in the queue. In addition,
* it observes the maximum cornering velocities that adjoining moves can sustain
* in a corner or a 'kink' to ensure that the jerk limit of any axis participating
* in the move is not violated. See mp_planner_callback() header comments for more detail.
*
* Forward planning is performed just-in-time and only once, right before the
* planner runtime needs the next buffer. Forward planning provides the final
* contouring of the move. It is invoked by mp_plan_move() and executed by
* mp_calculate_ramps() in plan_zoid.cpp.
*
* Planner timing operates at a few different levels:
*
* - New lines of ASCII containing commands and moves arriving from the USB are
* parsed and executed as the lowest priority background task from the main loop.
*
* - Backward planning is invoked by a main loop callback, so it also executes as
* a background task, albeit a higher priority one.
*
* - Forward planning and the ultimate preparation of the move for the runtime runs
* as an interrupt as a 'pull' from the planner queue that uses a series of
* interrupts at progressively lower priorities to ensure that the next planner
* buffer is ready before the runtime runs out of forward-planned moves and starves.
*
* Some other functions performed by the planner include:
*
* - Velocity throttling to ensure that very short moves do not execute faster
* than the serial interface can deliver them
*
* - Feedhold and resume operations
*
* - Feed rate override functions and replanning
*
* Some terms that are useful that we try to use consistently:
*
* - buffer - in this context a planner buffer holding a move or a command: mb._ or bf
* - block - a data structure for planning or runtime control. See mp_calculate_ramps() comments
* - move - a linear Gcode move, typically from a G0 or G1 code
* - command - a non-move executable in the planner
* - group - a collection of moves or commands that are treated as a unit
* - line - a line of ASCII gcode or arbitrary text
* - bootstrap - the startup period where the planner collects moves but does not yet execute them
*/
#ifndef PLANNER_H_ONCE
#define PLANNER_H_ONCE
@@ -172,7 +292,12 @@ typedef enum {
#define UPDATE_MP_DIAGNOSTICS { mp.plannable_time_ms = mp.plannable_time*60000; }
/*
* Planner structures
* Planner structures
*
* You should be aware of the distinction between 'buffers' and 'blocks'
* Please refer to header comments in for important details on buffers and blocks
* - plan_zoid.cpp / mp_calculate_ramps()
* - plan_exec.cpp / mp_exec_aline()
*/
struct mpBuffer_to_clear {
@@ -368,9 +493,9 @@ typedef struct mpMotionRuntimeSingleton { // persistent runtime variables
float encoder_steps[MOTORS]; // encoder position in steps - ideally the same as commanded_steps
float following_error[MOTORS]; // difference between encoder_steps and commanded steps
mpBlockRuntimeBuf_t *r; // what's running
mpBlockRuntimeBuf_t *p; // what's being planned, p might == r
mpBlockRuntimeBuf_t bf[2]; // the buffer
mpBlockRuntimeBuf_t *r; // block that is running
mpBlockRuntimeBuf_t *p; // block that is being planned, p might == r
mpBlockRuntimeBuf_t bf[2]; // buffer holding the two blocks
float entry_velocity; // entry values for the currently running block