Understand the two solve modes
Planning and streaming control use the same model and objective weights but run them differently:
Streaming control does not run convergence retries or random restarts. It must
produce one small, bounded command from the current command trajectory on every
tick.
PinkKinematicsConfig.dt is therefore a planning default; set the
coordinator rate correctly instead of using dt to tune teleoperation speed.
Tune the common objective
Start with scalar configuration before adding robot-specific tasks:skip
Costs are relative. Multiplying all costs by the same factor rarely changes the
motion. First balance position and orientation, then add only enough posture
cost to shape redundant joints.
Pass the same scalar configuration to planning when both paths should start
with the same objective:
skip
Customize the task stack
SubclassPinkPoseTargetSolver when scalar weights are insufficient—for
example, when a robot needs per-joint posture weights or a manipulability task.
Override _create_tasks(), call super(), and change only the required values:
skip
frame/<target frame> for frame tasks and
posture/current when posture_cost > 0. A generic bimanual subclass can add
shared tasks, and a robot-specific subclass can call super() and modify one
value. Each returned dictionary must contain new task instances because Pink
tasks are stateful and cannot be shared across control-task instances.
Pass the solver class, not an instance. The coordinator constructs one solver
for each control task:
skip
_before_solve() and _after_solve() only for a genuinely temporal Pink
task. Most tuning belongs in _create_tasks().
Bound streaming commands
Teleoperation task parameters bound the QP output against the URDF and live hardware feedback. They are independent of the objective weights:skip
Start hardware tests with conservative velocity caps. Use the scalar as the
fallback, then lower individual proximal or high-torque joints when they move
too aggressively. Every effective cap is also intersected with the joint’s
URDF velocity limit. Unknown joint names and non-positive or non-finite values
are rejected when the task is constructed.
The 5 Hz low-pass filter attenuates high-frequency changes in successive Pink
solutions. It is time-aware, so changing the coordinator tick rate does not
change its cutoff. Lower the cutoff when commands visibly oscillate; raise it
when smooth motion feels unnecessarily delayed. Gripper commands bypass this
filter.
The shared streaming solver also averages the three most recent bounded joint
increments with oldest-to-newest weights
0.1, 0.3, and 0.6. The newest
solution therefore dominates intentional direction changes, while alternating
IK solutions are attenuated. Startup weights are normalized over the samples
available, so the first command has no additional delay. Solver subclasses may
override joint_increment_filter_weights for robot-specific tuning.
The tracking-error limit must tolerate normal execution delay without allowing
the command trajectory to run far ahead. Keep feedback tolerance small: it
accounts for encoder noise, not extra workspace.
Each Pink candidate is filtered against the previous accepted command and
clamped to the per-joint/configured and URDF velocity step, the measured-state
tracking window, and the inward position margin. Its bounded increment enters
the weighted history, and the averaged command passes through the same safety
envelope again. Disengagement, timeout, preemption, and E-STOP clear both
command and increment history, so the next session starts from measured
feedback. Invalid feedback or a failed Pink solve produces no new command for
that tick.
Tune in order
- Verify joint names, order, base link, target frames, and startup forward kinematics.
- Use fake hardware to confirm that small translation and rotation targets move in the expected directions.
- Balance position and orientation with posture cost near zero.
- Add posture weights or a manipulability task to shape redundant motion.
- Exercise singular poses and joint limits; adjust damping and the inward posture margin.
- Move to hardware with conservative per-joint velocity caps. Tune the command filter for oscillation, then tune tracking error for measured latency and sensor noise.
- Test disengagement, target timeout, preemption, stop, and E-STOP. Each must clear the persistent command trajectory before re-engagement.
OpenArm canonical-zero example
OpenArm starts in the canonical all-zero pose, where both joint-4 coordinates are at their lower limits and the Cartesian Jacobian is rank-deficient. Its solver keeps the measured configuration as the streaming seed but uses a fixed inward-neutral posture target with both joint-4 coordinates at0.3 rad. Its
Pink objective follows the proven G1 bimanual tuning: 8:2 position-to-
orientation costs, a 0.01 posture cost, and per-arm posture weights
[4, 3, 0.1, 3, 1, 1, 0.1]. This keeps shoulders and elbows near a useful
posture while leaving the redundant elbow-roll and wrist-yaw coordinates freer
to refine the final pose. The 0.3 rad OpenArm joint-4 target is the only
model-specific departure from G1’s zero posture and supplies a deterministic
escape direction without a random restart or multi-iteration control tick.
Treat this as a model-specific response to a verified startup singularity, not
as a default reason to alter a robot’s home pose. Validate the complete IK path
with fake hardware, then add a self-hosted test that loads the real model and
takes bounded steps from its canonical startup pose.