---json {"name": "Sensors and multiple boards"} --- ====== Sensors and multiple Axioms ====== Complete the [[:axiom:ros2:getting_started|host-driver setup]] and leave discovery running. Automatic startup, stable board names and optional identity rules are described in [[:axiom:ros2:automatic_startup|automatic startup]]. The examples use the namespace ''%%/axiom%%''. Substitute your configured namespace and the actual discovered sensor identities. ''%%boards.yaml%%'' describes board connections and optional aliases; sensors do not need to be listed individually. ===== Discover topics and commands ===== First copy your full board namespace from ''/axiom_discovery/boards''. In a prepared ROS terminal, replace the placeholder below; the variable is local to this terminal: ros2 topic list -t --no-daemon ros2 service list -t BOARD_NS='PASTE_DISCOVERED_BOARD_NAMESPACE_HERE' ros2 topic echo "$BOARD_NS/devices" std_msgs/msg/String \ --qos-reliability reliable --qos-durability transient_local \ --qos-depth 1 --full-length --once ==== Sensor topic names ==== Measurements use names such as ''%%/axiom/lsm6ds_b1_76a/data_raw%%'', ''%%/axiom/vl53l4cd_b1_129/range%%'' and ''%%/axiom/amg8833_b1_169/image%%''. The pattern is ''%%//_b_/%%''. Bus and address always distinguish sensor instances, so another sensor of the same model does not rename an existing one. Unusual model/bus characters are escaped. Copy exact paths from the topic list or ''%%public_topics%%'' in the device inventory. Inventory ''%%topic%%'' is the relative sensor root; unresolved metadata rows have ''%%topic: null%%'' and should be skipped. ''%%public_topics.x.data_raw%%'' identifies a published IMU topic. Other payload groups have separate ''%%group_%%'' paths. The map gains outputs after their first sample and becomes empty when publishers retire. Output-only devices can expose commands without measurement topics. Their ''command_topics'' array advertises direct angle/colour inputs, while ''commands'' lists the remaining acknowledged services. These are separate from the measurement ''public_topics'' map. The parallel ''output_info[group][suffix]'' map records each canonical output's actual ''message_type'' and ''unit''. A ''null'' unit means unspecified/unitless or no single unit for a compound/generic message. See [[:axiom:ros2:standard_messages#find_the_actual_outputs|an inventory example]]. ^Output suffix ^Type and units ^ |''%%data_raw%%'' |''%%sensor_msgs/msg/Imu%%''; acceleration m/s² including gravity, angular velocity rad/s; no fused orientation | |''%%range%%'' |''%%sensor_msgs/msg/Range%%''; scalar distance in metres; invalid readings are NaN | |''%%image%%'' |''%%sensor_msgs/msg/Image%%''; thermal °C or supported multizone distances in metres, ''%%32FC1%%''; invalid pixels are NaN| |''%%temperature%%''|''%%sensor_msgs/msg/Temperature%%''; °C | |''%%humidity%%'' |''%%sensor_msgs/msg/RelativeHumidity%%''; fraction from 0 to 1 | |Light attribute, e.g. ''ambient''|''sensor_msgs/msg/Illuminance''; recognised scalar light quantities in lux| |Other scalar attribute, e.g. ''UVI''|''std_msgs/msg/Float64'', ''Bool'' or ''String''; published units in ''output_info''| |''%%grid%%'' |''%%axiom_interfaces/msg/ThermalGrid%%''; dimensions and pixel temperatures | |''%%sample%%'' |''%%axiom_interfaces/msg/DeviceSample%%''; decoded values, validity and timing | |''%%data%%'' |''%%std_msgs/msg/String%%''; JSON decoded measurement view | A descriptor with both thermal and distance grids uses ''%%thermal/image%%'' and ''%%range/image%%'' to distinguish them. Supported profiles supply generic ''%%sample%%'' and ''%%data%%''; standard typed outputs additionally need a supported mapping and known units. A Device Library profile does not automatically add a custom binary decoder. Old bus/device topic and command paths are removed. Update topic selections, recording lists, exact QoS override keys and alias ''%%source%%'' selectors when pulling this version. Frame IDs and firmware bus/address fields remain unchanged. See the [[https://github.com/robotical/axiom-ros2/blob/main/src/axiom_driver/README.md#migrating-topic-configuration|full contracts and migration keys]]. ===== Standard messages and scalar fallbacks ===== An LTR-390 exposes ''ambient'' as Illuminance in lux and ''UVI'' as Float64 in index units, in addition to generic ''sample'' and ''data''. Other numeric scalars use decoded SI values when known, otherwise decoded engineering values and their original units. Bool and text fields use Bool and String. Invalid numeric readings publish NaN; invalid bool/text readings are omitted. Arrays retain their existing image/grid or generic handling, and automatically mapped compound fields are not duplicated as automatic scalar topics. Float64, Bool and String have no timestamp or unit field. Use ''output_info'' for units and ''sample'' for timing, validity and decoded values. Attribute suffixes retain case and escape unsafe names; a reserved ''data'' attribute uses ''attribute_data''. Copy exact paths rather than constructing them from a model label. The [[:axiom:ros2:standard_messages|standard-message guide]] covers fields, compatible units, aliases and the optional ''devInfoJson.ros'' mapping schema. Optional mappings are included in current main; update, rebuild and restart the host driver to use them; they are not required for the defaults above. A mapping declaration does not install a device profile or change firmware acquisition. ===== Sensor hot swapping and board reconnection ===== With Axiom powered, connected and acquiring, plug in a supported sensor. Its publishers and descriptor services appear once its metadata is resolved. Unplug only that sensor, leaving Axiom connected. Its interfaces retire after firmware and driver detection. Reconnecting the sensor restores supported interfaces and measurements without another acquisition call. A subscriber can keep a topic name registered after its publisher disappears. Replace the placeholder with a discovered topic and check publisher count: SENSOR_TOPIC='PASTE_DISCOVERED_SENSOR_TOPIC_HERE' ros2 topic info "$SENSOR_TOPIC" --verbose With normal discovery startup, an unexpected disconnection is recovered automatically. A board reappearing over USB is verified again; if both transports are allowed, a verified Wi-Fi endpoint can provide fallback under the same namespace. The board must remain powered for Wi-Fi fallback. An intentional Disconnect is retained until an explicit Connect or process restart. With the explicit manual settings shown below, reconnect the board and request acquisition again: ros2 service call /axiom/connect axiom_interfaces/srv/Connect '{device_uri: ""}' ros2 service call /axiom/publish_data_subscription \ axiom_interfaces/srv/PublishedDataSubscription '{rate_hz: 20.0}' ''%%auto_reconnect: false%%'' leaves unexpected loss recovery manual. An intentional disconnect disables automatic reconnect. The subscription rate controls delivery, rather than the sensor's sampling rate. ===== Optional aliases ===== Merge aliases into an existing board entry, keeping its connection settings: topic_aliases: imu/data_raw: {type: LSM6DS, source: data_raw} range: {type: VL53L4CD, source: range} thermal/image: {type: AMG8833, source: image} light: {type: LTR-390, source: ambient} uv: {type: LTR-390, source: UVI} This is a fragment, not a replacement board file. Under board ''%%axiom%%'', the matching aliases become ''%%/axiom/imu/data_raw%%'', ''%%/axiom/range%%'', ''%%/axiom/thermal/image%%'', ''/axiom/light'' and ''/axiom/uv''. They add convenience paths carrying the same messages. Header-bearing messages retain source timestamps and frame IDs; Float64, Bool and String remain headerless. Alias ''%%source%%'' values use the canonical output suffixes in the table above. If an alias uses a generated readable path, the alias takes precedence: the automatic output at that exact path is suppressed, even while the alias is unbound, and is not listed in ''%%public_topics%%''. An alias binds only when exactly one matching device is online, resolved and fresh. For ambiguity, add string ''%%bus%%'' and ''%%address%%'' selectors from the inventory, or use the canonical sensor topic. Stop acquisition/disconnect, then relaunch the driver after changing its read-only configuration. ===== Direct output command topics ===== Servo angles now use subscribed ''std_msgs/msg/Float64'' topics in radians. LED colours use ''std_msgs/msg/ColorRGBA'' with normalized RGB values and one topic per pixel for indexed LEDs. Copy the relative inputs, types and units from ''devices.command_topics'' and prefix them with the board namespace. Before a controller starts, a command topic can have one driver subscription and no publisher. The former angle/colour services are removed. Enable/disable, LED brightness/off and supported configuration remain acknowledged services in ''commands''. Measurement aliases do not redirect command inputs. The supported four-tier StackLight pattern is also an acknowledged service. Use [[:axiom:ros2:lighting|the RViz lighting panel]] for whole colour, brightness, Off and stack controls. Follow [[:axiom:ros2:output_topics|Servo and LED command topics]] for direct examples and [[:axiom:ros2:servo_slider|the joint-slider demo]] for an interactive controller and optional right-leg preview. ===== Descriptor services ===== Remaining acknowledged descriptor services appear under ''%%/axiom/MODEL_bBUS_ADDRESS/commands/COMMAND%%''. The driver also uses that path family for typed command-topic inputs, so use inventory ''commands'' versus ''command_topics'' to distinguish them. Discover the exact service and inspect its descriptor's argument order, units and allowed values: ros2 service list -t BOARD_NS='PASTE_DISCOVERED_BOARD_NAMESPACE_HERE' ros2 topic echo "$BOARD_NS/device_metadata" std_msgs/msg/String \ --qos-reliability reliable --qos-durability transient_local \ --qos-depth 1 --full-length --once For a sensor exposing ''%%set_sample_rate%%'', replace the service path below and choose a rate it actually advertises; 52 Hz is an illustrative LSM6DS value: RATE_SERVICE='PASTE_DISCOVERED_SAMPLE_RATE_SERVICE_HERE' ros2 service call "$RATE_SERVICE" \ axiom_interfaces/srv/DeviceCommand '{values: [52.0]}' Numeric ''%%values%%'' follow descriptor order and units. No-argument commands use ''%%{values: []}%%''. Success means firmware acknowledgement, not physical completion of actuator movement. The four-tier ''vt: stacklight'' contract is supported as described in [[:axiom:ros2:lighting|the lighting guide]]. Other unsupported custom value formats are not callable. ===== Updating an existing installation ===== The current host driver uses only the new generated sensor paths. There is no option to publish the old bus/device family. Before starting an updated driver, migrate your local board/parameter files, subscriptions, plot selections and bag-recording topic lists. Git pulls do not replace ignored local configuration such as ''config/boards.yaml''. ^ Earlier setting or interface ^ Current setting or interface ^ | Generated IMU ''bus_1/device_76a/imu/data_raw'' | ''lsm6ds_b1_76a/data_raw'' | | Thermal grid ''bus_1/device_169/thermal/grid'' | ''amg8833_b1_169/grid'' | | Thermal image ''bus_1/device_169/thermal/image'' | ''amg8833_b1_169/image'' | | Multizone distance image ''bus_1/device_129/range/image'' | ''vl53l5cx_b1_129/image'' | | IMU alias source ''imu/data_raw'' | ''source: data_raw'' | | Thermal-grid alias source ''thermal/grid'' | ''source: grid'' | | Ordinary thermal/range-image alias sources ''thermal/image'' or ''range/image'' | ''source: image''; only dual-image descriptors retain separate image suffixes | | Per-sensor ''bus_1/device_76a/commands/set_sample_rate'' | ''lsm6ds_b1_76a/commands/set_sample_rate'' | | Inventory ''topic'' | New relative sensor root, or ''null'' before metadata resolves | | Servo-angle ''DeviceCommand'' service | Advertised ''command_topics'' Float64 topic in radians | | Indexed LED-colour ''DeviceCommand'' service | Advertised ColorRGBA topics ending in ''pixel_0'', ''pixel_1'', etc. | The paths above are relative examples beneath your board namespace. Copy your actual sensor paths from the live inventory; addresses and bus numbers are not fixed requirements. Aliases such as ''imu/data_raw'' or ''thermal/image'' remain valid user-chosen destination names with the new ''source'' values. They do not enable old generated topics. QoS override keys match the exact current relative path, with no fallback to an old key. For example, in a board entry: sensor_qos.overrides: vl53l4cd_b1_129/range: reliability: reliable depth: 20 Replace the example key with the sensor path from your inventory, without the board namespace. An alias uses its own exact QoS key. Old recordings still contain their recorded topic names; select or remap those paths when replaying them. Firmware bus/address fields, message units, timestamps and coordinate-frame IDs are unchanged by this naming migration. A frame such as ''axiom_bus_1_device_76a'' is a coordinate-frame identifier, not a legacy topic path. Preserve the correct mounting/TF configuration for your sensor. Disable active actuators and wait for acknowledgement before stopping their controller. Stop affected ROS processes, then update a clean checkout on ''main'', rebuild and source the workspace: git pull --ff-only origin main source scripts/setup_env.sh rosdep install --from-paths src --ignore-src -r -y colcon build --base-paths src --symlink-install source scripts/setup_env.sh Build the optional demo packages too if you use them, as described in their walkthroughs. Refresh each reused tmux pane with ''source scripts/setup_env.sh'' and restart the driver, bridges and visualisers with the same ROS domain and compatible middleware settings. Check the topic list and inventory again before using saved layouts. ===== Multiple boards ===== ==== Automatic startup ==== The normal ''%%axiom.launch.py%%'' already handles multiple physical boards independently. No endpoint file is required. For selected boards and readable names, use a complete discovery board file such as: discovery: usb: true wifi: true include_unconfigured: false axioms: axiom/front: id: '001122334455' axiom/rear: id: 'aabbccddeeff' Replace both example IDs with the firmware IDs from ''%%/axiom_discovery/boards%%''. Save the file and start it: ros2 launch axiom_driver axiom.launch.py boards_file:=/absolute/path/boards.yaml Connection and acquisition start automatically. The board keys give ''%%/axiom/front%%'' and ''%%/axiom/rear%%'' namespaces and distinct default frame prefixes. Use one entry per physical board; verified USB and Wi-Fi endpoints for the same ID share one driver, preferring USB. Setting ''%%include_unconfigured: false%%'' keeps other discovered boards out of acquisition. For independent demos on shared Wi-Fi, combine this board selection with a different ROS domain per demo. Namespaces alone do not isolate ROS traffic; domains do not select physical boards or provide ownership across computers. See [[:axiom:ros2:automatic_startup#multiple_demos_on_the_same_wi-fi|shared-network setup]]. ==== Explicit endpoints with manual startup ==== Use this alternative when you want to start a driver per configured endpoint and control connection through services. It does not run discovery or switch transports automatically. This board file is for ''%%multi_axiom.launch.py%%''; it contains endpoint parameters, with no discovery settings or ''%%id%%'' rules. Stop the discovery launch first. Use separate namespace keys and endpoints for **different physical Axioms**. Replace the USB path and illustrative Wi-Fi address below: axioms: axiom/front: transport: serial serial.port: /dev/serial/by-id/REPLACE_WITH_FRONT_AXIOM auto_connect: false auto_reconnect: false autosub: false axiom/rear: transport: ws device_uri: ws://192.0.2.11/ws auto_connect: false auto_reconnect: false autosub: false Save this in the board file selected by ''%%machine.env%%'', with one ''%%axioms%%'' mapping. Stop/disconnect any existing single-board driver before starting the multi-board launch. The [[:axiom:ros2:getting_started#use_tmux_for_the_ros_consoles|tmux helper]] can hold the launch in Terminal 1, service calls in Terminal 2, and one board's monitor in each of Terminals 3 and 4. In Terminal 1: ros2 launch axiom_driver multi_axiom.launch.py \ boards_file:="$AXIOM_ROS_BOARDS_FILE" In Terminal 2: ros2 service call /axiom/front/connect axiom_interfaces/srv/Connect '{device_uri: ""}' ros2 service call /axiom/front/publish_data_subscription \ axiom_interfaces/srv/PublishedDataSubscription '{rate_hz: 20.0}' ros2 service call /axiom/rear/connect axiom_interfaces/srv/Connect '{device_uri: ""}' ros2 service call /axiom/rear/publish_data_subscription \ axiom_interfaces/srv/PublishedDataSubscription '{rate_hz: 20.0}' Each driver has independent connection, acquisition, metadata, queues, aliases and frame prefix. Duplicate namespaces, frame prefixes and configured endpoints are rejected within this launch. Current host-driver sessions also take per-user endpoint and firmware-MAC locks on the same computer. Discovery deduplicates USB and Wi-Fi endpoints by firmware MAC; advisory locks do not coordinate other computers or unrelated applications. Automatic connection, recovery and acquisition are the defaults. The explicit manual example above disables all three flags. For automatic multi-board startup without endpoint configuration, use ''%%axiom.launch.py%%'' with optional identity rules as described in [[:axiom:ros2:automatic_startup|automatic startup]]. Stop echoes/viewers, then clean up in Terminal 2 while the launch is still running: ros2 service call /axiom/front/publish_data_subscription \ axiom_interfaces/srv/PublishedDataSubscription '{rate_hz: 0.0}' ros2 service call /axiom/front/disconnect axiom_interfaces/srv/Disconnect '{}' ros2 service call /axiom/rear/publish_data_subscription \ axiom_interfaces/srv/PublishedDataSubscription '{rate_hz: 0.0}' ros2 service call /axiom/rear/disconnect axiom_interfaces/srv/Disconnect '{}' Finally stop the launch with Ctrl+C. Other consumers should use ROS subscriptions, rather than additional firmware acquisition connections to the same board. Published walkthrough: [[https://userguides.robotical.io/axiom/ros2/sensors_and_boards|Sensors and multiple boards]].