ROS 2 Lifecycle Nodes (packages/)
Part of the OpenRAL public-symbol inventory. Hand-curated;
(LNN)markers are refreshed bytools/refresh_methods_linenos.py.
Thin wrappers around the Python-layer adapters; each exposes a single
main() entry point for ros2 run.
packages/openral_hal_node/openral_hal_node/lifecycle_node.py
main() -> None(L28) — The one manifest-driven HAL lifecycle node every robot runs:openral_hal.lifecycle.make_lifecycle_main_from_manifest(node_name="openral_hal_node"), i.e. aManifestHALLifecycleNodethat builds its HAL from therobot_yaml+hal_modeparameters viaopenral_hal.build_hal. Heartbeat,/openral/safe_actionconsumer and/openral/estoplatch come fromHALLifecycleNodeBase.openral deploy sim|runlaunches it under the ROS node NAMEopenral_hal_<robot_id>(a__node:=remap — not a package name). Covered bypackages/openral_hal_node/test/(parametrised by robot manifest) andtests/integration/test_openarm_hal_lifecycle.py.
packages/world_state/openral_world_state_ros/lifecycle_node.py
main() -> None(L73) —WorldStateAggregatorlifecycle node. Subscribes joint/policy/camera (camera_topic(name)percamera_namesentry; thecamera_topic_prefixparameter was removed by ADR-0108;object_depth_points_topicdefaults to""= no depth fallback)/attachment streams and publishes typed fast/slow world-state summaries; attachment updates atomically replace the prior set so multiple changes never expose a partial model; the envelope'splace_declarationandgrasp_declarationare relayedAttachmentState -> WorldStateStamped(_place_declaration_from_idl/_grasp_declaration_from_idl,_fill_place_declaration/_fill_grasp_declaration). Shared withrskill_runner_node._direct_image_frame_sensors() -> set[str]— Thedirect_image_frame_sensorsparameter as a set;_on_imageskips these cameras entirely, since the co-located sensor leg's pump already writes them to the aggregator at full cadence._IDL_DIAG_TO_STR: dict[int, str](L949) —DiagnosticState.diag_statusuint8 → the lowercase label ("ok"/"warn"/"stale"/"error")world_state_from_idlwrites ontoopenral_core.WorldState.world_state_from_idl(msg) -> WorldState(L957) — Symmetric inverse ofbuild_world_state_stamped_msg: reconstructs anopenral_core.WorldStatefrom aWorldStateStampedmsg. Used by the reasoner node to feed real state into itsContextRenderer;image_framesis alwaysNone(the IDL carries image topic refs, not inline pixels). Each detected object'sbbox_3dcomes from thedetected_object_bbox_valid/_bbox_min/_bbox_maxarrays (ignored unless index-aligned with the labels, so an older publisher decodes as no boxes); the reasoner seeds named grasp targets from it.build_world_state_stamped_msg(node, world_state) -> WorldStateStamped(L1140) — Inverse translation: a PydanticWorldState→WorldStateStampedIDL msg. Parallel arrays are deterministically ordered (sorted by key) so two consumers comparing snapshots at the same timestamp see the same byte layout.
packages/openral_nav2_bringup/openral_nav2_bringup/
_footprint_geometry.py— Footprint geometry shared by this package's nodes:convex_hull_2d(monotone-chain convex hull) andbase_footprint_polygon(manifest polygon, else an N-gon around the radius). The costmaps' footprint is now static, substituted at launch — no node publishes it.prop SHAPE_SPHERE, SHAPE_CAPSULE, SHAPE_BOX(_footprint_geometry.py L23–25) — Mirroropenral_msgs/AttachedCollisionPrimitive's shape enum as plain ints, so this pure module imports without the colcon message overlay._MIN_POLYGON_VERTICES(_footprint_geometry.py L27) —3; minimum distinct-vertex countconvex_hull_2daccepts.convex_hull_2d(points) -> list[tuple[float, float]](_footprint_geometry.py L30) — Counter-clockwise convex hull via Andrew's monotone chain; drops collinear points, raises on fewer than_MIN_POLYGON_VERTICESdistinct points.base_footprint_polygon(description, *, circle_samples=12) -> list[tuple[float, float]](_footprint_geometry.py L94) — The manifest'sfootprint_polygonif declared, else a circumscribed N-gon aroundfootprint_radius.payload_scan_filter_node.py— Removes a carried payload's and the robot's own silhouette from the/scanNav2's costmaps read, republishing on/openral/nav2/scan. Every failure mode leaves more obstacles in the scan, never fewer, except a startup gate that withholds output until the self-TF first resolves.DEFAULT_OUTPUT_TOPIC(payload_scan_filter_node.py L66) —"/openral/nav2/scan"; where the filtered scan is published — every costmap observation source andconfig/nav2_panda_mobile.yaml's collision monitor point here.points_in_primitive(points_xyz, shape_type, shape_dimensions, transform, *, margin_m=0.0)(payload_scan_filter_node.py L69) — The kernel'ssurface_distancecontainment at zero slack, vectorised over points._MIN_POLYGON_VERTICES(payload_scan_filter_node.py L45) — Now imported from_footprint_geometry(formerly a local copy of the constant).points_in_convex_polygon(points_xy, polygon, *, margin_m=0.0)(payload_scan_filter_node.py L140) — Half-plane containment test that verifies CCW convexity rather than assuming it (a concave or CW outline raises, so the node then removes nothing).filter_scan_ranges(ranges, *, angle_min, angle_increment, range_min, range_max, placements, margin_m=0.0, self_polygon=None, base_from_scan=None, self_margin_m=0.0)(payload_scan_filter_node.py L208) — Sets removed beams toinf(Nav2 discards those:inf_is_validdefaults toFalse) and leaves readings already outside[range_min, range_max]alone.main(args=None) -> None(payload_scan_filter_node.py L309) — ROS 2 entry point; definesPayloadScanFilterNodelazily inside so the module stays importable without the colcon overlay.twist_to_action_relay_node.py— Opt-in (nav2.launch.py cmd_vel_relay:=true) relay of Nav2's/cmd_velonto/openral/candidate_actionas horizon-1BODY_TWISTchunks viaROSPublishingHAL, so the safety kernel bounds base speed. Refuses (ROSConfigError) a manifest declaringbase_joints(MobileBaseBridgeowns that/cmd_vel, ADR-0040/0099) or lackingbody_twistor eithermax_base_*speed bound; aftercmd_vel_timeout_s(default 0.5 s, wall clock) of silence following a non-zero command it publishes one zero twist.twist_to_body_twist_row(msg) -> tuple[float, ...](twist_to_action_relay_node.py L45) —Twist→[linear.x, linear.y, 0, 0, 0, angular.z], the same mapping asMobileBaseBridge._on_cmd_vel.main(args=None) -> None(twist_to_action_relay_node.py L57) — ROS 2 entry point; definesTwistToActionRelayNodelazily inside.
packages/openral_nav2_bringup/launch/nav2.launch.py
generate_launch_description() -> LaunchDescription(L84) — Stand-alone bring-up of the upstream Nav2navigation_launch.pyinclude;robot_yamlrewritesrobot_radius/inflation_radius/motion_modelover the shared base params file (lidar or, forslam_backend=visual, the visual variant) and clamps its velocity limits to the manifest'smax_base_*bounds._cap_speeds(params, max_linear_m_s, max_angular_rad_s) -> None(L245) — Lowersvelocity_smoother.max_velocity/min_velocity(the(vx, vy)corner scaled onto the‖v‖disk) and MPPIvx_max/wz_maxin place to the kernelBODY_TWISTbounds; never raises a limit._speed_capped_params_file(params_file, max_linear_m_s, max_angular_rad_s) -> str(L292) —params_fileunchanged when neither bound is set, else a temp copy with_cap_speedsapplied (JazzyRewrittenYamlcannot set a list element).BOND_TIMEOUT_S(L60) —30.0; how long a Nav2 lifecycle manager waits on a bond heartbeat before treating a descheduled server as dead. Not disabled (0.0), so a server that really dies is still caught.DEFAULT_PARAMS_PATH(L402) — Defaultconfig/nav2_panda_mobile.yamlpath; used bytest/test_nav2_launch.pyfor hermetic argument validation without spawning a real ROS 2 graph.
packages/openral_perception_ros/openral_perception_ros/
main(args=None) -> None(ros_image_detector_node.py L222) — Standalone ROS-image object detector: subscribes a camera topic, runs anopenral_runnerbackend (RT-DETR ONNX or an open-vocab VLM), and publishes detections as aPromptStamped.continuousmode publishes every frame;on_demandinstead serves a one-shotlocate_in_viewquery.DETECTOR_LOG_LEVEL_ENV(ros_image_detector_node.py L79) —"OPENRAL_DETECTOR_LOG_LEVEL"; operators set this todebugto surface the continuous leg's per-publish DEBUG line, which the default INFO console hides._LOG_LEVEL_ALIASES(ros_image_detector_node.py L83) — Accepted spellings → the rclpyLoggingSeveritymember name ("warning"→"WARN").normalize_log_level(value) -> str | None(ros_image_detector_node.py L93) — Normalises an operator-supplied log level to an rclpy severity name; case-insensitive, whitespace-trimmed.Nonefor an empty/unrecognised value so a typo never silences the node.classify_continuous_tick(*, error, detection_count) -> tuple[str, str](ros_image_detector_node.py L119) — Maps one continuous-leg detect tick to(log_level, message): adetect()exception →warning, an empty result →infoliveness heartbeat, a non-empty result →debug.camera_geometry_from_info(msg) -> tuple[str, IntrinsicsPinhole](ros_image_detector_node.py L174) — A driversensor_msgs/CameraInfo's(header.frame_id, K)viaopenral_hal.depth_cloud.intrinsics_from_camera_info;ROSConfigErrorwhen uncalibrated or frameless.stamp_camera_geometry(md, geometry) -> ObjectsMetadata(ros_image_detector_node.py L197) —mdre-validated withcamera_frame_id/camera_intrinsicsfromgeometry, or unchanged forNone(noCameraInfoyet → the lift's logged manifest fallback). The node stamps every continuous batch andlocate_in_viewreply for cameras itscamera_infosparameter names.main(args=None) -> None(scene_vlm_node.py L46) — Scene-VLM query service node: caches each camera's latest frame and serves/openral/perception/query_scene, a free-text answer to a question about the current frame, from aQwenSceneVlmbackend. The reasoning counterpart oflocate_in_view.main(args=None) -> None(reward_monitor_node.py L79) — Reward-monitor query service node: buffers the active VLA's camera frames and serves/openral/perception/query_task_progressfrom a reward backend (Robometer or TOPReward). A scoring heartbeat also drives the dashboard's progress bar and an optional Tier-CCriticScorepublish. Advisory-only.main(args=None) -> None(segmenter_node.py L749) — Entry point: init ROS, spin the segmenter node, shut down cleanly.make_segmenter_node(node_name="openral_segmenter") -> Any(segmenter_node.py L734) — Constructs the real segmenter lifecycle node behind lazy ROS imports; the seamtest_segment_in_view_service.pyuses to stand up the node in-process. Node behaviour: Promptable-segmenter service node (kind: segmenterrSkill): serves/openral/perception/segment_in_view, turning a 3-D point into the pixel mask of the object there via an in-process SAM 2.1 model. Every failure path returns an emptyfailure_reasonrather than raising.on_activatebuilds and warms the model and burns the handler's lazyopenral_hal.depth_cloud/openral_core.geometryimports (theopenral_halimport alone was 839 ms of a 1019 ms first call on Thor), logging the warm-up time.DEFAULT_SEGMENT_SERVICE(segmenter_node.py L114) —"/openral/perception/segment_in_view"; the service name the HAL's vision attachment bridge calls by default.DEFAULT_DEBUG_MASKS_TOPIC(segmenter_node.py L120) —"/openral/perception/masks"; diagnostic topic the latest mask set is re-published on whenpublish_debug_masksis enabled.sensor_spec_by_name(description, name) -> SensorSpec | None(segmenter_node.py L123) — Finds aSensorSpecby name acrosssensors+sensor_bundles.mono8_bytes_from_mask(mask) -> bytes(segmenter_node.py L213) — 255 set / 0 clear; rawmono8, deliberately not RLE (a one-shot event call does not justify a bespoke codec).projection_geometry(spec, *, width, height, header_frame_id, camera_info=None) -> tuple[IntrinsicsPinhole, str, tuple[str, ...]](segmenter_node.py L143) — Picks the K and optical frame a 3-D prompt is projected in: the cached image'sheader.frame_idoverspec.frame_id(which may be a body frame — OpenArmhead_zed), and the driver's liveCameraInfoK (openral_hal.depth_cloud.intrinsics_from_camera_info) over the manifest's nominal intrinsics, rescaled to the frame viascale_intrinsics_to. Returns one note per fallback taken; the node logs each once at warning. RaisesROSConfigErroron an uncalibratedCameraInfoor no K source at all.parse_camera_entries(entries) -> dict[str, str](camera_topics.py L23) — The bare"id=topic"parse (blank / half entries skipped, declaration order kept, possibly empty) behindresolve_camera_topics; used as-is for optional per-camera side streams such assegmenter_node'scamera_infos. Pure and ROS-free.resolve_camera_topics(entries, *, primary_camera, image_topic) -> dict[str, str](camera_topics.py L49) — The one implementation of the camera-agnosticid=topic→ ordered map every node in this package resolves its cameras with. Insertion order matters: the first key is the primary camera. RaisesROSConfigErrorwhen neitherentriesnorimage_topicnames a camera — every node'simage_topicdefault is""(ADR-0108), so a node the launch did not wire refuses to configure instead of subscribing to a guessed name. Pure and ROS-free.image_to_bgr_bytes(msg) -> tuple[bytes, int, int](image_convert.py L31) — The onesensor_msgs/Image→ contiguous H·W·3 BGRuint8converter for every perception node (segmenter, detector, scene VLM, reward monitor, depth provider). Acceptsbgr8/8UC3,rgb8,bgra8/rgba8(alpha dropped — the ZED's default layout),mono8/8UC1(replicated); honoursstep, so pitch-padded rows are sliced off. Nocv_bridgedependency. Callers log a refusal at WARNING (throttled 5 s) naming camera and encoding.ImageConvertError(image_convert.py L27) — Raised byimage_to_bgr_byteson an unsupported encoding, astepshorter than a packed row, or a payload shorter thanheight*step.SUPPORTED_ENCODINGS: frozenset[str](image_convert.py L24) — the encodingsimage_to_bgr_bytesaccepts.depth_array_to_image_msg(depth_m, *, frame_id, stamp=None) -> Image(depth_convert.py L30) — Metric-depth message boundary (no torch). Builds a32FC1sensor_msgs/Imagein metres (NaN = no return) with tightly-packed rows.image_msg_to_depth_array(msg) -> ndarray(depth_convert.py L67) — Inverse ofdepth_array_to_image_msg.camera_info_from_intrinsics(*, fx, fy, cx, cy, width, height, frame_id, stamp=None) -> CameraInfo(depth_convert.py L89) — Builds a plain pinholeCameraInfo(plumb_bob, zero distortion, K/P matrices). Used bydepth_provider_nodeto feed nvblox.camera_info_problem(k, info_wh, image_wh) -> str | None(metric_depth.py L57) — Why aCameraInfocannot scale an image's DA3 depth: a non-positive / non-finite focal (an uncalibrated driver's all-zero K), an empty size, or another aspect ratio (a crop, another view). A uniform resize (a driver's full-resolution CameraInfo beside a downscaled republish) is usable. The metric provider drops such frames.is_metric_model(model_id) -> bool(metric_depth.py L39) — Whether a DA3 checkpoint predicts canonical metric depth (DA3METRIC-*); the main series is not documented as metric.scale_intrinsics(k, src_wh, dst_wh) -> tuple[float, float, float, float](metric_depth.py L92) —(fx, fy, cx, cy)of an image resized (not cropped), pixel centres to pixel centres.da3_metric_depth(canonical, *, fx, fy) -> ndarray(metric_depth.py L108) — DA3METRIC canonical output → metres (mean(fx, fy) * out / DA3_CANONICAL_FOCAL_PX, focal at the output raster); non-finite / non-positive →0.0.points_from_depth(depth, *, fx, fy, cx, cy, max_range_m) -> ndarray(metric_depth.py L123) — Metric raster →(N, 3)REP-103 optical cloud, dropping no-returns and z beyondmax_range_m.build_rectifier(k, size, model, coeffs) -> PinholeRectifier(lens_rectify.py L123) — The raw → exact-pinhole remap for one lens at one size, from a ROSCameraInfolens model:plumb_bob/rational_polynomial(cv2.initUndistortRectifyMap) orequidistantfisheye (cv2.fisheye.initUndistortRectifyMap); no distortion → no remap. Valid pixels only (alpha/balance0) and the camera's pixel aspect kept (one zoom);R(stereo) not applied, so the image stays in the camera's optical frame. RaisesValueErrorfor whatdistortion_problemrefuses.PinholeRectifier(lens_rectify.py L98) — Frozen:k(rectified pinhole(fx, fy, cx, cy)),size,maps(None= already pinhole). The metric provider rectifies every frame before DA3 sees it, rebuilding only when the calibration or size changes.PinholeRectifier.apply(image) -> ndarray(lens_rectify.py L111) — The rectifiedH x W x Cimage (cv2.remap, bilinear); the input itself when already pinhole.distortion_problem(model, coeffs) -> str | None(lens_rectify.py L58) — Why aCameraInfolens cannot be rectified: an unsupported model, a wrong coefficient count, a non-finite value;Nonewhen usable (incl. no distortion).rectified_stream_k(p) -> tuple[float, float, float, float](lens_rectify.py L84) —(fx, fy, cx, cy)of an already-rectified stream fromCameraInfo.p(animage_recttopic shares the raw stream'sCameraInfo; the deploy declares it withbackend_params.rectified: true).DISTORTION_COEFF_COUNTS: dict[str, int](lens_rectify.py L47) — coefficients per supported model (plumb_bob5,rational_polynomial8,equidistant4).DA3_CANONICAL_FOCAL_PX = 300.0(metric_depth.py L36) — the focal DA3METRIC's canonical depth is expressed at.DepthConvertError(depth_convert.py L26) — Raised on non-2D depth, a non-32FC1encoding, or a padded stride (step != width*4).main(args=None) -> None(depth_provider_node.py L39) — Monocular metric-depth provider: forwards each RGB frame to the DA3 depth sidecar over ZMQ and republishes the metric depth as a32FC1image +CameraInfofornvblox.launch.py.image_topic/depth_topic/camera_info_topichave no default (ADR-0108); an empty one raisesROSConfigErrorat startup. Best-effort; rebuilds its socket on a timed-out reply so a slow sidecar can't wedge depth forever. Metric mode (rgb_camera_info_topicset;DeployRuntime.estimated_depth_camera): refuses a reply whosemodelis notDA3METRIC-*, converts the canonical output to metres through the RGBCameraInfofocal scaled to DA3's raster (metric_depth), publishes depth + that scaledCameraInfoindepth_frame_id(the launch's resolved optical frame; empty = theCameraInfo's frame) and, withpoints_topic, the back-projected cloud (clipped atmax_range_m, default 3.0); drops frames until aCameraInfoarrives.
packages/openral_slam_bringup/openral_slam_bringup/
main(args=None) -> None(depth_height_filter_node.py L434) — Nvblox floor-exclusion prefilter: derives a robot-relative navigation-height band from the manifest's collision geometry and zeroes depth pixels outside it before nvblox integrates them. Needed because nvblox's occupancy integrator still projects raw floor returns into/map.input_depth_topic/input_camera_info_topichave no default (ADR-0108); an empty one raisesROSConfigError.RobotRelativeHeightBand(depth_height_filter_node.py L35) — Frozen dataclass:min_z_m/max_z_mnavigation-height band plussource("collision_geometry"or themin_body_height_mfloor default).quaternion_to_matrix_z_row(x, y, z, w) -> tuple[float, float, float](depth_height_filter_node.py L43) — The rotation matrix's third row only — enough to project a shape's local z-extent onto the world z axis.derive_robot_relative_height_band(description, *, floor_clearance_m=0.10, min_body_height_m=0.30) -> RobotRelativeHeightBand(depth_height_filter_node.py L294) — Measures the manifest's collision geometry (or falls back tomin_body_height_m) to produce the navigation-height band.filter_depth_by_global_height(depth_m, *, fx, fy, cx, cy, rotation_z_row, translation_z_m, min_height_m, max_height_m)(depth_height_filter_node.py L344) — Zeroes out-of-band/invalid pixels; raisesValueErroron invalid intrinsics, shape, or band.main(args=None) -> None(pycuvslam_node.py L221) — In-process cuVSLAM visual SLAM (PyCuVSLAM wheel): tracks a rectified stereo pair, or one RGB camera plus a metric-depth stream, publishesnav_msgs/Odometry, and broadcasts themap → odomTF edge. The pip-wheel alternative to the composableisaac_ros_visual_slampath. The four camera-topic params have no default (ADR-0108);_setup_inputsraisesROSConfigErrornaming any required one left empty (left always, right in stereo mode).stereo_baseline_m(right_p) -> float(pycuvslam_node.py L47) — RaisesValueErrorunless the rectified rightPencodes a positive baseline.transform_to_pose(transform)(pycuvslam_node.py L134) — Ageometry_msgs/Transform→rig_from_camerapose.compose_pose(a, b)(pycuvslam_node.py L106) — Compose two((qx,qy,qz,qw),(tx,ty,tz))poses.invert_pose(p)(pycuvslam_node.py L114) — Inverse of a((qx,qy,qz,qw),(tx,ty,tz))pose.map_from_odom(map_from_rig, odom_from_rig)(pycuvslam_node.py L122) — Composes the tracked pose with the liveodom ← rigTF to yield themap → odomedge.depth_to_uint16_mm(msg, width, height, scale)(pycuvslam_node.py L160) — A32FC1metres Image → theuint16grid cuVSLAM RGBD eats (PIL float-bilinear resize onto the RGB resolution,metres × scaleclipped touint16); raisesValueErroron a non-32FC1encoding.
packages/openral_octomap_bridge/launch/octomap_voxel_bridge.launch.py
generate_launch_description() -> LaunchDescription(L21) — Declares params (base_frame,octomap_topic,output_topic,resolution,coverage_radius_m,coverage_center_z,publish_rate_hz,max_octree_age_s) and spawns theoctomap_voxel_bridgenode that turns an OctoMap into theopenral_msgs/OccupancyVoxelsgrid the safety kernel consumes.
packages/openral_hal_openarm/launch/real_bringup.launch.py
Bringup-only package: no HAL node of its own (OpenArm runs openral_hal_node); robots/openarm/robot.yaml reaches this file through hal.real_bringup.
generate_launch_description() -> LaunchDescription(L54) — Real-hardwareros2_controlbringup for the OpenArm v2: includes upstreamopenarm_bringup's bimanual launch with this HAL's own CAN interface names. Never run alongside a deploy — two copies would double-publish/joint_states; activation energises the motors.prop _LEFT_CAN_INTERFACE, _RIGHT_CAN_INTERFACE(L45–46) —"openarm_left"/"openarm_right"; must match the udev namesopenral_hal.openarm_real._LEFT_CAN_INTERFACE/_RIGHT_CAN_INTERFACEassign to the USB CAN-FD adapter's two channels._ROBOT_CONTROLLER(L51) —"joint_trajectory_controller"; must match the controller typeOpenArmRealHALpublishesJointTrajectorymessages to, notforward_position_controller(which takesFloat64MultiArray).
packages/openral_slam_bringup/launch/
generate_launch_description() -> LaunchDescription(slam_toolbox.launch.py L40) — Stand-alone bring-up of upstreamslam_toolbox/async_slam_toolbox_nodeas aLifecycleNode(auto-transitions only to INACTIVE; the Reasoner promotes to ACTIVE viaLifecycleTransitionTool). Composed intodeploy_e2e.launch.pywhenenable_slam:=true._SLAM_NODE_NAME(slam_toolbox.launch.py L26) — Theasync_slam_toolbox_nodenode name.prop DEFAULT_PARAMS_PATH, NODE_NAME(slam_toolbox.launch.py L93–94) — Test-hermeticity re-exports used bypackages/openral_slam_bringup/test/test_slam_toolbox_launch.pywithout spawning a real ROS 2 graph.generate_launch_description() -> LaunchDescription(pycuvslam.launch.py L40) — Stand-alone bring-up of the in-process PyCuVSLAM stereo/mono-RGBD node (a plainNode, not aComposableNodeContainer); the pip-wheel alternative tocuvslam.launch.py's NITROS path.prop _NODE_NAME, _PACKAGE, _EXECUTABLE(pycuvslam.launch.py L26–28) — Thepycuvslam_nodenode/package/executable names.prop DEFAULT_PARAMS_PATH, NODE_NAME, PACKAGE, EXECUTABLE(pycuvslam.launch.py L140–143) — Test-hermeticity re-exports used bytest/test_pycuvslam_launch.pywithout spawning a real ROS 2 graph or the cuVSLAM wheel.generate_launch_description() -> LaunchDescription(nvblox.launch.py L55) — Stand-alone bring-up of the upstreamnvblox::NvbloxNodecomposable node inside aComposableNodeContainer, remapping its~/static_occupancy_gridonto the backend-agnostic/mapfor lidar-less Nav2.prop _NVBLOX_NODE_NAME, _NVBLOX_CONTAINER_NAME, _NVBLOX_PACKAGE, _NVBLOX_PLUGIN(nvblox.launch.py L38–43) — Node/container name and the Isaac ROS composable-node package+plugin id forNvbloxNode.prop DEFAULT_PARAMS_PATH, NODE_NAME, PACKAGE, PLUGIN(nvblox.launch.py L193–196) — Test-hermeticity re-exports used bytest/test_nvblox_launch.pywithout spawning a real ROS 2 graph or the nvblox engine.generate_launch_description() -> LaunchDescription(cuvslam.launch.py L53) — Stand-alone bring-up of the upstream Isaac ROSVisualSlamNode(cuVSLAM) composable node inside aComposableNodeContainerfor NITROS-zero-copy image topics.prop _VSLAM_NODE_NAME, _VSLAM_CONTAINER_NAME, _VSLAM_PACKAGE, _VSLAM_PLUGIN(cuvslam.launch.py L36–41) — Node/container name and the Isaac ROS composable-node package+plugin id forVisualSlamNode.prop DEFAULT_PARAMS_PATH, NODE_NAME, PACKAGE, PLUGIN(cuvslam.launch.py L145–148) — Test-hermeticity re-exports used bytest/test_cuvslam_launch.pywithout spawning a real ROS 2 graph or the cuVSLAM engine.
packages/openral_human_estop/openral_human_estop/forwarder_node.py
class HumanEstopForwarderNode(LifecycleNode)(L24) — Subscribes/openral/human_estopand republishes onto/openral/estopplus aFailureTriggeron/openral/failure/safety. No in-repo node currently publishes/openral/human_estop— the dashboard stop button publishes/openral/estopdirectly.on_configure(state) -> TransitionCallbackReturn(L42) — Opens the estop publisher, theFailureTriggerpublisher, and the/openral/human_estopsubscription.on_activate(state) -> TransitionCallbackReturn(L68) — No additional resources to start.on_deactivate(state) -> TransitionCallbackReturn(L73) — No additional resources to stop.on_cleanup(state) -> TransitionCallbackReturn(L78) — Releases the subscription + publishers.on_shutdown(state) -> TransitionCallbackReturn(L92) — Forces cleanup (delegates toon_cleanup).main(args=None) -> int(L122) — Entry point forros2 run openral_human_estop forwarder_node.
packages/openral_safety_watchdog/openral_safety_watchdog/
estop_qos() -> QoSProfile(_qos.py L19) — QoS for/openral/estop:RELIABLE/VOLATILE/KEEP_LAST=10.failure_qos() -> QoSProfile(_qos.py L30) — QoS for/openral/failure/safety:RELIABLE/VOLATILE/KEEP_LAST=50. Both factories are the single shared definition both nodes'on_configurecalls, keeping their QoS identical by construction.class DeadmanWatchdogNode(LifecycleNode)(deadman_watchdog_node.py L96) — Fires/openral/estopplus a timeoutFailureTriggerwhen/openral/safe_actiondies, in its own process so a kernel crash still brakes.arm_status_topicgates the deadline to an active goal; the post-estop latch releases only on a genuineSafetyStatustransition to clear.prop DEFAULT_SAFE_ACTION_DEADLINE_S, DEFAULT_CHECK_PERIOD_S, DEFAULT_FIRST_CHUNK_DEADLINE_S, DEFAULT_SAFETY_STATUS_TOPIC, SAFETY_STATUS_LIVENESS_S(deadman_watchdog_node.py L49–64) — Defaults for the safe-action deadline (0.2 s), the deadline-check timer period (50 ms), the goal-accepted→first-chunk grace (60 s), the latchedSafetyStatustopic, and the HZ-0096-1 liveness window (3 s) past which aSafetyStatussample is treated as unknown-not-safe._LIVE_GOAL_STATUS_NAMES(deadman_watchdog_node.py L73) —("STATUS_ACCEPTED", "STATUS_EXECUTING", "STATUS_CANCELING"); resolved to values off the generated message class rather than hardcoded, so an upstream renumbering cannot silently widen or narrow the gate.DeadmanWatchdogNode.on_configure(state) -> TransitionCallbackReturn(deadman_watchdog_node.py L162) — Opens the/openral/safe_actionsubscription, the estop/failure publishers (QoS from_qos.estop_qos/_qos.failure_qos), and (ifarm_status_topicis set) the goal-status subscription.DeadmanWatchdogNode.on_activate(state) -> TransitionCallbackReturn(deadman_watchdog_node.py L228) — Arms the deadline check; opens the execution window immediately when free-running, else waits for the first live goal status.DeadmanWatchdogNode.on_deactivate(state) -> TransitionCallbackReturn(deadman_watchdog_node.py L242) — Disarms — deadline checks become no-ops until the next activate.DeadmanWatchdogNode.on_cleanup(state) -> TransitionCallbackReturn(deadman_watchdog_node.py L248) — Releases the timer, subscriptions and publishers.DeadmanWatchdogNode.on_shutdown(state) -> TransitionCallbackReturn(deadman_watchdog_node.py L266) — Forces cleanup (delegates toon_cleanup).main(args=None) -> int(deadman_watchdog_node.py L396) — Entry point forros2 run openral_safety_watchdog deadman_watchdog_node.class HardwareEstopNode(LifecycleNode)(hardware_estop_node.py L48) — Polls a hardware E-stop source and publishes/openral/estopplus aFailureTriggeron the rising edge. With no device declared,on_configurerefuses to activate rather than coming up ACTIVE over a stub; a read that raises latches as unknown-therefore-unsafe.DEFAULT_POLL_RATE_HZ(hardware_estop_node.py L44) —100.0; how often to query the hardware estop state.HardwareEstopNode.on_configure(state) -> TransitionCallbackReturn(hardware_estop_node.py L97) — Opens publishers and starts the polling timer only if a device is really there; returnsFAILURE(leaving the node UNCONFIGURED) when this host has no hardware E-stop this node could read.HardwareEstopNode.on_activate(state) -> TransitionCallbackReturn(hardware_estop_node.py L130) — Starts braking; a button already held at activation brakes on the first poll.HardwareEstopNode.on_deactivate(state) -> TransitionCallbackReturn(hardware_estop_node.py L138) — Stops braking; the timer keeps running but_pollis a no-op.HardwareEstopNode.on_cleanup(state) -> TransitionCallbackReturn(hardware_estop_node.py L144) — Releases the timer + publishers.HardwareEstopNode.on_shutdown(state) -> TransitionCallbackReturn(hardware_estop_node.py L158) — Forces cleanup (delegates toon_cleanup).main(args=None) -> int(hardware_estop_node.py L261) — Entry point forros2 run openral_safety_watchdog hardware_estop_node.
packages/openral_rskill_ros/openral_rskill_ros/
RskillRunnerNode(*, node_name="openral_skill_runner", robot_description, aggregator, skill_resolver=None)(rskill_runner_node.py L244) — TheExecuteRskillaction server: a lifecycle node dispatching to the resolved rSkill atrate_hz(param0= the manifest'saction_spec.control_freq_hz, else 30 Hz — issue #303), aborting any blocking wait the instant/openral/estopor a latched/openral/safety_statusreports unsafe. Every terminal outcome stamps both a prosefailure_reasonand a typedfailure_kind.on_configuredraws a random nonzero 64-bit_runner_session_id(logged asrskill_runner session id 0x…) thatROSPublishingHALstamps on everyActionChunk; the tick counter restarts per process, so the HAL's replay watermark is keyed on(session, tick)(hazard log Entry 036 Amendment 3). Goal-scoped declarations: the goal's ownplace_declaration/grasp_declaration, else the scene'splace_declaration_json/grasp_declaration_jsonparam (for the grasp one, else withgrasp_approach_enableda goal-scope declaration:target_id="approach", every hand's links viaopenral_core.gripper_hands, no search box,timeout_s= the goal deadline capped at 120 s — the HAL's approach-armed target narrows it to one hand; for the place one, else withplace_approach_enableda goal-scope declaration:target_id="surface", no object, no search box,timeout_s= the goal deadline capped at 120 s — the HAL's place-target leg attaches the surface it measures under the carried payload and never declares on its own), is armed at goal start on/openral/place_declaration//openral/grasp_declaration(RELIABLE + TRANSIENT_LOCAL, depth 1) withrskill_id/trace_id/stamp_nsoverwritten and anyregionstripped, and retracted on every exit incl. E-stop (_arm_*_declaration/_retract_*_declaration). The goal-end publishes (theEpisodeEND marker, the declaration retractions) go through_publish_unless_shut_down: once SIGINT has shut the rclpy context down a publish that raisesInvalidHandle/RCLErroris skipped with onerskill_runner.publish_skipped_context_shutdowninfo line (no traceback out of the action server, no…_retractedline for a retraction that never reached the wire); with the context up the error is unchanged and every retraction is still attempted in the same order. A supplied place declaration (goal orplace_declaration_json) that does not validate aborts the goal with aROSConfigErrorreason before the skill is resolved — never a fall-back to the goal-scope one._new_execution_budget(budget_s) -> ExecutionBudget/_deadline_lapsed(budget, chunks) -> bool— The goal's execution budget (node_execution_budget(self, …)) runs on the graph clock underuse_sim_time(sim seconds, like Nav2's and MoveIt's own timeouts; re-read per goal) and ontime.monotonic()otherwise;_deadline_lapsedlogs the miss, adds thedeadline_missedspan event withmiss.kind, and stashes theBudgetMissso the aborted goal'sfailure_reasonreadsdeadline_exceeded: …orclock_stalled: …(bothFAILURE_DEADLINE_MISSED). ROS paramsexecution_wall_cap_factor(20) andgraph_clock_stall_s(10 s) are the wall-clock backstops, shared with every wrapped skill on the node and validated aton_configure(non-positive / non-finite fails the transition); a NaN goaldeadline_sis rejected asFAILURE_CONFIG_ERROR. Adapted from DsslRobot/openral._failure_kind_for_exception(exc) -> int(module-level) — Maps the OpenRAL exception hierarchy onto theExecuteRskill.Result.FAILURE_*uint8 constants, most-specific-first. AROSSafetyViolationother thanROSEStopRequestedhas no kind by design — it is re-raised to the safety supervisor, never folded into a Result._classify_runtime_failure(exc) -> tuple[str, int](staticmethod) — One message probe yielding both the typedfailure_reasonand the matchingfailure_kindfor a raw non-ROSErrorescape, so the string and the uint8 can never disagree._label_runtime_failure(exc) -> stris the thin projection onto the first element._apply_starting_pose(skill, goal_handle) -> tuple[int, str] | None/_interpolate_starting_pose(pose, goal_handle) -> tuple[int, str] | None/_dispatch_moveit_approach(pose, goal_handle) -> tuple[int, str] | None— Reach a manifeststarting_posebefore inference via either a configured MoveIt plan or a bounded joint ramp, checking cancellation between waypoints and failing the goal on any error.make_default_skill_resolver(ros_node, *, search_paths=(), scene_cameras=(), tf_lookup=None, tf_lookup_getter=None) -> SkillResolver(rskill_runner_node.py L2668) — Production resolver factory: routeskind: vlato the local/HF-Hub path andros_action/ros_serviceskills toROSActionRskill. The Hub fallback_default_skill_resolver(..., scene_cameras=(), tf_lookup=None)now bindsrSkill.from_pretrained's packaging handle to a runtime skill via_build_runtime_skill_from_manifest(handle.local_dir / "rskill.yaml", …)— until 2026-09-22 it returned the handle raw, so every Hub-hosted VLA aborted at the embodiment gate (which now raises a typedROSConfigErrornaming a non-rSkillBasereturn instead of a bare AttributeError with an emptyfailure_reason). Preload: parameterspreload_rskill_id/preload_rskill_revision/preload_promptmakeon_activatestart a daemon worker (_preload_resident_skill) that runs_acquire_skillunder_execute_serialso the skill is GPU-resident before any goal exists (a deactivate/activate cycle mid-load keeps the one worker:on_activatenever starts a second while the first is alive, so_preload_in_flightstays set until the load finishes);_goal_cbREJECTS goals while_preload_in_flightis set (acceptance is what arms the deadman's first-chunk window), and REJECTS every goal while the e-stop latch is held, loggingrskill_runner.goal_rejected: e-stop latchedat WARNING with the recovery (openral estop reset) since a ROS 2 rejection carries no reason, and_acquire_skillkeeps the weights resident per (id, revision): a skill exposingset_goal_prompt(prompt)(the VLA shim from_make_policy_adapter_skill, which applies_effective_goal_prompt'sdefault_promptfallback and resets the adapter so the previous goal's buffered chunk / prefetch is dropped) is retargeted to the new goal's prompt with no reload, sopreload_promptis only a warm-up prompt; any other skill keeps (prompt, prompt_metadata_json, goal_params_json) in its key, because a wrapped-ROS skill lowers them into its goal at construction. A key change logsrskill_runner.resident_key_mismatch, since it costs the full cold load inside the watchdog window. AROSSafetyViolationraised while preloading is logged asrskill_runner.preload_safety_violationand handed to_rethrow_on_executor(exc), a one-shot timer that re-raises it on the executor thread so it escapesspin()exactly as it does from the goal path (a bare re-raise on the worker thread would end only that thread while the node kept accepting goals).make_local_skill_resolver(search_paths, *, scene_cameras=(), tf_lookup=None, tf_lookup_getter=None) -> SkillResolver(rskill_runner_node.py L2825) — In-tree VLA resolver. Walks each search path once and indexes every*/rskill.yamlby manifest name; on resolve calls_build_runtime_skill_from_manifest. Acceptsros_node=Nonekwarg for signature uniformity with the default resolver.resolve_control_rate_hz(param_hz, description) -> float | None(rskill_runner_node.py L2930) — The tick rate:rate_hz > 0wins, else the manifest'saction_spec.control_freq_hz, elseNone;RskillRunnerNode._control_rate_hz()then warns (naming the missing field) and ticks at 30 Hz, which only a sim-only manifest can reach because a real ros2_control HAL refuses to build without the field. Issue #303: the real ros2_control HAL derives every trajectory deadline from that same manifest field, so runner and HAL cannot disagree unless an operator overrides the param on purpose.RskillRunnerNode._control_rate_hz()wraps it for both tick loops.starting_pose_joint_bounds(param_max_delta_per_s, param_tolerance_rad, description) -> list[tuple[float, float]](rskill_runner_node.py L2955) — Per-joint(max speed, arrival tolerance)for the move to a skill's starting pose, each in the joint's own units: prismatic joints usesafety.starting_pose_max_joint_speed_m_s/starting_pose_tolerance_m, every other joint the_rad_s/_radfields; each speed is capped at the joint'svelocity_limit. Declared per robot, never derived (issue #303). A positive param overrides the rad values only, never a prismatic joint.ROSConfigErrornaming the missing field. Wrapped byRskillRunnerNode._starting_pose_bounds(); replaced the scalarresolve_starting_pose_ramp(audit C F2: a 0.05 "rad" tolerance swallowed a 0.041 m gripper stroke and the Franka gripper ramped at 5x its limit).starting_pose_reached(current, target, bounds) -> bool(rskill_runner_node.py L3011) — Every joint within its own tolerance; the runner's arrival check before the first inference tick.starting_pose_ramp_steps(current, target, bounds, rate_hz) -> int(rskill_runner_node.py L3026) — Ticks of the linear ramp:0when already reached, else the longest per-joint|delta| / speed * rate_hz, so no joint exceeds its own cap.resolve_control_rate_source(param_hz, description) -> tuple[float, str](rskill_runner_node.py L3054) — The tick rate and its source (rate_hz_param/manifest/fallback=_FALLBACK_CONTROL_RATE_HZ, 30 Hz)._control_rate_hz()warns onfallback;_check_skill_control_raterecordscontrol.rate_hz/control.rate_sourceon therskill.executespan.check_skill_control_rate(trained_hz, tick_hz, *, skill_id, robot) -> None(rskill_runner_node.py L3075) —ROSConfigErrorwhen the skill'saction_contract.control_freq_hzis declared and differs from the tick rate (there is no action resampler); the goal is rejected before the starting-pose move.None= undeclared, unchecked._require_positive_param(node, name) -> float(rskill_runner_node.py L3102) — Reads a float ROS param the launch must set (joint_state_staleness_limit_s, declared0.0= unset) and raisesROSConfigErrornaming it when it is not positive;deploy_e2e.launch.pysets it from the manifest'ssafety.joint_state_staleness_limit_sin real mode (_runner_joint_state_staleness_s(description, hal_mode, *, republished); when the runner reads the HAL's rate-limited~/joint_statesrepublish it adds two republish periods,limit + 2 / control_freq_hz, capped at the former 0.5 s — the HAL's own check stays at the manifest value; sim keeps 0.5 s)._warmup_observation()/ shimon_warmup(rskill_runner_node.py) — Warm-up through the real tick path: the policy shim'son_warmuprunsadapter.stepon_warmup_observation()— zeros shaped like the cell's RGB intrinsics for every required camera slot,state_contract.dimzeros (falling back to the robot description's joint count; with neither it raises a typedROSConfigError, which the non-fatal wrapper logs asrskill_runner.warmup_failedand activate proceeds cold), the goal prompt — thenadapter.reset(), instead of a barewarm_up_lerobot_policyforward. The bare forward left the first real tick paying 306–357 s on a Jetson AGX Orin under a live graph (chunk executor thread, autocast, preprocessor on real-sized frames each have first-call costs) against the deadman's 120 s first-chunk window; a warm second dispatch in the same process took 426 ms, which is what the preload now pays.prop _CANCEL_DRAIN_S, _MAX_APPROACH_WAYPOINTS, _DEFAULT_EXECUTION_DEADLINE_S(rskill_runner_node.py L93–105) — The 100 ms cancel drain; the runaway guard on the MoveIt approach replay waypoint count; and the global fallback (45 s) for a goal's execution budget when neither the dispatch nor the skill manifest declares one.prop _SAFETY_STATUS_TOPIC, _SAFETY_STATUS_LIVENESS_S(rskill_runner_node.py L115–125) — ADR-0096's latched safety-state topic (/openral/safety_status) both the C++ kernel andSafetyPassthroughNodepublish on, and the HZ-0096-1 liveness window (3 s) past which a staleSafetyStatusis reported as an abort reason rather than downgraded to clear.main(args=None) -> int(rskill_runner_node.py L4074) — Entry point forros2 run openral_rskill_ros rskill_runner_node; bootstraps a standalone node with a freshRobotDescriptionandWorldStateAggregator. Production launches usecompose_so100_runtimeinstead, to share the aggregator with the colocatedworld_state_node.compose_runtime(robot_yaml, *, skill_resolver=None, enable_world_cloud_bridge=False, world_cloud_topic="", slam_source_node="", dataset_out=None, image_staleness_limit_s=None, deploy_sensors=(), sensor_overlays=(), …) -> ComposedRuntime(compose.py L179) — Loads a robot manifest and builds oneWorldStateAggregatorshared by a colocated world-state node andRskillRunnerNode, always attaching aSlamMapBridgeso any/mappublisher populates the dashboard. Cameras get their own freshness window, independent of joint/EE staleness.deploy_sensors(aDeployScene.sensorsblock) is merged into the manifest's sensors viamerge_deploy_sensorsbefore the aggregator and runner are built, so a scene's workcell cameras (and anyvla_feature_keythey carry; the scene never touches a robot camera) reach the runner's camera slots, the dataset recorder and world state alike — until 2026-09-23 only the sensor readers saw it and the OpenArm pick-and-place π0.5 ran with itscontext(ZED head) view replaced by lerobot's masked blank.sensor_overlays(the host'sRobotUnitoverlays, resolved byruntime_node) are applied to the manifest sensors first (apply_sensor_overlays).spin_until_shutdown(executor) -> None(compose.py L86) —executor.spin()until a signal-driven shutdown:KeyboardInterrupt,ExternalShutdownExceptionand aRuntimeError(coversRCLError/InvalidHandle, and a take racing the shutdown) raised after the context went down (the SIGINT handler invalidates it mid-spin: the next wait set, a dequeued callback's publish, a subscription take) all end it quietly; aRuntimeErrorwith the context still up propagates.runtime_nodeand the world-state events thread spin through it (the runtime exited 1 on that race). The executor twin ofopenral_hal.lifecycle.spin_until_shutdown, which spins a node; kept per layer (the rSkill layer does not import the HAL).start_world_state_executor(world_state_node, *, on_failure=None) -> Callable[[], None] | None(compose.py L112) —ExternalShutdownException(Ctrl-C, or an externalrclpy.shutdown()) is a clean exit, never a failure. A callback that raises endsspinon the daemon thread; the exception is logged asworld_state.executor_diedand handed toon_failure(theruntime_nodescript stops its own executor and exits 7), or re-raised when no handler is given; the stop callable's own shutdown is not a failure. spins the world-state node on rclpy's C++rclpy.experimental.EventsExecutorin a daemon thread and returns a stop callable (shutdown+ join), orNoneon an rclpy without it (pre-Jazzy 7.1) so the caller adds the node to its own executor. Why: the PythonMultiThreadedExecutorrebuilds its wait set in Python on every wake-up over every entity of every node it holds; with world_state's ~40 topics on it that loop held 64 % of the deploy runtime's GIL on an AGX Orin and the in-process π0.5 inference thread got <2 % (1.6 s idle forward → ~300 s live). The skill runner stays on the multi-threaded executor, whose reentrant group keepsexecute_rskillaccept/cancel/result live while a goal blocks a worker.compose_so100_runtime(*, skill_resolver=None) -> ComposedRuntime(compose.py L411) — SO-100 convenience wrapper overcompose_runtime.ComposedRuntime(compose.py L41) — Dataclass returned bycompose_runtime:description,aggregator,world_state_node,skill_runner_node,slam_bridge,world_cloud_bridge,dataset_recorder_bridgefields.joint_names_from_goal_json(default_goal_json) -> list[str](_starting_pose.py L10) — Extracts the planning-group joint names from a MoveGroupdefault_goal_json'sjoint.joint_namesblock; used to length-check a robot's flatstarting_posebefore building the retarget override. RaisesValueErroron malformed JSON or a missing/emptyjoint.joint_names.moveit_joint_goal_override(joint_names, positions) -> str(_starting_pose.py L47) — Builds thegoal_params_jsonthat retargets the approach manifest'sjoint.positionstopositions(deep-merge over the manifest, sojoint_names's order is preserved);joint_namesis used only to length-checkpositions.SensorLeg(sensor_leg.py L199) — Real-mode camera leg foropenral deploy run, the real-hardware counterpart of the sim HAL'sSimSensorBridge. Dataclass holding readers + publishers + which sensors write directly to the aggregator without a duplicate ROS-topic path.SensorLeg.start() -> None(sensor_leg.py L219) — Starts every prepared publisher after ROS lifecycle configuration; entity creation and lifecycle transitions remain single-threaded until this point.SensorLeg.close() -> None(sensor_leg.py L232) — Stops publishers first, then closes readers, idempotently and exception-safe (teardown must always reach the HAL shutdown behind it)._DEFAULT_PUBLISH_RATE_HZ(sensor_leg.py L61) —10.0; publish cadence when the binding'sbackend_paramscarry no fps. Matches theWorldStateAggregatorstaleness-gate expectation._RGB_CHANNELS(sensor_leg.py L65) —3; channel count of an interleaved colour frame — the only layout the 180° dashboard flip knows how to reshape._MAX_FALLBACK_TOPIC_RATE_HZ(sensor_leg.py L85) — Cap on the ROS topic cadence for the Python fallback publisher only (the native GStreamer tee is uncapped)._DEFAULT_TOPIC_MAX_SIZE(sensor_leg.py L96) —(320, 240); resolution ceilingtopic_frame_sizefalls back to.slam_camera_names(runtime) -> frozenset[str](sensor_leg.py L287) — Camera names feeding visual SLAM, which must never be rate-capped (cuVSLAM / PyCuVSLAM lose the track on a starved stream). Empty when SLAM is off, in which case the fallback-rate cap applies to every camera.open_deploy_sensor_readers(sensors, *, aggregator=None, ros_node=None, uncapped_sensors=(), topic_max_size=None) -> SensorLeg(sensor_leg.py L565) — Builds the complete deploy-bound sensor batch; every camera publishes onopenral_core.camera_topic(name)(+CAMERA_INFOwith intrinsics; thetopic_prefixargument andDEFAULT_TOPIC_PREFIXwere removed by ADR-0108) —gstreamerbackends via an in-pipeline ROS tee, others via a polling publisher. On both paths a spec withintrinsicsalso publishesCameraInfostamped withspec.frame_id(the tee gets them viaSensorReaderConfig.publish_frame_id/publish_camera_info). A sharedaggregatoralso gets a pump writing frames directly intoWorldStateAggregator; the fallback publisher'smax_sizecomes from_topic_max_size(capture size for cameras inuncapped_sensorsor withtopic_native: true, elsetopic_max_size)._AggregatorPump(sensor_leg.py L123) — Daemon-thread pump pollingread_latestat the spec rate into the aggregator, deduplicating on the frame's monotonic stamp. Emits the per-frame observability span after each write, guarded so a failing display path can never starve the policy._emit_frame_observability(sensor_name, frame, flip_180) -> None(sensor_leg.py L99) — Emits the dashboard's per-frame observability span for a pump-fed camera; exists because the ROS tee these cameras skip is what the dashboard would otherwise read from._sensor_topics(spec) -> tuple[str, str](sensor_leg.py L253) —(image, camera_info)topics by modality: adepthsensor →camera_topic(name, DEPTH_IMAGE / DEPTH_CAMERA_INFO)(where the sim bridge and every depth consumer look), anything else →IMAGE/CAMERA_INFO._publish_rate_hz(spec) -> float(sensor_leg.py L276) — Binding fps, else specrate_hz, else 10 Hz. This is the capture/native-tee cadence and is not capped._fallback_topic_rate_hz(spec, uncapped=()) -> float(sensor_leg.py L413) — Caps the ROS topic cadence for the Python fallback publisher only (at_MAX_FALLBACK_TOPIC_RATE_HZ, 3 Hz; an explicit positive topic rate wins and a camera inuncapped— the SLAM cameras — keeps its full rate), since every tick is a GIL-held full-resolution conversion; the native GStreamer tee and the policy's in-process read stay at full rate._topic_native(spec) -> bool(sensor_leg.py L447) —backend_params["topic_native"] is True: that camera's fallback topic publishes at capture size (and itsCameraInfounscaled) regardless oftopic_frame_size, for a bus reader that needs exact pixels (Foxglove,openral record, calibration). Resolution only — the 3 Hz rate cap still applies unlesstopic_rate_hzlifts it; a native 960x600 publish is ~55 ms of GIL per frame per camera, so it is per camera and opt-in. Only a booleantruecounts (YAML 1.1 also reads unquotedyes/onas one); any other value keeps the ceiling and logssensor_leg.topic_native_ignored. Off by default in every unit file, the OpenArm's included (a commented switch on each wrist for calibration passes). The in-process paths (the policy's aggregator read,--dataset-out) are native regardless._topic_max_size(spec, uncapped, ceiling) -> tuple[int, int] | None(sensor_leg.py L490) — Resolution ceiling for one camera's fallback topic, the per-camera counterpart of_fallback_topic_rate_hz:None(capture size) for a SLAM camera inuncappedor atopic_native: truebinding, else the launch-wideceilingfromtopic_frame_size. Whatopen_deploy_sensor_readershandsSensorRosPublisher.max_size.topic_frame_size(runtime) -> tuple[int, int] | None(sensor_leg.py L381) — Resolution ceiling for the fallback topic;None(native pixels) when the object detector or SLAM is on, since both need full resolution. The policy is unaffected either way, reading the aggregator in-process at capture resolution; a single camera opts out of the ceiling withtopic_native: true(_topic_native).apply_launch_overrides(runtime, *, enable_object_detector=None, enable_slam=None, slam_stereo_cameras=None, slam_mono_camera=None) -> object | None(sensor_leg.py L323) — Folds the launch's resolved auto-detected flags over the scene's raw runtime block before camera rate-capping decisions are made, since only the CLI resolves the scene's tri-stateautosettings.
packages/openral_rskill_ros/scripts/runtime_node
Composed-runtime entry point installed as lib/openral_rskill_ros/runtime_node. Reads the robot_yaml parameter, calls compose_runtime, and spins both lifecycle nodes on a MultiThreadedExecutor; for a real deploy it also opens every deploy-bound sensor reader before spinning. The script eagerly initializes GStreamer before any numpy/pydantic/rclpy import, since a later rclpy.Node() can segfault otherwise. Spins the world-state node on the C++ EventsExecutor (start_world_state_executor; falls back to sharing the multi-threaded executor with a stderr notice) and the skill runner on a 4-thread executor. Launch param joint_states_topic (resolved by deploy_e2e.launch.py via openral_hal.hal_joint_states_topic: scene override, else a real ros2_control HAL's /<hal_node>/joint_states) reaches both composed nodes through the launch's wildcard params: world_state's ingest and the runner's ROSPublishingHAL joint-state cache (RskillRunnerNode param joint_states_topic, "" = /joint_states) share the one topic, so a ros2_control arm's 750 Hz broadcaster stream never wakes the Python executors. With a deploy_config, it loads the scene BEFORE compose_runtime and passes its sensors: as deploy_sensors; the sensor leg then reads readers off that merged description.
_prewarm_vla_imports() -> None— Importstorch+lerobot.policies.factoryearly so the shared bulk of any VLA adapter's import cost is paid before any camera-reader thread exists. Must run ahead ofopen_deploy_sensor_readers, since atransformersimport can otherwise starve a reader thread of the GIL for minutes.
packages/openral_rskill_ros/launch/deploy_e2e.launch.py
compose_runtime_graph(context, *_args, **_kwargs) -> list(L1673) — Resolves every launch arg, loads the robot manifest, and assembles the full deploy-sim ROS graph — HAL, safety kernel, reasoner, SLAM/Nav2, sensor drivers, optional Foxglove viz. Onhal_mode:=realit also starts the robot's vendorros2_controlbringup itself. Always passes the kernelgrasp_allowance_enabled(launch arg, defaultfalse, fromDeployRuntime.grasp_allowance_enabled) andgrasp_contact_links= the manifest'srole: gripperchild_links (omitted when empty);grasp_allowance_enabled:=trueon a robot with no gripper joint raisesROSConfigError, and so does it on any non-sim launch unlessenable_vision_attachment:=trueand the HAL'shal_params_file(read back by_hal_file_params) setsvision_attachment_enabledandvision_attachment_grasp_target_enabledtrue (no region producer on real otherwise). Withworkspace_contact_enabled:=true(issue #349; plusworkspace_contact_max_speed_mps:=andworkspace_contact_max_lead_m:=) it passes the kernel_workspace_contact_kernel_params(...):workspace_contact_links=RobotDescription.hand_links(),workspace_contact_jaw_joints= therole: gripperjoint names,workspace_contact_brake_enabled: true, the speed and lead bounds, andworkspace_contact_control_period_s= 1 /action_spec.control_freq_hz; refused (ROSConfigError) on a non-simhal_mode, withoutgrasp_allowance_enabled, without a positive speed bound, without a lead bound in (0, 0.10] m, or for a robot with no gripper / no control rate; off, it passesworkspace_contact_enabled: false. It also passesgrasp_region_max_age_sandplace_region_max_age_s= 2 ×world_voxel_deadline_s(how old a producer-measured region may be and still exempt anything). The octomap voxel bridge getsattach_link_tf_framesfrom thevision_attachment_tf_frameslaunch arg (comma-joinedlink=frame, forwarded byresolve_launch_invocationwith the leg on as the exact strings the HAL gets) so a held payload's manifest attach link is found on the cell's TF tree; omitted when empty. The reasoner getsgrasp_target_voxel_m=_octomap_resolution(hal_mode)(one cell of the map the grasp-target producer searches). With the vision leg on, the HAL node also getsvision_attachment_grid_max_age_s= the kernel'sworld_voxel_deadline_sandvision_attachment_release_clear_m=_world_voxel_margin_m(hal_mode)+_octomap_resolution(hal_mode)(one octree cell), placed before the CLI's params file so an explicit--halvalue still wins; whatever the source, avision_attachment_grid_max_age_sabove the kernel'sworld_voxel_deadline_sor avision_attachment_release_clear_mbelowworld_voxel_margin_m+ resolution fails the launch withROSConfigError(checked whenever the launch arg or the HAL params file enables the leg). The robot self-filter (in front of octomap) runs wherever the input cloud shows the robot,_cloud_shows_the_robot(hal_mode, scene_backend): real cameras and anisaacsimdeploy scene (its depth camera renders the arm), not the MuJoCo sensor bridge (robot bodies transparent). It needs no rename: it poses a manifest attach link from its own collision model (link_index) and only falls back to TF for a non-model link._static_points_file(static_geometry_file, resolution_m) -> str(L318) — Samples the declared static geometry a quarter cell apart (openral_safety.static_geometry) into a temp points file for the octomap bridge'sstatic_points_file; the launch refusesstatic_geometry_filewithenable_octomapoff (declared walls never dropped silently). (#349)generate_launch_description() -> LaunchDescription(L3929) — Robot-agnostic deploy-sim launch graph entry point; wrapscompose_runtime_graphin anOpaqueFunction._foxglove_asset_env(robot_description_xml) -> dict[str, str](L1326) —foxglove_bridge'sadditional_env: prependsopenral_hal.ros_package_overlay.public_package_overlaytoAMENT_PREFIX_PATHso a viewer'sfetchAssetresolvespackage://meshes of a fetched public package (OpenArm'sopenarm_description);{}when none is needed or the overlay cannot be built (printed, never raised)._build_real_bringup_include(real_bringup) -> object | None(L1358) —IncludeLaunchDescriptionof the manifest'shal.real_bringup("<pkg>:<file>.launch.py") onhal_mode:=real;Nonewhen the manifest declares none; raisesRuntimeErrorwhen the declared package or file is not installed. There is no package-name convention fallback._VENV_SITE(L44) — Optional workspace-editable-install site-dir fromOPENRAL_VENV_SITE, registered viasite.addsitedir(plainPYTHONPATHis not enough:.pthfiles are only processed by thesitemodule on registered site-dirs)._REPO_ROOT(L130) — Resolved repo root (_resolve_repo_root())._RSKILLS_DIR(L131) —str(_REPO_ROOT / "rskills")._VENV_RAL(L133) —_REPO_ROOT / ".venv" / "bin" / "openral"._RAL_EXECUTABLE(L134) — The workspace venv'sopenralbinary when it exists, else the bare"openral"on PATH._stereo_camera_topics(names_csv) -> tuple[str, str, str, str] | None—"<left>,<right>"→ the fourcamera_topic(...)stereo topics;Nonewhen not exactly two names (the stereo SLAM impl then gets no topics and refuses to start, ADR-0108)._primary_rgb_camera(sensors) -> str— Optical-framed RGB sensor insensors(the deploy'spublishing_sensors) first, else the first RGB sensor, else""; the object detector's and the reasoner completion camera's view._live_camera_info_topic(spec, hal_mode) -> str— TheCameraInfocarrying a camera's live calibration: sim →camera_topic(name, CAMERA_INFO); real →openral_sensors.ros_publisher.camera_info_topic_for(<ros2_image binding topic>)(the driver's, never the sensor leg's manifest-built one); any other real backend →"". Feeds the detector's and locators'camera_infos._depth_camera(description) -> str— The manifest's first depth sensor with intrinsics (openral_hal.depth_cloud.is_depth_sensor), else""; feeds nvblox underenable_nav2._build_estimated_depth_nodes(camera, *, rgb_camera_info_topic, rgb_image_rectified, cloud_frame, optical_parent, use_sim_time) -> list(L771) —DeployRuntime.estimated_depth_camera's DA3METRIC sidecar (_ESTIMATED_DEPTH_MODELon_ESTIMATED_DEPTH_PORT5772, beside mono SLAM's 5771) +depth_provider_nodein metric mode on that camera's canonical image / depth / points topics, its focal and lens fromrgb_camera_info_topic(_live_camera_info_topic: the driver's own calibrated CameraInfo on a real deploy, else the sensor leg's, built from this unit's calibrated intrinsics);rgb_image_rectified(the binding'sbackend_params.rectified) reads an already-rectified stream throughCameraInfo.p. Stamps everything incloud_frame(depth_frame_id) and publishesoptical_parent -> cloud_frame(REP-103) on /tf_static for a body-convention mount._cloud_shows_the_robot(hal_mode, scene_backend, *, estimated_depth=False) -> bool(L742) — Whether octomap's input cloud contains the robot (real, an Isaac render, or a DA3 estimate), so the robot self filter runs on it._octomap_cloud_topic(pinned, description, hal_mode, estimated_camera="") -> str—openral_core.deploy_cloud_topic, raisingROSConfigErrorwhen it is empty (a real deploy with nothing pinned, or no depth sensor) so a directros2 launchnever spawns octomap_server against silence. The object-lift fallback (object_depth_points_topic) takesdeploy_cloud_topicunchecked (""disables it)._rig_from_launch_args(raw) -> DeployRuntime(L414) — The per-rigDeployRuntimevaluesopenral deployforwards as launch args (_RIG_LAUNCH_ARGS:world_voxel_deadline_s,max_octree_age_s,world_voxel_data_age_budget_s,robot_self_filter_padding_m; empty = schema default), validated throughDeployRuntime, which refuses an octree age above the deadline (hazard log Entry 033).compose_runtime_graphfeedsvoxel_freshness_sto the kernel'sworld_voxel_deadline_msand the octomap bridge'smax_octree_age_s, andworld_voxel_data_age_budget_sto the kernel'sworld_voxel_data_age_budget_ms(Entry 034)._SELF_FILTERED_CLOUD_TOPIC(L438) —/openral/world_cloud/self_filtered, octomap_server's input on the real path; deliberately not a/openral/cameras/...topic (ADR-0108), since it is the world map's input rather than one camera's cloud; x/y/z only (the self filter strips any other field)._SELF_FILTERED_RGB_CLOUD_TOPIC—/openral/world_cloud/self_filtered_rgb, the same robot-free cloud with every input field (a sim cloud'srgb), published by the self filter only while subscribed; for viewers (Foxglove), never the map._self_filter_params(collision_params, description, joint_states_topic, padding_m) -> dict[str, object](L445) — Therobot_self_filternode's parameters: everycollision_*key the kernel receives (so both pose the same model),collision_joint_namesfrom the manifest with each joint'ssim_joint_nameascollision_joint_aliases, the runtime nodes' joint-state topic (hal_joint_states_topic: scene override, else a real ros2_control HAL's~/joint_states, else/joint_states) andpadding_m(the rig'sDeployRuntime.robot_self_filter_padding_m, default 0.02 m, provisional; it is also the width of the blind shell around the arm). Wired only onhal_mode == "real"with a collision model;octomap_server'scloud_inis then remapped to the filtered topic._hal_file_params(path, node_name) -> dict[str, object](L879) — Theros__parametersthe HAL'shal_params_filegives it (/**, then its own node name), so the launch's HAL couplings judge the effective params whatever wrote the file; a missing/unreadable file reads as{}(launch_ros refuses it when the HAL starts, and an absent key reads as off)._attached_collision_enabled(hal_mode, vision_attachment_enabled) -> bool(L857) — Whether the kernel getsattached_collision_enabled:hal_mode == "sim"(the sim attachment manager) or the vision attachment leg is on (enable_vision_attachment:=true, fromDeployRuntime.vision_attachment). Coupling rule: the vision leg on real always turns the kernel's attached check on, never the leg alone — the octomap bridge's payload clearing and the robot self-filter consume/openral/world_state_fastregardless of the flag. The leg on a robot with no collision geometry raisesROSConfigError. With the leg on, the launch also composes theopenral_segmenterlifecycle node (segmenter_node.py:robot_yaml,manifest_path,cameras/camera_infos=<camera>=<rgb topic>/<camera>=<rgb camera_info>,primary_camera,device,use_sim_time), autostarted to ACTIVE._attached_collision_deadline_ms(hal_mode) -> float(L870) — The kernel'sattached_collision_deadline_ms: 5000 ms on sim, 1000 ms on real._cpuset_prefix(env) -> str(L196) — Ataskset -claunch prefix fromOPENRAL_PERCEPTION_CPUSET(octomap_server + the voxel bridge) orOPENRAL_RUNTIME_CPUSET(the runtime node), or""; unset = nothing pinned. Affinity needs no privilege where raising priority does (ulimit -e0 on Thor); a value that is not a cpu list is refused loudly.
python/runner/src/openral_runner/ros_publishing_hal.py
HAL Protocol adapter that publishes ActionChunk on /openral/candidate_action.
ROSPublishingHAL(*, node, description, skill_id_getter=..., skill_revision_getter=..., tick_index_getter=lambda: 0, runner_session_id=0, joint_state_topic="/joint_states", candidate_action_topic="/openral/candidate_action", action_applied_topic="/openral/action_applied", safety_abort_getter=lambda: None, action_applied_timeout_s=5.0, joint_state_staleness_limit_s=0.5)(L117) — Publishes typed candidate action chunks and blocks with bounded backpressure until the HAL acknowledges application, polling the injected safety latch every 50 ms and raisingROSEStopRequestedinstead of hiding a stop behind a plain timeout. Stampsrunner_session_id(the runner's random per-session uint64; 0 = session-less) on everyActionChunk, so the HAL keys its replay watermark on(session, tick)._row_major_flatten(rows) -> list[float](L57) — Private helper used by_action_to_chunk; preserves row-major ordering for the chunkflatarray._CONTROL_MODE_TO_UINT8: dict[ControlMode, int](L114) — Stable mapping fromopenral_core.ControlModeto the uint8 slot inActionChunk.control_mode.ROSPublishingHAL.connect() -> None(L218) — Opens the publisher oncandidate_action+ subscriber onjoint_states.ROSPublishingHAL.disconnect() -> None(L271) — Closes the publisher + subscription. Idempotent.ROSPublishingHAL.read_state() -> JointState(L288) — Returns the latest cachedJointStatefrom/joint_states; raisesROSRuntimeErrorif called beforeconnect()or before any message has arrived.ROSPublishingHAL.send_action(action) -> None(L304) — Publishes anActionChunkon/openral/candidate_action. Does NOT touch motors — the safety node republishes the chunk on/openral/safe_action, which the per-robot HAL lifecycle node consumes and forwards to the underlying controller.ROSPublishingHAL.begin_goal() -> None(L319) — Resets goal-local partial-group state.ROSPublishingHAL.estop() -> None(L394) — Triggers an emergency stop by raisingROSEStopRequestedso the safety supervisor boundary can record and brake; the owning lifecycle node also publishes on/openral/estopas defense-in-depth._SAFETY_ABORT_POLL_S(L54) —0.05; how often a blocked apply-wait polls the injected safety latch — a latched safety stop produces silence, nothing to wake the wait on, so this bounds how long the abort stays invisible.
python/runner/src/openral_runner/slam_bridge.py
rclpy → OTLP bridge for slam_toolbox /map. Throttles to 1 Hz, rasterises the nav_msgs/OccupancyGrid to a base64 PNG, looks up the robot's map-frame pose via tf2, and emits one slam.occupancy_grid OTel span the dashboard's SLAM Map card renders (store handler in openral_observability.dashboard.store).
SLAM_MAP_TOPIC_DEFAULT = "/map"(L43) — Defaultnav_msgs/OccupancyGridtopic slam_toolbox publishes on.encode_occupancy_grid_png(*, width: int, height: int, data: list[int]) -> str(L70) — Pure function rendering anOccupancyGrid.dataarray as a base64 PNG (unknown→mid-grey, free→white, occupied→black, in-between linear ramp; flipped so map-north points up). RaisesValueErroriflen(data) != width * height. Exercised directly by tests against synthetic grids.robot_pose_from_transform(*, translation_xyz: tuple[float, float, float], rotation_xyzw: tuple[float, float, float, float]) -> tuple[float, float, float](L128) — Planar(x, y, yaw)from a tf2 transform's translation + rotation (zignored); delegates yaw toopenral_core.geometry.quat_xyzw_to_yaw. Used bySlamMapBridgeto project themap→base_framelookup into the span attributes.class SlamMapBridge(L159) —rclpy.node.Node-hosted subscription on/map; on each accepted callback rasterises the grid, looks up the robot pose, and emits aslam.occupancy_gridspan the dashboard renders. Degrades gracefully (no robot marker) when TF is unavailable.__init__(node, *, topic=SLAM_MAP_TOPIC_DEFAULT, base_frame="base_link", footprint_radius_m=None, footprint_polygon=None, source_node_name="openral_slam_toolbox", publish_interval_s=1.0, max_cells=4_000_000)(L205) — Subscribes with slam_toolbox's QoS;footprint_polygon(base-frame XY) lets the dashboard draw the true oriented outline instead of falling back to thefootprint_radius_mcircle.destroy() -> None(L277) — Release the ROS subscription. Safe to call multiple times._DEFAULT_PUBLISH_INTERVAL_S(L49) —1.0; throttle so a busy SLAM run doesn't flood the OTLP pipeline (the dashboard re-renders on every span)._UNKNOWN_GREY(L58) —128; the 8-bit greyscale value an unknown (-1) occupancy cell renders as.prop _OCC_FREE_THRESHOLD, _OCC_OCCUPIED_THRESHOLD(L61–62) —0/100; thenav_msgs/OccupancyGridprobability bounds free/occupied map to (white/black), with values in between scaled linearly.prop _OVERSIZE_WARN_BURST, _OVERSIZE_WARN_RATE(L66–67) —3/100; how many oversize-grid warnings are emitted verbosely before falling back to a 1-in-100 rate to keep log noise bounded.
packages/openral_foxglove_bringup/
Read-only Foxglove live-scene surface, hybrid with the OTel dashboard which keeps traces, metrics, and every write path. Provides the bridge + republishers, a converter node, and an MCAP recorder; layout generated by python -m openral_foxglove_bringup.layout. Spawned into deploy-sim by openral deploy sim --foxglove.
SCENE_TOPICS: list[str](topics.py L26) — Allowlist group 1: the geometry needed to draw the robot in its world — camera images +camera_info,/map, octomap cloud,/scan,/odom,/joint_states,/robot_description,/openral/robot_description,/tf(_static).DEPTH_TOPICS: list[str](topics.py L56) — Allowlist group 2: the RGBD / nvblox / visual-SLAM leg — per-camera depth + points, the self-filtered world cloud with its colour (/openral/world_cloud/self_filtered_rgb, the viewer's robot-free cloud; octomap's x/y/z-only input/openral/world_cloud/self_filteredis not exposed), the DA3 sidecar's depth topics, nvblox's filtered depth + ESDF cloud,/openral/nav2/scan,/openral/imu,/openral/visual_slam/odometry. Published only under the matching deploy posture.BUCKET2_TOPICS: list[str](topics.py L79) — Allowlist group 3: thebucket2_markersconverter's outputs (/openral/world_voxels_cloud,/openral/viz/attachments,/openral/viz/static_geometry,/openral/viz/collisions).TELEMETRY_TOPICS: list[str](topics.py L106) — Allowlist group 4: the mission/state plane mirrored from the OTel dashboard's cards — world/policy state, episode, critic score, active task, objects, attachments, skill registry, diagnostics. Observation-only;/openral/safety_statusis excluded pending safety-WG sign-off.BUCKET1_TOPIC_WHITELIST: list[str](topics.py L125) — Thefoxglove_bridgetopic_whitelist: the four groups above concatenated, nothing else — notably never the safety/e-stop/action/command topics. Imported by both launch files so the allowlist has one source of truth.ASSET_URI_ALLOWLIST: list[str](topics.py L153) — The bridge'sasset_uri_allowlist, controlling which mesh/asset paths a connected viewer may fetch to draw the URDF; extended from upstream's default to admit dotted version segments while still refusing path traversal. Applied by both launch files.READ_ONLY_CAPABILITIES: list[str](topics.py L158) —["connectionGraph", "assets"]; omitsclientPublish/services/parametersso a connected viewer cannot publish, call services, or write params (cannot actuate).DEFAULT_CAMERAS: tuple[str, ...](layout.py L57) —("top", "wrist_left", "wrist_right"); the camera slots the shipped layout is generated for. These are sensor names from the robot manifest, not labels — a slot that doesn't exist on a given robot renders as a dead panel.DEFAULT_LAYOUT_PATH: str(layout.py L60) —"config/openral_layout.json"; where the generated layout is shipped, relative to the package root.camera_image_topic(camera, *, compressed=False) -> str(layout.py L63) —openral_core.camera_topic(camera), or theimage_transport/compressedsibling.ROBOT_DESCRIPTION_TOPIC: str(layout.py L89) —"/robot_description"; thestd_msgs/Stringtopic the layout's URDF layer reads (manifest URDF underdeploy sim, vendor URDF underdeploy run).urdf_layer(topic=ROBOT_DESCRIPTION_TOPIC) -> dict[str, dict[str, Any]](layout.py L92) — 3D-panellayersentry (layerId: "foxglove.Urdf",sourceType: "topic") that draws the robot. Needed because the 3D panel auto-loads a URDF only from the/robot_descriptionparameter, which the read-only bridge withholds; atopicsentry for the String topic draws nothing.build_layout(cameras=DEFAULT_CAMERAS, *, compressed=False, follow_frame="base_link") -> dict[str, Any](layout.py L293) — Builds the Foxglove layout for a scene's camera slots: a hero 3D panel (clouds +urdf_layer()robot) plus one Image panel per camera and tabbed panels for nav, joints, policy/world state, and diagnostics. Every referenced topic is onBUCKET1_TOPIC_WHITELIST, and no generated layout contains a write-capable panel. Both URDF-drawing 3D panels setscene.ignoreColladaUpAxis(Collada vertices read as authored, as RViz/Gazebo/Isaac do — OpenArm v2's Blender meshes bake Z-up yet declare Y_UP).main(argv=None) -> int(layout.py L476) —python -m openral_foxglove_bringup.layoutentry point:--cameras,--compressed,--follow-frame,-o/--output,--write-default(overwrite the shippedconfig/openral_layout.json).occupied_voxel_centers(origin, resolution, size, occupancy, orientation_xyzw=(0,0,0,1)) -> list[tuple[float,float,float]](bucket2_markers.py L68) — Pure: centre coordinates of occupied voxels, placed by the grid's own rotation — without it every voxel is drawn somewhere the obstacle is not. Raises on a mismatched occupancy length or a non-unit orientation quaternion.ATTACHMENT_MARKERS_TOPIC: str(bucket2_markers.py L56) —"/openral/viz/attachments"; where the attachment / declaration-regionMarkerArrayis published.outside_boxes(centers, boxes, pad_m) -> NDArray[np.bool_](bucket2_markers.py L129) — Mask of voxel centres outside every(base_from_box, half_extents)box grown bypad_m; the viewer's static-voxel filter.static_boxes(path) -> list[tuple[NDArray, NDArray]](bucket2_markers.py L151) — The declared box fixtures of aStaticGeometryYAML as(base_from_box 4x4, half_extents).COLLISION_MARKERS_TOPIC: str(bucket2_markers.py L176) —"/openral/viz/collisions"; where each kernel stop is drawn (latchedMarkerArray).COLLISION_RGBA(bucket2_markers.py L178) — solid red for a kernel stop.voxel_center(index, origin, resolution, size, orientation_xyzw)(bucket2_markers.py L181) — The centre of grid cellindex, the cell a kernel stop names asvoxel_<index>.collision_markers(stops) -> MarkerArray(bucket2_markers.py L199) — Every kernel stop so far as a red sphere +STOP <link>label, from(frame, xyz, label)triples.STATIC_GEOMETRY_MARKERS_TOPIC: str(bucket2_markers.py L58) —"/openral/viz/static_geometry"; where the declared static geometryMarkerArrayis published (latched, once).SEARCH_BOX_RGBA: tuple[float, float, float, float](bucket2_markers.py L275) — Faint white; the grasp declaration'ssearch_box.WORKSPACE_RGBA: tuple[float, float, float, float](bucket2_markers.py L277) — Amber; the map-confirmed manipulation workspace (workspace_region, #349). A declared-onlyworkspace_boxis drawn inINACTIVE_REGION_RGBA.MEASURED_RGBA: tuple[float, float, float, float](bucket2_markers.py L279) — White; a payload box's measured inner box (its stage-2 refinement).STATIC_RGBA: tuple[float, float, float, float](bucket2_markers.py L281) — Translucent red; declared static geometry.static_geometry_markers(path, frame) -> visualization_msgs/MarkerArray(bucket2_markers.py L507) — AStaticGeometryYAML (DeployRuntime.static_geometry_file) →DELETEALL+ oneCUBEper box at its pose and oneTRIANGLE_LISTper mesh.npz, all inframe(the manifestbase_framethe file is declared in). RaisesROSConfigErroron an unreadable/invalid file or a missing/malformed mesh.parse_attach_link_tf_frames(entries) -> dict[str, str](bucket2_markers.py L224) — Pure:"link=frame"strings → manifest-link → TF-frame map, the same strings and rules as the octomap bridge'sattach_link_tf_frames(payload_clearing.hpp::parse_attach_link_tf_frames, deliberate C++/Python mirror): empty entries skipped; a malformed entry or a link mapped to two frames raisesValueError.EVIDENCE_RGBA: dict[str, tuple[float, float, float, float]](bucket2_markers.py L263) — Payload marker colour perevidence_kind:grasp_target_regionorange,vision_segmentationcyan,gripper_closureyellow.OTHER_EVIDENCE_RGBA: tuple[float, float, float, float](bucket2_markers.py L269) — Magenta payload colour for every evidence kind not inEVIDENCE_RGBA(sim ground truth, operator, perception track).GRASP_REGION_RGBA: tuple[float, float, float, float](bucket2_markers.py L271) — Green; an active grasp declaration's region.PLACE_REGION_RGBA: tuple[float, float, float, float](bucket2_markers.py L272) — Blue; an active place declaration's region.INACTIVE_REGION_RGBA: tuple[float, float, float, float](bucket2_markers.py L273) — Grey; a retracted (active=false) declaration's region.attachment_markers(state, attach_link_tf_frames=None) -> visualization_msgs/MarkerArray(bucket2_markers.py L293) — Pure (msg types only):openral_msgs/AttachmentState→DELETEALL+ re-add markers. Each attached object's primitives (boxCUBE,SPHERE, capsuleCYLINDER+ end caps) atpose_in_link ∘ pose_in_objecton the attach link's TF frame (renamed via the map), frame-locked, translucent, coloured by evidence kind, plus a text label (short id · evidence ·support·releasedfor a frozen release record); the grasp / place declaration regions (only when declaration andregionare valid) as a translucent box +LINE_LISTwireframe + target label, active vs retracted coloured; a box primitive's measured inner box (first threetight_dop_lo/tight_dop_hiaxes, nsattached_measured); the grasp declaration'ssearch_boxand manipulation workspace (workspace_regionamber once confirmed, elseworkspace_boxgrey). RaisesValueErroron a bad primitive,ROSConfigErroron a degenerate quaternion.class Bucket2MarkersNode(bucket2_markers.py L572) —rclpy.node.Nodesubscribing/openral/world_voxelsand/openral/attachment_state(TRANSIENT_LOCAL); re-publishes/openral/world_voxels_cloud(PointCloud2) and/openral/viz/attachments(MarkerArray, TRANSIENT_LOCAL) via the pure functions. Parameterattach_link_tf_frames(string list,deploy_e2epasses the octomap bridge's);static_geometry_file+base_framepublishstatic_geometry_markersonce on/openral/viz/static_geometry(TRANSIENT_LOCAL;deploy_e2epasses the scene's file);hide_static_voxels(default true) leaves the voxels inside a declared box fixture out of/openral/world_voxels_cloud(viewer only). Subscribes/openral/failure/safetyand draws each kernel collision stop on/openral/viz/collisions(red sphere at the voxel centre via the grid matching the stop's disclosed origin, or at the link's frame for a link-vs-link stop). Read-only viz; defers rclpy/openral_msgs imports.spin() -> None(bucket2_markers.py L789) — Spins the node until shutdown.destroy() -> None(bucket2_markers.py L795) — Releases the subscriptions and publishers. Safe to call multiple times.main() -> None(bucket2_markers.py L800) — Console entry point (installed aslib/openral_foxglove_bringup/bucket2_markers);rclpy.init→ spin → shutdown.generate_launch_description() -> LaunchDescription(bucket2.launch.py L27) — Bucket-2 converter node launch; declaresuse_sim_timeand spawns thebucket2_markersnode.generate_launch_description() -> LaunchDescription(foxglove.launch.py L42) — Read-onlyfoxglove_bridgebring-up for the Bucket-1 topics;topic_whitelistis an explicit allowlist, so anything unmatched — including safety/e-stop/action/command topics — stays invisible.generate_launch_description() -> LaunchDescription(record.launch.py L35) — Opt-in MCAP recorder for the Bucket-1 allowlist (ros2 bag record -e, one regex per allowlisted topic).
packages/openral_reasoner_ros/openral_reasoner_ros/critic_producer_node.py
Tier-C critic producer, the default publisher for /openral/failure/critic. Subscribes the generic /openral/critic/score topic that any reward model publishes, routes each sample through a CriticWatchdogGroup, and on a stall emits a FailureTrigger that reasoner_node maps to a forced Tier-C tick. Advisory only.
class CriticProducerNode(L58) —rclpy.node.Node. Builds aCriticWatchdogGroupand aFailureBusPublisher, subscribesCriticScore, and routes each sample; on a stall publishes oneKIND_CRITIC/SEVERITY_FAILFailureTriggerper stall.destroy_node() -> None(L118) — Tears down theFailureBusPublisherbefore base teardown.main(args=None) -> None(L124) — Console entry point (installed aslib/openral_reasoner_ros/critic_producer_node.py);rclpy.init→ spin → shutdown.