Added scripts
This commit is contained in:
parent
e9bb224f1b
commit
a8a57616d2
|
|
@ -0,0 +1,340 @@
|
||||||
|
require(dplyr)
|
||||||
|
require(ggplot2)
|
||||||
|
require(reshape2)
|
||||||
|
require(stringr)
|
||||||
|
require(datetime)
|
||||||
|
|
||||||
|
ggplotRegression <- function (fit) {
|
||||||
|
ggplot(fit$model, aes_string(x = names(fit$model)[2], y = names(fit$model)[1])) +
|
||||||
|
geom_point() +
|
||||||
|
stat_smooth(method = "lm", col = "black", linetype=3, se = F) +
|
||||||
|
labs(subtitle = paste("R^2 =",signif(summary(fit)$adj.r.squared, 5),
|
||||||
|
"\nIntercept =",signif(fit$coef[[1]],5 ),
|
||||||
|
"\nSlope =",signif(fit$coef[[2]], 5)))+
|
||||||
|
theme_classic()
|
||||||
|
}
|
||||||
|
|
||||||
|
calc_z_comp <- function(t, tref, len, coeff) {
|
||||||
|
-1*(t-tref)*coeff*len*1000
|
||||||
|
}
|
||||||
|
|
||||||
|
datafiles <- list.files(pattern = ".csv")
|
||||||
|
|
||||||
|
extract_meta_item <- function(item, meta) {
|
||||||
|
line_string = meta[which(str_detect(meta,paste0("^# ",item)))]
|
||||||
|
value = str_split(line_string, pattern = "=",simplify = T)[2]
|
||||||
|
return(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for(file in datafiles) {
|
||||||
|
|
||||||
|
lines = readLines(con = file(file))
|
||||||
|
meta = lines[str_detect(lines, "#")]
|
||||||
|
|
||||||
|
frame_z = as.numeric(extract_meta_item("frame_z_length",meta))/1000
|
||||||
|
user = extract_meta_item("id",meta)
|
||||||
|
dataset_id = extract_meta_item("timestamp",meta)
|
||||||
|
printer_id = extract_meta_item("printer",meta)
|
||||||
|
measure_meth = extract_meta_item("measure_type",meta)
|
||||||
|
step_dist = as.numeric(extract_meta_item("step_dist", meta))
|
||||||
|
hot_dur = as.numeric(extract_meta_item("hot_duration",meta))*60
|
||||||
|
cool_dur = as.numeric(extract_meta_item("cool_duration",meta))*60
|
||||||
|
frame_coeff = as.numeric(extract_meta_item("coeff", meta))*1E-6
|
||||||
|
|
||||||
|
root = paste0(user,"/",dataset_id)
|
||||||
|
dir.create(root,recursive = TRUE)
|
||||||
|
|
||||||
|
data_raw = read.csv(file, comment.char = "#")
|
||||||
|
standard_columns = c(
|
||||||
|
"sample",
|
||||||
|
"time",
|
||||||
|
"mcu_pos_z",
|
||||||
|
"frame_t",
|
||||||
|
"bed_t",
|
||||||
|
"bed_target",
|
||||||
|
"he_t",
|
||||||
|
"he_target"
|
||||||
|
)
|
||||||
|
extra_columns <- colnames(data_raw)[!colnames(data_raw) %in% standard_columns]
|
||||||
|
|
||||||
|
bed_off_idx <- which.max(data_raw$bed_target == 0)
|
||||||
|
data <- data_raw[c(standard_columns, extra_columns)] %>%
|
||||||
|
mutate("delta_z" = (mcu_pos_z - mcu_pos_z[1])*step_dist,
|
||||||
|
"time" = as.POSIXct(time, format="%Y/%m/%d-%H:%M:%S"),
|
||||||
|
"elapsed_time" = as.numeric(difftime(time,min(time),units = 'min',))
|
||||||
|
) %>%
|
||||||
|
filter(delta_z < 2) %>%
|
||||||
|
select(-ends_with("target"))
|
||||||
|
|
||||||
|
data_mean_wide <- data %>%
|
||||||
|
select(-time) %>%
|
||||||
|
group_by(sample) %>%
|
||||||
|
summarise_all(.funs=list(mean))
|
||||||
|
|
||||||
|
data_mean <- data_mean_wide %>%
|
||||||
|
melt(id.vars = "elapsed_time") %>%
|
||||||
|
mutate(variable=factor(variable,labels = c(
|
||||||
|
"Sample",
|
||||||
|
"MCU Position",
|
||||||
|
"Frame",
|
||||||
|
"Bed",
|
||||||
|
"Hotend",
|
||||||
|
extra_columns,
|
||||||
|
"Delta Endstop"
|
||||||
|
))) %>%
|
||||||
|
rename(mean=value)
|
||||||
|
|
||||||
|
|
||||||
|
data_sd_wide = data %>%
|
||||||
|
select(-time) %>%
|
||||||
|
group_by(sample) %>%
|
||||||
|
summarise_all(.funs=list(sd))
|
||||||
|
data_sd <- data_sd_wide %>%
|
||||||
|
melt(id.vars = "elapsed_time") %>%
|
||||||
|
mutate(variable=factor(variable,labels = c(
|
||||||
|
"Sample",
|
||||||
|
"MCU Position",
|
||||||
|
"Frame",
|
||||||
|
"Bed",
|
||||||
|
"Hotend",
|
||||||
|
extra_columns,
|
||||||
|
"Delta Endstop"
|
||||||
|
)),
|
||||||
|
elapsed_time = data_mean$elapsed_time) %>%
|
||||||
|
rename(sd=value)
|
||||||
|
|
||||||
|
data_all <- left_join(data_mean,data_sd, by = c("elapsed_time", "variable"))
|
||||||
|
|
||||||
|
##############################
|
||||||
|
|
||||||
|
# Overview plot
|
||||||
|
data_all_overview <- data_all
|
||||||
|
data_all_overview[data_all$variable == "Delta Endstop", "mean"] = data_all[data_all$variable=="Delta Endstop","mean"]*500
|
||||||
|
data_all_overview[data_all$variable == "Delta Endstop", "sd"] = data_all[data_all$variable=="Delta Endstop","sd"]*500
|
||||||
|
|
||||||
|
|
||||||
|
ggplot(data=filter(data_all_overview, !variable %in% c("Sample", "MCU Position")),
|
||||||
|
aes(elapsed_time, mean, colour=variable)) +
|
||||||
|
geom_hline(yintercept = 0, linetype=2) +
|
||||||
|
geom_line() +
|
||||||
|
geom_errorbar(aes(ymin=mean-sd, ymax=mean+sd), color="black", width=0) +
|
||||||
|
scale_y_continuous(sec.axis = sec_axis(~./500,name = expression(Delta*"Endstop Position [mm]"),
|
||||||
|
breaks = seq(-5,5,0.02)),
|
||||||
|
breaks=seq(-400,400,10), labels = c(rep("",40),seq(0,400,10))) +
|
||||||
|
scale_x_continuous(breaks = seq(0,1000,15))+
|
||||||
|
scale_colour_brewer(type = "qual", palette = 6,direction = -1) +
|
||||||
|
labs(x = expression("Elapsed Time [min]"),
|
||||||
|
y = expression("Temperature ["*degree*"C]", colour="")) +
|
||||||
|
# title = "Frame Expansion Measurement", subtitle = file) +
|
||||||
|
theme_classic() +
|
||||||
|
theme(panel.grid.major = element_line(colour = gray(0.9)),
|
||||||
|
legend.position = "bottom",#c(0.8, 0.3),
|
||||||
|
legend.background = element_rect(colour = "black"),
|
||||||
|
legend.title = element_blank(),
|
||||||
|
# aspect.ratio = 1,
|
||||||
|
plot.title = element_text(hjust = 0.5),
|
||||||
|
plot.subtitle = element_text(hjust = 0.5))
|
||||||
|
ggsave(paste0(root,"/overview.png"),device = "png", width = 9, height = 5,
|
||||||
|
units = "in", dpi=300)
|
||||||
|
|
||||||
|
##############################
|
||||||
|
|
||||||
|
# Overview plot, facetted
|
||||||
|
ggplot(data=filter(data_all, !variable %in% c("Sample", "Hotend","Bed", "MCU Position")),
|
||||||
|
aes(elapsed_time, mean, colour=variable)) +
|
||||||
|
# geom_hline(yintercept = 0, linetype=2) +
|
||||||
|
geom_line() +
|
||||||
|
geom_errorbar(aes(ymin=mean-sd, ymax=mean+sd), color="black", width=0) +
|
||||||
|
# scale_y_continuous(sec.axis = sec_axis(~./500,name = expression(Delta*"Endstop Position [mm]"),
|
||||||
|
# breaks = seq(-5,5,0.025)),
|
||||||
|
# breaks=seq(-200,400,10), labels = c(rep("",20),seq(0,400,10))) +
|
||||||
|
# scale_x_continuous(breaks = seq(0,1000,30))+
|
||||||
|
scale_colour_brewer(type = "qual", palette = 6,direction = -1) +
|
||||||
|
labs(x = expression("Elapsed Time [min]"),
|
||||||
|
y = expression("Temperature ["*degree*"C]", colour=""),
|
||||||
|
title = "Frame Expansion Measurement", subtitle = file) +
|
||||||
|
theme_classic() +
|
||||||
|
theme(panel.grid.major = element_line(colour = gray(0.9)),
|
||||||
|
legend.position = "None",
|
||||||
|
legend.background = element_rect(colour = "black"),
|
||||||
|
legend.title = element_blank(),
|
||||||
|
aspect.ratio = 1,
|
||||||
|
plot.title = element_text(hjust = 0.5),
|
||||||
|
plot.subtitle = element_text(hjust = 0.5)) +
|
||||||
|
facet_wrap(~variable, nrow = 1, scales = "free")
|
||||||
|
|
||||||
|
ggsave(paste0(root,"/overview_facetted.png"),device = "png", width = 8, height = 5,
|
||||||
|
units = "in", dpi=300)
|
||||||
|
|
||||||
|
##############################
|
||||||
|
|
||||||
|
# Adjust delta Z for fitting
|
||||||
|
data_filtered <- data %>%
|
||||||
|
filter(20 < elapsed_time,
|
||||||
|
elapsed_time < hot_dur-5)
|
||||||
|
# filter(frame_t < max(frame_t)-0.3)
|
||||||
|
fit_t_vs_z_poly = lm(frame_t~poly(data_filtered$delta_z,3), data_filtered[c("frame_t", "delta_z")])
|
||||||
|
fit_t_vs_z_lin = lm(frame_t~poly(data_filtered$delta_z,1), data_filtered[c("frame_t", "delta_z")])
|
||||||
|
data_fitted <- data_filtered %>%
|
||||||
|
mutate(t_fit_lin = fitted(fit_t_vs_z_lin),
|
||||||
|
t_fit_poly = fitted(fit_t_vs_z_poly),
|
||||||
|
t_fit_poly_resid = residuals(fit_t_vs_z_poly)
|
||||||
|
# t_fit_logis = fitted(fit_t_vs_z_logis)
|
||||||
|
)
|
||||||
|
|
||||||
|
#png(paste0(root,"/residuals.png"))
|
||||||
|
#plot(fitted(fit_t_vs_z_poly), residuals(fit_t_vs_z_poly),
|
||||||
|
# xlab = "Frame Temp",
|
||||||
|
# ylab = "Residual",
|
||||||
|
# main = "Polynomial (3) Fit")
|
||||||
|
#abline(h=0,lty="dashed")
|
||||||
|
#dev.off()
|
||||||
|
|
||||||
|
|
||||||
|
ggplot(data = data_fitted,
|
||||||
|
aes(x=delta_z, y=frame_t,fill=frame_t)) +
|
||||||
|
geom_line(aes(y=t_fit_poly, color='Fit(Poly)'), linetype=1, size=rel(1)) +
|
||||||
|
geom_line(aes(y=t_fit_lin, color='Fit(Linear)'), linetype=2, size=rel(1)) +
|
||||||
|
# geom_line(aes(y=t_fit_logis, color='Fit(Logis)'), linetype=3, size=rel(1.5)) +
|
||||||
|
geom_point(aes(color='Measured'),shape=21) +
|
||||||
|
labs(x=expression(Delta*" Endstop Position [mm]"),
|
||||||
|
y=expression("Frame Temperature "*"["*degree*C*"]"),
|
||||||
|
colour="Data Type",
|
||||||
|
fill="Frame Temperature",
|
||||||
|
title = "Expansion Fitting", subtitle = file)+
|
||||||
|
scale_colour_brewer(type = "qual", palette = 6,direction = 1) +
|
||||||
|
scale_fill_gradient2(low="red", mid = "yellow",
|
||||||
|
midpoint = min(data_fitted$frame_t) + (max(data_fitted$frame_t)-min(data_fitted$frame_t))/2,
|
||||||
|
high="blue") +
|
||||||
|
theme_classic() +
|
||||||
|
theme(panel.grid.major = element_line(colour = gray(0.9)),
|
||||||
|
legend.position = c(0.8, 0.8),
|
||||||
|
legend.background = element_rect(colour = "black"),
|
||||||
|
# legend.title = element_blank(),
|
||||||
|
aspect.ratio = 1,
|
||||||
|
plot.title = element_text(hjust = 0.5),
|
||||||
|
plot.subtitle = element_text(hjust = 0.5))
|
||||||
|
#ggsave(paste0(root,"/curve_fitting.png"),device = "png", width = 6, height = 6,
|
||||||
|
# units = "in", dpi=300)
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# plot(data_filtered$frame_t, data_filtered$delta_z,xlab = "Frame Temperature [degC]",ylab = "Endstop Drift [mm]")
|
||||||
|
# lines(fitted(fit_t_vs_z_poly), data_filtered$delta_z, col="blue", lw=3)
|
||||||
|
# lines(fitted(fit_t_vs_z_lin), data_filtered$delta_z, col="Red", lw=1,lty=2)
|
||||||
|
# legend("topright",c("Poly(3)", "Linear"), col=c("blue","red"), lty=c(1,2), inset = c(0.1,0.1))
|
||||||
|
|
||||||
|
theoretical <- data.frame(frame_t = data_filtered$frame_t) %>%
|
||||||
|
mutate(comp_z = calc_z_comp(frame_t, frame_t[1], frame_z, frame_coeff),
|
||||||
|
delta_z = data_filtered$delta_z,
|
||||||
|
deviation = comp_z-delta_z,
|
||||||
|
elapsed_time = data_filtered$elapsed_time)
|
||||||
|
|
||||||
|
ggplot(theoretical, aes(x=frame_t, y=deviation, colour=elapsed_time)) +
|
||||||
|
geom_point() +
|
||||||
|
geom_hline(yintercept = 0, linetype=2) +
|
||||||
|
scale_color_gradient2(low="red", mid = "yellow",
|
||||||
|
midpoint = max(theoretical$elapsed_time/2),
|
||||||
|
high="blue") +
|
||||||
|
labs(x=expression("Frame Temperature "*"["*degree*C*"]"),
|
||||||
|
y="Compensation Error (Theoretical-Measured) [mm]",
|
||||||
|
color="Elapsed Time [min]") +
|
||||||
|
theme_classic() +
|
||||||
|
theme(panel.grid.major = element_line(colour=grey(0.5,0.2)),
|
||||||
|
legend.position = "bottom",)
|
||||||
|
#ggsave(paste0(root,"/comp_error.png"),device = "png", width = 6, height = 5,
|
||||||
|
# units = "in", dpi=300)
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
fit = lm(theoretical$delta_z~theoretical$comp_z)
|
||||||
|
fit_poly = lm(theoretical$delta_z~poly(theoretical$comp_z,3))
|
||||||
|
|
||||||
|
ggplotRegression(fit) +
|
||||||
|
geom_point(data=theoretical, aes(x=comp_z, y=delta_z, colour=frame_t)) +
|
||||||
|
# scale_color_gradient(low = "black", high = "red",guide = "colourbar") +
|
||||||
|
scale_color_gradient2(low="blue", mid = "yellow",
|
||||||
|
midpoint = min(theoretical$frame_t) + (max(theoretical$frame_t)-min(theoretical$frame_t))/2,
|
||||||
|
high="red") +
|
||||||
|
geom_abline(slope=1, intercept = 0, linetype=2, colour=gray(0.5)) +
|
||||||
|
# geom_point(aes(x=fitted(fit_poly)))+
|
||||||
|
# coord_equal() +
|
||||||
|
# scale_x_continuous(breaks=seq(-2,2,0.02))+
|
||||||
|
# scale_y_continuous(breaks=seq(-2,2,0.02))+
|
||||||
|
labs(x="Z Frame Comp Correction [mm]\ngantry_factor=1; 6005A-T5 Alu Alloy",
|
||||||
|
y="Measured Endstop Drift [mm]",
|
||||||
|
color = expression("Frame Temperature "*"["*degree*C*"]"),
|
||||||
|
title = "Measured v. Theoretical") +
|
||||||
|
theme(panel.grid.major = element_line(colour=grey(0.5,0.2)),
|
||||||
|
legend.position = "bottom",
|
||||||
|
plot.title = element_text(hjust = 0),)
|
||||||
|
|
||||||
|
ggsave(paste0(root,"/measured_v_comp.png"),device = "png",
|
||||||
|
width = 6,
|
||||||
|
height = 6,
|
||||||
|
units = "in",
|
||||||
|
dpi=300)
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# overview with theoretical corrected Z height
|
||||||
|
|
||||||
|
home_time <- 30
|
||||||
|
idx_home_ref <- which.max(data_mean_wide$elapsed_time > home_time)
|
||||||
|
|
||||||
|
data_all_wide <- left_join(data_mean_wide, data_sd_wide, by="sample",suffix = c("_mean", "_sd")) %>%
|
||||||
|
mutate(
|
||||||
|
delta_z_mean_homed = delta_z_mean-delta_z_mean[idx_home_ref],
|
||||||
|
z_corr_theoretical = delta_z_mean_homed - calc_z_comp(frame_t_mean,
|
||||||
|
frame_t_mean[idx_home_ref],
|
||||||
|
frame_z,
|
||||||
|
frame_coeff),
|
||||||
|
z_corr_meas_adjust = delta_z_mean_homed - calc_z_comp(frame_t_mean,
|
||||||
|
frame_t_mean[idx_home_ref],
|
||||||
|
frame_z,
|
||||||
|
frame_coeff)*fit$coefficients[2]
|
||||||
|
) %>%
|
||||||
|
filter(elapsed_time_mean < hot_dur)
|
||||||
|
|
||||||
|
ggplot(data=filter(data_all_wide, elapsed_time_mean >= home_time-10),
|
||||||
|
aes(elapsed_time_mean, alpha=(elapsed_time_mean >= home_time))) +
|
||||||
|
geom_hline(yintercept = 0, linetype=2) +
|
||||||
|
geom_line(aes(y=z_corr_meas_adjust, color=paste0("Fit-Adjusted Compensation\ngantry_factor=", round(fit$coefficients[2],2)))) +
|
||||||
|
geom_line(aes(y=z_corr_theoretical, color="Frame Expansion\nCompensation")) +
|
||||||
|
geom_line(aes(y=delta_z_mean_homed, color="Uncorrected")) +
|
||||||
|
geom_vline(xintercept = home_time, linetype=2) +
|
||||||
|
geom_errorbar(aes(ymin=z_corr_meas_adjust-delta_z_sd,
|
||||||
|
ymax=z_corr_meas_adjust+delta_z_sd),
|
||||||
|
color="black", width=0) +
|
||||||
|
geom_errorbar(aes(ymin=delta_z_mean_homed-delta_z_sd,
|
||||||
|
ymax=delta_z_mean_homed+delta_z_sd),
|
||||||
|
color="black", width=0) +
|
||||||
|
geom_errorbar(aes(ymin=z_corr_theoretical-delta_z_sd,
|
||||||
|
ymax=z_corr_theoretical+delta_z_sd),
|
||||||
|
color="black", width=0) +
|
||||||
|
geom_label(aes(x=home_time, y=0.05, label="<-Print Start"),
|
||||||
|
hjust=0, nudge_x = 5,) +
|
||||||
|
scale_y_continuous(breaks=seq(-5,5,0.01)) +
|
||||||
|
scale_x_continuous(limits = c(home_time-10,
|
||||||
|
max(data_all_wide$elapsed_time_mean)),
|
||||||
|
breaks = seq(0,1000,15))+
|
||||||
|
scale_colour_brewer(type = "qual", palette = 6,direction = -1) +
|
||||||
|
scale_alpha_discrete(guide=FALSE) +
|
||||||
|
labs(x = expression("Elapsed Time [min]"),
|
||||||
|
y = expression(Delta*"Endstop Position [mm]", colour=""),
|
||||||
|
title = "Theoretical Correction",
|
||||||
|
subtitle = paste0(user,"(",dataset_id,")",'\n',printer_id,"(",measure_meth,")")) +
|
||||||
|
# title = "Frame Expansion Measurement", subtitle = file) +
|
||||||
|
theme_classic() +
|
||||||
|
theme(panel.grid.major = element_line(colour = gray(0.9)),
|
||||||
|
legend.position = "bottom",
|
||||||
|
legend.background = element_rect(colour = "black"),
|
||||||
|
legend.title = element_blank(),
|
||||||
|
aspect.ratio = 1,
|
||||||
|
plot.title = element_text(hjust = 0.5),
|
||||||
|
plot.subtitle = element_text(hjust = 0.5))
|
||||||
|
|
||||||
|
ggsave(paste0(root,"/z_corrected_timeseries.png"),device = "png", width = 6, height = 5,
|
||||||
|
units = "in", dpi=300)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
#!/bin/bash
|
||||||
|
KLIPPER_PATH="${HOME}/klipper"
|
||||||
|
SYSTEMDDIR="/etc/systemd/system"
|
||||||
|
|
||||||
|
# Step 1: Verify Klipper has been installed
|
||||||
|
check_klipper()
|
||||||
|
{
|
||||||
|
if [ "$(sudo systemctl list-units --full -all -t service --no-legend | grep -F "klipper.service")" ]; then
|
||||||
|
echo "Klipper service found!"
|
||||||
|
else
|
||||||
|
echo "Klipper service not found, please install Klipper first"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 2: Install startup script
|
||||||
|
install_script()
|
||||||
|
{
|
||||||
|
# Create systemd service file
|
||||||
|
SERVICE_FILE="${SYSTEMDDIR}/klipper_config.service"
|
||||||
|
[ -f $SERVICE_FILE ] && return
|
||||||
|
echo "Installing system start script..."
|
||||||
|
sudo /bin/sh -c "cat > ${SERVICE_FILE}" << EOF
|
||||||
|
[Unit]
|
||||||
|
Description=Dummy Service for klipper-config
|
||||||
|
After=klipper.service
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/bin/bash -c 'exec -a klipper-config sleep 1'
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
# Use systemctl to enable the systemd service script
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable klipper-config.service
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 3: restarting Klipper
|
||||||
|
restart_klipper()
|
||||||
|
{
|
||||||
|
echo "Restarting Klipper..."
|
||||||
|
sudo systemctl restart klipper
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper functions
|
||||||
|
verify_ready()
|
||||||
|
{
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
echo "This script must not run as root"
|
||||||
|
exit -1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Force script to exit if an error occurs
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Find SRCDIR from the pathname of this script
|
||||||
|
SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/ && pwd )"
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
while getopts "k:" arg; do
|
||||||
|
case $arg in
|
||||||
|
k) KLIPPER_PATH=$OPTARG;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Run steps
|
||||||
|
verify_ready
|
||||||
|
install_script
|
||||||
|
restart_klipper
|
||||||
|
|
@ -0,0 +1,296 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from datetime import timedelta, datetime
|
||||||
|
from os import error
|
||||||
|
from time import sleep
|
||||||
|
from requests import get, post
|
||||||
|
import re
|
||||||
|
|
||||||
|
######### META DATA #################
|
||||||
|
# For data collection organizational purposes
|
||||||
|
USER_ID = '' # e.g. Discord handle
|
||||||
|
PRINTER_MODEL = '' # e.g. 'voron_v2_350'
|
||||||
|
MEASURE_TYPE = '' # e.g. 'nozzle_pin', 'microswitch_probe', etc.
|
||||||
|
NOTES = '' # anything note-worthy about this particular run, no "=" characters
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
######### CONFIGURATION #############
|
||||||
|
BASE_URL = 'http://127.0.0.1' # printer URL (e.g. http://192.168.1.15)
|
||||||
|
# leave default if running locally
|
||||||
|
BED_TEMPERATURE = 105 # bed temperature for measurements
|
||||||
|
HE_TEMPERATURE = 100 # extruder temperature for measurements
|
||||||
|
MEASURE_INTERVAL = 1. # measurement interval (minutes)
|
||||||
|
N_SAMPLES = 3 # number of repeated measures
|
||||||
|
HOT_DURATION = 3 # time after bed temp reached to continue
|
||||||
|
# measuring, in hours
|
||||||
|
COOL_DURATION = 0 # hours to continue measuring after heaters
|
||||||
|
# are disabled
|
||||||
|
MEASURE_GCODE = 'G28 Z' # G-code called on repeated measurements, single line/macro only
|
||||||
|
# chamber thermistor config name. Change to match your own, or "" if none
|
||||||
|
# will also work with temperature_fan configs
|
||||||
|
CHAMBER_CONFIG = "temperature_sensor chamber"
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
|
||||||
|
MCU_Z_POS_RE = re.compile(r'(?P<mcu_z>(?<=stepper_z:)-*[0-9.]+)')
|
||||||
|
DATA_FILENAME = "expansion_quant_%s_%s.csv" % (USER_ID,
|
||||||
|
datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
|
||||||
|
last_measurement = datetime.now() - timedelta(minutes=MEASURE_INTERVAL)
|
||||||
|
start_time = datetime.now() + timedelta(days=1)
|
||||||
|
index = 0
|
||||||
|
BASE_URL = BASE_URL.strip('/') # remove any errant "/" from the address
|
||||||
|
|
||||||
|
|
||||||
|
def gather_metadata():
|
||||||
|
resp = get(BASE_URL + '/printer/objects/query?configfile').json()
|
||||||
|
config = resp['result']['status']['configfile']['settings']
|
||||||
|
|
||||||
|
# Gather Z axis information
|
||||||
|
config_z = config['stepper_z']
|
||||||
|
if 'rotation_distance' in config_z.keys():
|
||||||
|
rot_dist = config_z['rotation_distance']
|
||||||
|
steps_per = config_z['full_steps_per_rotation']
|
||||||
|
micro = config_z['microsteps']
|
||||||
|
if 'gear_ratio' in config_z.keys():
|
||||||
|
gear_ratio_conf = config_z['gear_ratio'].split(':')
|
||||||
|
gear_ratio = float(gear_ratio_conf[0])/float(gear_ratio_conf[1])
|
||||||
|
else:
|
||||||
|
gear_ratio = 1.
|
||||||
|
step_distance = (rot_dist / (micro * steps_per))/gear_ratio
|
||||||
|
elif 'step_distance' in config_z.keys():
|
||||||
|
step_distance = config_z['step_distance']
|
||||||
|
else:
|
||||||
|
step_distance = "NA"
|
||||||
|
max_z = config_z['position_max']
|
||||||
|
if 'second_homing_speed' in config_z.keys():
|
||||||
|
homing_speed = config_z['second_homing_speed']
|
||||||
|
else:
|
||||||
|
homing_speed = config_z['homing_speed']
|
||||||
|
|
||||||
|
# Frame expansion configuration
|
||||||
|
config_frame_comp = config['frame_expansion_compensation']
|
||||||
|
|
||||||
|
# Organize
|
||||||
|
meta = {
|
||||||
|
'user': {
|
||||||
|
'id': USER_ID,
|
||||||
|
'printer': PRINTER_MODEL,
|
||||||
|
'measure_type': MEASURE_TYPE,
|
||||||
|
'measure_gcode': '"%s"' % MEASURE_GCODE,
|
||||||
|
'notes': NOTES,
|
||||||
|
'timestamp': datetime.now().strftime(
|
||||||
|
"%Y-%m-%d_%H-%M-%S")
|
||||||
|
},
|
||||||
|
'script':{
|
||||||
|
'data_structure': 3,
|
||||||
|
'hot_duration': HOT_DURATION,
|
||||||
|
'cool_duration': COOL_DURATION
|
||||||
|
},
|
||||||
|
'z_axis': {
|
||||||
|
'step_dist' : step_distance,
|
||||||
|
'max_z' : max_z,
|
||||||
|
'homing_speed': homing_speed
|
||||||
|
},
|
||||||
|
'frame_expansion_compensation' : config_frame_comp,
|
||||||
|
}
|
||||||
|
return meta
|
||||||
|
|
||||||
|
def write_metadata(meta):
|
||||||
|
with open(DATA_FILENAME, 'w') as dataout:
|
||||||
|
dataout.write('### METADATA ###\n')
|
||||||
|
for section in meta.keys():
|
||||||
|
print(section)
|
||||||
|
dataout.write("## %s ##\n" % section.upper())
|
||||||
|
for item in meta[section]:
|
||||||
|
dataout.write('# %s=%s\n' % (item, meta[section][item]))
|
||||||
|
dataout.write('### METADATA END ###\n')
|
||||||
|
|
||||||
|
def send_gcode(cmd='', retries=1):
|
||||||
|
url = BASE_URL + "/printer/gcode/script?script=%s" % cmd
|
||||||
|
resp = post(url)
|
||||||
|
success = None
|
||||||
|
for i in range(retries):
|
||||||
|
try:
|
||||||
|
success = 'ok' in resp.json()['result']
|
||||||
|
except KeyError:
|
||||||
|
print("G-code command '%s', failed. Retry %i/%i" % (cmd, i+1, retries))
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_bedtemp(t=0):
|
||||||
|
temp_set = False
|
||||||
|
cmd = 'SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET=%.1f' % t
|
||||||
|
temp_set = send_gcode(cmd, retries=3)
|
||||||
|
if not temp_set:
|
||||||
|
raise RuntimeError("Bed temp could not be set.")
|
||||||
|
|
||||||
|
|
||||||
|
def set_hetemp(t=0):
|
||||||
|
temp_set = False
|
||||||
|
cmd = 'SET_HEATER_TEMPERATURE HEATER=extruder TARGET=%.1f' % t
|
||||||
|
temp_set = send_gcode(cmd, retries=3)
|
||||||
|
if not temp_set:
|
||||||
|
raise RuntimeError("HE temp could not be set.")
|
||||||
|
|
||||||
|
def get_cached_gcode(n=1):
|
||||||
|
url = BASE_URL + "/server/gcode_store?count=%i" % n
|
||||||
|
resp = get(url).json()['result']['gcode_store']
|
||||||
|
return resp
|
||||||
|
|
||||||
|
def query_mcu_z_pos():
|
||||||
|
send_gcode(cmd='get_position')
|
||||||
|
gcode_cache = get_cached_gcode(n=1)
|
||||||
|
for msg in gcode_cache:
|
||||||
|
pos_matches = list(MCU_Z_POS_RE.finditer(msg['message']))
|
||||||
|
if len(pos_matches) > 1:
|
||||||
|
return int(pos_matches[0].group())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def query_frame_temp():
|
||||||
|
url = BASE_URL + '/printer/objects/query?frame_expansion_compensation'
|
||||||
|
resp = get(url).json()['result']
|
||||||
|
temp = float(resp['status']['frame_expansion_compensation']['temperature'])
|
||||||
|
return temp
|
||||||
|
|
||||||
|
def query_temp_sensors():
|
||||||
|
url = BASE_URL + '/printer/objects/query?extruder&heater_bed&%s' % CHAMBER_CONFIG
|
||||||
|
resp = get(url).json()['result']['status']
|
||||||
|
try:
|
||||||
|
chamber_current = resp[CHAMBER_CONFIG]['temperature']
|
||||||
|
except KeyError:
|
||||||
|
chamber_current = -180.
|
||||||
|
bed_current = resp['heater_bed']['temperature']
|
||||||
|
bed_target = resp['heater_bed']['target']
|
||||||
|
he_current = resp['extruder']['temperature']
|
||||||
|
he_target = resp['extruder']['target']
|
||||||
|
return {
|
||||||
|
'chamber_temp': chamber_current,
|
||||||
|
'bed_temp': bed_current,
|
||||||
|
'bed_target': bed_target,
|
||||||
|
'he_temp': he_current,
|
||||||
|
'he_target': he_target}
|
||||||
|
|
||||||
|
def collect_datapoint(index):
|
||||||
|
if not send_gcode(MEASURE_GCODE):
|
||||||
|
set_bedtemp()
|
||||||
|
set_hetemp()
|
||||||
|
err = 'MEASURE_GCODE (%s) failed. Stopping.' % MEASURE_GCODE
|
||||||
|
raise RuntimeError(err)
|
||||||
|
stamp = datetime.now()
|
||||||
|
pos = query_mcu_z_pos()
|
||||||
|
t_frame = query_frame_temp()
|
||||||
|
t_sensors = query_temp_sensors()
|
||||||
|
datapoint = {
|
||||||
|
index : {
|
||||||
|
'timestamp': stamp,
|
||||||
|
'mcu_z' : pos,
|
||||||
|
'frame_t' : t_frame,
|
||||||
|
'chamber_t' : t_sensors['chamber_temp'],
|
||||||
|
'bed_t' : t_sensors['bed_temp'],
|
||||||
|
'bed_target' : t_sensors['bed_target'],
|
||||||
|
'he_t' : t_sensors['he_temp'],
|
||||||
|
'he_target' : t_sensors['he_target']}
|
||||||
|
}
|
||||||
|
return datapoint
|
||||||
|
|
||||||
|
def log_datapoint(datapoint:list):
|
||||||
|
_index = list(datapoint[0].keys())[0]
|
||||||
|
with open(DATA_FILENAME, 'a') as datafile:
|
||||||
|
for sample in datapoint:
|
||||||
|
datafile.write('\n%s,%s,%s,%s,%s,%s,%s,%s,%s'
|
||||||
|
% (
|
||||||
|
_index,
|
||||||
|
sample[_index]['timestamp'].strftime("%Y/%m/%d-%H:%M:%S"),
|
||||||
|
sample[_index]['mcu_z'],
|
||||||
|
sample[_index]['frame_t'],
|
||||||
|
sample[_index]['chamber_t'],
|
||||||
|
sample[_index]['bed_t'],
|
||||||
|
sample[_index]['bed_target'],
|
||||||
|
sample[_index]['he_t'],
|
||||||
|
sample[_index]['he_target']
|
||||||
|
))
|
||||||
|
|
||||||
|
def wait_for_bedtemp():
|
||||||
|
global start_time
|
||||||
|
at_temp = False
|
||||||
|
print('Heating started')
|
||||||
|
while(1):
|
||||||
|
temps = query_temp_sensors()
|
||||||
|
if temps['bed_temp'] >= BED_TEMPERATURE-0.5:
|
||||||
|
at_temp = True
|
||||||
|
break
|
||||||
|
measure()
|
||||||
|
start_time = datetime.now()
|
||||||
|
print('\nBed temp reached')
|
||||||
|
|
||||||
|
def measure():
|
||||||
|
global last_measurement, index, start_time
|
||||||
|
now = datetime.now()
|
||||||
|
if (now - last_measurement) >= timedelta(minutes=MEASURE_INTERVAL):
|
||||||
|
last_measurement = now
|
||||||
|
data = []
|
||||||
|
print('\r', ' '*50,end='\r')
|
||||||
|
print('Measuring (#%i)...' % index, end='',flush=True)
|
||||||
|
for n in range(N_SAMPLES):
|
||||||
|
print('%i/%i...' % (n+1, N_SAMPLES), end='', flush=True)
|
||||||
|
data.append(collect_datapoint(index))
|
||||||
|
index += 1
|
||||||
|
log_datapoint(data)
|
||||||
|
print('DONE', " "*20)
|
||||||
|
else:
|
||||||
|
t_minus = ((last_measurement + timedelta(minutes=MEASURE_INTERVAL))-now).seconds
|
||||||
|
if now >= start_time:
|
||||||
|
total_remaining = (start_time + timedelta(hours=HOT_DURATION+COOL_DURATION)-now).seconds/60
|
||||||
|
print('%imin remaining. ' % total_remaining, end='')
|
||||||
|
print('Next measurement in %02is' % t_minus, end='\r', flush=True)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global last_measurement, start_time
|
||||||
|
write_metadata(gather_metadata())
|
||||||
|
print("Starting!\nHoming...", end='')
|
||||||
|
# Home all
|
||||||
|
if send_gcode('G28'):
|
||||||
|
print("DONE")
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Failed to home. Aborted.")
|
||||||
|
|
||||||
|
send_gcode('SET_FRAME_COMP enable=0')
|
||||||
|
|
||||||
|
with open(DATA_FILENAME, 'a') as datafile:
|
||||||
|
header = 'sample,time,mcu_pos_z,frame_t,chamber_t,bed_t,bed_target,he_t,he_target'
|
||||||
|
datafile.write(header)
|
||||||
|
|
||||||
|
set_bedtemp(BED_TEMPERATURE)
|
||||||
|
set_hetemp(HE_TEMPERATURE)
|
||||||
|
|
||||||
|
wait_for_bedtemp()
|
||||||
|
|
||||||
|
while(1):
|
||||||
|
now = datetime.now()
|
||||||
|
if (now - start_time) >= timedelta(hours=HOT_DURATION):
|
||||||
|
break
|
||||||
|
measure()
|
||||||
|
sleep(0.2)
|
||||||
|
print('Hot measurements complete!')
|
||||||
|
set_bedtemp()
|
||||||
|
|
||||||
|
start_time = datetime.now()
|
||||||
|
while(1):
|
||||||
|
now = datetime.now()
|
||||||
|
if (now - start_time) >= timedelta(hours=HOT_DURATION+COOL_DURATION):
|
||||||
|
break
|
||||||
|
measure()
|
||||||
|
sleep(0.2)
|
||||||
|
|
||||||
|
set_hetemp()
|
||||||
|
send_gcode('SET_FRAME_COMP enable=1')
|
||||||
|
print('Cooldown measurements complete!')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
set_bedtemp()
|
||||||
|
set_hetemp()
|
||||||
|
send_gcode('SET_FRAME_COMP enable=1')
|
||||||
|
print("\nAborted by user!")
|
||||||
Loading…
Reference in New Issue