Image

Исследовательский анализ данных: гамма-спектроскопия в Python (часть 3)

Давайте рассмотрим материю на атомном уровне.

Делиться

24491633544c1ce718a02d9e62ae0515

Различные предметы вокруг нас могут быть слегка радиоактивными. Америций в датчиках дыма, радий в некоторых винтажных часах или уран в старинном стекле — полный список может быть длинным. В большинстве случаев эти предметы безопасны и не представляют опасности для здоровья. Интересно также идентифицировать их и изучать вещество на атомном уровне. И мы можем сделать это с помощью детектора излучения. В первой части я провёл разведочный анализ данных гамма-спектроскопии. Во второй части я создал модель машинного обучения для обнаружения радиоактивных изотопов. Это последняя, третья часть, и пришло время добавить созданную модель в настоящее приложение!

В этой истории я проверю два подхода:

  • Я создам публичное приложение Streamlit, которое будет бесплатно размещено в Streamlit Cloud (ссылка на приложение добавлена в конец статьи).
  • В качестве более гибкого и универсального решения я создам приложение на базе Python HTMX, которое сможет взаимодействовать с реальным оборудованием и делать прогнозы в реальном времени.

Как и в предыдущей части, я буду использовать сцинтилляционный детектор Radiacode для получения данных (примечание: устройство, использованное в этом тесте, было предоставлено производителем; я не получаю никакой коммерческой прибыли от его продаж и не получал никаких редакционных комментариев по всем тестам). Читатели, у которых нет оборудования Radiacode, смогут протестировать приложение и модель, используя файлы, доступные на Kaggle.

Давайте начнем!

1. Модель классификации изотопов

Эта модель была описана в предыдущей части. Она основана на XGBoost, и я обучил её на различных радиоактивных образцах. Я использовал образцы, которые можно легально приобрести, например, старинное урановое стекло или старые часы с радиевым циферблатом, выпущенные в 1950-х годах. Как уже упоминалось, я также использовал сцинтилляционный детектор Radiacode, позволяющий получить гамма-спектр объекта. Всего 10–20 лет назад такие детекторы были доступны только в крупных лабораториях; сегодня их можно купить по цене смартфона среднего класса.

Модель содержит три компонента:

  • Сама модель на базе XGBoost.
  • Список радиоактивных изотопов (например, свинец-214 или актиний-228), на которых обучалась модель. Сцинтилляционный детектор Radiacode возвращает 1024 значения спектра, 23 из которых были использованы для модели.
  • Кодировщик меток для преобразования индексов списков в понятные человеку имена.

Давайте объединим все это в один класс Python:

из xgboost импорт XGBClassifier из sklearn.preprocessing импорт LabelEncoder класс IsotopesClassificationModel: «»» Модель классификации гамма-спектра «»» def __init__(self): «»» Загрузка моделей «»» path = self._get_models_path() self._classifier = self._load_model(path + «/XGBClassifier.json») self._isotopes = self._load_isotopes(path + «/isotopes.json») self._labels_encoder = self._load_labels_encoder(path + «/LabelEncoder.npy») def predict(self, spectrum: Spectrum) -> str: «»» Предсказать изотоп «»» features = SpectrumPreprocessing.convert_to_features( spectrum, self._isotopes ) preds = self._classifier.predict([features]) preds = self._labels_encoder.inverse_transform(preds) return preds[0] @staticmethod def _load_model(filename: str) -> XGBClassifier: «»» Загрузить модель из файла «»» bst = XGBClassifier() bst.load_model(filename) return bst @staticmethod def _load_isotopes(filename: str) -> Список: с открытым(filename, «r») как f_in: return json.load(f_in) @staticmethod def _load_labels_encoder(filename: str) -> LabelEncoder: le = LabelEncoder() le.classes_ = np.load(filename) return le @staticmethod def _get_models_path() -> str: «»» Получить путь к моделям. Файлы моделей хранятся в папке 'models/V1/' «»» parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) return parent_dir + f»/models/{IsotopesClassificationModel.VERSION}»

Класс Spectrum содержит данные спектра, которые мы получаем от детектора излучения:

@dataclass класс Спектр: «»» Данные спектра излучения «»» длительность: datetime.timedelta a0: float a1: float a2: float количество: list[int]

Здесь counts — это гамма-спектр, представленный списком из 1024 значений каналов. Данные спектра можно экспортировать с помощью официального приложения Radiacode для Android или получить непосредственно с устройства с помощью библиотеки Radiacode для Python.

Чтобы загрузить спектр в модель, я создал класс SpectrumPreprocessing:

class SpectrumPreprocessing: «»» Предварительная обработка гамма-спектра «»» @staticmethod def convert_to_features(spectrum: Spectrum, isotopes: List) -> np.array: «»» Преобразует спектр в список признаков для прогнозирования «»» sp_norm = SpectrumPreprocessing._normalize(spectrum) energy = [energy for _, energy in isotopes] channels = [SpectrumPreprocessing.energy_to_channel(spectrum, energy) for energy in energy] return np.array([sp_norm.counts[ch] for ch in channels]) @staticmethod def load_from_xml_file(file_path: str) -> Spectrum: «»» Загрузить спектр из файла приложения Radiacode для Android «»»

Здесь я пропускаю некоторые блоки кода, которые уже были опубликованы в предыдущей части. Там же объяснялось извлечение признаков из гамма-спектра, и я настоятельно рекомендую сначала прочитать эту часть.

А теперь давайте протестируем модель! Я взял детектор Radiacode и собрал гамма-спектр за 10 минут:

82be3990f23467a1520cce08f7648d3b

Этот китайский кулон рекламировался как «ионно-генерируемый», и он слегка радиоактивен. Его гамма-спектр, полученный в официальном приложении Radiacode для Android, выглядит так:

fced110b9858334670f85c5fd8a3d057

Подождав около 10 минут, я экспортировал спектр в XML-файл. Теперь можно запустить модель:

из спектра импорта SpectrumPreprocessing из ml_models импорта IsotopesClassificationModel sp = SpectrumPreprocessing.load_from_file(«spectrum.xml») model = IsotopesClassificationModel() result = model.predict(sp) print(result) #> Торий

Как видим, модель работает хорошо. Мы можем сравнить пики со спектрами известных изотопов (например, здесь или здесь) и подтвердить, что спектр принадлежит торию.

2. Стримлит

Модель работает, однако мы живём в XXI веке, и мало кто будет запускать консольное приложение, чтобы получить результаты. Вместо этого мы можем сделать приложение доступным онлайн, и все пользователи Radiacode смогут его запустить.

Существует множество фреймворков Python для создания браузерных приложений, и Streamlit, пожалуй, самый популярный в сообществе специалистов по анализу данных. И, что важно для нас, платформа Streamlit Community Cloud позволяет каждому публиковать свои приложения совершенно бесплатно. Для этого давайте сначала создадим приложение.

2.1 Приложение Streamlit

Фреймворк Streamlit относительно прост в использовании, по крайней мере, если нам подходит стандартное приложение. Лично я не сторонник такого подхода. Эти фреймворки скрывают от пользователей все низкоуровневые детали реализации. Создать прототип несложно, но логика пользовательского интерфейса будет тесно связана с узкоспециализированным фреймворком и не сможет быть использована где-либо ещё. Реализация чего-либо нестандартного, что не поддерживается фреймворком, может быть практически невозможна или сложно реализуема без погружения в огромное количество абстракций и страниц кода. Однако в нашем случае прототип — это всё, что нам нужно.

В целом код Streamlit прост, и нам нужно лишь описать логическую иерархию нашей страницы:

import streamlit as st import logging logger = logging.getLogger(__name__) def is_xml_valid(xml_data: str) -> bool: «»» Проверьте, имеет ли XML допустимый размер и данные «»» return len(xml_data) < 65535 и xml_data.startswith(" Optional[Spectrum]: «»» Загрузите спектр из потока StringIO «»» xml_data = stringio.read() if is_xml_valid(xml_data): return SpectrumPreprocessing.load_from_xml(xml_data) return None def main(): «»» Основное приложение «»» st.set_page_config(page_title=»Гамма-спектр») st.title(«Обнаружение спектра Radiacode») st.text( «Экспортируйте спектр в XML с помощью приложения Radiacode и » «загрузите его в см. результаты.» ) # Загрузка файла uploaded_file = st.file_uploader( «Выберите XML-файл», type=»xml», key=»uploader», ) if uploaded_file is not None: stringio = StringIO(uploaded_file.getvalue().decode(«utf-8″)) if sp := get_spectrum(stringio): # Модель прогнозирования = IsotopesClassificationModel() result = model.predict(sp) logger.info(f»Предсказание спектра: {result}») # Показать результат st.success(f»Результат прогнозирования: {result}») # Нарисовать fig = get_spectrum_barchart(sp) st.pyplot(fig) if __name__ == «__main__»: logger.setLevel(logging.INFO) main()

Как видите, для полноценного приложения требуется минимум кода на Python. Streamlit отрисует весь HTML-код, включая заголовок, загрузку файла и результаты. В качестве бонуса я также покажу спектр с помощью Matplotlib:

def get_spectrum_barchart(sp: Spectrum) -> plt.Figure: «»» Получить столбчатую диаграмму Matplotlib «»» counts = SpectrumPreprocessing.get_counts(sp) energy = [ SpectrumPreprocessing.channel_to_energy(sp, x) for x in range(len(counts)) ] fig, ax = plt.subplots(figsize=(9, 6)) ax.spines[«top»].set_color(«lightgray») ax.spines[«right»].set_color(«lightgray») # Столбцы ax.bar(energy, counts, width=3.0, label=»Counts») # Значения X ticks_x = [SpectrumPreprocessing.channel_to_energy(sp, ch) for ch in range(0, len(counts), len(counts) // 20)] labels_x = [f»{int(ch)}» for ch в ticks_x] ax.set_xticks(ticks_x, labels=labels_x, вращение=45) ax.set_xlim(energy[0], energy[-1]) ax.set_ylim(0, None) ax.set_title(«Гамма-спектр») ax.set_xlabel(«Энергия, кэВ») ax.set_ylabel(«Количество») вернуть рис.

Теперь мы можем запустить приложение локально:

streamlit запустите st-app.py

После этого наше приложение полностью работоспособно и его можно протестировать в браузере:

5c59ad774d71f34de4dc356a11eeaee0

Как я уже упоминал, я не фанат высокоуровневых фреймворков и предпочитаю лучше понимать, как всё работает «под капотом». Однако, учитывая, что я потратил всего около 100 строк кода на создание полнофункционального веб-приложения, жаловаться не на что — для прототипирования оно подходит отлично.

2.2 Облако сообщества Streamlit

После локального тестирования приложения пора выкладывать его в открытый доступ! Streamlit Cloud — бесплатный сервис, и, очевидно, у него есть ряд ограничений:

  • Приложение работает в контейнере, подобном Docker. Ваша учётная запись GitHub должна быть связана со Streamlit. При запуске контейнер загружает ваш код с GitHub и запускает его.
  • На момент написания этого текста ресурсы контейнера ограничены двумя ядрами и 2,7 ГБ оперативной памяти. Для запуска LLM размером 70 байт этого было бы слишком мало, но для небольшой модели XGBoost этого более чем достаточно.
  • Streamlit не предоставляет постоянного хранилища. После выключения или перезапуска все журналы и временные файлы будут утеряны (при необходимости вы можете использовать секреты API и подключиться к любому другому облачному хранилищу из своего кода на Python).
  • После периода бездействия (около 30 минут) контейнер будет остановлен, а все временные файлы будут удалены. Если кто-то откроет ссылку на приложение, оно запустится снова.

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

Чтобы опубликовать приложение в Streamlit , нам нужно выполнить три простых шага.

Для начала нам нужно закоммитить наше Python-приложение на GitHub. Файл requirements.txt также обязателен. Контейнер Streamlit использует его для установки необходимых зависимостей Python. В моём случае это выглядит так:

xgboost==3.0.2 scikit-learn==1.6.1 numpy==1.26.4 streamlit==1.47.0 pillow==11.1.0 matplotlib==3.10.3 xmltodict==0.14.2

Настройки сервера можно изменить с помощью файла .streamlit/config.toml. В моём случае я ограничил размер загружаемого файла 1 МБ, поскольку все файлы спектров меньше:

[сервер] # Максимальный размер файлов, загружаемых с помощью file_uploader, в мегабайтах. # По умолчанию: 200 maxUploadSize = 1

Во-вторых, нам нужно войти в share.streamlit.io, используя учетную запись GitHub, и дать разрешение на доступ к исходному коду.

Наконец, мы можем создать новый проект Streamlit. В настройках проекта мы также можем выбрать нужный URL и окружение:

3091806ab445e1993aac603264723aab

Если все сделано правильно, мы увидим работающее приложение:

e3d8a4ec641c9e5b01197545c443e4bb

Теперь к нашему приложению могут получить доступ пользователи по всему миру! В моём случае я выбрал имя для гамма-спектрального обнаружения, и приложение доступно по этому URL-адресу.

3. Приложение FastAPI + HTMX

Как видите читатели, Streamlit — отличное решение для простого прототипа. Однако в случае с детектором радиации мне хотелось бы видеть данные, поступающие с реального оборудования Radiacode. В Streamlit это сделать невозможно; эта библиотека просто не предназначена для этого. Вместо этого я буду использовать несколько готовых к использованию фреймворков:

  • Фреймворк HTMX позволяет нам создать полнофункциональный веб-интерфейс.
  • FastAPI запустит сервер.
  • Модель МО будет обрабатывать данные, полученные в режиме реального времени от детектора излучения, с использованием библиотеки Radiacode.

Как уже упоминалось, те читатели, у которых нет устройства Radiacode, смогут воспроизвести данные, используя необработанные файлы журнала, сохранённые с реального устройства. Ссылка на приложение и все файлы доступна в конце статьи.

Давайте приступим!

3.1 HTML/HTMX

Приложение подключено к детектору Radiacode, и я решил отображать на странице состояние подключения, уровень радиации и график спектра. Внизу будут отображаться время сбора спектра и прогноз модели машинного обучения.

Файл index.html для этого макета выглядит так:

Гамма-спектр и мониторинг

Устройство: н/д
CPS: н/д

Время сбора:
н/д
Прогноз:
н/д

Здесь я использовал Chart.js для создания графика, а все элементы управления пользовательским интерфейсом расположены в блоке div. HTMX позволяет создавать интерактивные веб-приложения без использования JavaScript. Эта строка HTMX выполняет фактическое обновление:

hx-get=»/device/poll» hx-target=»#device-info» hx-swap=»outerHTML» hx-trigger=»загрузка, каждые 1 с»

Здесь HTMX настроен на вызов конечной точки /device/poll каждую секунду, а результат будет помещен в элемент управления с идентификатором #device-info.

Читатели могут изменить HTML-код, если необходимо. Я не фронтенд-разработчик, и, возможно, этот интерфейс можно реализовать эффективнее. Тем не менее, страница работает и выполняет свою функцию:

98a3c7759ebdd9dffa80a8583c052755

3.2 FastAPI

Как я уже упоминал, я решил использовать FastAPI для бэкэнд-части.

Сначала нам нужно создать экземпляр FastAPI и добавить необходимые конечные точки:

из fastapi импорт FastAPI, Запрос, Ответ из fastapi.responses импорт FileResponse из contextlib импорт asynccontextmanager @asynccontextmanager async def lifespan(app: FastAPI): logging.info(«FastAPI::Started») app.state.device = RadiaCodeDevice() app.state.device.start_in_background() yield app.state.device.stop() logging.info(«FastAPI::Ended») app = FastAPI(lifespan=lifespan) app.mount(«/static», StaticFiles(directory=»static»), name=»static») @app.get(«/device/poll») async def device_poll(): «»» Получение данных устройства «»» # Код размещен ниже @app.post(«/device/reset_spectrum») async def device_reset_spectrum(): «»» Сброс данных гамма-спектра «»» app.state.device.reset_spectrum() возвращает «OK» @app.get(«/») def read_index(request: Request): context = {«request»: request} возвращает templates.TemplateResponse(«index.html», context) @app.get(«/favicon.ico», include_in_schema=False) async def favicon(): возвращает FileResponse(«static/favicon.ico») если __name__ == '__main__': import uvicorn uvicorn.run(«app:app», host='0.0.0.0', port=8000, reload=True)

Как видите, я использовал обратный вызов с продолжительностью жизни для запуска соединения Radiacode при запуске сервера FastAPI. App.state — это глобальный объект состояния в FastAPI. У нас есть только один экземпляр устройства, поэтому использование глобального состояния — подходящее место для этого. Сам FastAPI основан на asyncio, и поток соединения Radiacode работает в фоновом режиме. В asyncio я не могу блокировать основной поток, поэтому я использовал две очереди (одну для данных и одну для команд) для обмена данными.

Интересен способ обновления страницы в HTMX. Допустим, я хочу обновить значение CPS (количество запросов в секунду). Напомню, что в HTML у меня есть следующие элементы управления:

Устройство: н/д

Устройство: н/д
CPS: н/д

В ответе на /device/poll мне нужно вернуть правильный HTML для соответствующих идентификаторов:

def get_connection_data(self) -> str: «»» Получить данные устройства для запроса опроса HTML «»» status = «Подключено» return f'{status}' def get_cps_data(self) -> str: «»» Получить данные CPS для запроса опроса HTML «»» return f'CPS: {self.radiation_cps:.2f}' def get_spectrum_data(self) -> str: «»» Получить данные спектра для запроса опроса HTML «»» return f'' def get_spectrum_prediction(self) -> str: … @app.get(«/device/poll») async def device_poll(): «»» Получить данные устройства «»» poll = app.state.device.get_connection_data() cps = app.state.device.get_cps_data() spectrum = app.state.device.get_spectrum_data() predict = app.state.device.get_spectrum_prediction() return Response( content=poll + cps + predict + spectrum, media_type=»application/text» )

HTMX автоматически активирует конечную точку /device/poll и заменяет элементы управления на веб-странице новыми данными. Ключ hx-swap-oob позволяет обновить несколько элементов управления за один запрос. Как видите, HTMX позволяет легко связать фронтенд-страницу в браузере с бэкендом на Python.

Метод get_spectrum_data немного отличается. HTMX не поддерживает обновление столбчатой диаграммы Chart.js напрямую. Однако я могу вернуть блок кода JavaScript, который будет выполнен на странице. Я использую эту функцию для обновления графика, вызывая метод updateChartData, который ранее был размещён в index.html.

Полный исходный код длиннее, и здесь я показал только самые важные части. Ссылка на полный проект доступна в конце статьи.

4. Тестирование

Наконец, давайте посмотрим на оба приложения в действии! Моя модель умеет классифицировать изотопы, и я хотел проверить её на объекте, который модель ранее не «видела». Для этого я посетил магазин минералов в центре города:

fd717f8e0418bd0a499b48359c8d9394

Я знаю, что некоторые минералы могут быть слегка радиоактивными, поэтому с помощью детектора радиации я выбрал лучший (или худший, в зависимости от ваших предпочтений). Это был голубой апатит. Я проверил его дома, и мой радиометр GMC показал, что его уровень радиоактивности составляет 0,71 мкЗв/ч, что алгоритм прибора посчитал «высоким»:

2b6d978933938be8bdfcbd175161b5d5

Здесь «высокий» не означает «опасный» – это значение просто высокое для среднего уровня фонового излучения, который обычно составляет около 0,1 мкЗв/ч. Однако в данном случае более высокий уровень наблюдается всего в 1-2 см вокруг минерала. Он не опасен и не может причинить никакого вреда (для сравнения, излучение в самолёте, вызванное космическими лучами, примерно в 1,5 раза выше). И, как уже говорилось в предыдущей части, счётчик Гейгера может показать нам значение, но не может сказать, почему этот минерал радиоактивен. Поэтому я воспользуюсь гамма-детектором Radiacode, чтобы выяснить это.

Напоминаю, что сначала я создал приложение Streamlit и опубликовал его на платформе Streamlit Community Cloud. Приложение доступно онлайн, но пока им никто не пользуется, оно находится в «спящем режиме»:

45c03072c0b5828d2d13b80460d56a8b

Я сохранил спектр с помощью приложения Radiacode для Android. После запуска приложения Streamlit (развертка контейнера занимает 2–3 минуты) я загрузил файл:

4c0f5c84b672b75aefc421b21e9a7483

В качестве второго теста запустим локальное приложение . Оно более функционально, может взаимодействовать с детектором Radiacode по USB, а также может служить пользовательским интерфейсом для управления оборудованием. Ещё одно важное отличие заключается в том, что мы можем видеть данные с детектора в режиме реального времени.

Я нажал кнопку «Сброс спектра», чтобы перезапустить накопление, и через 2–3 минуты сбора данных минерал был идентифицирован как торий:

04268a7578812ebc05d5fc23c59ed702

Это была интересная находка, поскольку, согласно Википедии, апатит может содержать уран, но торий там не упоминался. Однако, согласно странице Геологической службы США (USGS), торий часто встречается в апатитах. В данном случае статья в Википедии была просто недостаточно полной.

Заключение

В этой статье я протестировал модель машинного обучения для обнаружения радиоактивных изотопов. Я протестировал два способа использования модели: приложение Streamlit, опубликованное в Streamlit Community Cloud, и полнофункциональное приложение FastAPI, которое получает данные от оборудования и может использоваться в качестве веб-интерфейса для настоящего детектора радиации.

Как видим, модель работает хорошо, и мне удалось обнаружить следовые количества тория в синем апатите. Об этом факте даже не упоминалось в Википедии, и всегда приятно узнать что-то новое. Однако, как уже упоминалось в предыдущей части, возможности модели ограничены. Я не работаю в ядерной отрасли, и модель обучалась только на радиоактивных объектах, которые можно легально приобрести, например, на винтажных часах с радиевым циферблатом. У меня нет тестовых источников, таких как цезий или плутоний. Тем не менее, было интересно обучать модель и использовать её в приложении. Вероятно, подобные проекты не имеют коммерческой ценности, а спрос на модель классификации изотопов составляет 0,00%. Однако, работая над этим, я смог использовать такие инструменты, как FastAPI, HTMX или CSS, которые могут быть полезны в других проектах. И наконец, я получил удовольствие, а это самое главное 😉

Все файлы данных были собраны с помощью сцинтилляционного детектора Radiacode. Это удобное и компактное устройство по цене среднего смартфона, которое, как мы видим, позволяет нам проводить интересные эксперименты (отказ от ответственности: я не получаю никакой прибыли или иного коммерческого интереса от его продажи). Для тех читателей, у кого нет оборудования Radiacode, все собранные данные находятся в свободном доступе на Kaggle. Архив содержит спектры различных объектов, использованных для обучения модели. Спектры в формате XML можно использовать для тестирования приложения Streamlit. Папка «replay» содержит журнал, сохранённый с устройства Radiacode, и может быть использована для тестирования приложения HTMX.

Полный исходный код этой статьи доступен на моей странице на Patreon. Эта поддержка помогает мне приобретать оборудование и электронику для будущих подобных тестов. Читатели также могут присоединиться к нам через LinkedIn, где я периодически публикую небольшие посты, объём которых недостаточен для полноценной статьи.

Спасибо за прочтение.

Источник: towardsdatascience.com

✅ Найденные теги: Исследовательский, новости
Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

Фото сгенерированных лиц: исследование показывает, что люди не могут отличить настоящие лица от сгенерированных
Нейросети построили капитализм за трое суток: 100 агентов Claude заперли…
Скетч: цифровой осьминог и виртуальный мир внутри компьютера с человечком.
Сцена с жестами пальцами, где один жест символизирует "VPN", а другой "KHP".
‼️Paramount купила Warner Bros. Discovery — сумма сделки составила безумные…
Скриншот репозитория GitHub "Claude Scientific Skills" AI для научных исследований.
Структура эффективного запроса Claude с элементами задачи, контекста и референса.
Эскиз и готовая веб-страница платформы для AI-дизайна в современном темном режиме.
ideipro logotyp
Image Not Found
Звёздное небо с галактиками и туманностями, космос, Вселенная, астрофотография.

Система оповещения обсерватории Рубина отправила 800 000 сигналов в первую ночь наблюдений.

Астрономы будут получать оповещения о небесных явлениях в течение нескольких минут после их обнаружения. Теренс О'Брайен, редактор раздела «Выходные». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной…

Мар 2, 2026
Женщина с длинными тёмными волосами в синем свете, нейтральный фон.

Расследование в отношении 61-фунтовой машины, которая «пожирает» пластик и выплевывает кирпичи.

Обзор компактного пресса для мягкого пластика Clear Drop — и что будет дальше. Шон Холлистер, старший редактор Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Мар 2, 2026
Черный углеродное волокно с текстурой плетения, отражающий свет.

Материал будущего: как работает «бессмертный» композит

Учёные из Университета штата Северная Каролина представили композит нового поколения, способный самостоятельно восстанавливаться после серьёзных повреждений.  Речь идёт о модифицированном армированном волокном полимере (FRP), который не просто сохраняет прочность при малом весе, но и способен «залечивать» внутренние…

Мар 2, 2026
Круглый экран с изображением замка и горы, рядом электронная плата.

Круглый дисплей Waveshare для креативных проектов

Круглый 7-дюймовый сенсорный дисплей от Waveshare создан для разработчиков и дизайнеров, которым нужен нестандартный экран.  Это IPS-панель с разрешением 1 080×1 080 пикселей, поддержкой 10-точечного ёмкостного сенсора, оптической склейкой и защитным закалённым стеклом, выполненная в круглом форм-факторе.…

Мар 2, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых