Updated licensing
This commit is contained in:
@ -1,21 +0,0 @@
|
||||
Runtime commands for Grbl
|
||||
=========================
|
||||
|
||||
In normal operation, grbl accepts g-code blocks followed by a carriage return. Each block is then parsed, processed, and placed into a ring buffer with computed acceleration profiles. Grbl will respond with an 'ok' or 'error:XXX' for each block received.
|
||||
|
||||
As of v0.8, grbl features multi-tasking events, which allow for immediate execution of run-time commands regardless of what grbl is doing. With this functionality, direct control of grbl may be possible, such as a controlled decelerating feed hold, resume, and system abort/reset. In addition, this provides the ability to report the real-time status of the CNC machine's current location and feed rates.
|
||||
|
||||
How it works: The run-time commands are defined as special characters, which are picked off the serial read buffer at an interrupt level. The serial interrupt then sets a run-time command system flag for the main program to execute when ready. The main program consists of run-time command check points placed strategically in various points in the program, where grbl maybe idle waiting for room in a buffer or the execution time from the last check point may exceed a fraction of a second.
|
||||
|
||||
How to interface: From a terminal connection, run-time commands may be sent at anytime via keystrokes. When streaming g-code, the user interface should be able to send these special characters independently of the stream. Grbl will not write these run-time command special characters to the buffer, so they may be placed anywhere in the stream at anytime, as long as there is room in the buffer. Also, ensure that the g-code program being streamed to grbl does not contain any of these special characters in the program. These characters may be defined per user requirements in the 'config.h' file.
|
||||
|
||||
Run-time commands:
|
||||
|
||||
- Feed Hold: This initiates an immediate controlled deceleration of the streaming g-code program to a stop. The deceleration, limited by the machine acceleration settings, ensures no steps are lost and positioning is maintained. Grbl may still receive and buffer g-code blocks as the feed hold is being executed. Once the feed hold completes, grbl will replan the buffer and resume upon a 'cycle start' command.
|
||||
|
||||
- Cycle Start: (a.k.a. Resume) For now, cycle start only resumes the g-code program after a feed hold. In later releases, this may also function as a way to initiate the steppers manually when a user would like to fill the planner buffer completely before starting the cycle.
|
||||
|
||||
- Reset: This issues an immediate shutdown of the stepper motors and a system abort. The main program will exit back to the main loop and re-initialize grbl.
|
||||
|
||||
- Status Report: (TODO) In future releases, this will provide real-time positioning, feed rate, and block processed data, as well as other important data to the user. This also may be considered a 'poor-man's' DRO (digital read-out), where grbl thinks it is, rather than a direct and absolute measurement.
|
||||
|
@ -1,97 +0,0 @@
|
||||
Mega328P Arduino Pin Mapping
|
||||
============================
|
||||
|
||||
Digital 0 PD0 (RX)
|
||||
Digital 1 PD1 (TX)
|
||||
Digital 2 PD2
|
||||
Digital 3 PD3
|
||||
Digital 4 PD4
|
||||
Digital 5 PD5
|
||||
Digital 6 PD6
|
||||
Digital 7 PD7
|
||||
Digital 8 PB0
|
||||
Digital 9 PB1
|
||||
Digital 10 PB2
|
||||
Digital 11 PB3 (MOSI)
|
||||
Digital 12 PB4 (MISO)
|
||||
Digital 13 PB5 (SCK)
|
||||
|
||||
Analog 0 PC0
|
||||
Analog 1 PC1
|
||||
Analog 2 PC2
|
||||
Analog 3 PC3
|
||||
Analog 4 PC4
|
||||
|
||||
|
||||
Mega2560 Arduino Pin Mapping
|
||||
============================
|
||||
Digital pin 22 PA0 ( AD0 )
|
||||
Digital pin 23 PA1 ( AD1 )
|
||||
Digital pin 24 PA2 ( AD2 )
|
||||
Digital pin 25 PA3 ( AD3 )
|
||||
Digital pin 26 PA4 ( AD4 )
|
||||
Digital pin 27 PA5 ( AD5 )
|
||||
Digital pin 28 PA6 ( AD6 )
|
||||
Digital pin 29 PA7 ( AD7 )
|
||||
Digital pin 53 (PWM)(RX1) PB0 ( SS/PCINT0 )
|
||||
Digital pin 52 (PWM)(SDA) PB1 ( SCK/PCINT1 )
|
||||
Digital pin 51 (PWM)(SCL) PB2 ( MOSI/PCINT2 )
|
||||
Digital pin 50 PB3 ( MISO/PCINT3 )
|
||||
Digital pin 10 (PWM) PB4 ( OC2A/PCINT4 )
|
||||
Digital pin 11 (PWM) PB5 ( OC1A/PCINT5 )
|
||||
Digital pin 12 (PWM) PB6 ( OC1B/PCINT6 )
|
||||
Digital pin 13 (PWM) PB7 ( OC0A/OC1C/PCINT7 )
|
||||
Digital pin 37 PC0 ( A8 )
|
||||
Digital pin 36 PC1 ( A9 )
|
||||
Digital pin 35 PC2 ( A10 )
|
||||
Digital pin 34 PC3 ( A11 )
|
||||
Digital pin 33 PC4 ( A12 )
|
||||
Digital pin 32 PC5 ( A13 )
|
||||
Digital pin 31 PC6 ( A14 )
|
||||
Digital pin 30 PC7 ( A15 )
|
||||
Digital pin 21 (SCL) PD0 ( SCL/INT0 )
|
||||
Digital pin 20 (SDA) PD1 ( SDA/INT1 )
|
||||
Digital pin 19 PD2 ( RXDI/INT2 )
|
||||
Digital pin 18 PD3 ( TXD1/INT3 )
|
||||
Digital pin 38 PD7 ( T0 )
|
||||
Digital pin 0 (PWM) (RX0) PE0 ( RXD0/PCINT8 )
|
||||
Digital pin 1 (PWM) (TX0) PE1 ( TXD0 )
|
||||
Digital pin 5 (PWM) PE3 ( OC3A/AIN1 )
|
||||
Digital pin 2 (PWM) PE4 ( OC3B/INT4 )
|
||||
Digital pin 3 (PWM) PE5 ( OC3C/INT5 )
|
||||
Analog pin 0 PF0 ( ADC0 )
|
||||
Analog pin 1 PF1 ( ADC1 )
|
||||
Analog pin 2 PF2 ( ADC2 )
|
||||
Analog pin 3 PF3 ( ADC3 )
|
||||
Analog pin 4 PF4 ( ADC4/TMK )
|
||||
Analog pin 5 PF5 ( ADC5/TMS )
|
||||
Analog pin 6 PF6 ( ADC6/PCINT14 )
|
||||
Analog pin 7 PF7 ( ADC7/PCINT15 )
|
||||
Digital pin 41 PG0 ( WR )
|
||||
Digital pin 40 PG1 ( RD )
|
||||
Digital pin 39 PG2 ( ALE )
|
||||
Digital pin 4 (PWM) PG5 ( OC0B )
|
||||
Digital pin 17 (PWM) PH0 ( RXD2 )
|
||||
Digital pin 16 (PWM) PH1 ( TXD2 )
|
||||
Digital pin 6 (PWM)(RX3 ) PH3 ( OC4A )
|
||||
Digital pin 7 (PWM)(TX2) PH4 ( OC4B )
|
||||
Digital pin 8 (PWM)(RX2 ) PH5 ( OC4C )
|
||||
Digital pin 9 (PWM)(TX1) PH6 ( OC2B )
|
||||
Digital pin 15 PJ0 ( RXD3/PCINT9 )
|
||||
Digital pin 14 PJ1 ( TXD3/PCINT10 )
|
||||
Analog pin 8 PK0 ( ADC8/PCINT16 )
|
||||
Analog pin 9 PK1 ( ADC9/PCINT17 )
|
||||
Analog pin 10 PK2 ( ADC10/PCINT18 )
|
||||
Analog pin 11 PK3 ( ADC11/PCINT19 )
|
||||
Analog pin 12 PK4 ( ADC12/PCINT20 )
|
||||
Analog pin 13 PK5 ( ADC13/PCINT21 )
|
||||
Analog pin 14 PK6 ( ADC14/PCINT22 )
|
||||
Analog pin 15 PK7 ( ADC15/PCINT23 )
|
||||
Digital pin 49 PL0 ( ICP4 )
|
||||
Digital pin 48 PL1 ( ICP5 )
|
||||
Digital pin 47 PL2 ( T5 )
|
||||
Digital pin 46 (PWM) PL3 ( OC5A )
|
||||
Digital pin 45 (PWM) PL4 ( OC5B )
|
||||
Digital pin 44 (PWM) PL5 ( OC5C )
|
||||
Digital pin 43 PL6
|
||||
Digital pin 42 PL7
|
@ -1,13 +0,0 @@
|
||||
Allocation of AVR peripherals in Grbl
|
||||
=====================================
|
||||
|
||||
See config.h for pin allocation.
|
||||
|
||||
The UART is handled by 'serial' and used primarily for streaming gcode
|
||||
|
||||
16 bit Timer 1 and the TIMER1_COMPA interrupt is used by the 'stepper' module to handle step events
|
||||
|
||||
8 bit Timer 2 and the TIMER2_OVF interrupt is used by the 'stepper' module to reset the step pins
|
||||
after a step event
|
||||
|
||||
|
@ -1,43 +0,0 @@
|
||||
The general structure of Grbl
|
||||
=============================
|
||||
|
||||
The main processing stack:
|
||||
|
||||
'protocol' : Accepts command lines from the serial port and passes them to 'gcode' for execution.
|
||||
Provides status responses for each command. Also manages run-time commands set by
|
||||
the serial interrupt.
|
||||
|
||||
'gcode' : Recieves gcode from 'protocol', parses it according to the current state
|
||||
of the parser and issues commands via '..._control' modules
|
||||
|
||||
'spindle_control' : Commands for controlling the spindle.
|
||||
|
||||
'motion_control' : Accepts motion commands from 'gcode' and passes them to the 'planner'. This module
|
||||
represents the public interface of the planner/stepper duo.
|
||||
|
||||
'planner' : Receives linear motion commands from 'motion_control' and adds them to the plan of
|
||||
prepared motions. It takes care of continuously optimizing the acceleration profile
|
||||
as motions are added.
|
||||
|
||||
'stepper' : Executes the motions by stepping the steppers according to the plan.
|
||||
|
||||
|
||||
|
||||
|
||||
Supporting files:
|
||||
|
||||
'config.h' : Compile time user settings
|
||||
|
||||
'settings' : Maintains the run time settings record in eeprom and makes it available
|
||||
to all modules.
|
||||
|
||||
'eeprom' : A library from Atmel that provides methods for reading and writing the eeprom with
|
||||
a small addition from us that read and write binary streams with check sums used
|
||||
to verify validity of the settings record.
|
||||
|
||||
'nuts_bolts.h' : A collection of global variable definitions, useful constants, and macros used everywhere
|
||||
|
||||
'serial' : Low level serial communications and picks off run-time commands real-time for asynchronous
|
||||
control.
|
||||
|
||||
'print' : Functions to print strings of different formats (using serial)
|
@ -1,437 +0,0 @@
|
||||
% ----------------------------------------------------------------------------------------
|
||||
% The MIT License (MIT)
|
||||
%
|
||||
% Copyright (c) 2014 Sungeun K. Jeon
|
||||
%
|
||||
% Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
% of this software and associated documentation files (the "Software"), to deal
|
||||
% in the Software without restriction, including without limitation the rights
|
||||
% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
% copies of the Software, and to permit persons to whom the Software is
|
||||
% furnished to do so, subject to the following conditions:
|
||||
%
|
||||
% The above copyright notice and this permission notice shall be included in
|
||||
% all copies or substantial portions of the Software.
|
||||
%
|
||||
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
% 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.
|
||||
% ----------------------------------------------------------------------------------------
|
||||
|
||||
% This MATLAB script was written for the purpose of being a GRBL planner simulator. This
|
||||
% simulator is a rough representation of the actual workings of Grbl on the Arduino, but
|
||||
% was used to hone and proof the actual planner by providing quick visual feedback on its
|
||||
% functionality when experimented on. This script should be considered for educational
|
||||
% purposes only. This script requires and executes a pre-parsed g-code file from the
|
||||
% matlab_convert.py script that is in a specific non-g-code format.
|
||||
|
||||
% There will be two figures plotted. The first is the line motion paths of the complete
|
||||
% g-code program. The second is a representation of Grbl's planner buffer as new line
|
||||
% motions are fed to it, plotting the velocity profiles the stepper motors will execute.
|
||||
% Every time the user inputs an <Enter>, this feeds the simulator planner one line motion
|
||||
% block. The left side is the first block in the buffer and the one that will be executed
|
||||
% by the stepper module first. The right side is the end of the planner buffer, where the
|
||||
% most recent streamed block is appended onto the planner buffer. Grbl's planner
|
||||
% optimizes the velocity profiles between the beginning and end of the buffer based on
|
||||
% the acceleration limits, intended velocity/feedrate, and line motion junction angles
|
||||
% with their corresponding velocity limits (i.e. junctions with acute angles needs to come
|
||||
% to a complete stop vs straight junctions can continue through at full speed.)
|
||||
|
||||
% ----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
% Main function
|
||||
% NOTE: This is just a way to keep all functions in one place, but all non-global variables
|
||||
% are cleared as soon as this script completes.
|
||||
function main()
|
||||
|
||||
% Load pre-parsed gcode moves.
|
||||
close all;
|
||||
warning off;
|
||||
clearvars -global
|
||||
fid = fopen('matlab.gcode','r');
|
||||
gcode = textscan(fid,'%d8%f32%f32%f32%f32');
|
||||
nblock = length(gcode{1});
|
||||
|
||||
% Plot all g-code moves.
|
||||
figure
|
||||
line(gcode{3},gcode{4},gcode{5});
|
||||
axis equal;
|
||||
% axis([min(gcode{3}) max(gcode{3}) min(gcode{4}) max(gcode{4}) min(gcode{5}) max(gcode{5})]);
|
||||
title('G-code programming line motions');
|
||||
view(3);
|
||||
|
||||
% Set up figure for planner queue
|
||||
figure
|
||||
|
||||
% Print help.
|
||||
disp('<NOTE: Press Enter to Advance One G-Code Line Motion>');
|
||||
disp(' BLUE line indicates completed planner blocks that require no recalculation.');
|
||||
disp(' RED line indicates planner blocks that have been recalculated.');
|
||||
disp(' GREEN line indicates the location of the BPLANNED pointer. Always a recalculated block.');
|
||||
disp(' BLACK dotted-line and ''x'' indicates block nominal speed and max junction velocity, respectively.');
|
||||
disp(' CYAN ''.'' indicates block initial entry speed.');
|
||||
|
||||
% Define Grbl settings.
|
||||
BUFFER_SIZE = 18; % Number of planner blocks in its ring buffer.
|
||||
steps_per_mm = 200;
|
||||
seekrate = 2500; % mm/min
|
||||
acceleration = [100 100 100]; % mm/sec^2 [ X Y Z ] axes
|
||||
junction_deviation = 0.1; % mm. See Grbl documentation on this parameter.
|
||||
inch_2_mm = 25.4;
|
||||
ACCELERATION_TICKS_PER_SECOND = 100;
|
||||
|
||||
gcode{2} = gcode{2};
|
||||
gcode{2} = inch_2_mm*gcode{2};
|
||||
gcode{3} = inch_2_mm*gcode{3};
|
||||
gcode{4} = inch_2_mm*gcode{4};
|
||||
gcode{5} = inch_2_mm*gcode{5};
|
||||
|
||||
% Initialize blocks
|
||||
block.steps = [];
|
||||
block.step_event_count = [];
|
||||
block.delta_mm = [];
|
||||
block.millimeters = [];
|
||||
block.acceleration = [];
|
||||
block.speed = [];
|
||||
block.nominal_speed = [];
|
||||
block.max_entry_speed = [];
|
||||
block.entry_speed = [];
|
||||
block.recalculate_flag = false;
|
||||
for i = 2:BUFFER_SIZE
|
||||
block(i) = block(1);
|
||||
end
|
||||
|
||||
% Initialize planner
|
||||
position = [0 0 0];
|
||||
prev_unit_vec = [0 0 0];
|
||||
previous_nominal_speed = 0;
|
||||
pos = 0;
|
||||
|
||||
% BHEAD and BTAIL act as pointers to the block head and tail.
|
||||
% BPLANNED acts as a pointer of the location of the end of a completed/optimized plan.
|
||||
bhead = 1;
|
||||
btail = 1;
|
||||
bplanned = 1;
|
||||
|
||||
global block bhead btail bplanned nind acceleration BUFFER_SIZE pos ACCELERATION_TICKS_PER_SECOND
|
||||
|
||||
% Main loop. Simulates plan_buffer_line(). All of the precalculations for the newest incoming
|
||||
% block occurs here. Anything independent of the planner changes.
|
||||
for i = 1:nblock
|
||||
|
||||
target = round([gcode{3}(i) gcode{4}(i) gcode{5}(i)].*steps_per_mm);
|
||||
if gcode{1}(i) == 1
|
||||
feedrate = gcode{2}(i);
|
||||
else
|
||||
feedrate = seekrate;
|
||||
end
|
||||
|
||||
nind = next_block_index(bhead);
|
||||
if nind == btail
|
||||
% Simulate a constantly full buffer. Move buffer tail.
|
||||
bind = next_block_index(btail);
|
||||
% Push planned pointer if encountered. Prevents it from looping back around the ring buffer.
|
||||
if btail == bplanned; bplanned = bind; end
|
||||
btail = bind;
|
||||
end
|
||||
|
||||
block(bhead).steps = abs(target-position);
|
||||
block(bhead).step_event_count = max(block(bhead).steps);
|
||||
|
||||
% Bail if this is a zero-length block
|
||||
if block(bhead).step_event_count == 0
|
||||
disp(['Zero-length block in line ',int2str(i)]);
|
||||
else
|
||||
|
||||
% Compute path vector in terms of absolute step target and current positions
|
||||
delta_mm = single((target-position)./steps_per_mm);
|
||||
block(bhead).millimeters = single(norm(delta_mm));
|
||||
inverse_millimeters = single(1/block(bhead).millimeters);
|
||||
|
||||
% Compute path unit vector
|
||||
unit_vec = delta_mm/block(bhead).millimeters;
|
||||
|
||||
% Calculate speed in mm/minute for each axis
|
||||
inverse_minute = single(feedrate * inverse_millimeters);
|
||||
block(bhead).speed = delta_mm*inverse_minute;
|
||||
block(bhead).nominal_speed = block(bhead).millimeters*inverse_minute;
|
||||
|
||||
% Calculate block acceleration. Operates on absolute value of unit vector.
|
||||
[max_acc,ind] = max(abs(unit_vec)./acceleration); % Determine limiting acceleration
|
||||
block(bhead).acceleration = acceleration(ind)/abs(unit_vec(ind));
|
||||
|
||||
% Compute maximum junction speed
|
||||
block(bhead).max_entry_speed = 0.0;
|
||||
if previous_nominal_speed > 0.0
|
||||
cos_theta = dot(-previous_unit_vec,unit_vec);
|
||||
if (cos_theta < 0.95)
|
||||
block(bhead).max_entry_speed = min([block(bhead).nominal_speed,previous_nominal_speed]);
|
||||
if (cos_theta > -0.95)
|
||||
sin_theta_d2 = sqrt(0.5*(1.0-cos_theta));
|
||||
block(bhead).max_entry_speed = min([block(bhead).max_entry_speed,sqrt(block(bhead).acceleration*3600*junction_deviation*sin_theta_d2/(1.0-sin_theta_d2))]);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
block(bhead).entry_speed = 0; % Just initialize. Set accurately in the replanning function.
|
||||
block(bhead).recalculate_flag = true; % Plotting flag to indicate this block has been updated.
|
||||
|
||||
previous_unit_vec = unit_vec;
|
||||
previous_nominal_speed = block(bhead).nominal_speed;
|
||||
position = target;
|
||||
|
||||
bhead = nind; % Block complete. Push buffer pointer.
|
||||
planner_recalculate();
|
||||
|
||||
plot_buffer_velocities();
|
||||
end
|
||||
end
|
||||
return
|
||||
|
||||
% Computes the next block index in the planner ring buffer
|
||||
function block_index = next_block_index(block_index)
|
||||
global BUFFER_SIZE
|
||||
block_index = block_index + 1;
|
||||
if block_index > BUFFER_SIZE
|
||||
block_index = 1;
|
||||
end
|
||||
return
|
||||
|
||||
% Computes the previous block index in the planner ring buffer
|
||||
function block_index = prev_block_index(block_index)
|
||||
global BUFFER_SIZE
|
||||
block_index = block_index-1;
|
||||
if block_index < 1
|
||||
block_index = BUFFER_SIZE;
|
||||
end
|
||||
return
|
||||
|
||||
|
||||
% Planner recalculate function. The magic happens here.
|
||||
function planner_recalculate(block)
|
||||
|
||||
global block bhead btail bplanned acceleration
|
||||
|
||||
bind = prev_block_index(bhead);
|
||||
if bind == bplanned; return; end % Bail, if only one block in buffer. Can't be operated on.
|
||||
|
||||
% Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
|
||||
% block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
|
||||
% NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
|
||||
next = [];
|
||||
curr = bind; % Last block in buffer.
|
||||
|
||||
% Calculate maximum entry speed for last block in buffer, where the exit speed is always zero.
|
||||
block(curr).entry_speed = min([block(curr).max_entry_speed,sqrt(2*block(curr).acceleration*60*60*block(curr).millimeters)]);
|
||||
|
||||
bind = prev_block_index(bind); % Btail or second to last block
|
||||
if (bind == bplanned)
|
||||
% Only two plannable blocks in buffer. Reverse pass complete.
|
||||
% Check if the first block is the tail. If so, notify stepper module to update its current parameters.
|
||||
% if bind == btail; update_tail_block; end
|
||||
else
|
||||
% Three or more plannable blocks in buffer. Loop it.
|
||||
while bind ~= bplanned % Loop until bplanned point hits. Replans to last plan point.
|
||||
next = curr;
|
||||
curr = bind;
|
||||
bind = prev_block_index( bind ); % Previous block pointer.
|
||||
|
||||
% Check if the first block is the tail. If so, notify stepper module to update its current parameters.
|
||||
% if bind == btail; update_tail_block; end
|
||||
|
||||
% Compute maximum entry speed decelerating over the current block from its exit speed.
|
||||
if block(curr).entry_speed ~= block(curr).max_entry_speed
|
||||
block(curr).recalculate_flag = true; % Plotting flag to indicate this block has been updated.
|
||||
block(curr).entry_speed = min([ block(curr).max_entry_speed,...
|
||||
sqrt(block(next).entry_speed^2 + 2*block(curr).acceleration*60*60*block(curr).millimeters)]);
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
% For two blocks, reverse pass is skipped, but forward pass plans second block entry speed
|
||||
% onward. This prevents the first, or the potentially executing block, from being over-written.
|
||||
% NOTE: Can never be bhead, since bsafe is always in active buffer.
|
||||
next = bplanned;
|
||||
bind = next_block_index(bplanned); % Start at bplanned
|
||||
while bind ~= bhead
|
||||
curr = next;
|
||||
next = bind;
|
||||
|
||||
% An acceleration block is always an optimally planned block since it starts from the first
|
||||
% block's current speed or a maximum junction speed. Compute accelerations from this block
|
||||
% and update the next block's entry speed.
|
||||
if (block(curr).entry_speed < block(next).entry_speed)
|
||||
% Once speed is set by forward planner, the plan for this block is finished and optimal.
|
||||
% Increment the planner pointer forward one block.
|
||||
|
||||
entry_speed = sqrt(block(curr).entry_speed^2 + 2*block(curr).acceleration*60*60*block(curr).millimeters);
|
||||
if (block(next).entry_speed > entry_speed)
|
||||
block(next).entry_speed = entry_speed;
|
||||
bplanned = bind;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
% Check if the next block entry speed is at max_entry_speed. If so, move the planned pointer, since
|
||||
% this entry speed cannot be improved anymore and all prior blocks have been completed and optimally planned.
|
||||
if block(next).entry_speed == block(next).max_entry_speed
|
||||
bplanned = bind;
|
||||
end
|
||||
|
||||
% Recalculate trapezoid can be installed here, since it scans through all of the plannable blocks.
|
||||
% NOTE: Eventually this will only be computed when being executed.
|
||||
|
||||
bind = next_block_index( bind );
|
||||
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
% ----------------------------------------------------------------------------------------
|
||||
% PLOTTING FUNCTIONS
|
||||
|
||||
% Plots the entire buffer plan into a MATLAB figure to visual the plan.
|
||||
% BLUE line indicates completed planner blocks that require no recalculation.
|
||||
% RED line indicates planner blocks that have been recalculated.
|
||||
% GREEN line indicates the location of the BPLANNED pointer. Always a recalculated block.
|
||||
% BLACK dotted-line and 'x' indicates block nominal speed and max junction velocity, respectively.
|
||||
% CYAN '.' indicates block initial entry speed.
|
||||
function plot_buffer_velocities()
|
||||
global block bhead btail bplanned acceleration pos ACCELERATION_TICKS_PER_SECOND
|
||||
bind = btail;
|
||||
curr = [];
|
||||
next = [];
|
||||
|
||||
pos_initial = 0;
|
||||
pos = 0;
|
||||
while bind ~= bhead
|
||||
curr = next;
|
||||
next = bind;
|
||||
hold on;
|
||||
if ~isempty(curr)
|
||||
accel_d = estimate_acceleration_distance(block(curr).entry_speed, block(curr).nominal_speed, block(curr).acceleration*60*60);
|
||||
decel_d = estimate_acceleration_distance(block(curr).nominal_speed, block(next).entry_speed,-block(curr).acceleration*60*60);
|
||||
plateau_d = block(curr).millimeters-accel_d-decel_d;
|
||||
if plateau_d < 0
|
||||
accel_d = intersection_distance(block(curr).entry_speed, block(next).entry_speed, block(curr).acceleration*60*60, block(curr).millimeters);
|
||||
if accel_d < 0
|
||||
accel_d = 0;
|
||||
elseif accel_d > block(curr).millimeters
|
||||
accel_d = block(curr).millimeters;
|
||||
end
|
||||
plateau_d = 0;
|
||||
end
|
||||
color = 'b';
|
||||
if (block(curr).recalculate_flag || block(next).recalculate_flag)
|
||||
block(curr).recalculate_flag = false;
|
||||
color = 'r';
|
||||
end
|
||||
if bplanned == curr
|
||||
color = 'g';
|
||||
end
|
||||
|
||||
plot_trap(pos,block(curr).entry_speed,block(next).entry_speed,block(curr).nominal_speed,block(curr).acceleration,accel_d,plateau_d,block(curr).millimeters,color)
|
||||
plot([pos pos+block(curr).millimeters],block(curr).nominal_speed*[1 1],'k:') % BLACK dotted indicates
|
||||
plot(pos,block(curr).max_entry_speed,'kx')
|
||||
|
||||
pos = pos + block(curr).millimeters;
|
||||
plot(pos,block(next).entry_speed,'c.');
|
||||
end
|
||||
bind = next_block_index( bind );
|
||||
end
|
||||
|
||||
accel_d = estimate_acceleration_distance(block(next).entry_speed, block(next).nominal_speed, block(next).acceleration*60*60);
|
||||
decel_d = estimate_acceleration_distance(block(next).nominal_speed, 0, -block(next).acceleration*60*60);
|
||||
plateau_d = block(next).millimeters-accel_d-decel_d;
|
||||
if plateau_d < 0
|
||||
accel_d = intersection_distance(block(next).entry_speed, 0, block(next).acceleration*60*60, block(next).millimeters);
|
||||
if accel_d < 0
|
||||
accel_d = 0;
|
||||
elseif accel_d > block(next).millimeters
|
||||
accel_d = block(next).millimeters;
|
||||
end
|
||||
plateau_d = 0;
|
||||
end
|
||||
block(next).recalculate_flag = false;
|
||||
color = 'r';
|
||||
if bplanned == next
|
||||
color= 'g';
|
||||
end
|
||||
|
||||
plot_trap(pos,block(next).entry_speed,0,block(next).nominal_speed,block(next).acceleration,accel_d,plateau_d,block(next).millimeters,color)
|
||||
plot([pos pos+block(next).millimeters],block(next).nominal_speed*[1 1],'k:')
|
||||
plot(pos,block(next).max_entry_speed,'kx')
|
||||
|
||||
plot(pos,block(next).entry_speed,'.');
|
||||
pos = pos + block(next).millimeters;
|
||||
plot(pos,0,'rx');
|
||||
xlabel('mm');
|
||||
ylabel('mm/sec');
|
||||
xlim([pos_initial pos])
|
||||
title('Planner buffer optimized velocity profile');
|
||||
pause();
|
||||
hold off;
|
||||
|
||||
plot(pos,0)
|
||||
return
|
||||
|
||||
|
||||
function d_a = estimate_acceleration_distance(initial_rate, target_rate, acceleration,rate_delta)
|
||||
d_a = (target_rate*target_rate-initial_rate*initial_rate)/(2*acceleration);
|
||||
return
|
||||
|
||||
function d_i = intersection_distance(initial_rate, final_rate, acceleration, distance, rate_delta)
|
||||
d_i = (2*acceleration*distance-initial_rate*initial_rate+final_rate*final_rate)/(4*acceleration);
|
||||
return
|
||||
|
||||
|
||||
% Simply plots the ac/de-celeration curves and plateaus of a trapezoid.
|
||||
function plot_trap(pos,initial_rate,final_rate,rate,accel,accel_d,plateau_d,millimeters,color)
|
||||
|
||||
dx = 1.0; % Line segment length
|
||||
linex = [pos]; liney = [initial_rate];
|
||||
|
||||
% Acceleration
|
||||
np = floor(accel_d/dx);
|
||||
if np
|
||||
v = initial_rate;
|
||||
for i = 1:np
|
||||
v = sqrt(v^2+2*accel*60*60*dx);
|
||||
linex = [linex pos+i*dx];
|
||||
liney = [liney v];
|
||||
end
|
||||
end
|
||||
|
||||
% Plateau
|
||||
v = sqrt(initial_rate^2 + 2*accel*60*60*accel_d);
|
||||
if v < rate
|
||||
rate = v;
|
||||
end
|
||||
linex = [linex pos+[accel_d accel_d+plateau_d]];
|
||||
liney = [liney [rate rate]];
|
||||
|
||||
% Deceleration
|
||||
np = floor((millimeters-accel_d-plateau_d)/dx);
|
||||
if np
|
||||
v = rate;
|
||||
for i = 1:np
|
||||
v = sqrt(v^2-2*accel*60*60*dx);
|
||||
linex = [linex pos+i*dx+accel_d+plateau_d];
|
||||
liney = [liney v];
|
||||
end
|
||||
end
|
||||
|
||||
linex = [linex pos+millimeters];
|
||||
liney = [ liney final_rate];
|
||||
plot(linex,liney,color);
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,270 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""\
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Sungeun K. Jeon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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.
|
||||
----------------------------------------------------------------------------------------
|
||||
"""
|
||||
"""\
|
||||
G-code preprocessor for the grbl_sim.m MATLAB script. Parses the g-code program to a
|
||||
specific file format for the MATLAB script to use. Based on PreGrbl by @chamnit.
|
||||
|
||||
How to use: When running this python script, it will process the g-code program under
|
||||
the filename "test.gcode" (may be changed below) and produces a file called "matlab.gcode"
|
||||
that the grbl_sim.m MATLAB script will search for and execute.
|
||||
"""
|
||||
|
||||
import re
|
||||
from math import *
|
||||
from copy import *
|
||||
|
||||
# -= SETTINGS =-
|
||||
filein = 'test.gcode' # Input file name
|
||||
fileout = 'matlab.gcode' # Output file name
|
||||
ndigits_in = 4 # inch significant digits after '.'
|
||||
ndigits_mm = 2 # mm significant digits after '.'
|
||||
# mm_per_arc_segment = 0.38 # mm per arc segment
|
||||
arc_tolerance = 0.00005*25.4
|
||||
n_arc_correction = 20
|
||||
inch2mm = 25.4 # inch to mm conversion scalar
|
||||
verbose = False # Verbose flag to show all progress
|
||||
remove_unsupported = True # Removal flag for all unsupported statements
|
||||
|
||||
# Initialize parser state
|
||||
gc = { 'current_xyz' : [0,0,0],
|
||||
'feed_rate' : 0, # F0
|
||||
'motion_mode' : 'SEEK', # G00
|
||||
'plane_axis' : [0,1,2], # G17
|
||||
'inches_mode' : False, # G21
|
||||
'inverse_feedrate_mode' : False, # G94
|
||||
'absolute_mode' : True} # G90
|
||||
|
||||
def unit_conv(val) : # Converts value to mm
|
||||
if gc['inches_mode'] : val *= inch2mm
|
||||
return(val)
|
||||
|
||||
def fout_conv(val) : # Returns converted value as rounded string for output file.
|
||||
if gc['inches_mode'] : return( str(round(val/inch2mm,ndigits_in)) )
|
||||
else : return( str(round(val,ndigits_mm)) )
|
||||
|
||||
# Open g-code file
|
||||
fin = open(filein,'r');
|
||||
fout = open(fileout,'w');
|
||||
|
||||
# Iterate through g-code file
|
||||
l_count = 0
|
||||
for line in fin:
|
||||
l_count += 1 # Iterate line counter
|
||||
|
||||
# Strip comments/spaces/tabs/new line and capitalize. Comment MSG not supported.
|
||||
block = re.sub('\s|\(.*?\)','',line).upper()
|
||||
block = re.sub('\\\\','',block) # Strip \ block delete character
|
||||
block = re.sub('%','',block) # Strip % program start/stop character
|
||||
|
||||
if len(block) == 0 : # Ignore empty blocks
|
||||
|
||||
print "Skipping: " + line.strip()
|
||||
|
||||
else : # Process valid g-code clean block. Assumes no block delete characters or comments
|
||||
|
||||
g_cmd = re.findall(r'[^0-9\.\-]+',block) # Extract block command characters
|
||||
g_num = re.findall(r'[0-9\.\-]+',block) # Extract block numbers
|
||||
|
||||
# G-code block error checks
|
||||
# if len(g_cmd) != len(g_num) : print block; raise Exception('Invalid block. Unbalanced word and values.')
|
||||
if 'N' in g_cmd :
|
||||
if g_cmd[0]!='N': raise Exception('Line number must be first command in line.')
|
||||
if g_cmd.count('N') > 1: raise Exception('More than one line number in block.')
|
||||
g_cmd = g_cmd[1:] # Remove line number word
|
||||
g_num = g_num[1:]
|
||||
# Block item repeat checks? (0<=n'M'<5, G/M modal groups)
|
||||
|
||||
# Initialize block state
|
||||
blk = { 'next_action' : 'DEFAULT',
|
||||
'absolute_override' : False,
|
||||
'target_xyz' : deepcopy(gc['current_xyz']),
|
||||
'offset_ijk' : [0,0,0],
|
||||
'radius_mode' : False,
|
||||
'unsupported': [] }
|
||||
|
||||
# Pass 1
|
||||
for cmd,num in zip(g_cmd,g_num) :
|
||||
fnum = float(num)
|
||||
inum = int(fnum)
|
||||
if cmd is 'G' :
|
||||
if inum is 0 : gc['motion_mode'] = 'SEEK'
|
||||
elif inum is 1 : gc['motion_mode'] = 'LINEAR'
|
||||
elif inum is 2 : gc['motion_mode'] = 'CW_ARC'
|
||||
elif inum is 3 : gc['motion_mode'] = 'CCW_ARC'
|
||||
elif inum is 4 : blk['next_action'] = 'DWELL'
|
||||
elif inum is 17 : gc['plane_axis'] = [0,1,2] # Select XY Plane
|
||||
elif inum is 18 : gc['plane_axis'] = [0,2,1] # Select XZ Plane
|
||||
elif inum is 19 : gc['plane_axis'] = [1,2,0] # Select YZ Plane
|
||||
elif inum is 20 : gc['inches_mode'] = True
|
||||
elif inum is 21 : gc['inches_mode'] = False
|
||||
elif inum in [28,30] : blk['next_action'] = 'GO_HOME'
|
||||
elif inum is 53 : blk['absolute_override'] = True
|
||||
elif inum is 54 : pass
|
||||
elif inum is 80 : gc['motion_mode'] = 'MOTION_CANCEL'
|
||||
elif inum is 90 : gc['absolute_mode'] = True
|
||||
elif inum is 91 : gc['absolute_mode'] = False
|
||||
elif inum is 92 : blk['next_action'] = 'SET_OFFSET'
|
||||
elif inum is 93 : gc['inverse_feedrate_mode'] = True
|
||||
elif inum is 94 : gc['inverse_feedrate_mode'] = False
|
||||
else :
|
||||
print 'Unsupported command ' + cmd + num + ' on line ' + str(l_count)
|
||||
if remove_unsupported : blk['unsupported'].append(zip(g_cmd,g_num).index((cmd,num)))
|
||||
elif cmd is 'M' :
|
||||
if inum in [0,1] : pass # Program Pause
|
||||
elif inum in [2,30,60] : pass # Program Completed
|
||||
elif inum is 3 : pass # Spindle Direction 1
|
||||
elif inum is 4 : pass # Spindle Direction -1
|
||||
elif inum is 5 : pass # Spindle Direction 0
|
||||
else :
|
||||
print 'Unsupported command ' + cmd + num + ' on line ' + str(l_count)
|
||||
if remove_unsupported : blk['unsupported'].append(zip(g_cmd,g_num).index((cmd,num)))
|
||||
elif cmd is 'T' : pass # Tool Number
|
||||
|
||||
# Pass 2
|
||||
for cmd,num in zip(g_cmd,g_num) :
|
||||
fnum = float(num)
|
||||
if cmd is 'F' : gc['feed_rate'] = unit_conv(fnum) # Feed Rate
|
||||
elif cmd in ['I','J','K'] : blk['offset_ijk'][ord(cmd)-ord('I')] = unit_conv(fnum) # Arc Center Offset
|
||||
elif cmd is 'N' : pass
|
||||
elif cmd is 'P' : p = fnum # Misc value parameter
|
||||
elif cmd is 'R' : r = unit_conv(fnum); blk['radius_mode'] = True # Arc Radius Mode
|
||||
elif cmd is 'S' : pass # Spindle Speed
|
||||
elif cmd in ['X','Y','Z'] : # Target Coordinates
|
||||
if (gc['absolute_mode'] | blk['absolute_override']) :
|
||||
blk['target_xyz'][ord(cmd)-ord('X')] = unit_conv(fnum)
|
||||
else :
|
||||
blk['target_xyz'][ord(cmd)-ord('X')] += unit_conv(fnum)
|
||||
|
||||
# Execute actions
|
||||
if blk['next_action'] is 'GO_HOME' :
|
||||
gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position
|
||||
elif blk['next_action'] is 'SET_OFFSET' :
|
||||
pass
|
||||
elif blk['next_action'] is 'DWELL' :
|
||||
if p < 0 : raise Exception('Dwell time negative.')
|
||||
else : # 'DEFAULT'
|
||||
if gc['motion_mode'] is 'SEEK' :
|
||||
fout.write('0 '+fout_conv(gc['feed_rate']))
|
||||
fout.write(' '+fout_conv(blk['target_xyz'][0]))
|
||||
fout.write(' '+fout_conv(blk['target_xyz'][1]))
|
||||
fout.write(' '+fout_conv(blk['target_xyz'][2]))
|
||||
fout.write('\n')
|
||||
gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position
|
||||
elif gc['motion_mode'] is 'LINEAR' :
|
||||
fout.write('1 '+fout_conv(gc['feed_rate']))
|
||||
fout.write(' '+fout_conv(blk['target_xyz'][0]))
|
||||
fout.write(' '+fout_conv(blk['target_xyz'][1]))
|
||||
fout.write(' '+fout_conv(blk['target_xyz'][2]))
|
||||
fout.write('\n')
|
||||
gc['current_xyz'] = deepcopy(blk['target_xyz']) # Update position
|
||||
elif gc['motion_mode'] in ['CW_ARC','CCW_ARC'] :
|
||||
axis = gc['plane_axis']
|
||||
|
||||
# Convert radius mode to ijk mode
|
||||
if blk['radius_mode'] :
|
||||
x = blk['target_xyz'][axis[0]]-gc['current_xyz'][axis[0]]
|
||||
y = blk['target_xyz'][axis[1]]-gc['current_xyz'][axis[1]]
|
||||
if not (x==0 and y==0) : raise Exception('Same target and current XYZ not allowed in arc radius mode.')
|
||||
h_x2_div_d = -sqrt(4 * r*r - x*x - y*y)/hypot(x,y)
|
||||
if isnan(h_x2_div_d) : raise Exception('Floating point error in arc conversion')
|
||||
if gc['motion_mode'] is 'CCW_ARC' : h_x2_div_d = -h_x2_div_d
|
||||
if r < 0 : h_x2_div_d = -h_x2_div_d
|
||||
blk['offset_ijk'][axis[0]] = (x-(y*h_x2_div_d))/2;
|
||||
blk['offset_ijk'][axis[1]] = (y+(x*h_x2_div_d))/2;
|
||||
else :
|
||||
radius = sqrt(blk['offset_ijk'][axis[0]]**2+blk['offset_ijk'][axis[1]]**2)
|
||||
|
||||
center_axis0 = gc['current_xyz'][axis[0]]+blk['offset_ijk'][axis[0]]
|
||||
center_axis1 = gc['current_xyz'][axis[1]]+blk['offset_ijk'][axis[1]]
|
||||
linear_travel = blk['target_xyz'][axis[2]]-gc['current_xyz'][axis[2]]
|
||||
r_axis0 = -blk['offset_ijk'][axis[0]]
|
||||
r_axis1 = -blk['offset_ijk'][axis[1]]
|
||||
rt_axis0 = blk['target_xyz'][axis[0]] - center_axis0;
|
||||
rt_axis1 = blk['target_xyz'][axis[1]] - center_axis1;
|
||||
clockwise_sign = 1
|
||||
if gc['motion_mode'] is 'CW_ARC' : clockwise_sign = -1
|
||||
|
||||
angular_travel = atan2(r_axis0*rt_axis1-r_axis1*rt_axis0, r_axis0*rt_axis0+r_axis1*rt_axis1)
|
||||
if gc['motion_mode'] is 'CW_ARC' :
|
||||
if angular_travel >= 0 :
|
||||
angular_travel -= 2*pi
|
||||
else :
|
||||
if angular_travel <= 0 :
|
||||
angular_travel += 2*pi
|
||||
|
||||
millimeters_of_travel = sqrt((angular_travel*radius)**2 + abs(linear_travel)**2)
|
||||
|
||||
mm_per_arc_segment = sqrt(4*(2*radius*arc_tolerance-arc_tolerance**2))
|
||||
segments = int(millimeters_of_travel/mm_per_arc_segment)
|
||||
print segments
|
||||
print l_count
|
||||
theta_per_segment = angular_travel/segments
|
||||
linear_per_segment = linear_travel/segments
|
||||
cos_T = 1-0.5*theta_per_segment*theta_per_segment
|
||||
sin_T = theta_per_segment-theta_per_segment**3/6
|
||||
print(fout_conv(mm_per_arc_segment))
|
||||
print theta_per_segment*180/pi
|
||||
|
||||
arc_target = [0,0,0]
|
||||
arc_target[axis[2]] = gc['current_xyz'][axis[2]]
|
||||
|
||||
count = 0
|
||||
for i in range(1,segments+1) :
|
||||
if i < segments :
|
||||
if count < n_arc_correction :
|
||||
r_axisi = r_axis0*sin_T + r_axis1*cos_T
|
||||
r_axis0 = r_axis0*cos_T - r_axis1*sin_T
|
||||
r_axis1 = deepcopy(r_axisi)
|
||||
count += 1
|
||||
else :
|
||||
cos_Ti = cos((i-1)*theta_per_segment)
|
||||
sin_Ti = sin((i-1)*theta_per_segment)
|
||||
print n_arc_correction*(r_axis0 -( -blk['offset_ijk'][axis[0]]*cos_Ti + blk['offset_ijk'][axis[1]]*sin_Ti))
|
||||
print n_arc_correction*(r_axis1 -( -blk['offset_ijk'][axis[0]]*sin_Ti - blk['offset_ijk'][axis[1]]*cos_Ti))
|
||||
cos_Ti = cos(i*theta_per_segment)
|
||||
sin_Ti = sin(i*theta_per_segment)
|
||||
r_axis0 = -blk['offset_ijk'][axis[0]]*cos_Ti + blk['offset_ijk'][axis[1]]*sin_Ti
|
||||
r_axis1 = -blk['offset_ijk'][axis[0]]*sin_Ti - blk['offset_ijk'][axis[1]]*cos_Ti
|
||||
count = 0
|
||||
arc_target[axis[0]] = center_axis0 + r_axis0
|
||||
arc_target[axis[1]] = center_axis1 + r_axis1
|
||||
arc_target[axis[2]] += linear_per_segment
|
||||
else :
|
||||
arc_target = deepcopy(blk['target_xyz']) # Last segment at target_xyz
|
||||
# Write only changed variables.
|
||||
fout.write('1 '+fout_conv(gc['feed_rate']))
|
||||
fout.write(' '+fout_conv(arc_target[0]))
|
||||
fout.write(' '+fout_conv(arc_target[1]))
|
||||
fout.write(' '+fout_conv(arc_target[2]))
|
||||
fout.write('\n')
|
||||
gc['current_xyz'] = deepcopy(arc_target) # Update position
|
||||
|
||||
|
||||
print 'Done!'
|
||||
|
||||
# Close files
|
||||
fin.close()
|
||||
fout.close()
|
2363
doc/test/test.gcode
2363
doc/test/test.gcode
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user