-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
polar: Experimental support for polar kinematics
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
- Loading branch information
1 parent
7e3e02a
commit ec9cb3a
Showing
5 changed files
with
298 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# This file serves as documentation for config parameters of "polar" | ||
# style printers. One may copy and edit this file to configure a new | ||
# polar printer. | ||
|
||
# POLAR KINEMATICS ARE A WORK IN PROGRESS. Moves around the 0,0 | ||
# position are known to not work properly. Moves to a negative Y | ||
# coordinate from a positive Y coordinate (and vice-versa) when the | ||
# head is at a negative X coordinate also do not work properly. | ||
|
||
# Only parameters unique to polar printers are described here - see | ||
# the "example.cfg" file for description of common config parameters. | ||
|
||
# The stepper_bed section is used to describe the stepper controlling | ||
# the bed. | ||
[stepper_bed] | ||
step_pin: ar54 | ||
dir_pin: ar55 | ||
enable_pin: !ar38 | ||
step_distance: 0.000981748 | ||
# On a polar printer the step_distance is the amount each step pulse | ||
# moves the bed in radians (for example, a 1.8 degree stepper with | ||
# 16 micro-steps would be 1.8 / 360 * pi / 16 == 0.000981748). This | ||
# parameter must be provided. | ||
|
||
# The stepper_arm section is used to describe the stepper controlling | ||
# the carriage on the arm. | ||
[stepper_arm] | ||
step_pin: ar60 | ||
dir_pin: ar61 | ||
enable_pin: !ar56 | ||
step_distance: .01 | ||
endstop_pin: ^ar14 | ||
position_endstop: 300 | ||
position_max: 300 | ||
homing_speed: 50 | ||
|
||
# The stepper_z section is used to describe the stepper controlling | ||
# the Z axis. | ||
[stepper_z] | ||
step_pin: ar46 | ||
dir_pin: ar48 | ||
enable_pin: !ar62 | ||
step_distance: .0025 | ||
endstop_pin: ^ar18 | ||
position_endstop: 0.5 | ||
position_max: 200 | ||
|
||
[extruder] | ||
step_pin: ar26 | ||
dir_pin: ar28 | ||
enable_pin: !ar24 | ||
step_distance: .0022 | ||
nozzle_diameter: 0.400 | ||
filament_diameter: 1.750 | ||
heater_pin: ar10 | ||
sensor_type: ATC Semitec 104GT-2 | ||
sensor_pin: analog13 | ||
control: pid | ||
pid_Kp: 22.2 | ||
pid_Ki: 1.08 | ||
pid_Kd: 114 | ||
min_temp: 0 | ||
max_temp: 250 | ||
|
||
[heater_bed] | ||
heater_pin: ar8 | ||
sensor_type: EPCOS 100K B57560G104F | ||
sensor_pin: analog14 | ||
control: watermark | ||
min_temp: 0 | ||
max_temp: 130 | ||
|
||
[fan] | ||
pin: ar9 | ||
|
||
[mcu] | ||
serial: /dev/ttyACM0 | ||
pin_map: arduino | ||
|
||
[printer] | ||
kinematics: polar | ||
# This option must be "polar" for polar printers. | ||
max_velocity: 300 | ||
max_accel: 3000 | ||
max_z_velocity: 25 | ||
max_z_accel: 30 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Polar kinematics stepper pulse time generation | ||
// | ||
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net> | ||
// | ||
// This file may be distributed under the terms of the GNU GPLv3 license. | ||
|
||
#include <math.h> // sqrt | ||
#include <stdlib.h> // malloc | ||
#include <string.h> // memset | ||
#include "compiler.h" // __visible | ||
#include "itersolve.h" // struct stepper_kinematics | ||
|
||
static double | ||
polar_stepper_radius_calc_position(struct stepper_kinematics *sk, struct move *m | ||
, double move_time) | ||
{ | ||
struct coord c = move_get_coord(m, move_time); | ||
return sqrt(c.x*c.x + c.y*c.y); | ||
} | ||
|
||
static double | ||
polar_stepper_angle_calc_position(struct stepper_kinematics *sk, struct move *m | ||
, double move_time) | ||
{ | ||
struct coord c = move_get_coord(m, move_time); | ||
// XXX - handle x==y==0 | ||
// XXX - handle angle wrapping | ||
return atan2(c.y, c.x); | ||
} | ||
|
||
struct stepper_kinematics * __visible | ||
polar_stepper_alloc(char type) | ||
{ | ||
struct stepper_kinematics *sk = malloc(sizeof(*sk)); | ||
memset(sk, 0, sizeof(*sk)); | ||
if (type == 'r') | ||
sk->calc_position = polar_stepper_radius_calc_position; | ||
else if (type == 'a') | ||
sk->calc_position = polar_stepper_angle_calc_position; | ||
return sk; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# Code for handling the kinematics of polar robots | ||
# | ||
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net> | ||
# | ||
# This file may be distributed under the terms of the GNU GPLv3 license. | ||
import logging, math | ||
import stepper, homing | ||
|
||
class PolarKinematics: | ||
def __init__(self, toolhead, config): | ||
# Setup axis steppers | ||
stepper_bed = stepper.PrinterStepper(config.getsection('stepper_bed')) | ||
rail_arm = stepper.PrinterRail(config.getsection('stepper_arm')) | ||
rail_z = stepper.LookupMultiRail(config.getsection('stepper_z')) | ||
stepper_bed.setup_itersolve('polar_stepper_alloc', 'a') | ||
rail_arm.setup_itersolve('polar_stepper_alloc', 'r') | ||
rail_z.setup_itersolve('cartesian_stepper_alloc', 'z') | ||
self.rails = [rail_arm, rail_z] | ||
self.steppers = [stepper_bed] + [ s for r in self.rails | ||
for s in r.get_steppers() ] | ||
# Setup boundary checks | ||
max_velocity, max_accel = toolhead.get_max_velocity() | ||
self.max_z_velocity = config.getfloat( | ||
'max_z_velocity', max_velocity, above=0., maxval=max_velocity) | ||
self.max_z_accel = config.getfloat( | ||
'max_z_accel', max_accel, above=0., maxval=max_accel) | ||
self.need_motor_enable = True | ||
self.limit_z = [(1.0, -1.0)] | ||
self.limit_xy2 = -1. | ||
# Setup stepper max halt velocity | ||
max_halt_velocity = toolhead.get_max_axis_halt() | ||
stepper_bed.set_max_jerk(max_halt_velocity, max_accel) | ||
rail_arm.set_max_jerk(max_halt_velocity, max_accel) | ||
rail_z.set_max_jerk(max_halt_velocity, max_accel) | ||
def get_steppers(self, flags=""): | ||
if flags == "Z": | ||
return self.rails[1].get_steppers() | ||
return list(self.steppers) | ||
def calc_position(self): | ||
bed_angle = self.steppers[0].get_commanded_position() | ||
arm_pos = self.rails[0].get_commanded_position() | ||
z_pos = self.rails[1].get_commanded_position() | ||
return [math.cos(bed_angle) * arm_pos, math.sin(bed_angle) * arm_pos, | ||
z_pos] | ||
def set_position(self, newpos, homing_axes): | ||
for s in self.steppers: | ||
s.set_position(newpos) | ||
if 2 in homing_axes: | ||
self.limit_z = self.rails[1].get_range() | ||
if 0 in homing_axes and 1 in homing_axes: | ||
self.limit_xy2 = self.rails[0].get_range()[1]**2 | ||
def _home_axis(self, homing_state, axis, rail): | ||
# Determine movement | ||
position_min, position_max = rail.get_range() | ||
hi = rail.get_homing_info() | ||
homepos = [None, None, None, None] | ||
homepos[axis] = hi.position_endstop | ||
if axis == 0: | ||
homepos[1] = 0. | ||
forcepos = list(homepos) | ||
if hi.positive_dir: | ||
forcepos[axis] -= hi.position_endstop - position_min | ||
else: | ||
forcepos[axis] += position_max - hi.position_endstop | ||
# Perform homing | ||
limit_speed = None | ||
if axis == 2: | ||
limit_speed = self.max_z_velocity | ||
homing_state.home_rails([rail], forcepos, homepos, limit_speed) | ||
def home(self, homing_state): | ||
# Always home XY together | ||
homing_axes = homing_state.get_axes() | ||
home_xy = 0 in homing_axes or 1 in homing_axes | ||
home_z = 2 in homing_axes | ||
updated_axes = [] | ||
if home_xy: | ||
updated_axes = [0, 1] | ||
if home_z: | ||
updated_axes.append(2) | ||
homing_state.set_axes(updated_axes) | ||
# Do actual homing | ||
if home_xy: | ||
self._home_axis(homing_state, 0, self.rails[0]) | ||
if home_z: | ||
self._home_axis(homing_state, 2, self.rails[1]) | ||
def motor_off(self, print_time): | ||
self.limit_z = [(1.0, -1.0)] | ||
self.limit_xy2 = -1. | ||
for s in self.steppers: | ||
s.motor_enable(print_time, 0) | ||
self.need_motor_enable = True | ||
def _check_motor_enable(self, print_time, move): | ||
if move.axes_d[0] or move.axes_d[1]: | ||
self.steppers[0].motor_enable(print_time, 1) | ||
self.rails[0].motor_enable(print_time, 1) | ||
if move.axes_d[2]: | ||
self.rails[1].motor_enable(print_time, 1) | ||
need_motor_enable = not self.steppers[0].is_motor_enabled() | ||
for rail in self.rails: | ||
need_motor_enable |= not rail.is_motor_enabled() | ||
self.need_motor_enable = need_motor_enable | ||
def check_move(self, move): | ||
end_pos = move.end_pos | ||
xy2 = end_pos[0]**2 + end_pos[1]**2 | ||
if xy2 > self.limit_xy2: | ||
if self.limit_xy2 < 0.: | ||
raise homing.EndstopMoveError(end_pos, "Must home axis first") | ||
raise homing.EndstopMoveError(end_pos) | ||
if move.axes_d[2]: | ||
if end_pos[2] < self.limit_z[0] or end_pos[2] > self.limit_z[1]: | ||
if self.limit_z[0] > self.limit_z[1]: | ||
raise homing.EndstopMoveError( | ||
end_pos, "Must home axis first") | ||
raise homing.EndstopMoveError(end_pos) | ||
# Move with Z - update velocity and accel for slower Z axis | ||
z_ratio = move.move_d / abs(move.axes_d[2]) | ||
move.limit_speed( | ||
self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio) | ||
def move(self, print_time, move): | ||
if self.need_motor_enable: | ||
self._check_motor_enable(print_time, move) | ||
axes_d = move.axes_d | ||
cmove = move.cmove | ||
if axes_d[0] or axes_d[1]: | ||
self.steppers[0].step_itersolve(cmove) | ||
self.rails[0].step_itersolve(cmove) | ||
if axes_d[2]: | ||
self.rails[1].step_itersolve(cmove) | ||
|
||
def load_kinematics(toolhead, config): | ||
return PolarKinematics(toolhead, config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Test case for basic movement on polar printers | ||
CONFIG ../../config/example-polar.cfg | ||
DICTIONARY atmega2560-16mhz.dict | ||
|
||
; Start by homing the printer. | ||
G28 | ||
G90 | ||
G1 F6000 | ||
|
||
; Z / X / Y moves | ||
G1 Z1 | ||
G1 X1 | ||
G1 Y1 | ||
|
||
; Delayed moves | ||
G1 Y2 | ||
G4 P100 | ||
G1 Y1.5 | ||
M400 | ||
G1 Y1 | ||
|
||
; diagonal moves | ||
G1 X10 Y0 | ||
G1 X1 Z2 | ||
G1 X0 Y1 Z1 | ||
|
||
; extrude only moves | ||
G1 E1 | ||
G1 E0 | ||
|
||
; regular extrude move | ||
G1 X10 Y0 E.01 |