167 lines
8.8 KiB
Plaintext
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
|