Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## How it was tested

- [ ] `colcon test` passes (CI green, simulator end-to-end test included)
- [ ] Tried in the simulator (`ros2 launch robot_sim sim.launch.py`), on the robot or in the dev
- [ ] Tried in the simulator (`ros2 launch pashupati_sim sim.launch.py`), on the robot or in the dev
container: <!-- what you ran and saw -->

## Checklist
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ env:
ROSDEP_SKIP: >-
livox_ros_driver2 fast_lio legkilo apriltag_draw
PACKAGES: >-
pashupati_perception robot_bridge robot_fleet robot_inspection robot_health robot_rviz
pashupati robot_sim
pashupati_perception pashupati_bridge pashupati_fleet pashupati_inspection pashupati_health pashupati_rviz
pashupati pashupati_sim

jobs:
scripts:
Expand Down Expand Up @@ -128,7 +128,7 @@ jobs:
source install/setup.bash
ros2 launch pashupati robot.launch.py --show-args > /dev/null
ros2 launch dharma_odometry odometry.launch.py --show-args > /dev/null
ros2 launch robot_sim sim.launch.py --show-args > /dev/null
ros2 launch pashupati_sim sim.launch.py --show-args > /dev/null
- name: Test reports
if: always()
uses: actions/upload-artifact@v4
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ pure-logic tests also run with plain `pytest` on a laptop.
Anything that changes how the robot moves (speeds, limits, e-stop, avoidance, the control mux, the
teleop gateway) needs a test that drives the logic, a note in the PR on how it was tried on the robot
(slow speed, open area, someone at the e-stop), and a CHANGELOG entry. Try it in the simulator
first (`ros2 launch robot_sim sim.launch.py autostart:=true rviz:=true`); its end-to-end test
first (`ros2 launch pashupati_sim sim.launch.py autostart:=true rviz:=true`); its end-to-end test
(sliding around a person, stopping for a crowd, the handheld driving at a person) runs in CI and
must stay green.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,13 @@ flowchart LR
| [`dharma_localization`](src/dharma_localization) | FAST-LIO frame bridge, `map→odom` from AprilTags / RViz, tag recording |
| [`pashupati_perception`](src/pashupati_perception) | LiDAR sectors, AprilTag, person detection (YOLO11n ONNX) |
| [`dharma_navigation`](src/dharma_navigation) | route processing, 5 controllers, 4 avoidance algorithms, final approach, safety |
| [`robot_bridge`](src/robot_bridge) | AUTO / REMOTE control mux, UDP teleop gateway ([protocol](src/robot_bridge/PROTOCOL.md)), WebRTC video |
| [`robot_fleet`](src/robot_fleet) | NETRA uplink: telemetry, events, crowd, anomalies, routes ([API](src/robot_fleet/API.md)) |
| [`robot_inspection`](src/robot_inspection) | VLM inspection (Ollama): continuous, interval queue or stop points, own finding types |
| [`robot_health`](src/robot_health) | node liveness, odometry / safety scan / e-stop state, sensor rates, CPU, GPU and RAM |
| [`robot_rviz`](src/robot_rviz) | RViz panels: Mission, Status, Tuning, AprilTags |
| [`pashupati_bridge`](src/pashupati_bridge) | AUTO / REMOTE control mux, UDP teleop gateway ([protocol](src/pashupati_bridge/PROTOCOL.md)), WebRTC video |
| [`pashupati_fleet`](src/pashupati_fleet) | NETRA uplink: telemetry, events, crowd, anomalies, routes ([API](src/pashupati_fleet/API.md)) |
| [`pashupati_inspection`](src/pashupati_inspection) | VLM inspection (Ollama): continuous, interval queue or stop points, own finding types |
| [`pashupati_health`](src/pashupati_health) | node liveness, odometry / safety scan / e-stop state, sensor rates, CPU, GPU and RAM |
| [`pashupati_rviz`](src/pashupati_rviz) | RViz panels: Mission, Status, Tuning, AprilTags |
| [`pashupati`](src/pashupati) | the robot: one launch with respawning nodes, site configuration |
| [`robot_sim`](src/robot_sim) | simulated quadruped (driver, LiDAR, FAST-LIO interfaces) for the whole stack, end-to-end test |
| [`pashupati_sim`](src/pashupati_sim) | simulated quadruped (driver, LiDAR, FAST-LIO interfaces) for the whole stack, end-to-end test |

</details>

Expand Down Expand Up @@ -206,7 +206,7 @@ nothing, or only the vendor's topics:
echo "alias pashu='source ~/dev/Pashupati/script/env.sh'" >> ~/.bashrc # once
pashu
ros2 topic hz /perception/safety_scan
pnode robot_bridge teleop_udp_node bridge_params.yaml # one node, as robot.launch.py runs it
pnode pashupati_bridge teleop_udp_node bridge_params.yaml # one node, as robot.launch.py runs it
```

<details>
Expand Down Expand Up @@ -304,7 +304,7 @@ the tuned values with **Save YAML**.
| Inspection | `ros2 topic echo /anomaly_detector/status`: state, model, pace, queue, finding types and the last result |
| People | `ros2 topic echo /perception/people_status`: state, backend, count and boxes |
| Logs | `journalctl -fu pashupati`; ROS logs older than 7 days are pruned at start |
| Debug view | RViz `robot_rviz/rviz/robot.rviz`: route coloured by speed, lookahead, cross-track, avoidance cone, approach target, tags |
| Debug view | RViz `pashupati_rviz/rviz/robot.rviz`: route coloured by speed, lookahead, cross-track, avoidance cone, approach target, tags |

<details>
<summary><b>Services</b></summary>
Expand Down Expand Up @@ -395,10 +395,10 @@ On the robot during development, `script/start_tmux.sh teach|patrol` opens one p
RViz, with the package default parameters and the env file's settings.

**Simulation.** The whole stack runs against a simulated quadruped
([robot_sim](src/robot_sim)), with no robot, Gazebo or GPU:
([pashupati_sim](src/pashupati_sim)), with no robot, Gazebo or GPU:

```bash
ros2 launch robot_sim sim.launch.py autostart:=true rviz:=true
ros2 launch pashupati_sim sim.launch.py autostart:=true rviz:=true
# a person steps onto the route
ros2 topic pub --once /sim/obstacles geometry_msgs/msg/PoseArray "{poses: [{position: {x: 2.0, y: -3.0}}]}"
```
Expand Down
4 changes: 2 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ parameter reference holds for every robot.
|---|---|
| [README](../README.md) | install, configuration, workflow, operations, development |
| [Robot packages](../src/xtras/README.md) | what a robot package must provide, checklist for a new robot |
| [Handheld protocol](../src/robot_bridge/PROTOCOL.md) | the UDP protocol between the robot and the Pashupati Controller |
| [NETRA API](../src/robot_fleet/API.md) | what the robot sends to and takes from NETRA |
| [Handheld protocol](../src/pashupati_bridge/PROTOCOL.md) | the UDP protocol between the robot and the Pashupati Controller |
| [NETRA API](../src/pashupati_fleet/API.md) | what the robot sends to and takes from NETRA |
| Package READMEs (`src/robot_*/README.md`) | nodes, topics, services and parameters of each package |
| [CONTRIBUTING](../CONTRIBUTING.md) | branches, commits, rules that come from real crashes |
| [CHANGELOG](../CHANGELOG.md) | changes per version |
Expand Down
8 changes: 4 additions & 4 deletions docs/pdf/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
# Config files of the parameter reference, in reading order: (package, file, what it covers)
PARAMETER_FILES = [
('pashupati', 'navigation_params.yaml', 'route following, avoidance, stops'),
('robot_bridge', 'bridge_params.yaml', 'control mux, protective field, handheld, video'),
('pashupati_bridge', 'bridge_params.yaml', 'control mux, protective field, handheld, video'),
('pashupati', 'perception_params.yaml', 'LiDAR sectors, people, AprilTags'),
('pashupati', 'localization_params.yaml', 'map frame from AprilTags'),
('robot_inspection', 'inspection_params.yaml', 'AI inspection'),
('pashupati_inspection', 'inspection_params.yaml', 'AI inspection'),
('pashupati', 'odometry_params.yaml', 'odometry start, monitor, route recording'),
('robot_health', 'health_params.yaml', 'health monitor'),
('robot_fleet', 'fleet_params.yaml', 'NETRA uplink'),
('pashupati_health', 'health_params.yaml', 'health monitor'),
('pashupati_fleet', 'fleet_params.yaml', 'NETRA uplink'),
]

KEY_LINE = re.compile(r'^(?P<indent> *)(?P<key>[A-Za-z0-9_./]+):\s*(?P<rest>.*)$')
Expand Down
2 changes: 1 addition & 1 deletion docs/tuning/tuning-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ the site file. Only some nodes react to live changes:
| `person_detector_node` | `enabled`, `confidence`, `rate_hz` |
| all other nodes | nothing: `ros2 param set` is accepted, but the node only reads its parameters at start. Use the site file and restart |

The **Tuning** panel of RViz (`robot_rviz`) edits the navigation and avoidance parameters with
The **Tuning** panel of RViz (`pashupati_rviz`) edits the navigation and avoidance parameters with
sliders; its **Save YAML…** button writes them to a file. RViz runs on a laptop with ROS 2
Humble on the robot's network: set `ROS_LOCALHOST_ONLY=0` in the env file, restart, and use
the robot's `ROS_DOMAIN_ID` and `RMW_IMPLEMENTATION` on the laptop.
Expand Down
2 changes: 1 addition & 1 deletion script/env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# source ~/dev/Pashupati/script/env.sh # e.g. alias pashu='source ~/dev/Pashupati/script/env.sh'
# ros2 topic list # sees the stack, not a vendor stack on another RMW
# pnode robot_bridge teleop_udp_node bridge_params.yaml [ros args]
# pnode pashupati_bridge teleop_udp_node bridge_params.yaml [ros args]
#
# pnode runs one node the way robot.launch.py does: package defaults, then SITE_CONFIG, then args.
# Env: PASHUPATI_ENV (/etc/pashupati/pashupati.env), ROS_DISTRO (humble)
Expand Down
10 changes: 5 additions & 5 deletions script/start_tmux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ PERCEPTION="ros2 launch dharma_perception perception.launch.py visitors:=false p
[[ "$PEOPLE" == "true" ]] && PERCEPTION+=" & ros2 launch pashupati_perception people.launch.py"
run robot.2 "$PERCEPTION"
if [[ "$RVIZ" == "1" ]]; then
run robot.3 "ros2 run rviz2 rviz2 -d \$(ros2 pkg prefix robot_rviz)/share/robot_rviz/rviz/robot.rviz \
run robot.3 "ros2 run rviz2 rviz2 -d \$(ros2 pkg prefix pashupati_rviz)/share/pashupati_rviz/rviz/robot.rviz \
--ros-args -r battery:=/$ROBOT_NS/battery -r cmd_vel:=/$ROBOT_NS/cmd_vel \
-r motor_temperature:=/$ROBOT_NS/motor_temperature -r diagnostics:=/$ROBOT_NS/diagnostics"
fi
Expand All @@ -85,21 +85,21 @@ if [[ "$MODE" == "patrol" ]]; then
cmd_vel_topic:=/bridge/nav_cmd_vel battery_topic:=/$ROBOT_NS/battery"
fi
tmux split-window -h -t "$SESSION:nav.0"
run nav.1 "ros2 launch robot_bridge bridge.launch.py robot_ns:=$ROBOT_NS"
run nav.1 "ros2 launch pashupati_bridge bridge.launch.py robot_ns:=$ROBOT_NS"
tmux split-window -v -t "$SESSION:nav.1"
FLEET_ARGS="robot_ns:=$ROBOT_NS"
[[ -n "$CC_URL" ]] && FLEET_ARGS+=" server_url:=$CC_URL"
[[ -n "$CC_TOKEN" ]] && FLEET_ARGS+=" token:=$CC_TOKEN"
run nav.2 "ros2 launch robot_fleet fleet.launch.py $FLEET_ARGS"
run nav.2 "ros2 launch pashupati_fleet fleet.launch.py $FLEET_ARGS"
tmux send-keys -t "$SESSION:nav.3" "$SETUP" C-m
tmux send-keys -t "$SESSION:nav.3" "# ros2 service call /localization/start std_srvs/srv/Trigger" C-m

# Window 3: health and, when enabled, on-robot inspection (Moondream)
tmux new-window -t "$SESSION" -n extra
run extra.0 "ros2 launch robot_health health.launch.py"
run extra.0 "ros2 launch pashupati_health health.launch.py"
if truthy "$INSPECTION"; then
tmux split-window -h -t "$SESSION:extra"
run extra.1 "ros2 launch robot_inspection inspection.launch.py"
run extra.1 "ros2 launch pashupati_inspection inspection.launch.py"
fi

tmux select-window -t "$SESSION:robot"
Expand Down
18 changes: 9 additions & 9 deletions src/pashupati/launch/robot.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def video_server(webrtc: str, binary: str, params_files: list):
)
try:
executable = os.path.join(
get_package_prefix('robot_bridge'), 'lib', 'robot_bridge', 'video_stream_node'
get_package_prefix('pashupati_bridge'), 'lib', 'pashupati_bridge', 'video_stream_node'
)
except PackageNotFoundError:
executable = ''
Expand Down Expand Up @@ -161,7 +161,7 @@ def node(package, executable, defaults, overrides=None, remappings=None, **kwarg

nav = share('pashupati', 'config', 'navigation_params.yaml')
perception = share('pashupati', 'config', 'perception_params.yaml')
bridge = share('robot_bridge', 'config', 'bridge_params.yaml')
bridge = share('pashupati_bridge', 'config', 'bridge_params.yaml')
version = package_version(share('pashupati', 'package.xml'))
git = os.environ.get('PASHUPATI_GIT', 'unknown')

Expand Down Expand Up @@ -251,7 +251,7 @@ def node(package, executable, defaults, overrides=None, remappings=None, **kwarg
remappings=[('cmd_vel', topic('cmd_vel'))],
),
node(
'robot_bridge',
'pashupati_bridge',
'teleop_udp_node',
[bridge],
teleop,
Expand All @@ -265,9 +265,9 @@ def node(package, executable, defaults, overrides=None, remappings=None, **kwarg
if arg('cc_url').strip():
actions.append(
node(
'robot_fleet',
'pashupati_fleet',
'fleet_uplink_node',
[share('robot_fleet', 'config', 'fleet_params.yaml')],
[share('pashupati_fleet', 'config', 'fleet_params.yaml')],
{
'server_url': arg('cc_url'),
'token': arg('cc_token'),
Expand All @@ -286,17 +286,17 @@ def node(package, executable, defaults, overrides=None, remappings=None, **kwarg
if truthy(arg('inspection')):
actions.append(
node(
'robot_inspection',
'pashupati_inspection',
'anomaly_detector_node',
[share('robot_inspection', 'config', 'inspection_params.yaml')],
[share('pashupati_inspection', 'config', 'inspection_params.yaml')],
)
)
if truthy(arg('health')):
actions.append(
node(
'robot_health',
'pashupati_health',
'health_monitor_node',
[share('robot_health', 'config', 'health_params.yaml')],
[share('pashupati_health', 'config', 'health_params.yaml')],
{'expected_nodes': expected_nodes(started, odometry), 'robot_ns': ns},
)
)
Expand Down
8 changes: 4 additions & 4 deletions src/pashupati/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
<exec_depend>pashupati_perception</exec_depend>
<exec_depend>dharma_control</exec_depend>
<exec_depend>dharma_navigation</exec_depend>
<exec_depend>robot_bridge</exec_depend>
<exec_depend>robot_fleet</exec_depend>
<exec_depend>robot_inspection</exec_depend>
<exec_depend>robot_health</exec_depend>
<exec_depend>pashupati_bridge</exec_depend>
<exec_depend>pashupati_fleet</exec_depend>
<exec_depend>pashupati_inspection</exec_depend>
<exec_depend>pashupati_health</exec_depend>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
Expand Down
2 changes: 1 addition & 1 deletion src/pashupati/test/test_bringup.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def servers(webrtc):
config = yaml.safe_load(f)
assert config['authInternalUsers'][1]['pass'] == password_hash('s3cret')
command = shlex.split(config['paths']['camera']['runOnDemand'])
assert command[0].endswith(os.path.join('robot_bridge', 'video_stream_node'))
assert command[0].endswith(os.path.join('pashupati_bridge', 'video_stream_node'))
assert str(site) in command # the encoder gets the same site parameters
assert servers('false') == []
binary.write_text('#!/bin/sh\necho v1.9.0\n')
Expand Down
4 changes: 2 additions & 2 deletions src/pashupati/test/test_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ def test_gstreamer_check(monkeypatch):
def test_only_the_handheld_may_watch_and_only_this_computer_publish(tmp_path):
"""Reading needs the token (stored as a hash), publishing comes from the loopback only."""
command = video.node_command(
'/ws/install/robot_bridge/lib/robot_bridge/video_stream_node',
'/ws/install/pashupati_bridge/lib/pashupati_bridge/video_stream_node',
['/ws/my params.yaml', '/etc/pashupati/site.yaml'],
video.rtsp_url(),
)
assert shlex.split(command) == [
'/ws/install/robot_bridge/lib/robot_bridge/video_stream_node',
'/ws/install/pashupati_bridge/lib/pashupati_bridge/video_stream_node',
'--ros-args',
'--params-file',
'/ws/my params.yaml',
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions src/robot_bridge/README.md → src/pashupati_bridge/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# robot_bridge
# pashupati_bridge

Who drives the robot, and how the handheld talks to it without ROS.

Expand Down Expand Up @@ -73,7 +73,7 @@ MediaMTX serves WebRTC (WHEP) to the handheld. MediaMTX, its configuration and t
## Run

```bash
ros2 launch robot_bridge bridge.launch.py robot_ns:=robot
ros2 launch pashupati_bridge bridge.launch.py robot_ns:=robot
ros2 launch dharma_navigation navigation.launch.py cmd_vel_topic:=/bridge/nav_cmd_vel ...
```

Expand All @@ -83,5 +83,5 @@ publish a compressed image (`camera_compressed: true`); raw images are JPEG-enco
## Test

```bash
colcon test --packages-select robot_bridge && colcon test-result --verbose
colcon test --packages-select pashupati_bridge && colcon test-result --verbose
```
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

def generate_launch_description():
"""Launch the control mux and the UDP teleop gateway."""
share = get_package_share_directory('robot_bridge')
share = get_package_share_directory('pashupati_bridge')
params = os.path.join(share, 'config', 'bridge_params.yaml')
robot_ns = LaunchConfiguration('robot_ns')
return LaunchDescription(
Expand All @@ -26,7 +26,7 @@ def generate_launch_description():
remappings=[('cmd_vel', ['/', robot_ns, '/cmd_vel'])],
),
Node(
package='robot_bridge',
package='pashupati_bridge',
executable='teleop_udp_node',
name='teleop_udp_node',
output='screen',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>robot_bridge</name>
<name>pashupati_bridge</name>
<version>0.1.0</version>
<description>Auto/remote control mux, the UDP teleop gateway (no ROS on the handheld) and its WebRTC video encoder.</description>
<maintainer email="darmawandeveloper@gmail.com">Agus Darmawan</maintainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from diagnostic_msgs.msg import DiagnosticArray
from geometry_msgs.msg import Twist
import numpy as np
from pashupati_bridge import protocol
from rcl_interfaces.msg import ParameterType
from rcl_interfaces.srv import GetParameters, SetParameters
import rclpy
Expand All @@ -23,7 +24,6 @@
from rclpy.parameter import Parameter
from rclpy.qos import qos_profile_sensor_data, QoSDurabilityPolicy, QoSProfile
from rclpy.time import Time
from robot_bridge import protocol
from sensor_msgs.msg import BatteryState, CameraInfo, CompressedImage, Image, PointCloud2
from std_msgs.msg import Bool, String
from std_srvs.srv import SetBool, Trigger
Expand Down
4 changes: 4 additions & 0 deletions src/pashupati_bridge/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[develop]
script_dir=$base/lib/pashupati_bridge
[install]
install_scripts=$base/lib/pashupati_bridge
6 changes: 3 additions & 3 deletions src/robot_bridge/setup.py → src/pashupati_bridge/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from setuptools import find_packages, setup

package_name = 'robot_bridge'
package_name = 'pashupati_bridge'

setup(
name=package_name,
Expand All @@ -24,8 +24,8 @@
license='Apache-2.0',
entry_points={
'console_scripts': [
'teleop_udp_node = robot_bridge.teleop_udp_node:main',
'video_stream_node = robot_bridge.video_stream_node:main',
'teleop_udp_node = pashupati_bridge.teleop_udp_node:main',
'video_stream_node = pashupati_bridge.video_stream_node:main',
],
},
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import types

import numpy as np
from pashupati_bridge import protocol
import pytest
from robot_bridge import protocol


def test_tag_quads_are_image_fractions():
Expand Down Expand Up @@ -65,7 +65,7 @@ def test_discovery_beacon_and_seq():
"""A discover probe gets a beacon without the token; states carry an increasing seq."""
import socket

node_module = pytest.importorskip('robot_bridge.teleop_udp_node')
node_module = pytest.importorskip('pashupati_bridge.teleop_udp_node')
node = node_module.TeleopUdpNode.__new__(node_module.TeleopUdpNode)
node._robot_name, node._token, node._client, node._client_seen = 'dog-01', 'secret', None, 0.0
node._client_timeout, node._client_name, node._seq = 1.0, '', 0
Expand Down Expand Up @@ -97,7 +97,7 @@ def test_discovery_beacon_and_seq():

def test_camera_rate_from_the_handheld_is_bounded():
"""camera_fps from the client is capped by the parameter; NaN, text and bools are ignored."""
node_module = pytest.importorskip('robot_bridge.teleop_udp_node')
node_module = pytest.importorskip('pashupati_bridge.teleop_udp_node')
node = node_module.TeleopUdpNode.__new__(node_module.TeleopUdpNode)
node.get_parameter = lambda name: types.SimpleNamespace(value=8.0)
node._camera_period = 0.125
Expand Down
Loading
Loading