Tutorial 14: System Identification of a Franka#

Tutorial

Note

The System Identification extension is in beta. Its workflow, run contracts, exported run specifications, and APIs are not guaranteed to stay future compatible.

This tutorial runs a reproducible, free-space system-identification experiment for a fixed-base Franka Panda. To make the optimization behavior visible, it begins with a deliberately over-frictional working asset. The source asset is preserved; only a separate tutorial overlay is degraded. After recovering joint friction, the tutorial diagnoses the remaining held-out residual and runs a separately bounded joint-damping phase. Each phase starts from the previously persisted candidate and must pass its own replay gate.

The example uses recorded MCAP telemetry, explicit command alignment, separate training and validation motions, uncaptured Newton MuJoCo rollouts, and CMA-ES. Optimization, USD mutation, replay, and promotion remain separate operations so that every stage is reviewable and reversible.

Franka system identification in Isaac Sim, progressing from an excited reference through a mismatched initial model to an identified model that reproduces the reference pose.

The walkthrough follows the same progression: reference excitation and telemetry on the left, the deliberately poor initial model in orange, and the recovered candidate in green.#

After completing this tutorial, you will be able to:

  • Construct a controlled poor initialization without modifying a source asset.

  • Validate telemetry mapping, timing, excitation, and effort semantics.

  • Configure and export a bounded friction-only RunSpec.

  • Solve, persist, and independently replay one parameter phase.

  • Diagnose held-out residuals and configure a conservative second phase.

  • Compare candidates with a predeclared acceptance policy.

  • Decide when to promote a candidate and when to collect new excitation data.

  • Interpret predictive improvement without overstating physical identifiability.

For the complete feature reference, see System Identification Extension.

35-50 minute tutorial, excluding optimization time

Reference outcome#

The tutorial intentionally doubles all seven arm-joint friction values in a working overlay before fitting. The independently reloaded friction candidate reduces held-out normalized cost by 42.7%. Position, velocity, and torque RMSE improve by 8.3%, 13.7%, and 24.4%, respectively.

A second phase opens only seven joint-damping scales. Its independently reloaded candidate reduces torque RMSE from 1.6573 N*m to 1.3946 N*m and normalized cost by another 28.9%. Position and velocity RMSE regress slightly relative to the friction candidate, by 2.0% and 1.1%, while remaining better than the poor seed. The final decision therefore demonstrates an explicit application tradeoff instead of selecting the lowest scalar objective without review.

This is a stress-test initialization, not a recommended engineering prior. It demonstrates a staged tuning cycle from a meaningfully poor model while keeping every experiment stable and reviewable.

Prerequisites#

  • A built NVIDIA Isaac Sim source checkout.

  • isaacsim.robot_setup.sysid.ui enabled. Open Window > Extensions, search for system identification, and enable isaacsim.robot_setup.sysid.ui. The interactive extension automatically enables the isaacsim.robot_setup.sysid backend.

  • The Franka tutorial assets from the Content Browser at IsaacSim/Samples/SystemIdentification/, including a fixed-base Franka stage whose articulation resolves to /World/envs/env_0/panda_instanceable, the MCAP recording, topic mapping, and collection manifest.

  • A CUDA-capable GPU for the Newton MuJoCo population rollout.

  • A local working directory for RunSpecs, reports, checkpoints, and output USDs. Copy Samples inputs into that directory when a headless command needs a filesystem path, and keep writable outputs local. Do not modify the Samples originals.

The reference recording contains 9,439 samples for seven arm joints at approximately 30 Hz. It provides joint position, velocity, command, and link-side torque.

Important

System Identification consumes excitation data; it does not generate or certify safe robot commands. Validate joint, velocity, acceleration, effort, collision, and workspace limits before executing a trajectory on hardware. See Trajectory generation and excitation for external experiment-design resources.

Step 1: Create a deliberately poor working asset#

Copy IsaacSim/Samples/SystemIdentification/franka_poor_seed.usda into a local working directory such as C:\sysid_tutorial\, or open the nominal Franka stage from the same Samples folder, confirm that the articulation initializes, and save a separate local overlay with the over-frictional values below. Do not edit the Samples originals.

The nominal tutorial overlay used 0.5 N*m*s/rad of physical friction on each arm joint. For this experiment, set each of the seven working-copy values to 1.0 N*m*s/rad. Leave stiffness, damping, armature, masses, inertias, and controller settings unchanged. This isolates the injected error to the parameter family that will be optimized.

Property

Nominal tutorial overlay

Poor working overlay

Joint 1-7 physical friction

0.5 N*m*s/rad

1.0 N*m*s/rad

Other physical properties

Unchanged

Unchanged

The poor seed remains numerically stable, but its excess friction creates a measurable trajectory and torque mismatch. This controlled construction is preferable to changing several coupled families at once because the cause of the initial error is known.

Warning

The 0.5 N*m*s/rad nominal values are tutorial seed values, not ground-truth measurements of a physical Franka. The recovered result is evaluated as a better predictive model for this recording, not as a certificate of true joint friction.

Step 2: Load and verify telemetry#

With isaacsim.robot_setup.sysid.ui enabled, open Tools > Robotics > Asset Editors > System Identification. On Data:

  1. Select /World/envs/env_0/panda_instanceable.

  2. Select MCAP and choose the recording and topic mapping from IsaacSim/Samples/SystemIdentification/.

  3. Choose the collection manifest from the same Samples folder if it defines named motion segments.

  4. Click Load Trajectory.

  5. Confirm 9,439 samples, seven joints, approximately 30 Hz, and the expected signal channels.

  6. Confirm that effort mapping explicitly declares torque_semantics: link_side.

The torque-semantics field matters because motor-side, link-side, commanded, and measured efforts are not interchangeable. Also verify joint order, signs, units, timestamp gaps, clipping, and long flat intervals.

System Identification Data page with a loaded Franka trajectory and separate train and validation chunks.

Review the trajectory preview, chunk table, and quality summary together before configuring a solve.#

Step 3: Correct recording alignment#

The telemetry diagnostic estimates a common timing offset between commands and measured response. The reference recording produced:

Diagnostic

Value

Common lag

16.0825 samples

Common lag

0.536075 s

Joint-to-joint residual spread

0.5336 samples

Set telemetry.command_alignment_seconds to 0.5360748729665944 and leave the actuator command-delay parameter disabled for this phase.

Command alignment corrects the recorded series before fitting. Actuator command delay changes the simulated model. Applying both to the same logging offset would double-count the effect.

Step 4: Separate training and validation#

Disable Auto split and retain two non-overlapping motions:

Chunk

Role

Excitation

Duration

coupled_multisine_fast

Train

Sine

23.721 s

coupled_multisine_validation

Validation

Sine

27.374 s

The training multisine contributes to the optimizer. The validation multisine is withheld from optimization and is used for model selection and the final replay. Both motions exercise all seven joints, but they do not guarantee that every selected physical parameter is independently identifiable.

Step 5: Configure the rollout#

Use the following Newton settings:

Setting

Reference value

Simulation engine

Newton

Newton solver

MuJoCo

Robot prim

/World/envs/env_0/panda_instanceable

Device

cuda:0

Controller

pd

Feedforward

inverse_dynamics

Effort clamp

max_effort

Offline stepping

Enabled

CUDA graph capture

Enabled (default)

Newton MuJoCo supports batched, derivative-free population evaluation. The differentiable Newton Featherstone rollout is a separate headless-only path with a narrower supported parameter set; it is not used by this CMA-ES walkthrough.

Leave CUDA graph capture enabled for repeated, fixed-shape CMA-ES evaluations. The first evaluation warms the kernels and solver storage, the next captures that shape, and later evaluations reuse the graph. Each captured and uncaptured MuJoCo rollout resets rollout-local warm-start, actuator-activation, applied-force, and control buffers before restoring the initial joint state, so a preceding candidate batch does not seed the next one.

In a reference Franka run with 16 candidates and 800 steps, graph replay reduced median evaluation time from 13.59 seconds uncaptured to 0.515 seconds captured, a 26.4x speedup after warmup. The ratio is specific to that workload and hardware. For a new stage or RunSpec, compare a captured result with an uncaptured result using declared numerical tolerances. Small GPU floating-point differences are expected; disable capture if the difference exceeds the acceptance threshold rather than requiring bitwise equality.

Step 6: Select and check the parameter phase#

Select seven joint-friction scale parameters:

Family

Count

Initial

Minimum

Maximum

Joint friction

7

1.0

0.1

1.5

The scale value 1.0 represents the deliberately poor 1.0 N*m*s/rad property authored in Step 1. The bounds therefore correspond to 0.1-1.5 N*m*s/rad on this working asset.

Click Run Check and read the current report. The reference friction-only regressor had rank 7 and condition number 1.024. All seven rows were identifiable. With the bounds above, four analytical seeds were in bounds and three were left unchanged because their unconstrained estimates were outside the configured range. A count such as 7 selected is not an identifiability result; use per-parameter sensitivity, rank, conditioning, and verdicts.

Do not add stiffness, damping, or armature to this first phase simply because the UI permits it. Adding coupled families would make a larger numerical improvement harder to interpret and would no longer match the controlled error introduced in Step 1. The later damping phase starts only after the friction candidate has been persisted, replayed, and accepted as its own baseline.

Step 7: Configure and export#

On Solve, use:

Setting

Reference value

Optimizer

CMA-ES

Maximum iterations

48

Population and batch size

16

Sigma

0.2

Seed

20260730

Maximum rollout steps

800

Position weight

1.0

Velocity weight

1.0

Torque weight

0.01

Export franka_friction_run_spec.json into your local working directory. Review every input path, output path, bound, and writeback flag. Point input paths at your local copies of the Samples assets under IsaacSim/Samples/SystemIdentification/, and keep output paths local. Store the exported file with its topic mapping, collection manifest, source revision, and input hashes.

Step 8: Establish the poor-seed baseline#

Run the analyzer before optimization. The examples below use C:\sysid_tutorial as the local working directory that holds copies of Samples inputs plus writable RunSpecs, reports, and candidate USDs:

.\_build\windows-x86_64\release\python.bat `
    source\extensions\isaacsim.robot_setup.sysid\tools\headless_sysid_analyze.py `
    --run-spec C:\sysid_tutorial\franka_friction_run_spec.json `
    --stage C:\sysid_tutorial\franka_poor_seed.usda `
    --result-json C:\sysid_tutorial\poor_seed_baseline.json `
    --mode baseline

The aligned held-out baseline was:

Metric

Value

Normalized cost

0.008066

Position RMSE

0.004443 rad

Velocity RMSE

0.017925 rad/s

Torque RMSE

2.1922 N*m

Keep this report unchanged. A post-solve value without the same-stage, same-RunSpec baseline cannot establish improvement.

Step 9: Solve, then apply to a new USD#

Run the exported specification without writeback:

.\_build\windows-x86_64\release\python.bat `
    source\extensions\isaacsim.robot_setup.sysid\tools\headless_sysid_solve.py `
    --run-spec C:\sysid_tutorial\franka_friction_run_spec.json `
    --result-json C:\sysid_tutorial\franka_friction_result.json `
    --checkpoint-json C:\sysid_tutorial\franka_friction_checkpoint.json

The selected candidate was the last accepted candidate at iteration 47. Review selected_status rather than assuming the final attempted generation is accepted. The selected scales were:

Joint

Initial scale

Recovered scale

Bound status

Joint 1

1.000

0.214

Interior

Joint 2

1.000

0.105

Interior

Joint 3

1.000

0.848

Interior

Joint 4

1.000

0.184

Interior

Joint 5

1.000

0.172

Interior

Joint 6

1.000

0.147

Interior

Joint 7

1.000

0.156

Interior

No selected value is classified as a bound hit. Joint 2 is close to the lower bound, so extending the search range would be a useful sensitivity study before interpreting its value physically.

Author the selected vector onto a new stage:

.\_build\windows-x86_64\release\python.bat `
    source\extensions\isaacsim.robot_setup.sysid\tools\headless_sysid_apply_theta_to_stage.py `
    --run-spec C:\sysid_tutorial\franka_friction_run_spec.json `
    --result-json C:\sysid_tutorial\franka_friction_result.json `
    --stage C:\sysid_tutorial\franka_poor_seed.usda `
    --output-stage C:\sysid_tutorial\franka_friction_candidate.usda `
    --summary-json C:\sysid_tutorial\franka_friction_apply_summary.json

This keeps optimization, asset mutation, and acceptance separately reviewable.

Step 10: Reload and compare held-out metrics#

Reload the persisted candidate in a fresh process and run a no-op baseline analysis:

.\_build\windows-x86_64\release\python.bat `
    source\extensions\isaacsim.robot_setup.sysid\tools\headless_sysid_analyze.py `
    --run-spec C:\sysid_tutorial\franka_friction_run_spec.json `
    --stage C:\sysid_tutorial\franka_friction_candidate.usda `
    --result-json C:\sysid_tutorial\franka_candidate_replay.json `
    --mode baseline

The independently reloaded candidate produced:

Held-out metric

Poor seed

Persisted candidate

Change

Normalized cost

0.008066

0.004620

42.7% lower

Position RMSE

0.004443 rad

0.004073 rad

8.3% lower

Velocity RMSE

0.017925 rad/s

0.015476 rad/s

13.7% lower

Torque RMSE

2.1922 N*m

1.6573 N*m

24.4% lower

Franka held-out error and joint-friction scales for the poor seed and independently reloaded candidate.

The left panel normalizes each error channel by its own poor-seed value. The right panel shows the large scale correction produced by the deliberately over-frictional initialization.#

Inspect time-series agreement as well as aggregate values:

Measured and simulated Franka joint-position traces from the persisted candidate on the held-out multisine.

The persisted candidate follows the held-out position motion across all seven joints. Look for phase error, bias, and localized divergence that an aggregate RMSE can hide.#

Measured and simulated Franka joint-effort traces from the persisted candidate on the held-out multisine.

Torque RMSE improves substantially but remains higher in absolute units than the state-channel RMSE. Residual effort error can include model-form, controller, effort-calibration, unmodeled friction, and timing mismatch; friction-only fitting is not expected to remove all of it.#

Step 11: Diagnose the residual and declare the next gate#

The friction candidate passes the first-phase fit gate because normalized cost and all three aggregate held-out channels improve after the result is persisted and reloaded. The effort overlay still contains structured amplitude and bias error, however. A tuning cycle should use that residual to form a new, testable phase rather than adding every available parameter at once.

For this tutorial, define an illustrative torque-refinement gate before running the second solve:

Requirement

Gate

Persisted-stage normalized cost

Lower than the friction candidate

Persisted-stage torque RMSE

At least 10% lower than the friction candidate

Position and velocity RMSE

No more than 2.5% higher than the friction candidate

Parameter bounds

No selected bound hits

Evidence source

Fresh-process replay of the written USD

These thresholds demonstrate the mechanism; define application-specific tolerances before fitting your own robot. A strict Pareto policy would instead reject any candidate that worsens any channel.

Duplicate the friction RunSpec and change its source stage to franka_friction_candidate.usda. Replace the friction entries with seven joint-damping scale parameters:

Family

Count

Initial

Minimum

Maximum

Joint damping

7

1.0

0.5

1.5

Keep the telemetry, chunks, command alignment, backend, controller, and channel weights unchanged. Use CMA-ES with population 16, 48 iterations, sigma 0.15, and seed 20260731. Export the new contract as franka_damping_run_spec.json.

Run a local sensitivity analysis before optimizing:

.\_build\windows-x86_64\release\python.bat `
    source\extensions\isaacsim.robot_setup.sysid\tools\headless_sysid_analyze.py `
    --run-spec C:\sysid_tutorial\franka_damping_run_spec.json `
    --stage C:\sysid_tutorial\franka_friction_candidate.usda `
    --result-json C:\sysid_tutorial\damping_baseline_sensitivity.json `
    --mode sensitivity

All seven reference perturbations produced non-low sensitivity and matching train/validation gradient signs. The check report also warns that drive damping is not analytically observable from link-side torque at the true trajectory; the optimizer identifies it through simulated position response. Sensitivity supports a conservative trial, but it is not proof of unique physical identification.

Step 12: Solve, persist, and replay the damping phase#

Run the second RunSpec without writeback:

.\_build\windows-x86_64\release\python.bat `
    source\extensions\isaacsim.robot_setup.sysid\tools\headless_sysid_solve.py `
    --run-spec C:\sysid_tutorial\franka_damping_run_spec.json `
    --result-json C:\sysid_tutorial\franka_damping_result.json `
    --checkpoint-json C:\sysid_tutorial\franka_damping_checkpoint.json

The reference solve selected the last accepted candidate at iteration 33:

Joint

Initial scale

Selected scale

Bound status

Joint 1

1.000

1.290

Interior

Joint 2

1.000

0.670

Interior

Joint 3

1.000

0.923

Interior

Joint 4

1.000

0.871

Interior

Joint 5

1.000

0.567

Interior

Joint 6

1.000

0.836

Interior

Joint 7

1.000

0.579

Interior

Write the selected vector to another new stage:

.\_build\windows-x86_64\release\python.bat `
    source\extensions\isaacsim.robot_setup.sysid\tools\headless_sysid_apply_theta_to_stage.py `
    --run-spec C:\sysid_tutorial\franka_damping_run_spec.json `
    --result-json C:\sysid_tutorial\franka_damping_result.json `
    --stage C:\sysid_tutorial\franka_friction_candidate.usda `
    --output-stage C:\sysid_tutorial\franka_friction_damping_candidate.usda `
    --summary-json C:\sysid_tutorial\franka_damping_apply_summary.json

Then reload the written stage in a new process:

.\_build\windows-x86_64\release\python.bat `
    source\extensions\isaacsim.robot_setup.sysid\tools\headless_sysid_analyze.py `
    --run-spec C:\sysid_tutorial\franka_damping_run_spec.json `
    --stage C:\sysid_tutorial\franka_friction_damping_candidate.usda `
    --result-json C:\sysid_tutorial\franka_damping_candidate_replay.json `
    --mode baseline

Use the replay report as the evidence record. Do not substitute the optimizer’s in-process validation value.

Step 13: Compare the stages and choose the candidate#

The independently replayed stages produced:

Held-out metric

Poor seed

Friction candidate

Friction+damping candidate

Normalized cost

0.008066

0.004620

0.003285

Position RMSE

0.004443 rad

0.004073 rad

0.004153 rad

Velocity RMSE

0.017925 rad/s

0.015476 rad/s

0.015644 rad/s

Torque RMSE

2.1922 N*m

1.6573 N*m

1.3946 N*m

Held-out Franka error for the poor seed, friction candidate, and friction-plus-damping candidate.

The damping phase lowers aggregate cost and torque error, while position and velocity regress slightly relative to the friction-only candidate. All values come from independent persisted-stage replays.#

Relative to the friction candidate, the second phase lowers normalized cost by 28.9% and torque RMSE by 15.9%. Position and velocity RMSE increase by 2.0% and 1.1%. It passes the illustrative torque-refinement gate, but it does not strictly dominate the friction candidate.

Choose according to the policy declared in Step 11:

  • For torque-sensitive validation with the stated state-error tolerances, advance the friction+damping candidate to task-level testing.

  • For a strict no-regression policy or position-priority task, retain the friction-only candidate.

  • Preserve both stages. Promotion never requires overwriting the earlier accepted result.

Step 14: Decide whether another parameter phase is justified#

The second-stage report contains useful stopping evidence:

  • No damping scale hit a bound.

  • Parameter-confidence curvature was inconclusive for all seven damping scales.

  • Validation cost remained approximately 8.15 times the estimated noise floor.

  • Joint 2 retained the largest torque-residual power in the report.

  • Training and validation are still two segments of one multisine recording.

Do not respond by opening armature, link mass, center of mass, and inertia together on the same data. These families can explain overlapping acceleration-dependent residuals. Instead, collect independently designed motions: slow or static configurations for gravity terms, acceleration-rich segments for inertial terms, and a separate set of held-out frequencies. Then repeat the same cycle for one supported family at a time:

  1. Establish the persisted-stage baseline.

  2. Run the check and local sensitivity analysis.

  3. Solve without writeback.

  4. Apply the reviewed selected status to a new USD.

  5. Replay the new stage in a fresh process.

  6. Compare against the predeclared gate and retain or reject the stage.

For a separate known-truth algorithm and backend check, run Tutorial 15: Validate System Identification with Sim-to-Sim Recovery. That tutorial uses a simple double pendulum and transfers a Newton-recovered model into an NVIDIA Isaac Sim PhysX stage. It complements this recorded-data walkthrough; it does not turn the Franka fit into a known-truth result.

Step 15: Preserve evidence and decide#

Keep:

  • Source, poor-seed, and every candidate USD.

  • Recording, topic mapping, collection manifest, and declared train/validation split.

  • One exported RunSpec per parameter phase.

  • Baseline, sensitivity, result, checkpoint, apply summary, and independent replay reports.

  • Validation figures, acceptance policy, and any failed diagnostic reports.

  • Source revision, extension version, input hashes, GPU, driver, and optimizer seed.

The selected candidate is suitable for additional task validation only under the policy you declared before its solve. Keep the source USD untouched, and do not treat either tutorial outcome as a hardware-safety or physical-parameter certification.

Summary#

This tutorial covered:

  1. Creating a controlled over-frictional working asset without modifying the source.

  2. Declaring link-side torque semantics and correcting a measured 0.5361 s recording offset.

  3. Separating coupled-multisine training and held-out frequency content.

  4. Solving and independently replaying a bounded seven-friction phase.

  5. Diagnosing the persisted residual before opening a second parameter family.

  6. Solving and independently replaying a bounded seven-damping phase.

  7. Comparing both candidates with an explicit application tradeoff.

  8. Stopping when the existing excitation no longer supports a defensible broader solve.

  9. Separating predictive fit quality from physical identifiability and known-truth validation.

Back to Gallery View