A small, reusable toolbox for bootstrapping simulation and Jacobian-based control of a 3-chamber pneumatic soft continuum actuator (PCC: Piecewise Constant Curvature).
It refactors the original one-off scripts into a clean set of MATLAB APIs + an Arduino firmware that can be driven directly from MATLAB.
This package is aimed at two workflows:
- Simulation / modeling
- Pressure↔length calibration (piecewise linear)
- PCC forward kinematics (FK) in length-space and pressure-space
- Workspace sampling + visualization
- PCC Jacobian (two methods)
- Jacobian-based resolved-rate control (Resolved-Rate IK)
- Velocity-based IK / control using the PCC Jacobian
- Damped least-squares (DLS) pseudoinverse for stability near singularities
- Optional velocity limiting and iteration controls
- MATLAB ↔ Arduino serial streaming: MATLAB computes pressure commands, Arduino applies them and returns measured pressures
Given:
- a desired tip position
x_target = [x y z](mm) - current pressures
p = [p1 p2 p3](kPa)
we want to compute the next pressure command to move the tip toward the target using a Jacobian-based method.
Core model chain (as implemented):
- Calibration:
p (kPa) -> L (mm)via piecewise linear mapping - PCC FK:
L -> x = [x y z] - Jacobian:
J = ∂x/∂L(3×3) - Resolved-rate step:
- position error:
e = x_target - x - desired tip velocity:
x_dot = clamp(k * e, v_max) - length rate:
L_dot = J^+ * x_dot(DLS pseudoinverse by default) - integrate:
L_next = L + L_dot * dt - inverse calibration:
p_next = L_to_p(L_next)
- position error:
- Hardware loop (optional):
- MATLAB sends
p_nextto Arduino - Arduino applies valve/PWM logic to the regulators
- Arduino returns measured pressures for feedback (optional)
- MATLAB sends
+pcc/: PCC math (calibration, FK, Jacobian, IK, workspace)+pccio/: MATLAB ↔ Arduino serial I/O helpers (handshake, send/read pressures)examples/: runnable demos (simulation + serial control)arduino/PressureRegulators_MATLAB/: Arduino sketch compatible with the MATLAB serial protocol
- Add toolbox to MATLAB path:
addpath(genpath(pwd));- Run a pure simulation IK demo (no Arduino):
examples/demo_ik_sim- Run workspace sampling / visualization:
examples/demo_workspace- Run MATLAB IK → Arduino pressure streaming (Arduino sketch required):
examples/demo_ik_arduino_serialcalib = pcc.defaultCalib(...)L = pcc.pressureToLength(P, calib)% kPa → mmP = pcc.lengthToPressure(L, calib)% mm → kPa
Calibration struct fields:
calib.d: chamber-to-center radius (mm)calib.p0: neutral pressure (kPa)calib.h0: neutral length (mm)calib.k_pos,calib.k_neg: slopes (mm/kPa) forp>=p0andp<p0- optional safety:
calib.p_min,calib.p_max(kPa)
Piecewise map:
L = h0 + k*(p - p0)p = p0 + (L - h0)/k
pos = pcc.fkLength(L, calib)% L: Nx3 → pos: Nx3pos = pcc.fkPressure(P, calib)% P: Nx3 → pos: Nx3
- Finite-difference (fast, no dependencies):
J = pcc.jacobianFD(L, calib, epsilon)
- Symbolic (analytic, needs Symbolic toolbox; first call compiles):
J = pcc.jacobianSymbolic(L, calib)
Practical tip: Use
jacobianFDfor real-time loops (robust + simple), usejacobianSymbolicfor validation / debugging.
- One control step:
[p_next, pos_next, info] = pcc.resolvedRateStep(p_current, target_pos, calib, opts)
- Full iterative loop:
[p_traj, pos_traj, info] = pcc.resolvedRateIK(p_init, target_pos, calib, opts)
Common options (opts):
dt(default 0.01)k_gain(default 2) : proportional gain from position error to tip velocityv_max(default 10 mm/s) : velocity magnitude clamplambda(default 1e-3) : DLS damping (larger → more stable but slower)jacobian='fd'|'sym'max_iters,tol
Resolved-rate math implemented:
e = x_target - xx_dot = clamp(k_gain * e, v_max)L_dot = (J' * inv(J*J' + lambda^2 I)) * x_dot(DLS pseudoinverse)L_next = L + L_dot * dtp_next = L_to_p(L_next)
pts = pcc.computeWorkspace(p1_range, p2_range, p3_range, calib)pcc.plotWorkspace(pts)
- MATLAB sends: 3×
int16(0)then 1×char(shake char) - Arduino replies:
'x'then'y'
- MATLAB → Arduino:
- 3×
int16command pressures:round(p_cmd_kPa * 100)
- 3×
- Arduino → MATLAB:
- 3×
int16measured pressures:round(p_meas_kPa * 100)
- 3×
This is wrapped in:
s = pccio.openSerial(port, baud, timeout_s)ok = pccio.handshake(s, shakeChar, timeout_s)pccio.sendPressures(s, p_cmd_kpa)p_meas_kpa = pccio.readPressures(s)
The provided sketch:
- Parses incoming 3×int16 pressure commands (kPa*100)
- Applies a simple open-loop mapping
desiredPressure -> PWM OCR+ valve direction control - Reads analog pressure sensors and returns 3×int16 measured pressures (kPa*100)
- On Arduino Mega2560, not all digital pins support PWM.
- If your vacuum PWM pins are not PWM-capable on your wiring, you must remap them to true PWM pins and update the sketch accordingly.
If you use this toolbox in academic work, please cite:
J. Su, K. Zuo and Z. Chua, "Three Degree-of-Freedom Soft Continuum Kinesthetic Haptic Display for Telemanipulation Via Sensory Substitution at the Finger," 2024 IEEE Conference on Telepresence, Pasadena, CA, USA, 2024, pp. 79-86, doi: 10.1109/Telepresence63209.2024.10841502. keywords: {Pneumatic actuators;Power system measurements;Teleoperators;Telepresence;Density measurement;Force;Force feedback;Fingers;Kinematics;Indexes}.