agi_fleet/agent/.env.example
2026-08-04 15:14:59 +04:00

167 lines
8.8 KiB
Plaintext

# sanad_api_x2 — AGIBOT X2. Copy to .env and fill in.
# ONE agent = telemetry + map + logs + alerts + remote (see fleet/README.md).
#
# READ THIS FIRST — the X2 exposes no fixed topic contract, so EVERY topic name
# and field path used to read robot state is an env var in this file. Nothing is
# hard-coded in the agent. Discover the real ones with:
#
# ./fleet_install.sh probe x2 <ip> (or: tools/probe_x2.sh <user>@<ip>)
#
# then set them below and restart — no code change, no image rebuild.
# ── fleet server (REQUIRED — YS Lootah gives you these two) ──────────────────
SERVER_URL=https://eco.yslootahrobotics.com
DEVICE_TOKEN=REPLACE_WITH_DEVICE_TOKEN
# ── identity ─────────────────────────────────────────────────────────────────
# The robot's REAL serial — keys the robot on the server ({sn} routes).
SN=REPLACE_WITH_X2_SERIAL
# Friendly display name shown on the dashboard (default <model>_<ip-octet>).
ROBOT_NAME=x2_10
ROBOT_BRAND=agibot
ROBOT_TYPE=humanoid
ROBOT_MODEL=x2
# Maps subdir (<MAPS_DIR>/<ROBOT>/…) + X-Robot-Name header.
ROBOT=sanad
# Optional: app data dir whose size is reported in storage (as /host/<path>
# when the installer's read-only /:/host mount is used). Empty = omit.
STORAGE_DATA_PATH=
# ═════════════════════════════════════════════════════════════════════════════
# X2 STATE SOURCE — the one robot-specific section
# ═════════════════════════════════════════════════════════════════════════════
# auto = http if X2_STATE_URL is set, else ros2 if rclpy imports, else aimrt if
# aimrt_py imports, else none (heartbeat: battery null, status offline).
# http = poll X2_STATE_URL for one JSON object; map fields with X2_FIELD_* .
# ros2 = subscribe X2_TOPIC_* (needs a ROS base image — see Dockerfile).
# aimrt = AgiBot's runtime. NOT natively bound (channel/message defs are not
# public). Enable AimRT's ROS 2 plugin and use X2_SOURCE=ros2 instead.
# none = never read; always heartbeat. Useful to bring a robot online on the
# dashboard (identity, storage, maps, logs) before the state map is known.
X2_SOURCE=auto
# ── backend: http ────────────────────────────────────────────────────────────
# A vendor endpoint returning one JSON object with the robot's state.
X2_STATE_URL=
X2_HTTP_INTERVAL=1
# ── backend: ros2 ────────────────────────────────────────────────────────────
# Topic names from `ros2 topic list` on the robot. Blank = don't subscribe.
# The TYPE must match `ros2 topic info <topic>`; custom vendor messages work as
# long as their python package is importable inside the container.
X2_TOPIC_BATTERY=/battery_state
X2_TYPE_BATTERY=sensor_msgs/msg/BatteryState
X2_TOPIC_JOINTS=/joint_states
X2_TYPE_JOINTS=sensor_msgs/msg/JointState
X2_TOPIC_ODOM=/odom
X2_TYPE_ODOM=nav_msgs/msg/Odometry
# Must match the robot's domain or ROS 2 discovery silently sees nothing.
ROS_DOMAIN_ID=0
# ── field mapping (applies to EVERY backend) ─────────────────────────────────
# Dotted paths into the message/JSON. They work over both ROS message objects
# and plain dicts, support list indices ("bms.cell_temp[0]"), and yield null for
# any missing link — a wrong path degrades a field, it never crashes the agent.
# Defaults below are the STANDARD ROS messages: correct for a stock ros2 setup,
# almost certainly wrong for a vendor http payload. Empty = field unavailable.
X2_FIELD_SOC=percentage
X2_FIELD_VOLTAGE=voltage
X2_FIELD_CURRENT=current
X2_FIELD_TEMP=temperature
X2_FIELD_SOH=
X2_FIELD_CYCLES=
# Standard sensor_msgs/JointState has NO temperature array — leave empty unless
# the X2 publishes a custom message that carries one (motor_temp reports null).
X2_FIELD_TEMPS=
# Joint velocities → the "moving" status (max |velocity| > 0.15).
X2_FIELD_VEL=velocity
X2_FIELD_X=pose.pose.position.x
X2_FIELD_Y=pose.pose.position.y
# http backend only: an FSM id and a {name: version} firmware dict, if exposed.
X2_FIELD_FSM=
X2_FIELD_FW=
# ── unit conventions (verify these on the real robot!) ───────────────────────
# auto = a value in (0, 1] is treated as a 0..1 fraction and scaled to %.
# fraction = always scale x100. percent = never scale.
X2_SOC_SCALE=auto
# +1 = positive current means CHARGING (the ROS BatteryState convention).
# Set -1 if the X2 reports the opposite sign, or "charging" will be inverted.
X2_CURRENT_SIGN=1
# The telemetry schema is VOLTS and AMPS. BMS firmware often publishes mV/mA
# instead — set 0.001 for those, so battery_detail isn't off by 1000x.
X2_VOLTAGE_SCALE=1
X2_CURRENT_SCALE=1
# ── position / status ────────────────────────────────────────────────────────
# none | ros2 | rosbridge. ros2 is INDEPENDENT of X2_SOURCE: the Control Dash
# carries no odometry, but ROS publishes it — so http battery + ros2 position
# run side by side. Needs rclpy, i.e. the unit must source the ROS overlay.
# On the X2 as deployed: /aima/mc/leg_odometry (nav_msgs/msg/Odometry).
X2_POSITION_SOURCE=ros2
ROSBRIDGE_URL=ws://127.0.0.1:9090
# Subscription reliability. MUST stay best_effort unless you know otherwise:
# the X2 publishes /aima/mc/leg_odometry as BEST_EFFORT, and a RELIABLE
# subscriber receives NOTHING from a BEST_EFFORT publisher — the subscription
# is created, no error is raised, and position silently stays null forever.
# A BEST_EFFORT subscriber reads from either kind of publisher.
X2_ROS_QOS=best_effort
# There is no known X2 loco FSM RPC — leave 0.
X2_READ_FSM=0
# ── identity / networking ────────────────────────────────────────────────────
# Which NIC's MAC is reported as the robot identity.
MAC_INTERFACE=eth0
# ── fault thresholds ─────────────────────────────────────────────────────────
LOW_SOC=50
# Only meaningful when X2_FIELD_TEMPS is mapped; otherwise motor_temp is null.
MOTOR_TEMP_MAX=85
# ── cadence / transport ──────────────────────────────────────────────────────
POLL_INTERVAL=2
VERIFY_TLS=1
HTTP_TIMEOUT=30
# ── map sync (uploaded ONCE per content; status shown in telemetry "map") ────
# pgm+yaml sets are rendered to PNG with pure stdlib; RTAB-Map .db is sent
# as-is (skipped above the server's upload cap).
MAPS_DIR=/data/maps
EXTRA_MAP_DIRS=/data/nav2_maps
DATA_DIR=/data/web_data
STATE_DIR=/data/state
MAP_SELECT=all
MAP_UPLOAD_MODE=multipart
MAP_POLL_INTERVAL=30
MAP_MAX_UPLOAD_MB=7
# ── logs + alerts ────────────────────────────────────────────────────────────
LOGS_INTERVAL=60
# auto = find a RUNNING sanad* container via the /host mount and tail its
# json-log. If the X2 doesn't run the Sanad app, telemetry.project_logs is null
# and only the agent's own lines ship — no configuration needed.
PROJECT_LOG_CONTAINER=auto
PROJECT_LOG_PATH=
PROJECT_LOG_LABEL=
ALERT_SCAN_INTERVAL=10
ALERT_LOG_COOLDOWN=300
# ── remote (register a dashboard URL + ssh for the fleet UI) ─────────────────
# If the X2 exposes no dashboard, remote.web is null and only ssh registers.
REMOTE_ENABLE=1
REMOTE_PORTS=8001,8014,8011,8012,8013,8000,8080
REMOTE_KIND=web
REMOTE_INTERVAL=60
SSH_REGISTER=1
# The X2's LOGIN user. The installer writes the user it deployed with; set it
# here for manual runs.
# Left empty, ssh registration is skipped rather than registering a wrong user.
SSH_USER=
# ── control panel (READ-ONLY; remote mode-SWITCH is off by design) ───────────
# Blank URL = use the auto-discovered dashboard port. No known X2 FSM id map, so
# control.mode reports "unknown" until the ids are filled into the agent.
CONTROL_STATUS_URL=
CONTROL_ENABLE=0