Skip to content

Commit 1016caf

Browse files
committed
merge #58
1 parent 1481a4f commit 1016caf

20 files changed

Lines changed: 451 additions & 76 deletions

File tree

_meta/features/evaluation/main

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ uninstall(){
1919

2020
# === MAIN SCRIPT ===
2121
help(){
22-
echo "Usage: $name <install|update|uninstall|source|launch|list|status|tail>"
22+
echo "Usage: $name <install|update|uninstall|source|launch|list|status|tail|extract|run|process|report|plot>"
2323
echo ""
2424
echo "arena_evaluation for recording, metrics, and benchmarking."
2525
echo "This enables:"
@@ -31,6 +31,11 @@ help(){
3131
echo " list -- list available evaluations"
3232
echo " status -- show status of a running or completed evaluation"
3333
echo " tail -- stream live output of a running evaluation"
34+
echo " extract -- extract MCAP topics into the Parquet cache"
35+
echo " run -- process recording and generate HTML report"
36+
echo " process -- process recording to generate metrics.parquet"
37+
echo " report -- generate HTML report from processed metrics"
38+
echo " plot -- generate static PNG plots from processed metrics"
3439
}
3540
if [ $# -lt 1 ]; then
3641
help
@@ -77,6 +82,11 @@ case "$1" in
7782
ros2 run arena_evaluation evaluation_cli tail "$@"
7883
exit $?
7984
;;
85+
extract|run|process|report|plot)
86+
_feature_registry require "$name" || exit 1
87+
ros2 run arena_evaluation evaluation "$@"
88+
exit $?
89+
;;
8090
*)
8191
help
8292
exit 1

arena_bringup/launch/arena_runtime.launch.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ def generate_launch_description():
3939
default_value='False',
4040
)
4141

42+
record_data_dir = LaunchArgument(
43+
name='record_data_dir',
44+
default_value='',
45+
)
46+
47+
disable_auto_recorder = LaunchArgument(
48+
name='disable_auto_recorder',
49+
default_value='false',
50+
)
51+
4252
launch_sim = launch.actions.IncludeLaunchDescription(
4353
PathJoinSubstitution([
4454
FindPackageShare('arena_bringup'),

arena_evaluation

Submodule arena_evaluation updated 101 files

arena_runtime/arena_runtime/arena_runtime/cam/cli.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,11 @@ def main(argv: list[str] | None = None) -> None:
113113
parser.add_argument("params", nargs="*", metavar="key=value", help="verb or shot parameters")
114114
parser.add_argument("--sim", action="store_true", help="drive the sim GUI camera")
115115
parser.add_argument(
116-
"--viz", nargs="?", const=_VIZ_ALL, default=None, metavar="ENV_ID",
116+
"--viz",
117+
nargs="?",
118+
const=_VIZ_ALL,
119+
default=None,
120+
metavar="ENV_ID",
117121
help="drive rviz cameras: bare for all, or an env id for one",
118122
)
119123
parser.add_argument("-f", "--force", action="store_true", help="overwrite a non-empty record dir")

arena_runtime/arena_runtime/arena_runtime/cam/client.py

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def _env_id_from_ns(ns: str) -> int | None:
8686
for part in ns.strip("/").split("/"):
8787
if part.startswith("env_"):
8888
try:
89-
return int(part[len("env_"):])
89+
return int(part[len("env_") :])
9090
except ValueError:
9191
return None
9292
return None
@@ -106,12 +106,8 @@ def __init__(self, node: CamNode, ns: str, offset: tuple[float, float]) -> None:
106106
self.ns = ns
107107
self._ox, self._oy = offset
108108
self.set_view = node.create_client_wrapper(ViewportSetView, f"{ns}/viewport/set_view", timeout=10.0)
109-
self.set_reference = node.create_client_wrapper(
110-
ViewportSetReferenceFrame, f"{ns}/viewport/set_reference_frame", timeout=10.0
111-
)
112-
self.set_projection = node.create_client_wrapper(
113-
ViewportSetProjection, f"{ns}/viewport/set_projection", timeout=10.0
114-
)
109+
self.set_reference = node.create_client_wrapper(ViewportSetReferenceFrame, f"{ns}/viewport/set_reference_frame", timeout=10.0)
110+
self.set_projection = node.create_client_wrapper(ViewportSetProjection, f"{ns}/viewport/set_projection", timeout=10.0)
115111
# Generous timeout: a capture round-trips a full rendered frame.
116112
self.capture = node.create_client_wrapper(ViewportCapture, f"{ns}/viewport/capture", timeout=30.0)
117113
self.cmd_view = node.create_publisher(ViewportView, f"{ns}/viewport/cmd_view", _STREAM_QOS)
@@ -164,9 +160,7 @@ async def setup(self) -> None:
164160
rclpy.try_shutdown()
165161
return
166162
if self._recorder is not None and len(found) != 1:
167-
self.get_logger().error(
168-
f"record needs exactly one target, found {len(found)}, narrow with --sim or --viz <env_id>"
169-
)
163+
self.get_logger().error(f"record needs exactly one target, found {len(found)}, narrow with --sim or --viz <env_id>")
170164
rclpy.try_shutdown()
171165
return
172166

@@ -325,9 +319,7 @@ async def drive(self, duration: float, world_orientation: bool, frame_at: Frame)
325319
break
326320
await asyncio.sleep(period)
327321

328-
async def capture(
329-
self, endpoint: _Endpoint, position: Vec3, quat: Quat, world_orientation: bool, fov: float
330-
) -> object | None:
322+
async def capture(self, endpoint: _Endpoint, position: Vec3, quat: Quat, world_orientation: bool, fov: float) -> object | None:
331323
req = ViewportCapture.Request()
332324
req.pose = _ros_pose(self._local(endpoint, position), quat)
333325
req.world_orientation = bool(world_orientation)
@@ -338,9 +330,7 @@ async def capture(
338330
self.get_logger().warning(f"capture call failed: {e}")
339331
return None
340332

341-
async def _record_frame(
342-
self, endpoint: _Endpoint, position: Vec3, quat: Quat, world_orientation: bool, fov: float
343-
) -> bool:
333+
async def _record_frame(self, endpoint: _Endpoint, position: Vec3, quat: Quat, world_orientation: bool, fov: float) -> bool:
344334
res = await self.capture(endpoint, position, quat, world_orientation, fov)
345335
if res is None or not res.success:
346336
detail = "service timed out" if res is None else res.message

arena_runtime/arena_runtime/arena_runtime/cam/curves.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,7 @@ def catmull_rom(points: list[Vec3], t: float) -> Vec3:
127127
f3 = f2 * f
128128

129129
def comp(a: float, b: float, c: float, d: float) -> float:
130-
return 0.5 * (
131-
(2 * b)
132-
+ (-a + c) * f
133-
+ (2 * a - 5 * b + 4 * c - d) * f2
134-
+ (-a + 3 * b - 3 * c + d) * f3
135-
)
130+
return 0.5 * ((2 * b) + (-a + c) * f + (2 * a - 5 * b + 4 * c - d) * f2 + (-a + 3 * b - 3 * c + d) * f3)
136131

137132
return (
138133
comp(p0[0], p1[0], p2[0], p3[0]),

arena_runtime/arena_runtime/arena_runtime/cam/shot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ def load_shot(path: str | Path, targets: TargetSelection) -> Camera:
3434
args = spec.get("params", {})
3535
cam = Camera(targets)
3636
for step in desugar(spec):
37-
(name, raw), = step.items()
37+
((name, raw),) = step.items()
3838
cam.add(name, substitute(raw, args))
3939
return cam

arena_runtime/arena_runtime/arena_runtime/cam/shots.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,6 @@ def expand_spec(spec: dict, params: object, stack: tuple[str, ...]) -> list[_Act
154154
args = {**spec.get("params", {}), **(params or {})}
155155
actions: list[_Action] = []
156156
for step in desugar(spec):
157-
(name, raw), = step.items()
157+
((name, raw),) = step.items()
158158
actions.extend(resolve(name, substitute(raw, args), stack))
159159
return actions

arena_runtime/arena_runtime/arena_runtime/sim/isaac_simulator.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -418,11 +418,7 @@ async def build_prim(obstacle: Obstacle) -> Prim | None:
418418
return prim
419419

420420
level = obstacles_optim_level(self.node)
421-
boxes = (
422-
await asyncio.gather(*(resolve_obstacle_box(o) for o in obstacles))
423-
if level is ObstaclesOptim.BBOX
424-
else [None] * len(obstacles)
425-
)
421+
boxes = await asyncio.gather(*(resolve_obstacle_box(o) for o in obstacles)) if level is ObstaclesOptim.BBOX else [None] * len(obstacles)
426422
box_indices = [i for i, box in enumerate(boxes) if box is not None]
427423
mesh_indices = [i for i, box in enumerate(boxes) if box is None]
428424

arena_simulation_setup/launch/robot.launch.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ def generate_launch_description():
2424
robot = LaunchArgument("robot")
2525
frame = LaunchArgument("frame")
2626

27-
record_data_dir = LaunchArgument('record_data_dir', default_value='')
28-
2927
# launch robot control
3028
state_pub_launch = launch.actions.IncludeLaunchDescription(
3129
launch.launch_description_sources.PythonLaunchDescriptionSource(
@@ -44,22 +42,11 @@ def generate_launch_description():
4442
}.items(),
4543
)
4644

47-
data_recorder = launch_ros.actions.Node(
48-
package='arena_evaluation',
49-
executable='record',
50-
name=PythonExpression(['"data_recorder" + "', namespace.substitution, '".replace("/","_")']),
51-
arguments=[
52-
['--dir', ' ', record_data_dir.substitution],
53-
],
54-
condition=launch.conditions.IfCondition(PythonExpression(['bool("', record_data_dir.substitution, '")'])),
55-
)
56-
5745
ld = launch.LaunchDescription([
5846
*ld_items,
5947
PushRosNamespace(namespace=namespace.substitution),
6048
# robot_localization_node,
6149
# state_pub_launch,
62-
data_recorder,
6350
])
6451
return ld
6552

0 commit comments

Comments
 (0)