Скрипт на питоне для правки G-кода после Candle

GSXR

МЕСТНЫЙ
ПРОВЕРЕННЫЙ
Регистрация
26.03.2023
Сообщения
12
Реакции
31
Баллы
83
Последнее редактирование:
Доброго времени суток.
Решил поделиться , вдруг кому пригодится. ( Тем, кто режет платы )

Напоминаю одну старую тему:
После того, как я устал бороться с различными кабелями, (то есть внезапными остановками станка) я стал делать так:
Cнимаю карту высот, ставлю галочку "use heightmap" после этого меню Candle появляется пункт "Save transformed as"
Я сохраняю получившийся G-код, пишу его на карточку, вставляю карточку в пульт и говорю пульту "File"
Всё работает отлично (нет) - "ни единого разрыва" (с)

А "нет" потому что Candle при сохранении такого вот "transformed" допускает кучу ошибок.
G1 вместо G01
G01 вместо G00
G00 вместо G01
теряет скорости F
итд итп.
Долгое время я проверял код руками, попутно вычисляя характерные ошибки Candle
Когда вычислил их все, попросил AI написать скрипт, который бы все эти ошибки правил.
Кроме того, скрипт, обнаруживая в коде глубокий рез ( глубже полмиллиметра ) принудительно меняет скорость на F5
Так же скрипт предохраняет втыкание фрезы в плату на полном ходу, то есть
G00 Z-** меняет на G00 Z0.5

Ну и вот - делюсь этим скриптом, вдруг кому пригодится:

Код:
import re
import sys
import os
import argparse
import shutil

def detect_encoding(filename):
    """
    Определяет кодировку файла
    """
    encodings = ['utf-8', 'cp1251', 'cp1252', 'iso-8859-1', 'latin1', 'windows-1251']
 
    for encoding in encodings:
        try:
            with open(filename, 'r', encoding=encoding) as file:
                file.read()
            return encoding
        except UnicodeDecodeError:
            continue
 
    return 'cp1251'  # дефолтная для CNC

def add_spaces_to_gcode(line):
    """
    Добавляет пробелы перед F, Y, Z, X где их нет
    """
    # Сначала добавляем пробелы между параметрами
    # Ищем комбинации типа X10Y20Z30F100 и разбиваем их
    line = re.sub(r'([XYZF])(?=[YZF])', r'\1 ', line)
 
    # Добавляем пробелы после G-кодов
    line = re.sub(r'(G0[01])(?=[XYZF])', r'\1 ', line)
 
    # Добавляем пробелы между числами и следующими параметрами
    line = re.sub(r'(\d)(?=[XYZF])', r'\1 ', line)
 
    return line

def fix_x_position(line):
    """
    Исправляет позицию X: если X не в начале строки и перед ним не пробел, добавляет пробел
    """
    # Ищем X с числом, который не в начале строки и перед ним не пробел
    pattern = r'(?<!\s)(?<!^)(X[-+]?\d*\.?\d+)'
    line = re.sub(pattern, r' \1', line)
 
    return line

def adjust_feedrate_for_deep_z(line):
    """
    Если Z < -0.5, гарантированно устанавливаем F5 (удаляя все существующие F-параметры)
    """
    original_line = line
 
    # Ищем Z-параметр в строке
    z_match = re.search(r'Z([-+]?\d*\.?\d+)', line)
 
    if z_match:
        try:
            z_value = float(z_match.group(1))
         
            # Если Z < -0.5, гарантируем что есть F5 (и только один)
            if z_value < -0.5:
                # Удаляем все существующие F-параметры
                line = re.sub(r'F\d*\.?\d+\s*', '', line)
                # Убираем возможные двойные пробелы после удаления
                line = re.sub(r'  +', ' ', line).strip()
             
                # Добавляем F5 после G-кода
                g_match = re.search(r'(G0[01])', line)
                if g_match:
                    # Если G-код найден, добавляем F5 после него
                    line = line.replace(g_match.group(1), g_match.group(1) + ' F5', 1)
                    print(f"🔹 Установлен F5 (Z={z_value})")
                else:
                    # Если G-кода нет, добавляем в начало
                    line = 'F5 ' + line
                    print(f"🔹 Добавлен F5 в начало (Z={z_value})")
     
        except ValueError as e:
            print(f"⚠️  Ошибка чтения Z-значения: {e}")
 
    return line

def fix_negative_z_in_rapid_move(line):
    """
    Исправляет отрицательный Z в быстрых перемещениях (G00) - меняет на Z0.5
    """
    original_line = line
 
    # Проверяем, что строка содержит G00 (быстрое перемещение)
    if re.search(r'\bG00\b', line):
        # Ищем Z с отрицательным значением
        z_match = re.search(r'Z(-?\d*\.?\d+)', line)
     
        if z_match:
            try:
                z_value = float(z_match.group(1))
             
                # Если Z отрицательный, заменяем на Z0.5
                if z_value < 0:
                    # Заменяем отрицательное Z на Z0.5
                    new_line = re.sub(r'Z-?\d*\.?\d+', 'Z0.5', line)
                    print(f"🔹 Исправлен отрицательный Z в G00: {z_value} -> 0.5")
                    return new_line
         
            except ValueError as e:
                print(f"⚠️  Ошибка чтения Z-значения в G00: {e}")
 
    return line

def reformat_gcode(input_filename, output_filename=None):
    """
    Улучшенная функция для реформатирования G-кода с созданием нового файла
    """
    try:
        # Проверяем существование файла
        if not os.path.exists(input_filename):
            print(f"❌ Ошибка: Файл '{input_filename}' не найден")
            return False
     
        # Определяем кодировку
        encoding = detect_encoding(input_filename)
        print(f"📖 Определена кодировка: {encoding}")
     
        # Генерируем имя выходного файла если не указано
        if output_filename is None:
            name, ext = os.path.splitext(input_filename)
            output_filename = f"{name}_formatted{ext}"
     
        # Читаем файл с правильной кодировкой
        with open(input_filename, 'r', encoding=encoding, errors='replace') as file:
            lines = file.readlines()
     
        processed_lines = []
        changes_count = 0
     
        for i, line in enumerate(lines, 1):
            original_line = line.rstrip('\n\r')
            processed_line = original_line
         
            # Пропускаем пустые строки и комментарии
            if not processed_line.strip() or processed_line.strip().startswith(('(', ';', '%', '#')):
                processed_lines.append(processed_line)
                continue
         
            # 1) Сначала добавляем все необходимые пробелы
            processed_line = add_spaces_to_gcode(processed_line)
         
            # 2) Исправляем позицию X
            processed_line = fix_x_position(processed_line)
         
            # 3) Обработка строк, начинающихся с X
            if re.match(r'^\s*X[-+]?\d', processed_line) and not re.search(r'\bG0[01]\b', processed_line):
                # Ищем Z-параметр в строке
                z_match = re.search(r'Z([-+]?\d*\.?\d+)', processed_line)
             
                if z_match:
                    try:
                        z_value = float(z_match.group(1))
                     
                        if z_value > 1:
                            # 6а) Добавляем G00 для Z > 1
                            processed_line = 'G00 ' + processed_line.lstrip()
                            changes_count += 1
                            print(f"🔹 Строка {i}: Добавлен G00 (Z={z_value})")
                        elif z_value < 1:
                            # 6б) Добавляем G01 F15 для Z < 1
                            processed_line = 'G01 F15 ' + processed_line.lstrip()
                            changes_count += 1
                            print(f"🔹 Строка {i}: Добавлен G01 F15 (Z={z_value})")
                    except ValueError as e:
                        print(f"⚠️  Предупреждение в строке {i}: {e}")
                        pass
         
            # 4) Заменяем G1 на G01 (теперь пробелы уже добавлены)
            processed_line = re.sub(r'\bG1\b', 'G01', processed_line)
         
            # 5) Корректировка подачи для глубокого Z
            processed_line = adjust_feedrate_for_deep_z(processed_line)
         
            # 6) В САМЫЙ КОНЕЦ: Исправление отрицательного Z в быстрых перемещениях
            processed_line = fix_negative_z_in_rapid_move(processed_line)
         
            # Считаем изменения
            if processed_line != original_line:
                changes_count += 1
                # Показываем изменения для отладки
                if original_line != processed_line:
                    print(f"📝 Строка {i}:")
                    print(f"   БЫЛО: {original_line}")
                    print(f"   СТАЛО: {processed_line}")
         
            processed_lines.append(processed_line)
     
        # Записываем результат в НОВЫЙ файл
        with open(output_filename, 'w', encoding='utf-8') as file:
            file.write('\n'.join(processed_lines))
     
        print(f"✅ Создан новый файл: '{output_filename}'")
        print(f"📊 Внесено изменений: {changes_count}")
     
        # Показываем пример изменений
        print("\n🔍 Примеры изменений:")
        example_count = 0
        for i, (orig, new) in enumerate(zip(lines[:10], processed_lines[:10])):
            if orig.strip() != new.strip() and example_count < 3:
                print(f"Строка {i+1}:")
                print(f"   БЫЛО: {orig.strip()}")
                print(f"   СТАЛО: {new.strip()}")
                print()
                example_count += 1
     
        return True
     
    except Exception as e:
        print(f"❌ Критическая ошибка при обработке файла: {e}")
        import traceback
        traceback.print_exc()
        return False

def main():
    """
    Главная функция
    """
    parser = argparse.ArgumentParser(description='Реформатирование G-кода для фрезерного станка')
    parser.add_argument('input_file', help='Путь к исходному файлу')
    parser.add_argument('-o', '--output', help='Путь к выходному файлу (опционально)')
    parser.add_argument('--inplace', action='store_true', help='Заменить исходный файл (с созданием backup)')
 
    if len(sys.argv) == 1:
        parser.print_help()
        return
 
    args = parser.parse_args()
 
    print(f"🔧 Начинаем обработку файла: {args.input_file}")
 
    if args.inplace:
        # Создаем backup и заменяем исходный файл
        backup_name = args.input_file + '.backup'
        shutil.copy2(args.input_file, backup_name)
        print(f"💾 Создан backup: {backup_name}")
        success = reformat_gcode(args.input_file, args.input_file)
    else:
        # Создаем новый файл
        success = reformat_gcode(args.input_file, args.output)
 
    if success:
        print("🎉 Обработка завершена успешно!")
    else:
        print("💥 Обработка завершена с ошибками!")
        sys.exit(1)

if __name__ == "__main__":
    main()


Скрипт запускается в формате python reformat.py file.nc
Питон нужен не ниже 3-его, то есть на Win7 работать будет.

Так же обнаружилась ещё одна проблема - очень сложно откалибровать станок по оси Z так чтобы было совсем точно. Нужно ловить сотые доли миллиметра.
Если по осям X Y + - 0.1 - ерунда, то когда работаешь с фольгой, толщина которой 0.05 , это часто вызывает недорез или перерез.
И если небольшой перерез еще можно стерпеть, то недорез - это очень неприятно.
Я как то создавал дaже тему, что-то вроде "можно ли в процессе реза, сместить Z0 ?"
Для решения этой проблемы использую второй скрипт, которым можно изменить положение оси Z на любую величину вниз по всему коду.

Код:
import re
import sys

def adjust_z_value(match, delta):
    z_number = float(match.group(1))
    new_z = z_number - delta
    if abs(new_z) < 1e-9:
        new_z = 0.0
    decimals = len(match.group(1).split('.')[1]) if '.' in match.group(1) else 0
    return f"Z{new_z:.{decimals}f}"

def process_file(input_file, output_file, delta, mode):
    with open(input_file, 'r') as f:
        content = f.read()
 
    # Выбор шаблона в зависимости от режима
    if mode == 'positive':
        pattern = re.compile(r'Z(0\.\d+)')  # Ищем Z0.xxx (без плюса!)
    elif mode == 'negative':
        pattern = re.compile(r'Z(-0\.\d+)')  # Ищем Z-0.xxx
    else:
        pattern = re.compile(r'Z([+-]?0\.\d+)')  # Все варианты
 
    modified_content = pattern.sub(lambda m: adjust_z_value(m, delta), content)
 
    with open(output_file, 'w') as f:
        f.write(modified_content)

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Использование: python deeper.py <file.nc> <число> [режим]")
        print("Режимы: 'positive' (только Z0.xxx), 'negative' (только Z-0.xxx), по умолчанию — все")
        sys.exit(1)
 
    input_file = sys.argv[1]
    delta = float(sys.argv[2])
    mode = sys.argv[3].lower() if len(sys.argv) > 3 else None  # Регистронезависимо
    output_file = input_file.replace('.nc', f'_deeped_{mode or "all"}.nc')
 
    process_file(input_file, output_file, delta, mode)
    print(f"Файл обработан в режиме '{mode or 'all'}'. Результат в {output_file}")

строка запуска python deeper.py file.nc <число> <для кода Z-> <для кода Z+>
например python deeper.py file.nc 0.05 pozitive
все строчки типа G01 F15 X10 Y10 Z0.01 "углубит" на 0.05 то есть получится G01 F15 X10 Y10 Z-0.04
 
Последнее редактирование:
Раз пошла такая тема.
Попробуйте разобраться, у меня пока знаний не хватает. (придётся учить Питон)
Есть большое желание немного упростить данный скрипт.
И оставить там только работу со сверловкой отверстий.

Вот ссылку на youtube нашёл.
 
Последнее редактирование:
Я тоже не великий спец по питону.
Раньше пытался писать сам ( даже что-то получалось ) но с появлением AI обленился напрочь.
Заходите на deepseek
Говорите "здравствуй, дорогая нейросеть, я тебе сейчас покажу один скрипт, проанализируй его, пожалуйста"
И вставляете этот скрипт.
Она ответит "да, всё понятно, он делает то-то и то-то"
А вы ей - "а теперь исправь его пожалуйста так, чтобы он делал то-то и то-то"

Вот, собственно, и всё.

P.S. скрипт, как я вижу, под Линукс
Если у вас Windows, то надо будет еще попросить "И чтобы это работало под Windows (версия )"
Ну а сам питон качается с оффсайта Python Releases for Windows


Если по каким то причинам не прокатит ( ну например не влезет в лимит сессии ) , можно сделать ещё проще.
"Уважаемая нейросеть, напиши мне пожалуйста скрипт чтобы анализировал файлы GBR и создавал из них G-код , но только сверловки"
Точно прокатит.

Так что моя "работа" в этих ☝️ скриптах не в том, что я их написал, а в том что я вычислил все ошибки Candle смог внятно обьяснить нейросети чего я хочу а потом проверял как это работает на разных кодах, проверяя всё руками.
 
1759241930987.png

На самом деле это не приговор.
Если скрипт написан грамтно, то заработает под чем угодно, был бы питон.
Но мне попадались скрипты которые были очень ... специализированы под OS и не работали под другой OS

Кстати, из строк

Код:
# Use KiCad's naming convention to  get the copper front layer, ege cuts, and drill files.
gerber_traces = Gerber_Traces_Parser(base_name+"-F_Cu.gbr")
gerber_edgecuts = Gerber_EdgeCuts_Parser(base_name+"-Edge_cuts.gbr")
drilldata = Drillfile_Parser(base_name+"-PTH.drl")
следует, что с, например Sprint-Layot, скрипт, скорее всего, не заработает.
Он заточен на KiCad

Так что, я бы сделал платку из одной дорожки и двух пятачков, сделал из неё GBR и DRL , сунул бы это нейросети и попросиль бы её сделать мне скрипт для генерации G-кода
Платка нужна именно маленькая, потому что в бесплатных сессиях у всех нейросетей ограничение на объём (размер).
Если сунуть ей большой файл, то весь лимит сессии уйдёт на обработку этого файла.
 
Сверху Снизу
Обнаружен блокировщик рекламы AdBlock

МЫ ДОГАДЫВАЕМСЯ, ЧТО РЕКЛАМА ВАС РАЗДРАЖАЕТ!

Конечно, Ваше программное обеспечение для блокировки рекламы отлично справляется с блокировкой рекламы на нашем сайте, но оно также блокирует полезные функции. Мы стараемся для Вас и не обязываем Вас донатить и скидывать денег на наши кошельки, чтобы пользоваться форумом, но реклама это единственное, что позволяет поддерживать проект и развивать его.

Спасибо за Ваше понимание!

Я отключил свой AdBlock    Нет, я не буду ничего отключать