Tools¶
Reporting & Verification¶
The reporting & verification suite's HTML report generators and the static two-run compare page.
report¶
HTML diagnostic report generation for calibration results.
Renders the same statistics as
BaseCalibration.print_quality_report() (convergence, per-DOF
residuals, parameter uncertainty, correlation, validation) as a
self-contained, shareable HTML page instead of terminal text — with a
visual encoding of which parameters are well identified and which are
not, and an auto-generated "insights" list flagging things worth a
second look.
generate_calibration_report(calibrator, output_path=None, title=None)
¶
Render a self-contained HTML diagnostic report for a calibration run.
Reuses the metrics already computed by
BaseCalibration.print_quality_report() — no new computation is
performed here, only presentation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
calibrator
|
A |
required | |
output_path
|
Optional[str]
|
If given, the HTML is also written to this path. |
None
|
title
|
Optional[str]
|
Optional report title. Defaults to the robot's class name. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The rendered HTML document as a string. |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If |
Source code in src/figaroh/tools/report.py
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | |
identification_report¶
HTML diagnostic report generation for dynamic identification results.
Renders the same statistics as
BaseIdentification.print_quality_report() (condition number, torque
residuals, base-parameter uncertainty, held-out validation, optional
physical-consistency / reconstruction status) as a self-contained,
shareable HTML page — the identification analogue of
tools/report.py's calibration report.
Identification and calibration produce structurally different
diagnostics (a one-shot linear QR solve vs. an iterative nonlinear fit
with outlier removal), so this is a separate adapter rather than a
forced re-use of generate_calibration_report — see
docs/decisions/roadmap-mujoco-sysid-inspired-features.md (Feature
1b) for the comparison that motivated this split. Only the
domain-independent HTML/CSS layer is shared, via
tools/_report_common.py.
generate_identification_report(identifier, output_path=None, title=None)
¶
Render a self-contained HTML diagnostic report for an identification run.
Reuses the metrics already computed by
BaseIdentification.print_quality_report() — no new computation
is performed here, only presentation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
A |
required | |
output_path
|
Optional[str]
|
If given, the HTML is also written to this path. |
None
|
title
|
Optional[str]
|
Optional report title. Defaults to the robot's class name. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The rendered HTML document as a string. |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If |
Source code in src/figaroh/tools/identification_report.py
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 | |
compare_report¶
Static, offline, two-run comparison page (Feature 6, Phase C).
Unlike tools/report.py / tools/identification_report.py, this
module does not render a specific run's data at generation time. It
emits a self-contained HTML shell that a user opens directly in a
browser and then loads two *_verification.json files into (drag-and-
drop or a file picker) — the two exports produced by
BaseCalibration.export_verification_report() /
BaseIdentification.export_verification_report() (Step 2, extended in
Step 3 with the series/compat fields this page reads). Everything
happens client-side in JavaScript: no backend, no network request, no
change to either base class (see the roadmap's Step 5 "Files (modified):
none required").
Per D3 (roadmap), a compatibility check (same domain, same DOF/joint
names, same decimate setting for identification, comparable sample
counts) runs before anything is rendered; on mismatch the page blocks
the comparison with a clear message and offers an explicit "compare
anyway" override rather than silently overlaying incompatible runs.
generate_compare_page(output_path=None, title=None)
¶
Render the static, self-contained two-run comparison page.
Unlike the calibration/identification report generators, this takes
no run object — it emits a template that loads two
export_verification_report() JSON files client-side (drag-and-
drop or a file picker) and, after a mandatory compatibility check
(D3), renders a per-metric diff table and an overlaid before/after
series chart. No network request, no backend: the returned document
is fully self-contained and can be opened directly from disk.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
output_path
|
Optional[str]
|
If given, the HTML is also written to this path. |
None
|
title
|
Optional[str]
|
Optional page title. Defaults to a generic comparison title. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The rendered HTML document as a string. |
Source code in src/figaroh/tools/compare_report.py
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 | |
_report_common¶
Shared HTML/CSS primitives and the VerificationVerdict/ThresholdCheck
dataclasses used by both report generators.
Shared HTML/CSS primitives for the calibration and identification
diagnostic reports (tools/report.py and
tools/identification_report.py).
Calibration and identification produce structurally different diagnostics (iterative nonlinear fit with outlier removal vs. one-shot linear QR solve), so each gets its own report module — but both render as a self-contained HTML page with the same look, the same escaping rules, and the same confidence-tier vocabulary. This module holds only that shared, domain-independent layer.
ThresholdCheck(name, value, threshold, comparison, passed)
dataclass
¶
One metric checked against one threshold.
VerificationVerdict(passed, checks, metrics, insights=list(), metadata=dict(), series=dict(), compat=dict())
dataclass
¶
Machine-checkable pass/fail against a set of quality thresholds.
Produced by :func:evaluate_thresholds; insights/metadata/
series/compat are filled in by the caller
(BaseCalibration.verify() / BaseIdentification.verify())
after construction, since those are domain-specific / provenance
data rather than threshold arithmetic. metadata holds the full
nested provenance record from
:func:figaroh.tools.provenance.collect_run_provenance (run id,
asset identity, nominal model, config, software, data, timestamps).
evaluate_thresholds(metrics, thresholds)
¶
Check each metric named in thresholds against its threshold.
A threshold whose metric is missing from metrics (or is NaN) —
e.g. a validation-set threshold when no validation data was
provided — is silently skipped rather than counted as a failure;
verify() callers can note the gap via insights instead. An
empty check list (nothing was evaluable) counts as passed: there is
nothing to fail on.
Source code in src/figaroh/tools/_report_common.py
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | |
build_provenance_metadata(config_file_path, robot_name)
¶
Git commit, config file hash, timestamp, robot name — for a
:class:VerificationVerdict's metadata field.
Source code in src/figaroh/tools/_report_common.py
539 540 541 542 543 544 545 546 547 548 549 | |
provenance¶
Run provenance metadata — git commit, config hash, timestamps, robot/asset identity — attached to every calibration/identification run.
Run provenance capture — the single source of truth for "what produced this fit": the nominal reference model, the exact config used, software versions, input data files, and (if configured) the physical asset identity. Consumed identically by the terminal report, the HTML report, the JSON verification verdict, and the run archive, so the four can never disagree about what produced a given result.
Reuses :func:figaroh.tools._report_common._git_commit_hash and
:func:figaroh.tools._report_common._config_file_sha256 rather than
duplicating hashing logic.
collect_run_provenance(obj, task)
¶
Build the full provenance record for one V&V run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
Any
|
A |
required |
task
|
str
|
|
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
A JSON-serializable nested dict (see module docstring for the |
Dict[str, Any]
|
four consumers). Never raises: every sub-lookup is best-effort. |
Source code in src/figaroh/tools/provenance.py
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | |
run_archive¶
Writes each run to a timestamped results/runs/<robot>/<task>/<timestamp>/
directory (config snapshot, provenance JSON, report) instead of overwriting
a single results/ path.
Longitudinal run archive — a fleet's V&V history.
export_html_report()/export_verification_report() write to fixed
filenames that get overwritten by the next run, so a robot calibrated
monthly only ever has its most recent report on disk. :func:archive_run
instead writes each run to its own immutable, timestamped directory under
results/runs/{asset_id}/{task}/{timestamp}_{git_short}/ and appends a
one-line summary to results/runs/index.jsonl — a zero-dependency,
git-diffable, jq-able index of every run ever performed, per asset.
Call after :meth:~figaroh.identification.base_identification.BaseIdentification.solve
(and, typically, after export_html_report()/export_verification_report()
so their output gets copied in alongside the raw provenance/parameters).
compute_run_dir(obj, root='results/runs')
¶
Compute this run's dedicated directory path from provenance.
Deterministic for a given obj._run_provenance — safe to call more
than once (e.g. once for the HTML report's output_path, again for the
verdict's) and get back the identical path, since the timestamp is
read from the already-fixed provenance record, never re-derived from
wall clock.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
Any
|
A |
required |
root
|
str
|
Archive root directory (created if missing). |
'results/runs'
|
Returns:
| Name | Type | Description |
|---|---|---|
Path |
Path
|
The directory for this run, created if missing. |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If called before :meth: |
Source code in src/figaroh/tools/run_archive.py
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | |
archive_run(obj, run_dir)
¶
Finalize archiving into an already-computed run_dir.
Writes provenance.json / config.snapshot.yaml / parameters.csv, and appends the index.jsonl entry (reading run_dir/verdict.json for pass/metrics if the caller already wrote one there).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
Any
|
A |
required |
run_dir
|
Path
|
The directory for this run (typically obtained via
:func: |
required |
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
The path to this run's archive directory (echoed back for convenience). |
Raises:
| Type | Description |
|---|---|
AttributeError
|
If called before :meth: |
Source code in src/figaroh/tools/run_archive.py
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | |
Linear Solver¶
Multivariate linear solver for robot parameter identification.
This module provides advanced linear regression solvers optimized for overdetermined systems (thin, tall matrices) with support for: - Multiple regularization methods (Ridge, Lasso, Elastic Net, Tikhonov) - Linear equality and inequality constraints - Robust regression methods - Physical parameter bounds - Iterative refinement for improved accuracy
LinearSolver(method='lstsq', regularization=None, alpha=0.0, l1_ratio=0.5, weights=None, constraints=None, bounds=None, max_iter=1000, tol=1e-06, verbose=False)
¶
Advanced linear solver for overdetermined systems: Ax = b
Optimized for robot identification where A is typically: - Dense (not sparse) - Large (many samples) - Thin (more rows than columns, overdetermined)
Attributes:
| Name | Type | Description |
|---|---|---|
method |
str
|
Solving method to use |
regularization |
str
|
Type of regularization |
alpha |
float
|
Regularization strength |
constraints |
dict
|
Linear constraints on solution |
bounds |
tuple
|
Box constraints on variables |
solver_info |
dict
|
Information about the solution |
Initialize linear solver.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
Solving method (see METHODS) |
'lstsq'
|
regularization
|
str
|
'l1', 'l2', 'elastic_net', or None |
None
|
alpha
|
float
|
Regularization strength (>=0) |
0.0
|
l1_ratio
|
float
|
Elastic net mixing (0=Ridge, 1=Lasso) |
0.5
|
weights
|
ndarray
|
Sample weights for weighted least squares |
None
|
constraints
|
dict
|
Linear constraints: - 'A_eq': Equality constraint matrix (A_eq @ x = b_eq) - 'b_eq': Equality constraint vector - 'A_ineq': Inequality constraint matrix (A_ineq @ x <= b_ineq) - 'b_ineq': Inequality constraint vector |
None
|
bounds
|
tuple
|
Box constraints (lower, upper) for each variable |
None
|
max_iter
|
int
|
Maximum iterations for iterative methods |
1000
|
tol
|
float
|
Convergence tolerance |
1e-06
|
verbose
|
bool
|
Print solver information |
False
|
Source code in src/figaroh/tools/solver.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | |
solve(A, b)
¶
Solve the linear system Ax = b.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
A
|
ndarray
|
Design matrix (n_samples, n_features) |
required |
b
|
ndarray
|
Target vector (n_samples,) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ndarray |
Solution vector x (n_features,) |
Raises:
| Type | Description |
|---|---|
ValueError
|
If inputs are incompatible |
LinAlgError
|
If solution fails |
Source code in src/figaroh/tools/solver.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
solve_linear_system(A, b, method='lstsq', **kwargs)
¶
Convenience function to solve linear system Ax = b.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
A
|
ndarray
|
Design matrix (n_samples, n_features) |
required |
b
|
ndarray
|
Target vector (n_samples,) |
required |
method
|
str
|
Solving method |
'lstsq'
|
**kwargs
|
Additional arguments for LinearSolver |
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
(solution x, solver_info dict) |
Example
A = np.random.randn(1000, 50) # 1000 samples, 50 features b = np.random.randn(1000) x, info = solve_linear_system(A, b, method='ridge', alpha=0.1)
Source code in src/figaroh/tools/solver.py
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 | |
The linear solver provides comprehensive solving methods for robot parameter identification:
- Basic methods: lstsq, QR decomposition, SVD
- Regularized methods: Ridge (L2), Lasso (L1), Elastic Net, Tikhonov
- Advanced methods: Constrained optimization, robust regression, weighted least squares
- Constraint support: Box constraints, linear equality/inequality constraints
- Quality metrics: RMSE, R², condition number, residual analysis
Robot Management¶
Robot(robot_urdf, package_dirs, isFext=False, freeflyer_ori=None, freeflyer_limits=None)
¶
Bases: RobotWrapper
Enhanced Robot class with advanced free-flyer support and visualization utilities.
This class extends RobotWrapper with: - Sophisticated free-flyer configuration and limits - Integrated visualization support - Convenient API for common robot operations - Enhanced error handling and validation
Initialize enhanced robot model from URDF.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
robot_urdf
|
str
|
Path to URDF file |
required |
package_dirs
|
str
|
Package directories for mesh files |
required |
isFext
|
bool
|
Whether to add floating base joint |
False
|
freeflyer_ori
|
Optional[ndarray]
|
Optional 3x3 rotation matrix for floating base orientation |
None
|
freeflyer_limits
|
Optional[Tuple[float, float]]
|
Optional custom limits for free-flyer (default: (-1, 1)) |
None
|
Source code in src/figaroh/tools/robot.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | |
num_joints
property
¶
Number of actuated joints (excluding universe and free-flyer).
actuated_joint_names
property
¶
Names of actuated joints only.
freeflyer_limits
property
¶
Current free-flyer position limits.
backend
property
¶
Lazily-created DynamicsBackend wrapping this robot's model.
Returns a PinocchioBackend that shares the same pin.Model and pin.Data as this Robot instance — no URDF re-loading, no model duplication. Used by RegressorBuilder and other tools to route dynamics computation through the backend abstraction.
Returns:
| Type | Description |
|---|---|
|
PinocchioBackend instance |
update_freeflyer_limits(limits)
¶
Update free-flyer limits dynamically.
Source code in src/figaroh/tools/robot.py
100 101 102 103 104 105 106 | |
display_q0(visualizer_type=None, q=None)
¶
Enhanced visualization with configuration options.
Source code in src/figaroh/tools/robot.py
108 109 110 111 112 113 114 115 116 117 118 119 120 121 | |
get_configuration_bounds()
¶
Get configuration space bounds with proper handling of quaternions.
Source code in src/figaroh/tools/robot.py
177 178 179 180 181 182 183 184 185 186 187 188 189 | |
validate_configuration(q)
¶
Validate if configuration is within bounds and constraints.
Source code in src/figaroh/tools/robot.py
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | |
__repr__()
¶
Enhanced string representation.
Source code in src/figaroh/tools/robot.py
210 211 212 213 214 215 216 217 | |
create_robot(robot_urdf, package_dirs=None, loader='figaroh', enhanced_features=True, **kwargs)
¶
Factory function to create robots with optional enhanced features.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
robot_urdf
|
str
|
Robot identifier or URDF path |
required |
package_dirs
|
Optional[str]
|
Package directories |
None
|
loader
|
str
|
Loader type from load_robot.py |
'figaroh'
|
enhanced_features
|
bool
|
If True, returns Robot class with enhanced features |
True
|
**kwargs
|
Additional arguments for loaders |
{}
|
Returns:
| Type | Description |
|---|---|
Union[Robot, RobotWrapper]
|
Robot instance (if enhanced_features=True) or RobotWrapper |
Source code in src/figaroh/tools/robot.py
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | |
load_robot(robot_urdf, package_dirs=None, isFext=False, load_by_urdf=True, robot_pkg=None, loader='figaroh', **kwargs)
¶
Load robot model from various sources with multiple loader options.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
robot_urdf
|
str
|
Path to URDF file or robot name for robot_description |
required |
package_dirs
|
Optional[str]
|
Package directories for mesh files |
None
|
isFext
|
bool
|
Whether to add floating base joint |
False
|
load_by_urdf
|
bool
|
Whether to load from URDF file (vs ROS param server) |
True
|
robot_pkg
|
Optional[str]
|
Name of robot package for path resolution |
None
|
loader
|
str
|
Loader type - "figaroh", "robot_description", "yourdfpy" |
'figaroh'
|
**kwargs
|
Additional arguments passed to the specific loader |
{}
|
Returns:
| Type | Description |
|---|---|
Union[Robot, RobotWrapper, Any]
|
Robot object based on loader type: |
Union[Robot, RobotWrapper, Any]
|
|
Union[Robot, RobotWrapper, Any]
|
|
Union[Robot, RobotWrapper, Any]
|
|
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If URDF file not found |
ImportError
|
If required packages not available |
ValueError
|
If the specified loader is not supported |
For loading by URDF, robot_urdf and package_dirs can be different.
1/ If package_dirs is not provided directly, robot_pkg is used to look up the package directory. 2/ If no mesh files, package_dirs and robot_pkg are not used. 3/ If load_by_urdf is False, the robot is loaded from the ROS parameter server. 4/ For robot_description loader, robot_urdf should be the robot name. 5/ For yourdfpy loader, returns URDF object suitable for viser visualization.
Source code in src/figaroh/tools/load_robot.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | |
get_available_loaders()
¶
Get information about available robot loaders.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict
|
Information about each loader and its availability |
Source code in src/figaroh/tools/load_robot.py
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | |
list_available_robots(loader='robot_description')
¶
List available robot descriptions for a given loader.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
loader
|
str
|
Loader type to check for available robots |
'robot_description'
|
Returns:
| Name | Type | Description |
|---|---|---|
list |
list
|
Available robot names |
Source code in src/figaroh/tools/load_robot.py
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | |
Regressor Builder¶
Regressor matrix computation utilities for robot dynamic identification.
RegressorConfig(has_friction=False, has_actuator_inertia=False, has_joint_offset=False, is_joint_torques=True, is_external_wrench=False, force_torque=None, additional_columns=0)
dataclass
¶
Configuration for regressor computation.
RegressorBuilder(robot, config=None)
¶
Enhanced regressor builder with better organization.
Source code in src/figaroh/tools/regressor.py
25 26 27 28 29 30 31 | |
build_basic_regressor(q, v, a, identif_config=None)
¶
Build basic regressor matrix.
Source code in src/figaroh/tools/regressor.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
build_regressor_basic(robot, q, v, a, identif_config, tau=None)
¶
Legacy function for backward compatibility.
Source code in src/figaroh/tools/regressor.py
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | |
eliminate_non_dynaffect(W, params_std, tol_e=1e-06)
¶
Eliminate columns with small L2 norm.
Source code in src/figaroh/tools/regressor.py
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | |
get_index_eliminate(W, params_std, tol_e=1e-06)
¶
Get indices of columns to eliminate based on tolerance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
W
|
Joint torque regressor matrix |
required | |
params_std
|
Standard parameters dictionary |
required | |
tol_e
|
Tolerance value |
1e-06
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
|
Source code in src/figaroh/tools/regressor.py
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
build_regressor_reduced(W, idx_e)
¶
Build reduced regressor matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
W
|
Input regressor matrix |
required | |
idx_e
|
Indices of columns to eliminate |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ndarray |
Reduced regressor matrix |
Source code in src/figaroh/tools/regressor.py
292 293 294 295 296 297 298 299 300 301 302 303 | |
build_total_regressor_current(W_b_u, W_b_l, W_l, I_u, I_l, param_standard_l, identif_config)
¶
Build regressor for total least squares with current measurements.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
W_b_u
|
Base regressor for unloaded case |
required | |
W_b_l
|
Base regressor for loaded case |
required | |
W_l
|
Full regressor for loaded case |
required | |
I_u
|
Joint currents in unloaded case |
required | |
I_l
|
Joint currents in loaded case |
required | |
param_standard_l
|
Standard parameters in loaded case |
required | |
identif_config
|
Dictionary of settings |
required |
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
|
Source code in src/figaroh/tools/regressor.py
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | |
build_total_regressor_wrench(W_b_u, W_b_l, W_l, tau_u, tau_l, param_standard_l, param)
¶
Build regressor for total least squares with external wrench measurements.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
W_b_u
|
Base regressor for unloaded case |
required | |
W_b_l
|
Base regressor for loaded case |
required | |
W_l
|
Full regressor for loaded case |
required | |
tau_u
|
External wrench in unloaded case |
required | |
tau_l
|
External wrench in loaded case |
required | |
param_standard_l
|
Standard parameters in loaded case |
required | |
param
|
Dictionary of settings |
required |
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
|
Source code in src/figaroh/tools/regressor.py
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | |
Robot Visualization¶
Enhanced robot visualization utilities with better performance.
VisualizationConfig(com_color=None, axes_color=None, force_color=None, bbox_color=None, scale_factor=1.0)
dataclass
¶
Configuration for robot visualization.
RobotVisualizer(model, data, viz, config=None)
¶
Enhanced robot visualizer with better organization.
Source code in src/figaroh/tools/robotvisualization.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
update_kinematics(q)
¶
Update robot kinematics efficiently.
Source code in src/figaroh/tools/robotvisualization.py
64 65 66 67 68 69 | |
display_com(q, frame_indices)
¶
Display center of mass positions with better naming.
Source code in src/figaroh/tools/robotvisualization.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
display_axes(q, axis_length=0.15, axis_radius=0.01)
¶
Display coordinate axes for joints.
Source code in src/figaroh/tools/robotvisualization.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | |
display_force(force, placement, scale=0.001, name='force_vector')
¶
Display force vector with better scaling.
Source code in src/figaroh/tools/robotvisualization.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |
display_bounding_boxes(q, com_min, com_max, frame_indices)
¶
Display COM bounding boxes for optimization visualization.
Source code in src/figaroh/tools/robotvisualization.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
display_joints(q)
¶
Display joint frames efficiently.
Source code in src/figaroh/tools/robotvisualization.py
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | |
place(viz, name, M)
¶
Legacy placement function.
Source code in src/figaroh/tools/robotvisualization.py
229 230 231 | |
display_COM(model, data, viz, q, IDX)
¶
Legacy COM display function.
Source code in src/figaroh/tools/robotvisualization.py
234 235 236 237 238 239 | |
display_axes(model, data, viz, q)
¶
Legacy axes display function.
Source code in src/figaroh/tools/robotvisualization.py
242 243 244 245 | |
rotation_matrix_from_vectors(vec1, vec2)
¶
Legacy rotation function.
Source code in src/figaroh/tools/robotvisualization.py
248 249 250 251 | |
display_force(viz, phi, M_se3)
¶
Legacy force display function.
Source code in src/figaroh/tools/robotvisualization.py
254 255 256 257 | |
display_bounding_boxes(viz, model, data, q, COM_min, COM_max, IDX)
¶
Legacy bounding box display function.
Source code in src/figaroh/tools/robotvisualization.py
260 261 262 263 264 265 266 267 268 269 270 271 | |
display_joints(viz, model, data, q)
¶
Legacy joint display function.
Source code in src/figaroh/tools/robotvisualization.py
274 275 276 277 | |
Robot Collisions¶
Enhanced collision detection and visualization utilities.
CollisionManager(robot, geom_model=None, geom_data=None, viz=None)
¶
Enhanced collision detection with better performance and safety.
Source code in src/figaroh/tools/robotcollisions.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | |
setup_collision_pairs(srdf_model_path=None)
¶
Setup collision pairs with optional SRDF filtering.
Source code in src/figaroh/tools/robotcollisions.py
50 51 52 53 54 55 56 57 58 59 60 | |
check_collisions(q, update_geometry=True)
¶
Check for collisions at given configuration.
Source code in src/figaroh/tools/robotcollisions.py
62 63 64 65 66 67 68 69 70 71 72 73 74 | |
get_collision_details()
¶
Get detailed collision information.
Source code in src/figaroh/tools/robotcollisions.py
76 77 78 79 80 81 82 83 84 85 | |
get_collision_distances(collision_details=None)
¶
Get minimum distances for collision pairs.
Source code in src/figaroh/tools/robotcollisions.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | |
get_all_distances()
¶
Get distances for all collision pairs.
Source code in src/figaroh/tools/robotcollisions.py
107 108 109 110 111 112 113 114 115 116 117 | |
print_collision_pairs()
¶
Print all collision pair information.
Source code in src/figaroh/tools/robotcollisions.py
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
visualize_collisions(collision_details=None)
¶
Visualize collision contacts with enhanced display.
Source code in src/figaroh/tools/robotcollisions.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |
CollisionWrapper(robot, geom_model=None, geom_data=None, viz=None)
¶
Bases: CollisionManager
Legacy wrapper for backward compatibility.
Source code in src/figaroh/tools/robotcollisions.py
201 202 203 204 205 206 207 208 | |
add_collisions()
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
210 211 212 213 | |
remove_collisions(srdf_model_path)
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
215 216 217 218 | |
computeCollisions(q, geom_data=None)
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
220 221 222 223 224 | |
getCollisionList()
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
226 227 228 | |
getCollisionDistances(collisions=None)
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
230 231 232 | |
getDistances()
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
234 235 236 | |
getAllpairs()
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
238 239 240 | |
check_collision(q)
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
242 243 244 | |
displayCollisions(collisions=None)
¶
Legacy method.
Source code in src/figaroh/tools/robotcollisions.py
246 247 248 | |
QR Decomposition¶
QR decomposition utilities for robot parameter identification.
This module provides QR-based helpers to:
- identify a set of base parameters (a full-rank subset) from a rank-deficient regressor
- express those base parameters as linear combinations of the original/full parameters
In particular, both QR workflows compute a matrix $M$ such that:
$$ \phi_{base} = M\,\theta_{full} $$
where columns of $M$ correspond to the remaining parameter vector ordered as
params_r.
Note
In FIGAROH, params_r typically refers to the list of
standard parameter
names remaining after column elimination (e.g., after removing all-zero
regressor columns). It is not necessarily the complete set of standard
parameters (which is often available as a dictionary like params_std).
Use :meth:QRDecomposer.expand_mapping_matrix_to_full if you need an "M"
defined over a full, canonical parameter ordering.
The resulting row ordering matches the returned base-parameter expressions.
QRResult(rank, base_indices, pivot_order, W_b, beta, M, base_param_expressions, phi_b, phi_b_nom, method, diag_R, cond_R1)
dataclass
¶
Structured output of a QR base-parameter decomposition.
All numerical fields are stored at full float64 precision. Rounding for display is done only at the expression-building stage.
Attributes:
| Name | Type | Description |
|---|---|---|
rank |
int
|
Identified numerical rank of the regressor. |
base_indices |
List[int]
|
Column indices into |
pivot_order |
Optional[List[int]]
|
Full pivot permutation used by the pivoting path
( |
W_b |
ndarray
|
Base regressor matrix, shape |
beta |
ndarray
|
Dependency coefficient matrix, shape |
M |
ndarray
|
Base mapping matrix satisfying |
base_param_expressions |
List[str]
|
Human-readable expression strings, length
|
phi_b |
Optional[ndarray]
|
Identified base-parameter values, shape |
phi_b_nom |
Optional[ndarray]
|
Nominal base-parameter values from |
method |
str
|
|
diag_R |
ndarray
|
Absolute diagonal of |
cond_R1 |
float
|
Condition number of the |
QRDecomposer(tolerance=TOL_QR, beta_tolerance=TOL_BETA, relative_tolerance=None)
¶
Enhanced QR decomposition handler for robot parameter identification.
Source code in src/figaroh/tools/qrdecomposition.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | |
get_M()
¶
Return the last computed base mapping matrix M (or None).
Source code in src/figaroh/tools/qrdecomposition.py
113 114 115 | |
get_M_labels()
¶
Return (base_param_expr, params_r) labels for the last stored M.
Source code in src/figaroh/tools/qrdecomposition.py
117 118 119 | |
get_base_indices()
¶
Return the base-column indices (into params_r) from the last
double_decomposition() call, or None if not yet run.
These indices select the columns of a reduced regressor (built
with the same params_r ordering) that reproduce base-parameter
predictions: tau ≈ W_reduced[:, base_indices] @ phi_base. Since
the base/dependent column split reflects the robot's kinematic
structure rather than a specific trajectory, this selection is
valid for any regressor built with the same params_r ordering
— e.g. to evaluate an identified model on held-out validation data.
Source code in src/figaroh/tools/qrdecomposition.py
121 122 123 124 125 126 127 128 129 130 131 132 133 | |
get_diagnostics()
¶
Return stability diagnostics from the last decomposition.
Keys
rank: identified rank.
diag_R: absolute diagonal of R at the factorisation point.
cond_R1: condition number of the R[:rank, :rank] block.
method: "pivoting" or "double".
Source code in src/figaroh/tools/qrdecomposition.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
decompose(W_e, params_r, tau=None, params_std=None, method='double')
¶
Unified entry point that returns a structured :class:QRResult.
This is the preferred API for new code. It delegates to either
:meth:decompose_with_pivoting (method="pivoting") or
:meth:double_decomposition (method="double"), but always
returns a :class:QRResult regardless of path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
W_e
|
ndarray
|
Full regressor matrix, shape |
required |
params_r
|
List[str]
|
Remaining parameter names (columns of |
required |
tau
|
Optional[ndarray]
|
Torque/effort vector, shape |
None
|
params_std
|
Optional[Dict[str, float]]
|
Optional prior parameter dict used to compute
|
None
|
method
|
str
|
|
'double'
|
Returns:
| Type | Description |
|---|---|
QRResult
|
A fully-populated :class: |
Source code in src/figaroh/tools/qrdecomposition.py
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | |
decompose_with_pivoting(tau, W_e, params_r)
¶
Perform QR decomposition with column pivoting.
This identifies an effective rank, computes base parameters, and
returns a base regressor W_b along with a dict mapping
base-parameter expressions to values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tau
|
ndarray
|
Torque/effort vector, shape (m,). |
required |
W_e
|
ndarray
|
Full regressor matrix, shape (m, n). |
required |
params_r
|
List[str]
|
Remaining parameter names (columns of W_e), length n. |
required |
Returns:
| Type | Description |
|---|---|
Tuple[ndarray, Dict[str, float]]
|
(W_b, base_parameters) where: - W_b has shape (m, r) with r = rank(W_e) - base_parameters maps expression strings (length r) to values. |
Source code in src/figaroh/tools/qrdecomposition.py
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | |
double_decomposition(tau, W_e, params_r, params_std=None)
¶
Perform the "double QR" workflow for base-parameter identification.
This follows a two-stage procedure
1) Identify an independent/base subset via QR on W_e.
2) Regroup columns and run a second QR to compute linear
dependencies.
The output base parameters are returned as expression strings built from the original parameter names.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tau
|
ndarray
|
Torque/effort vector, shape (m,). |
required |
W_e
|
ndarray
|
Full regressor matrix, shape (m, n). |
required |
params_r
|
List[str]
|
Remaining parameter names (columns of W_e), length n. |
required |
params_std
|
Optional[Dict[str, float]]
|
Optional mapping from full parameter name -> value. If provided, also returns the standard-parameter values of the base expressions. Returns: If params_std is None: (W_b, base_parameters, params_base_expr, phi_b) else: (W_b, base_parameters, params_base_expr, phi_b, phi_b_nom) |
None
|
Where
|
|
required |
Source code in src/figaroh/tools/qrdecomposition.py
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | |
compute_nominal_base_parameters(base_params, regroup_params, beta, params_std)
¶
Compute numerical values of base parameters from priors.
This computes the numerical value of each base expression using:
phi_std[i] = params_std[base_param]
+ sum_j beta[i, j] * params_std[dep_param]
Source code in src/figaroh/tools/qrdecomposition.py
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | |
get_base_mapping_matrix_pivoting(W_e, params_r)
¶
Return the base-parameter mapping matrix M for pivoting-QR.
The matrix M satisfies phi_base = M @ theta_r where
theta_r is ordered as params_r (the remaining parameters).
Row order matches the returned base-parameter expression strings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
W_e
|
ndarray
|
Full regressor matrix, shape (m, n). |
required |
params_r
|
List[str]
|
Remaining parameter names (columns of W_e), length n. |
required |
Returns:
| Type | Description |
|---|---|
Tuple[ndarray, List[str]]
|
(M, base_params_expr) - M has shape (r, n) where r is the identified rank - base_params_expr is a list[str] of length r |
Source code in src/figaroh/tools/qrdecomposition.py
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 | |
get_base_mapping_matrix_double(W_e, params_r)
¶
Return the base-parameter mapping matrix M for the double-QR workflow.
The returned M satisfies phi_base = M @ theta_r with columns
ordered as params_r (the remaining parameters).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
W_e
|
ndarray
|
Full regressor matrix, shape (m, n). |
required |
params_r
|
List[str]
|
Remaining parameter names (columns of W_e), length n. |
required |
Returns:
| Type | Description |
|---|---|
Tuple[ndarray, List[str], List[int], List[int]]
|
(M, base_params_expr, base_indices, regroup_indices) - M has shape (r, n) - base_params_expr is a list[str] of length r - base_indices and regroup_indices index into params_r |
Source code in src/figaroh/tools/qrdecomposition.py
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 | |
get_base_mapping_matrix(W_e, params_r, method='double')
¶
Convenience wrapper to retrieve the base mapping matrix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
W_e
|
ndarray
|
Full regressor matrix, shape (m, n). |
required |
params_r
|
List[str]
|
Remaining parameter names (columns of W_e), length n. |
required |
method
|
str
|
"double" (default) or "pivoting". |
'double'
|
Returns:
| Type | Description |
|---|---|
Tuple[ndarray, List[str]]
|
(M, base_params_expr) |
Raises:
| Type | Description |
|---|---|
ValueError
|
if method is not recognized. |
Source code in src/figaroh/tools/qrdecomposition.py
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 | |
expand_mapping_matrix_to_full(M, params_r, full_param_names)
staticmethod
¶
Expand an M defined on params_r into a full parameter ordering.
This is useful when the QR routines operate on a reduced set of parameters (after removing all-zero columns), but you want a mapping matrix defined over the complete standard-parameter list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
M
|
ndarray
|
Base mapping matrix for remaining parameters, shape (r, n_r). |
required |
params_r
|
List[str]
|
Remaining parameter names (columns of |
required |
full_param_names
|
List[str]
|
Full/canonical parameter ordering to expand into. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
M_full of shape (r, n_full) where columns not present in |
ndarray
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
if M has incompatible shape or if a name in params_r is missing from full_param_names. |
Source code in src/figaroh/tools/qrdecomposition.py
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 | |
QR_pivoting(tau, W_e, params_r, tol_qr=TOL_QR)
¶
Legacy QR pivoting function for backward compatibility.
Source code in src/figaroh/tools/qrdecomposition.py
773 774 775 776 777 778 779 780 781 | |
double_QR(tau, W_e, params_r, params_std=None, tol_qr=TOL_QR)
¶
Legacy double QR function for backward compatibility.
Source code in src/figaroh/tools/qrdecomposition.py
784 785 786 787 788 789 790 791 792 793 | |
get_baseParams(W_e, params_r, params_std=None, tol_qr=TOL_QR)
¶
Legacy function for getting base parameters.
Source code in src/figaroh/tools/qrdecomposition.py
796 797 798 799 800 801 802 803 804 805 806 807 | |
get_baseIndex(W_e, params_r, tol_qr=TOL_QR)
¶
Legacy function for getting base indices.
Source code in src/figaroh/tools/qrdecomposition.py
810 811 812 813 814 815 816 817 818 | |
build_baseRegressor(W_e, idx_base)
¶
Legacy function for building base regressor.
Source code in src/figaroh/tools/qrdecomposition.py
821 822 823 | |
cond_num(W_b, norm_type=None)
¶
Calculate condition number with various norms.
Source code in src/figaroh/tools/qrdecomposition.py
826 827 828 829 830 831 832 833 | |
Robot IPOPT¶
General IPOPT-based optimization framework for robotics applications.
This module provides a comprehensive, flexible framework for setting up and solving nonlinear optimization problems using IPOPT (Interior Point OPTimizer), with specialized support for robotics applications including trajectory optimization, parameter identification, and general robot optimization problems.
Key Features
- Unified IPOPT interface with robotics-specific configurations
- Automatic differentiation support for gradients and Jacobians
- Built-in problem validation and result analysis
- Specialized classes for trajectory optimization problems
- Factory functions for rapid problem setup
- Comprehensive logging and iteration tracking
Main Classes
- IPOPTConfig: Configuration management for IPOPT solver settings
- BaseOptimizationProblem: Abstract base class for optimization problems
- RobotIPOPTSolver: High-level solver interface with result analysis
- TrajectoryOptimizationProblem: Specialized class for trajectory problems
Usage Examples
Basic trajectory optimization:
from figaroh.tools.robotipopt import IPOPTConfig, RobotIPOPTSolver
# Create custom problem class
class MyProblem(BaseOptimizationProblem):
def objective(self, x):
return np.sum(x**2) # Minimize sum of squares
def constraints(self, x):
return np.array([x[0] + x[1] - 1.0]) # x[0] + x[1] = 1
# ... implement other required methods
# Solve the problem
problem = MyProblem()
config = IPOPTConfig.for_trajectory_optimization()
solver = RobotIPOPTSolver(problem, config)
success, results = solver.solve()
Using the factory function:
from figaroh.tools.robotipopt import create_trajectory_solver
def my_objective(x):
return np.sum(x**2)
def my_constraints(x):
return np.array([x[0] + x[1] - 1.0])
def get_bounds():
var_bounds = ([-10, -10], [10, 10])
cons_bounds = ([0], [0])
return var_bounds, cons_bounds
def initial_guess():
return [0.5, 0.5]
solver = create_trajectory_solver(
my_objective, my_constraints, get_bounds, initial_guess
)
success, results = solver.solve()
Complete robotics example:
import numpy as np
from figaroh.tools.robotipopt import (
BaseOptimizationProblem, RobotIPOPTSolver, IPOPTConfig
)
class RobotTrajectoryProblem(BaseOptimizationProblem):
def __init__(self, robot, waypoints, active_joints):
super().__init__("RobotTrajectory")
self.robot = robot
self.waypoints = waypoints
self.active_joints = active_joints
self.n_joints = len(active_joints)
self.n_waypoints = len(waypoints)
self.n_vars = self.n_joints * self.n_waypoints
def get_variable_bounds(self):
# Joint limits for all waypoints
lb = [-np.pi] * self.n_vars
ub = [np.pi] * self.n_vars
return lb, ub
def get_constraint_bounds(self):
# Boundary conditions (start/end positions)
n_constraints = 2 * self.n_joints
return [0] * n_constraints, [0] * n_constraints
def get_initial_guess(self):
# Linear interpolation between start and end
return np.linspace(
self.waypoints[0], self.waypoints[-1],
self.n_waypoints * self.n_joints
)
def objective(self, x):
# Minimize trajectory smoothness (squared accelerations)
q = x.reshape(self.n_waypoints, self.n_joints)
acc = np.diff(q, n=2, axis=0) # Second differences
return np.sum(acc**2)
def constraints(self, x):
# Enforce boundary conditions
q = x.reshape(self.n_waypoints, self.n_joints)
constraints = np.concatenate([
q[0] - self.waypoints[0], # Start position
q[-1] - self.waypoints[-1] # End position
])
return constraints
# Usage
problem = RobotTrajectoryProblem(robot, waypoints, active_joints)
config = IPOPTConfig.for_trajectory_optimization()
solver = RobotIPOPTSolver(problem, config)
if solver.validate_problem_setup():
success, results = solver.solve()
if success:
optimal_trajectory = results['x_opt'].reshape(
problem.n_waypoints, problem.n_joints
)
print("Optimization successful!")
print(f"Final objective: {results['obj_val']:.6f}")
print(solver.get_solution_summary())
Dependencies
- cyipopt: Python interface to IPOPT solver
- numpy: Numerical computations
- numdifftools: Automatic differentiation (fallback)
References
- IPOPT documentation: https://coin-or.github.io/Ipopt/
- cyipopt: https://github.com/mechmotum/cyipopt
IPOPTConfig(tolerance=1e-06, acceptable_tolerance=0.0001, max_iterations=3000, max_cpu_time=1000000.0, print_level=5, output_file=None, hessian_approximation='limited-memory', warm_start=True, check_derivatives=True, linear_solver='mumps', custom_options=dict())
dataclass
¶
Configuration parameters for IPOPT solver.
This class provides a convenient way to set IPOPT solver options with sensible defaults for robotics applications. It includes predefined configurations for common robotics optimization scenarios.
The configuration is designed to work well with: - Trajectory optimization problems (smooth, continuous trajectories) - Parameter identification (high-precision parameter estimation) - General robotics optimization (balanced performance/accuracy)
Attributes:
| Name | Type | Description |
|---|---|---|
tolerance |
float
|
Primary convergence tolerance for optimization. Default: 1e-6. Smaller values increase precision but runtime. |
acceptable_tolerance |
float
|
Fallback tolerance if primary fails. Default: 1e-4. Used when primary tolerance cannot be achieved. |
max_iterations |
int
|
Maximum number of optimization iterations. Default: 3000. Increase for complex problems. |
max_cpu_time |
float
|
Maximum CPU time in seconds. Default: 1e6. Prevents infinite loops in difficult problems. |
print_level |
int
|
IPOPT output verbosity (0-12). Default: 5. Higher values provide more detailed output. |
output_file |
Optional[str]
|
File to save IPOPT output. Default: None. Useful for debugging and analysis. |
hessian_approximation |
str
|
Hessian computation method. Default: "limited-memory". Options: "exact", "limited-memory". |
warm_start |
bool
|
Use previous solution as starting point. Default: True. Speeds up subsequent optimizations. |
check_derivatives |
bool
|
Enable derivative checking. Default: True. Helps detect implementation errors. |
linear_solver |
str
|
Linear algebra solver backend. Default: "mumps". Options: "mumps", "ma27", "ma57", "ma77". |
custom_options |
Dict[str, Any]
|
Additional IPOPT options. Default: {}. For advanced users to set specialized options. |
Examples:
Basic usage:
# Use default configuration
config = IPOPTConfig()
# Customize specific parameters
config = IPOPTConfig(
tolerance=1e-8,
max_iterations=5000,
print_level=3
)
Predefined configurations:
# For trajectory optimization (speed-focused)
config = IPOPTConfig.for_trajectory_optimization()
# For parameter identification (precision-focused)
config = IPOPTConfig.for_parameter_identification()
Custom IPOPT options:
config = IPOPTConfig(
custom_options={
"mu_strategy": "adaptive",
"nlp_scaling_method": "gradient-based"
}
)
Note
All string options in custom_options should be provided as strings. They will be automatically encoded to bytes for IPOPT compatibility.
to_ipopt_options()
¶
Convert configuration to IPOPT option dictionary.
Transforms the configuration parameters into the format expected by IPOPT, including proper encoding of string options to bytes.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dict[str, Any]: Dictionary of IPOPT options ready for use with cyipopt.Problem.add_option() |
Note
All string values are automatically encoded to bytes as required by the IPOPT C interface via cyipopt.
Source code in src/figaroh/tools/robotipopt.py
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 | |
for_trajectory_optimization()
classmethod
¶
Create configuration optimized for trajectory optimization problems.
This configuration prioritizes convergence speed over extreme precision, making it suitable for real-time or interactive trajectory planning. Uses adaptive barrier parameter strategy for better convergence.
Returns:
| Name | Type | Description |
|---|---|---|
IPOPTConfig |
IPOPTConfig
|
Optimized configuration for trajectory problems. |
Source code in src/figaroh/tools/robotipopt.py
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | |
for_parameter_identification()
classmethod
¶
Create configuration optimized for parameter identification problems.
This configuration prioritizes high precision over speed, making it suitable for accurate parameter estimation in robotics applications. Uses exact Hessian computation and monotone barrier strategy for numerical stability.
Returns:
| Name | Type | Description |
|---|---|---|
IPOPTConfig |
IPOPTConfig
|
Optimized configuration for parameter identification. |
Source code in src/figaroh/tools/robotipopt.py
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | |
BaseOptimizationProblem(name='OptimizationProblem')
¶
Bases: ABC
Abstract base class for IPOPT optimization problems.
This class defines the interface that all IPOPT problems must implement. Subclasses should implement the objective function, constraints, and their derivatives (or use automatic differentiation).
The class provides default implementations for gradient and Jacobian computation using automatic differentiation via numdifftools. For better performance, subclasses can override these methods with analytical derivatives.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Human-readable name for the optimization problem. |
logger |
Logger
|
Logger instance for this problem. |
iteration_data |
Dict
|
Storage for tracking optimization iterations. |
callback_data |
Dict
|
Storage for passing data between methods. |
Required Methods (must be implemented by subclasses): - get_variable_bounds(): Return variable bounds as (lower, upper) - get_constraint_bounds(): Return constraint bounds as (lower, upper) - get_initial_guess(): Return initial guess for optimization variables - objective(x): Evaluate objective function at point x - constraints(x): Evaluate constraint functions at point x
Optional Methods (have default implementations): - gradient(x): Compute objective gradient (uses auto-diff by default) - jacobian(x): Compute constraint Jacobian (uses auto-diff by default) - hessian(x, lagrange, obj_factor): Compute Hessian (IPOPT approx by default) - intermediate(...): Callback for iteration tracking
Examples:
Basic implementation:
class QuadraticProblem(BaseOptimizationProblem):
def get_variable_bounds(self):
return ([-10, -10], [10, 10])
def get_constraint_bounds(self):
return ([0], [0]) # Equality constraint
def get_initial_guess(self):
return [1.0, 1.0]
def objective(self, x):
return x[0]**2 + x[1]**2 # Minimize sum of squares
def constraints(self, x):
return np.array([x[0] + x[1] - 1.0]) # x[0] + x[1] = 1
With custom derivatives:
class CustomProblem(BaseOptimizationProblem):
# ... implement required methods ...
def gradient(self, x):
# Custom analytical gradient
return np.array([2*x[0], 2*x[1]])
def jacobian(self, x):
# Custom analytical Jacobian
return np.array([[1.0, 1.0]])
Initialize the optimization problem.
Source code in src/figaroh/tools/robotipopt.py
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 | |
get_variable_bounds()
abstractmethod
¶
Get bounds for optimization variables.
Returns:
| Type | Description |
|---|---|
Tuple[List[float], List[float]]
|
Tuple of (lower_bounds, upper_bounds) |
Source code in src/figaroh/tools/robotipopt.py
457 458 459 460 461 462 463 464 465 | |
get_constraint_bounds()
abstractmethod
¶
Get bounds for constraints.
Returns:
| Type | Description |
|---|---|
Tuple[List[float], List[float]]
|
Tuple of (constraint_lower_bounds, constraint_upper_bounds) |
Source code in src/figaroh/tools/robotipopt.py
467 468 469 470 471 472 473 474 475 | |
get_initial_guess()
abstractmethod
¶
Get initial guess for optimization variables.
Returns:
| Type | Description |
|---|---|
List[float]
|
Initial guess as list of floats |
Source code in src/figaroh/tools/robotipopt.py
477 478 479 480 481 482 483 484 485 | |
objective(x)
abstractmethod
¶
Evaluate objective function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Optimization variables |
required |
Returns:
| Type | Description |
|---|---|
float
|
Objective function value |
Source code in src/figaroh/tools/robotipopt.py
487 488 489 490 491 492 493 494 495 496 497 498 | |
constraints(x)
abstractmethod
¶
Evaluate constraint functions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Optimization variables |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Constraint function values |
Source code in src/figaroh/tools/robotipopt.py
500 501 502 503 504 505 506 507 508 509 510 511 | |
gradient(x)
¶
Compute gradient of objective function.
Default implementation uses automatic differentiation. Override for custom implementations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Optimization variables |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Gradient vector |
Source code in src/figaroh/tools/robotipopt.py
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | |
jacobian(x)
¶
Compute Jacobian of constraint functions.
Default implementation uses automatic differentiation. Override for custom implementations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Optimization variables |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Jacobian matrix |
Source code in src/figaroh/tools/robotipopt.py
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | |
hessian(x, lagrange, obj_factor)
¶
Compute Hessian of Lagrangian.
Default implementation returns False to use IPOPT's approximation. Override for custom implementations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Optimization variables |
required |
lagrange
|
ndarray
|
Lagrange multipliers |
required |
obj_factor
|
float
|
Objective scaling factor |
required |
Returns:
| Type | Description |
|---|---|
Union[bool, ndarray]
|
Hessian matrix or False to use approximation |
Source code in src/figaroh/tools/robotipopt.py
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | |
intermediate(alg_mod, iter_count, obj_value, inf_pr, inf_du, mu, d_norm, regularization_size, alpha_du, alpha_pr, ls_trials)
¶
Intermediate callback for iteration tracking.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
alg_mod
|
int
|
Algorithm mode |
required |
iter_count
|
int
|
Iteration count |
required |
obj_value
|
float
|
Current objective value |
required |
inf_pr
|
float
|
Primal infeasibility |
required |
inf_du
|
float
|
Dual infeasibility |
required |
mu
|
float
|
Barrier parameter |
required |
d_norm
|
float
|
Step size |
required |
regularization_size
|
float
|
Regularization parameter |
required |
alpha_du
|
float
|
Dual step size |
required |
alpha_pr
|
float
|
Primal step size |
required |
ls_trials
|
int
|
Line search trials |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True to continue optimization, False to stop |
Source code in src/figaroh/tools/robotipopt.py
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | |
RobotIPOPTSolver(problem, config=None)
¶
General IPOPT solver for robotics optimization problems.
This class provides a high-level interface for solving optimization problems using IPOPT, with built-in support for result analysis, error handling, and problem validation specifically designed for robotics applications.
Features
- Automatic problem setup and configuration
- Built-in problem validation before solving
- Comprehensive result analysis and logging
- Solution history tracking for multiple solves
- Robotics-specific error handling and diagnostics
- Support for iterative solving and warm starts
Attributes:
| Name | Type | Description |
|---|---|---|
problem |
BaseOptimizationProblem
|
The optimization problem to solve. |
config |
IPOPTConfig
|
IPOPT solver configuration. |
logger |
Logger
|
Logger for solver operations. |
last_solution |
ndarray
|
Most recent optimization solution. |
last_info |
Dict
|
Most recent IPOPT solver information. |
solve_history |
List[Dict]
|
History of all solve attempts. |
Examples:
Basic usage:
# Create problem and solver
problem = MyOptimizationProblem()
solver = RobotIPOPTSolver(problem)
# Solve the problem
success, results = solver.solve()
if success:
print(f"Optimal solution: {results['x_opt']}")
print(f"Objective value: {results['obj_val']}")
With custom configuration:
# Create custom configuration
config = IPOPTConfig(
tolerance=1e-8,
max_iterations=5000,
print_level=3
)
# Create solver with custom config
solver = RobotIPOPTSolver(problem, config)
# Validate problem before solving
if solver.validate_problem_setup():
success, results = solver.solve()
Multiple solves with warm start:
solver = RobotIPOPTSolver(problem)
# First solve
success1, results1 = solver.solve()
# Modify problem parameters...
problem.update_parameters(new_params)
# Second solve (uses warm start automatically)
success2, results2 = solver.solve()
# Access solve history
print(f"Solved {len(solver.solve_history)} problems")
Initialize the IPOPT solver.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
problem
|
BaseOptimizationProblem
|
Optimization problem to solve |
required |
config
|
Optional[IPOPTConfig]
|
IPOPT configuration (default: trajectory optimization config) |
None
|
Source code in src/figaroh/tools/robotipopt.py
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 | |
solve()
¶
Solve the optimization problem.
Performs the complete optimization process including problem setup, IPOPT configuration, solving, and result analysis. The method handles all IPOPT interactions and provides comprehensive error handling and logging.
Returns:
| Type | Description |
|---|---|
Tuple[bool, Dict[str, Any]]
|
Tuple[bool, Dict[str, Any]]: A tuple containing: - success (bool): True if optimization succeeded, False otherwise - results (Dict[str, Any]): Dictionary containing: * 'x_opt': Optimal solution vector * 'obj_val': Final objective function value * 'status': IPOPT exit status code * 'status_msg': Human-readable status message * 'solve_time': Total optimization time in seconds * 'iterations': Number of iterations performed * 'iteration_data': Detailed iteration history * 'callback_data': Custom data from problem callbacks * 'ipopt_info': Complete IPOPT solver information * 'success': Copy of success flag for convenience |
Raises:
| Type | Description |
|---|---|
Exception
|
If critical errors occur during problem setup or solving. Non-critical errors are caught and returned in results. |
Note
IPOPT status codes for success: -1 (solved to acceptable level), 0 (solved), 1 (solved to acceptable level). All other codes indicate various types of failures or early termination.
Source code in src/figaroh/tools/robotipopt.py
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 | |
get_solution_summary()
¶
Get a summary of the last solution.
Source code in src/figaroh/tools/robotipopt.py
832 833 834 835 836 837 838 839 840 841 842 843 844 845 | |
validate_problem_setup()
¶
Validate that the optimization problem is properly set up.
Returns:
| Type | Description |
|---|---|
bool
|
True if problem appears valid, False otherwise |
Source code in src/figaroh/tools/robotipopt.py
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 | |
TrajectoryOptimizationProblem(name='TrajectoryOptimization')
¶
Bases: BaseOptimizationProblem
Specialized optimization problem for trajectory optimization.
This class provides a framework for trajectory optimization problems with common patterns like waypoint parameterization and constraint handling.
Initialize trajectory optimization problem.
Source code in src/figaroh/tools/robotipopt.py
904 905 906 907 908 909 910 911 912 913 914 | |
set_objective_function(obj_func)
¶
Set custom objective function.
Source code in src/figaroh/tools/robotipopt.py
916 917 918 | |
set_constraint_function(cons_func)
¶
Set custom constraint function.
Source code in src/figaroh/tools/robotipopt.py
920 921 922 | |
objective(x)
¶
Evaluate objective function.
Source code in src/figaroh/tools/robotipopt.py
924 925 926 927 928 929 930 931 | |
constraints(x)
¶
Evaluate constraint functions.
Source code in src/figaroh/tools/robotipopt.py
933 934 935 936 937 938 939 940 | |
create_trajectory_solver(objective_func, constraint_func, get_bounds_func, initial_guess_func, name='CustomTrajectory')
¶
Factory function to create a trajectory optimization solver.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
objective_func
|
Callable[[ndarray], float]
|
Function to evaluate objective |
required |
constraint_func
|
Callable[[ndarray], ndarray]
|
Function to evaluate constraints |
required |
get_bounds_func
|
Callable[[], Tuple[Tuple[List, List], Tuple[List, List]]]
|
Function that returns ((var_lb, var_ub), (cons_lb, cons_ub)) |
required |
initial_guess_func
|
Callable[[], List[float]]
|
Function that returns initial guess |
required |
name
|
str
|
Problem name |
'CustomTrajectory'
|
Returns:
| Type | Description |
|---|---|
RobotIPOPTSolver
|
Configured IPOPT solver |
Source code in src/figaroh/tools/robotipopt.py
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 | |
Random Data Generation¶
generate_waypoints(N, robot, mlow, mhigh)
¶
Generate random values for joint positions, velocities, accelerations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
N
|
Number of samples |
required | |
robot
|
Robot model object |
required | |
mlow
|
Lower bound for random values |
required | |
mhigh
|
Upper bound for random values |
required |
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
(q, v, a) joint position, velocity, acceleration arrays |
Source code in src/figaroh/tools/randomdata.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | |
generate_waypoints_fext(N, robot, mlow, mhigh)
¶
Generate random values for joints with external forces.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
N
|
Number of samples |
required | |
robot
|
Robot model object |
required | |
mlow
|
Lower bound for random values |
required | |
mhigh
|
Upper bound for random values |
required |
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
(q, v, a) joint position, velocity, acceleration arrays |
Source code in src/figaroh/tools/randomdata.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | |
get_torque_rand(N, robot, q, v, a, identif_config)
¶
Calculate random torque values including various dynamic effects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
N
|
Number of samples |
required | |
robot
|
Robot model object |
required | |
q
|
Joint positions array |
required | |
v
|
Joint velocities array |
required | |
a
|
Joint accelerations array |
required | |
identif_config
|
Dictionary of dynamic parameters |
required |
Returns:
| Name | Type | Description |
|---|---|---|
array |
Joint torques array |
Source code in src/figaroh/tools/randomdata.py
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |
URDF Export¶
Applies calibrated joint parameters back onto a URDF file.
URDF exporter for identified/calibrated robot parameters.
Reads a nominal URDF and produces a modified URDF by applying parameter
overlays. Parameter names encode the semantics: d_px_joint2 is additive
(geometric offset), m_link1 is absolute (mass override).
This is used after identification or calibration to materialize results into a tangible URDF file for downstream use (simulation, visualization, model-based control).
Two parameter categories
Joint-level parameters (auto-applied to the URDF): These directly modify the URDF's joint origins, link inertias, and dynamics attributes. They come from figaroh's identification or calibration solvers and can be applied automatically:
- Joint placement (additive): ``d_px_{joint}``, ``d_py_{joint}``,
``d_pz_{joint}``, ``d_phix_{joint}``, ``d_phiy_{joint}``,
``d_phiz_{joint}``
- Joint offset / calibration (additive): ``offsetPX_{joint}``,
``offsetPY_{joint}``, ``offsetPZ_{joint}``, ``offsetRX_{joint}``,
``offsetRY_{joint}``, ``offsetRZ_{joint}``
- Legacy offset (absolute): ``off_{joint}``
- Mass (absolute): ``m_{link}``
- First moments (absolute): ``mx_{link}``, ``my_{link}``, ``mz_{link}``
- Inertia tensor (absolute): ``Ixx_{link}``, ``Ixy_{link}``, ...,
``Izz_{link}``
- Viscous/static friction (absolute): ``fv_{joint}``, ``fs_{joint}``
- Armature (absolute): ``Ia_{joint}``
- Joint elasticity (additive): ``k_PX_{joint}``, ..., ``k_RZ_{joint}``
Metrology frame parameters (user-defined, not auto-applied):
These define the transformation between the robot (URDF) and the
external measurement system (mocap, camera, chessboard, etc.).
They depend on the calibration setup, not on the URDF itself.
export_urdf() will not auto-apply them; instead it logs a
reminder and returns them as metadata for the user to configure::
base_px, base_py, base_pz, base_phix, base_phiy, base_phiz
Transform from the **metrology frame** (e.g. mocap world,
Vicon origin) to the robot's ``base_link``.
Default: identity (no offset from origin).
pEEx_{frame}, pEEy_{frame}, pEEz_{frame}
phiEEx_{frame}, phiEEy_{frame}, phiEEz_{frame}
Transform from the last robot joint (e.g. ``arm_7_joint``,
``head_2_link``) to the **measurement frame** mounted on the
end-effector. What this frame is depends on calibration type:
- **Mocap calibration**: optical marker cluster frame
(markers attached to the end-effector).
- **Eye-hand calibration**: camera optical frame
(e.g. ``xtion_rgb_optical_frame``) or chessboard frame
(pattern on the gripper).
Default: identity (measurement frame coincides with the joint).
Typical usage::
from figaroh.tools.urdf_exporter import export_urdf, frame_settings_doc
# Joint-level params (auto-applied to URDF)
params = {
"d_px_joint2": 0.05,
"m_link1": 2.5,
"fv_joint1": 0.2,
}
modified_path = export_urdf("robot.urdf", params, verbose=True)
# Metrology frames (user-defined — see frame_settings_doc())
defaults = frame_settings_doc()
# → prints descriptions + default values for base and EE frame params
frame_settings_doc(*, calibration_type=None, verbose=True)
¶
Return default metrology-frame parameter values with explanations.
These parameters define the transformation between the robot and the external measurement system. They are not intrinsic to the URDF and must be configured by the user for each calibration setup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
calibration_type
|
Optional[str]
|
Optional hint for context-specific defaults.
|
None
|
verbose
|
bool
|
If True (default), prints descriptions to stderr. |
True
|
Returns:
| Type | Description |
|---|---|
dict
|
dict with default values for all base-frame and EE-frame params:: { "base_px": 0.0, ... "pEEx_arm_7_link": 0.0, ... } |
Source code in src/figaroh/tools/urdf_exporter.py
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 | |
export_urdf(nominal_urdf_path, params, *, output_path=None, verbose=False)
¶
Apply identified/calibrated joint-level parameters to a nominal URDF.
This function auto-applies parameters that modify the URDF directly
(joint placements, mass, inertias, friction, etc. — see module docstring).
It does not apply metrology frame parameters (base_*, pEE*,
phiEE*); those depend on the calibration setup and must be
configured by the user — see :func:frame_settings_doc.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nominal_urdf_path
|
Union[str, Path]
|
Path to the nominal (reference) URDF file. |
required |
params
|
dict
|
Dictionary of |
required |
output_path
|
Optional[Union[str, Path]]
|
Path for the modified URDF. If |
None
|
verbose
|
bool
|
If True, log which params were applied. |
False
|
Returns:
| Type | Description |
|---|---|
str
|
Absolute path to the modified URDF file (joint params applied). Use |
str
|
func: |
str
|
separately. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If nominal_urdf_path does not exist. |
ValueError
|
If an unknown parameter name is encountered. |
Source code in src/figaroh/tools/urdf_exporter.py
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 | |
Export Validation¶
FK consistency checks and interactive viser visualization for a calibration-exported URDF.
URDF model comparison and validation utilities.
Provides tools to compare two URDF models (nominal vs. modified after parameter application) through forward-kinematics analysis and viser-based visualization.
Typical usage::
from figaroh.tools.export_validation import URDFComparison
comp = URDFComparison("robot.urdf", "robot_modified.urdf")
print(comp.fk_consistency_check())
# → FkConsistencyResult(rmse_position=0.051, rmse_orientation=0.098, ...)
# Interactive viser visualization:
comp.show_trajectory_animation()
comp.show_static_grid()
PoseDelta(translation, rotation, twist, q)
dataclass
¶
FK pose difference between two models at a single configuration.
The delta is expressed as M_nominal⁻¹ · M_modified, i.e. the
SE3 transform from the nominal end-effector frame to the modified one.
position_error()
¶
Euclidean distance in meters.
Source code in src/figaroh/tools/export_validation.py
54 55 56 | |
orientation_error()
¶
Angle-axis magnitude in radians.
Source code in src/figaroh/tools/export_validation.py
58 59 60 | |
FkConsistencyResult(rmse_position, rmse_orientation, max_position, max_orientation, per_sample)
dataclass
¶
Aggregated FK consistency check — compares nominal vs. exported URDF FK.
PoseError(q, position_error_mm, orientation_error_deg, pose_delta, label='')
dataclass
¶
FK error at a single static configuration with user label.
URDFComparison(nominal_urdf, modified_urdf)
¶
Compare two URDF models via FK analysis and viser visualization.
Loads both URDFs into Pinocchio for FK computation and into yourdfpy (when needed) for viser rendering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nominal_urdf
|
Union[str, Path]
|
Path to the original/reference URDF. |
required |
modified_urdf
|
Union[str, Path]
|
Path to the URDF after parameter application. |
required |
Source code in src/figaroh/tools/export_validation.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | |
fk_consistency_check(n_samples=100, seed=42)
¶
Check the exported URDF reproduces the calibrated FK numerically.
This is a file-integrity check — it verifies the exported URDF file is self-consistent with the calibration results. It does NOT test against ground-truth measurements (use calibration validation for that).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_samples
|
int
|
Number of random configurations to sample. |
100
|
seed
|
int
|
Random seed for reproducibility. |
42
|
Returns:
| Type | Description |
|---|---|
FkConsistencyResult
|
FkConsistencyResult with aggregated FK consistency metrics. |
Source code in src/figaroh/tools/export_validation.py
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | |
static_poses(poses=None)
¶
Compute FK errors at specific joint configurations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
poses
|
Optional[List[Union[List[float], ndarray]]]
|
List of joint configuration vectors. If None, a default set of 20 meaningful poses is used (covering home, limits, mixed-angle combinations). |
None
|
Returns:
| Type | Description |
|---|---|
List[PoseError]
|
List of PoseError, one per configuration. |
Source code in src/figaroh/tools/export_validation.py
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | |
show_overlay(server=None, port=8080, orig_color=(51, 102, 229), mod_color=(229, 51, 51), duration=5.0)
¶
Display both models overlaid in viser.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
server
|
Existing viser server, or None to create one. |
None
|
|
port
|
int
|
Port for new server (ignored if server is given). |
8080
|
orig_color
|
Tuple[int, int, int]
|
RGB color for the original model (0-255). |
(51, 102, 229)
|
mod_color
|
Tuple[int, int, int]
|
RGB color for the modified model (0-255). |
(229, 51, 51)
|
duration
|
float
|
Seconds to keep the display open. |
5.0
|
Source code in src/figaroh/tools/export_validation.py
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | |
show_trajectory_animation(n_configs=50, seed=42, server=None, port=8080, duration=10.0)
¶
Animate through random joint configs, tracing end-effector paths.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_configs
|
int
|
Number of random configurations to animate. |
50
|
seed
|
int
|
Random seed. |
42
|
server
|
Existing viser server, or None to create one. |
None
|
|
port
|
int
|
Port for new server. |
8080
|
duration
|
float
|
Seconds to keep the display open after animation. |
10.0
|
Source code in src/figaroh/tools/export_validation.py
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | |
show_static_grid(poses=None, server=None, port=8080, duration=15.0, spacing=2.0)
¶
Display a grid of static configurations with error labels.
Each cell shows both models at the same configuration, with the position (mm) and orientation (deg) error overlaid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
poses
|
Optional[List[Union[List[float], ndarray]]]
|
Joint configurations. If None, uses 24 default poses. |
None
|
server
|
Existing viser server, or None to create one. |
None
|
|
port
|
int
|
Port for new server. |
8080
|
duration
|
float
|
Seconds to keep the display open. |
15.0
|
spacing
|
float
|
Distance between grid cells in meters. |
2.0
|
Source code in src/figaroh/tools/export_validation.py
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 | |
show_interactive_validation(n_trajectory=50, seed=42, port=8080)
¶
Interactive combined visualization with trajectory, static comparison, error plots, replay, and opacity controls.
Opens a single viser server containing:
- Trajectory animation — 50 random configurations with path traces for both nominal and modified models. Replay button allows re-running.
- Static pose comparison — overlays both models at the same configuration with opacity slider for the modified model.
- Error plots — uplot charts for trajectory and static pose errors (position in mm, orientation in degrees).
- Pose selector — slider to cycle through static configurations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_trajectory
|
int
|
Number of random configurations to animate. |
50
|
seed
|
int
|
Random seed for reproducibility. |
42
|
port
|
int
|
Port for the viser server. |
8080
|
Source code in src/figaroh/tools/export_validation.py
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 | |