Примеры и поддержка
Готовые программы роботов и устранение неполадок
Примеры роботов
Готовые шаблоны программ для типовых устройств. Перед запуском создайте params.json с адресом сервера (см. Конфигурация).
Пример 1. Светодиодная лампа (тип LED)
Страница LED в веб-интерфейсе позволяет выбрать цвет (#RRGGBB) и яркость (0–100 %). Робот хранит текущее состояние и возвращает его в телеметрии.
from artex_robot import Robot
robot = Robot({"type": "LED", "name": "lamp-01", "telem_can": True})
robot.log.set_level("skip_TODO")
state = {"color": "#FFFFFF", "brightness": 100}
def hex_to_rgb(value):
value = value.lstrip("#")
return tuple(int(value[i:i + 2], 16) for i in (0, 2, 4))
@robot.com_handler
def on_command(type_command, command, author=None):
robot.log("INFO", f"LED: {type_command}={command} от {author}")
if isinstance(command, str) and command.startswith("#") and len(command) == 7:
state["color"] = command.upper()
elif isinstance(command, (int, float)) or (isinstance(command, str) and command.isdigit()):
state["brightness"] = max(0, min(100, int(command)))
# здесь — вывод на реальный светодиод (PWM, NeoPixel и т.п.)
r, g, b = hex_to_rgb(state["color"])
k = state["brightness"] / 100
print("RGB:", int(r * k), int(g * k), int(b * k))
@robot.telemetry("led")
def report():
return dict(state)
@robot.mainloop
def loop(ctx):
report()
robot.init_connector()
robot.start()
Формат команд цвета и яркости зависит от версии веб-интерфейса — проверьте его по журналу робота (сообщение recieved command from ...) и при необходимости поправьте разбор в on_command.
Пример 2. Колёсный робот с камерой (Raspberry Pi)
Робот на двух моторах (драйвер L298N / TB6612), FPV-камера, контроль заряда АКБ, остановка при потере команд.
import time
import cv2
from gpiozero import Robot as Chassis # pip install gpiozero
from artex_robot import Robot
robot = Robot({"type": "Robot", "name": "tank-01",
"telem_can": True, "video_can": True,
"max_main_freq": 0.05, "max_send_freq": 0.05})
robot.log.set_level("skip_TODO")
chassis = Chassis(left=(17, 18), right=(22, 23)) # номера GPIO — по схеме робота
cam = cv2.VideoCapture(0)
cam.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG"))
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
robot["speed"] = 0.6
robot["last_cmd"] = 0.0
COMMAND_TIMEOUT = 0.5
MOVES = {
"forward": lambda s: chassis.forward(s),
"back": lambda s: chassis.backward(s),
"left": lambda s: chassis.left(s),
"right": lambda s: chassis.right(s),
"stop": lambda s: chassis.stop(),
}
@robot.com_handler
def on_command(type_command, command, author=None):
if isinstance(command, str) and command.startswith("speed:"):
robot["speed"] = int(command.split(":")[1]) / 100
return
move = MOVES.get(command)
if move is None:
robot.log("WARN", f"Неизвестная команда: {command}")
return
move(robot["speed"])
robot["last_cmd"] = time.monotonic()
@robot.telemetry("battery")
def read_battery():
return 11.9 # замените чтением АЦП (например, ADS1115)
@robot.telemetry("motion")
def read_motion():
return {"left": chassis.left_motor.value, "right": chassis.right_motor.value}
@robot.video("rgb")
def send_frame(frame):
return frame
@robot.mainloop
def loop(ctx):
# сторожевой таймер: нет команд — стоим
if time.monotonic() - ctx["last_cmd"] > COMMAND_TIMEOUT:
chassis.stop()
read_battery()
read_motion()
ok, frame = cam.read()
if ok:
send_frame(frame)
try:
robot.init_connector()
robot.start()
finally:
chassis.stop()
cam.release()
Пример 3. Виртуальный робот (без оборудования)
Робот-симулятор: хранит координаты и курс, рисует «вид сверху» в кадр и передаёт телеметрию. Подходит для отладки интерфейса, моделей ИИ и сервера без реального железа.
import math, time
import numpy as np, cv2
from artex_robot import Robot
robot = Robot({"type": "Robot", "name": "sim-01",
"telem_can": True, "video_can": True})
robot.log.set_level("skip_TODO")
sim = {"x": 0.0, "y": 0.0, "heading": 0.0, "v": 0.0, "w": 0.0, "battery": 12.6}
ACTIONS = {"forward": (0.5, 0), "back": (-0.5, 0), "left": (0, 1.2),
"right": (0, -1.2), "stop": (0, 0)}
@robot.com_handler
def on_command(type_command, command, author=None):
if command in ACTIONS:
sim["v"], sim["w"] = ACTIONS[command]
@robot.telemetry("pose")
def pose():
return {k: round(sim[k], 3) for k in ("x", "y", "heading")}
@robot.telemetry("battery")
def battery():
return round(sim["battery"], 2)
@robot.video("rgb")
def render(frame):
return frame
def draw():
img = np.full((360, 360, 3), 40, np.uint8)
cx, cy = int(180 + sim["x"] * 40), int(180 - sim["y"] * 40)
hx = int(cx + 15 * math.cos(sim["heading"]))
hy = int(cy - 15 * math.sin(sim["heading"]))
cv2.circle(img, (cx, cy), 12, (0, 200, 255), -1)
cv2.line(img, (cx, cy), (hx, hy), (255, 255, 255), 2)
return img
@robot.mainloop
def loop(ctx):
dt = 0.1
sim["heading"] += sim["w"] * dt
sim["x"] += sim["v"] * math.cos(sim["heading"]) * dt
sim["y"] += sim["v"] * math.sin(sim["heading"]) * dt
sim["battery"] = max(10.0, sim["battery"] - 0.0005 - 0.002 * abs(sim["v"]))
pose(); battery(); render(draw())
robot.init_connector()
robot.start()
Пример 4. Бортовая модель ИИ: признаки в телеметрии
Бортовой компьютер может сам обрабатывать видео и отправлять на сервер не только кадры, но и выделенные признаки — например, результаты детекции. Модели ИИ на сервере получают их вместе с телеметрией.
from ultralytics import YOLO # pip install ultralytics
model = YOLO("yolov8n.pt") # лёгкая модель для бортового компьютера
@robot.telemetry("detections")
def detections(frame):
result = model(frame, verbose=False)[0]
return [
{"cls": result.names[int(b.cls)], "conf": round(float(b.conf), 2),
"xyxy": [round(v) for v in b.xyxy[0].tolist()]}
for b in result.boxes
]
@robot.mainloop
def loop(ctx):
ok, frame = ctx["cam"].read()
if ok:
send_frame(frame)
detections(frame)
На слабых бортовых компьютерах запускайте детекцию не на каждом кадре (например, раз в 3–5 итераций) или переносите её в вычислительную среду платформы — см. книгу «Управление роботами с помощью ИИ».
Устранение неполадок
Частые проблемы
| Симптом | Причина | Решение |
|---|---|---|
ModuleNotFoundError: No module named 'cv2' при импорте SDK |
OpenCV не входит в зависимости пакета | pip install opencv-python (или opencv-python-headless) и numpy |
AttributeError: 'Conductor' object has no attribute 'command_handler' |
Не объявлен обработчик команд до init_connector() |
Объявите функцию с декоратором @robot.com_handler (можно пустую) |
AttributeError: ... 'main_loop_func' |
Не объявлен основной цикл | Объявите функцию с декоратором @robot.mainloop |
TypeError: ... takes 0 positional arguments but 1 was given в основном цикле |
Функция основного цикла не принимает context |
def loop(context): ... |
socketio.exceptions.ConnectionError при запуске |
Сервер недоступен или неверный адрес | Проверьте server и port в params.json, доступность сервера: curl http://<сервер>:<порт>/socket.io/?EIO=4&transport=polling |
Робот не находит сервер artex.arcon |
Не настроено имя по умолчанию | Укажите IP-адрес сервера в params.json или добавьте запись в /etc/hosts |
Ошибка OpenCV !_img.empty() в imencode |
В функцию @robot.video передан/возвращён пустой кадр (None) |
Проверяйте результат cam.read() перед отправкой кадра |
Параметры из params.json не применяются |
Программа запущена из другого каталога | Запускайте из каталога с params.json или задайте WorkingDirectory в systemd |
Консоль заполнена сообщениями [TODO] |
Уровень журнала all |
"logger_level": "skip_TODO" или robot.log.set_level("skip_TODO") |
| Робот в списке, но видео/телеметрии нет | Сервер не прислал Ready, или функции-источники не вызываются в основном цикле |
Проверьте в журнале сообщение in heartbeat recieved [...]; вызовите функции @robot.telemetry / @robot.video в @robot.mainloop |
| Команды не доходят до обработчика | Робот ещё не получил Ready |
Дождитесь heartbeat; проверьте журнал на recieved command |
| Видео отстаёт или «рвётся» | Слишком большие кадры для канала | Уменьшите разрешение (например, 320×240), увеличьте max_send_freq до 0.1 |
| Программа «висит» без связи после перезапуска сервера | SDK исчерпал 5 попыток переподключения | Используйте сторожевую проверку (ниже) и автоперезапуск через systemd |
| Обработчик команд подтормаживает | Долгие операции в com_handler блокируют приём событий |
Выполняйте долгие действия в отдельном потоке, в обработчике только меняйте состояние |
Сторожевая проверка соединения
SDK делает 5 попыток переподключения. Если сервер был недоступен дольше, соединение не восстановится само. Завершите программу — systemd перезапустит её, и подключение будет выполнено заново:
import os, time
DISCONNECT_LIMIT = 30 # секунд
@robot.mainloop
def loop(ctx):
if robot.sio.sio.connected:
ctx["online_at"] = time.monotonic()
elif time.monotonic() - ctx.get("online_at", time.monotonic()) > DISCONNECT_LIMIT:
robot.log("ERROR", "Нет связи с сервером, перезапуск")
stop_motors()
os._exit(1)
...
Ошибки в основном цикле
SDK не перехватывает исключения в функции основного цикла — любая ошибка завершает программу. Для долгой автономной работы оборачивайте опасные участки:
@robot.mainloop
def loop(ctx):
try:
read_sensors()
except Exception as ex:
robot.log("ERROR", f"Датчики: {ex}")
Диагностика
- Включите полный журнал:
"logger_level": "all","logger_type": "console". - Проверьте последовательность в журнале:
connected→in heartbeat recieved ['d_meta', 'Ready']→recieved command .... - Проверьте робота без платформы на минимальном тестовом сервере.
- Запросите журнал робота удалённо командой
{"file": "log"}(при"logger_type": "file").
Известные ограничения версии 0.1.1
opencv-pythonне указан в зависимостях пакета.- Параметры
max_send_freqиmax_main_freq— это периоды в секундах. - Поле
sidв состоянии робота не заполняется SDK (всегдаnull). - Команды
web_settingsпринимаются, но не обрабатываются. - Раздел
externalконфигурации зарезервирован и не используется. - Нет встроенного сторожевого таймера команд и соединения — реализуйте их в своей программе (примеры выше и на странице Обработка команд).