Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
eea92cc
animated plotting of tracking example
gchenfc Apr 6, 2021
14fe322
faster simulation
gchenfc Apr 6, 2021
49f21c6
a little refactoring
gchenfc Apr 6, 2021
dffa704
unit test easier priors adding functions
gchenfc Apr 6, 2021
4252df8
values-based priors functions
gchenfc Apr 6, 2021
1b32c9d
bug fix for some reason
gchenfc Apr 6, 2021
b50166b
move cdpr controller ilqr part 1
gchenfc Apr 6, 2021
32ce160
move cdpr controller ilqr part 2
gchenfc Apr 6, 2021
a4aca11
unit test for a tension distribution conroller
gchenfc Apr 6, 2021
decb82b
tension distribution controller - not quite working yet
gchenfc Apr 6, 2021
4f09d2a
Merge branch 'master' into feature/cablerobot-ilqr
gchenfc Apr 8, 2021
7599c33
PriorFactorVector6 - use gtd verison
gchenfc Apr 8, 2021
b9decde
paint parsing unit test
gchenfc Apr 8, 2021
10531d6
paint file parsing works
gchenfc Apr 11, 2021
3d2c17a
fix boolean parsing
gchenfc Apr 11, 2021
b35f1f3
begin trajectory tracking script
gchenfc Apr 11, 2021
7d2c038
clean up ipython notebook
gchenfc Apr 12, 2021
5113538
working simulation with iros logo
gchenfc Apr 12, 2021
6bf0e85
Pott09's mid-tension iLQR
gchenfc Apr 12, 2021
ae12b38
refactor plotting
gchenfc Apr 12, 2021
66c58ec
expose more options to python notebook
gchenfc Apr 12, 2021
c7fc779
Merge branch 'master' into feature/cablerobot-ilqr
gchenfc Apr 12, 2021
e8f8929
unit test for local feedback gains
gchenfc Apr 13, 2021
5750d5f
expand unit test to include twist and feedforward terms
gchenfc Apr 13, 2021
7a11d03
sequential elimination that preserves ordering in bayes net
gchenfc Apr 14, 2021
c2ea56f
block elimination
gchenfc Apr 14, 2021
a1e6d4e
reduce copy-pasta in EliminateSequential
gchenfc Apr 14, 2021
c27e258
preliminary gains calculation
gchenfc Apr 19, 2021
9d45d7f
feedforward calculation for gains
gchenfc Apr 19, 2021
f40df13
repackage controller gains with ff terms
gchenfc Apr 20, 2021
acf2376
bugfix in unit test: set "median" tension to 0
gchenfc Apr 20, 2021
a7fcd2a
unit test gains using infinite horizon approximation
gchenfc Apr 20, 2021
7499a7d
full IROS trajectory with iLQR
gchenfc Apr 20, 2021
6d0ea28
format comment line in customwrap.cpp
gchenfc Apr 20, 2021
822e27c
minor plotting improvements
gchenfc Apr 21, 2021
918daa7
iros trajectory - higher framerate
gchenfc Apr 21, 2021
8cb5045
id/fd were flip flopped
gchenfc Apr 21, 2021
2a551aa
update cdpr unit test with correct ID/FD and inertia
gchenfc Apr 21, 2021
340978f
python side of ID/FD with joint angles and winch inertia
gchenfc Apr 21, 2021
761168e
winch factor
gchenfc Apr 22, 2021
72d13d0
cable acceleration factor unit test
gchenfc Apr 22, 2021
9f8a0fe
CableAccelFactor passes unit test for simplified
gchenfc Apr 22, 2021
1daaf24
wrap WinchFactor and CableAccelerationFactor
gchenfc Apr 22, 2021
77bfd05
create and wrap TensionKey
gchenfc Apr 22, 2021
7d19163
cdpr with winch inertia passes unit tests :)
gchenfc Apr 22, 2021
fa05de8
update sim and iLQR to pass unit tests
gchenfc Apr 22, 2021
b1a9259
fix torque real values and plotting
gchenfc Apr 22, 2021
d618e65
add functions to visualize gains
gchenfc Apr 22, 2021
45599ad
update "zeroValues" to include joint accel and tension
gchenfc Apr 26, 2021
c1173fa
write out gains to .h file
gchenfc Apr 26, 2021
149501a
switch to ATL painting and make EE mass more realistic
gchenfc May 5, 2021
103f564
add raw trajectories to source control (git lfs)
gchenfc May 5, 2021
9b961b1
Merge branch 'master' into feature/gerry/cdprwinch
gchenfc Aug 26, 2021
6c8233f
WIP: fix some unit tests:
gchenfc Aug 28, 2021
e0508f0
remove debug statements
gchenfc Aug 28, 2021
8879146
WIP: bugfix in gtsam python wrapper: see branch:
gchenfc Aug 28, 2021
4c30d2c
WIP: cdpr_planar (post gtsam bugfix)
gchenfc Aug 28, 2021
23d2eda
WIP: cdpr_planar_sim (post gtsam bugfix)
gchenfc Aug 28, 2021
8f46cef
WIP: cdpr_controller_tension_dist (post gtsam bugfix)
gchenfc Aug 28, 2021
08997a5
WIP: cdpr_controller_ilqr (post gtsam bugfix)
gchenfc Aug 28, 2021
a44e6a1
cleanup
gchenfc Aug 29, 2021
dc709fc
parameter tweaking
gchenfc Aug 29, 2021
2b0e523
minor refactoring
gchenfc Sep 7, 2021
d1bb841
initial code: per-stroke optimization
gchenfc Sep 7, 2021
d33f09e
optimize utils.InitValues
gchenfc Sep 8, 2021
cee41cc
iLQR "initial guess" - doesn't help that much
gchenfc Sep 8, 2021
a3ba8be
make iLQR way faster by using less strict optim params
gchenfc Sep 8, 2021
6a8665c
update stroke tracking
gchenfc Sep 8, 2021
578610f
comments and easier running from command line
gchenfc Sep 8, 2021
9595f96
LQR-style plots for ICRA paper
gchenfc Oct 3, 2021
a0122ab
fix - don't need "intermediate control variables" - must have had a t…
gchenfc Oct 21, 2021
d36c9bc
Merge branch 'master' into feature/gerry/cdprwinch
gchenfc Jan 12, 2022
e2dc95d
Fix merge issues related to:
gchenfc Jan 12, 2022
d89c5e0
clean up large data-file diffs
gchenfc Jan 12, 2022
e9ba1b5
fix controller docstrings
gchenfc Jan 12, 2022
1d34bba
leftover merge fixes
gchenfc Jan 12, 2022
3353dc2
minor syntax updates
gchenfc Jun 3, 2022
e480144
Merge branch 'codex/pr331-additions-only' into feature/gerry/cdprwinch
dellaert Feb 14, 2026
377241b
Skip failing tests
dellaert Feb 14, 2026
fe116a9
Apply suggestions from code review
gchenfc Feb 18, 2026
3c15854
Merge branch 'master' into feature/gerry/cdprwinch
dellaert Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions gtdynamics/cablerobot/cablerobot.i
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,44 @@ class PriorFactor : gtsam::NonlinearFactor {
const gtsam::KeyFormatter &keyFormatter);
};

#include <gtdynamics/cablerobot/utils/CustomWrap.h>
gtsam::GaussianBayesNet* EliminateSequential(gtsam::GaussianFactorGraph graph,
const gtsam::Ordering &ordering);
gtsam::GaussianBayesNet* BlockEliminateSequential(
Comment thread
dellaert marked this conversation as resolved.
gtsam::GaussianFactorGraph graph, const gtdynamics::BlockOrdering &ordering);

// Tension Key Stuff
gtdynamics::DynamicsSymbol TensionKey(int j, int t = 0);

void InsertTension(gtsam::Values @values, int j, int t, double value);

void InsertTension(gtsam::Values @values, int j, double value);

double Tension(const gtsam::Values &values, int j, int t = 0);
Comment on lines +48 to +55
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter names in the .i file should match the C++ declarations exactly for wrapped functions. In CustomWrap.h, TensionKey/InsertTension/Tension use parameter name k for the timestep, but here it is t. Please align these parameter names to avoid wrapper-generation inconsistencies.

Copilot generated this review using guidance from repository custom instructions.

#include <gtdynamics/cablerobot/factors/WinchFactor.h>
class WinchParams {
double radius_;
double inertia_;
double staticFriction_;
double viscousFriction_;
WinchParams(double radius = 1, double inertia = 0, double staticFriction = 0,
double viscousFriction = 0);
};
class WinchFactor : gtsam::NonlinearFactor {
WinchFactor(gtsam::Key torque, gtsam::Key tension, gtsam::Key cableVelocity,
gtsam::Key cableAcceleration,
const gtsam::noiseModel::Base *cost_model, gtdynamics::WinchParams params);
void print(const string &s, const gtsam::KeyFormatter &keyFormatter);
};

#include <gtdynamics/cablerobot/factors/CableAccelerationFactor.h>
class CableAccelerationFactor : gtsam::NonlinearFactor {
CableAccelerationFactor(gtsam::Key ldot_key, gtsam::Key wTee_key,
gtsam::Key Vee_key, gtsam::Key VA_key,
const gtsam::noiseModel::Base *cost_model,
const gtsam::Point3 &wPb, const gtsam::Point3 &eePem);
void print(const string &s, const gtsam::KeyFormatter &keyFormatter);
};

} // namespace gtdynamics
4 changes: 2 additions & 2 deletions gtdynamics/cablerobot/src/cdpr_planar.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def priors_ik(self, ks=[], Ts=[], Vs=[]):
Vs (list, optional): List of Twists for each time step. Defaults to [[]].

Returns:
gtsam.NonlinearFactorGraph: The inverve kinematics prior factors
gtsam.NonlinearFactorGraph: The inverse kinematics prior factors
"""
graph = gtsam.NonlinearFactorGraph()
for k, T, V in zip(ks, Ts, Vs):
Expand All @@ -262,7 +262,7 @@ def priors_ik(self, ks=[], Ts=[], Vs=[]):
def priors_id(self, ks=[], torquess=[[]]):
"""Creates factors roughly corresponding to the inverse dynamics problem. While strictly
inverse dynamics in Lynch & Park refers to the problem of calculating joint accelerations
given joint torques, temproarily this function is more convenient which directly relates
given joint torques, temporarily this function is more convenient which directly relates
constrains joint torques (to obtain twist accelerations when used with dynamics_factors).

Args:
Expand Down
116 changes: 0 additions & 116 deletions gtdynamics/cablerobot/src/cdpr_planar_controller.py

This file was deleted.

135 changes: 84 additions & 51 deletions gtdynamics/cablerobot/src/cdpr_planar_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import gtsam
import gtdynamics as gtd
import numpy as np
from utils import MyLMParams

class CdprSimulator:
"""Simulates a cable robot forward in time, given a robot, initial state, and controller.
Expand Down Expand Up @@ -56,71 +57,86 @@ def __init__(self, cdpr, x0, controller, dt=0.01):
self.reset()

@staticmethod
def update_kinematics(cdpr, fg, x, k):
"""Runs IK to solve for the cable lengths and velocities at time step k
def update_kinematics(cdpr, x, k):
"""Runs IK to solve for the cable lengths and velocities at time step k.

This method creates and returns a new ``gtsam.Values`` containing the solved
kinematic quantities and does not modify the input ``x`` in place.
Args:
fg (gtsam.NonlineaFactorGraph): any previous factors, if applicable
x (gtsam.Values): Values object containing the current Pose and current Twist, plus any
other values that may be needed (as initial guesses) for the `fg` argument.
x (gtsam.Values): Values object containing at least the current Pose and current Twist
k (int): current time step

Returns:
tuple(gtsam.NonlinearFactorGraph, gtsam.Values): the updated factor graph and values
tuple(gtsam.NonlinearFactorGraph, gtsam.Values): the factor graph used to perform IK
and the solution values which includes the current Pose, Twist, JointAngles, and
JointVels.
"""
fg = gtsam.NonlinearFactorGraph()
lid = cdpr.ee_id()
# local copy of values
xk = gtsam.Values()
gtd.InsertPose(xk, lid, k, gtd.Pose(x, lid, k))
gtd.InsertTwist(xk, lid, k, gtd.Twist(x, lid, k))
# IK for this time step, graph
fg.push_back(cdpr.kinematics_factors(ks=[k]))
fg.push_back(
cdpr.priors_ik(ks=[k],
Ts=[gtd.Pose(x, cdpr.ee_id(), k)],
Vs=[gtd.Twist(x, cdpr.ee_id(), k)]))
fg.push_back(cdpr.priors_ik(ks=[k], values=xk))
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update_kinematics calls cdpr.priors_ik(ks=[k], values=xk), but Cdpr.priors_ik in cdpr_planar.py currently takes Ts/Vs and does not accept a values keyword argument. This will raise a TypeError at runtime; either add a values-based overload/parameter to priors_ik or change this call to pass Ts=[...] and Vs=[...].

Suggested change
fg.push_back(cdpr.priors_ik(ks=[k], values=xk))
fg.push_back(
cdpr.priors_ik(
ks=[k],
Ts=[gtd.Pose(xk, lid, k)],
Vs=[gtd.Twist(xk, lid, k)],
)
)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't recall whether everything should be switched to (ks, Ts, Vs) or to values. May need a different agent to assess this in the codebase.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another PR where you use @yetongatech 's CmOpt. So maybe take a look at that and see what you decided on? I can try to have the AI bring that up to speed, but I would need your guidance on this particular priors_ik refactor.

# IK initial estimate
for j in range(4):
gtd.InsertJointAngle(x, j, k, 0)
gtd.InsertJointVel(x, j, k, 0)
gtd.InsertJointAngle(xk, j, k, 0)
gtd.InsertJointVel(xk, j, k, 0)
# IK solve
result = gtsam.LevenbergMarquardtOptimizer(fg, x).optimize()
result = gtsam.LevenbergMarquardtOptimizer(fg, xk, MyLMParams()).optimize()
assert abs(fg.error(result)) < 1e-20, "inverse kinematics didn't converge"
x.update(result)
return fg, x
xk.update(result)
return fg, xk

@staticmethod
def update_dynamics(cdpr, fg, x, u, k, dt):
def update_dynamics(cdpr, x, u, k, dt):
"""Runs ID to solve for the twistAccel, and also runs collocation to get the next timestep
Pose/Twist
Pose/Twist. Modifies x inplace!!!

Args:
cdpr (Cdpr): the cable robot
fg (gtsam.NonlinearFactorGraph): a factor graph containing any previous factors
x (gtsam.Values): Values object containing at least the current Pose and Twist, and any
other values that may be needed (as initial guesses) for the `fg` argument
x (gtsam.Values): Values object containing at least the current Pose and Twist
u (gtsam.Values): The current joint torques
k (int): The current time index
dt (float): the time slice duration

Returns:
tuple(gtsam.NonlinearFactorGraph, gtsam.Values): the factor graph with added factors,
and the solution Values which adds the TwistAccel, next Pose, and next Twist to the `x`
argument.
tuple(gtsam.NonlinearFactorGraph, gtsam.Values): the factor graph used to update the
dynamics, and the solution Values which consists of the Pose, Twist, torque, TwistAccel,
next Pose, and next Twist.
"""
# ID for this timestep + collocation to next time step
fg = gtsam.NonlinearFactorGraph()
lid = cdpr.ee_id()
# local copy of values
xd = gtsam.Values()
xd.insert(0, dt)
gtd.InsertPose(xd, lid, k, gtd.Pose(x, lid, k))
gtd.InsertTwist(xd, lid, k, gtd.Twist(x, lid, k))
# FD for this timestep + collocation to next time step
fg.push_back(cdpr.dynamics_factors(ks=[k]))
fg.push_back(cdpr.collocation_factors(ks=[k], dt=dt))
# ID priors (torque inputs)
fg.push_back(
cdpr.priors_id(ks=[k], torquess=[[gtd.Torque(u, ji, k) for ji in range(4)]]))
# ID initial guess
# priors (pose/twist and torque inputs)
fg.push_back(cdpr.priors_ik(ks=[k], values=xd))
fg.push_back(cdpr.priors_fd(ks=[k], values=u))
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update_dynamics uses cdpr.priors_fd(ks=[k], values=u), but Cdpr.priors_fd in cdpr_planar.py currently accepts VAs (twist accelerations) and does not accept a values keyword argument. As written this will throw TypeError; adjust the call to match the existing API or update priors_fd to accept the intended input type.

Suggested change
fg.push_back(cdpr.priors_fd(ks=[k], values=u))
fg.push_back(cdpr.priors_fd(ks=[k], VAs=u))

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

# FD initial guess
for ji in range(4):
gtd.InsertTorque(x, ji, k, gtd.Torque(u, ji, k))
gtd.InsertWrench(x, cdpr.ee_id(), ji, k, np.zeros(6))
gtd.InsertPose(x, cdpr.ee_id(), k+1, gtsam.Pose3(gtsam.Rot3(), (1.5, 0, 1.5)))
gtd.InsertTwist(x, cdpr.ee_id(), k+1, np.zeros(6))
gtd.InsertTwistAccel(x, cdpr.ee_id(), k, np.zeros(6))
gtd.InsertJointVel(xd, ji, k, 0)
gtd.InsertJointAccel(xd, ji, k, 0)
gtd.InsertTorque(xd, ji, k, gtd.Torque(u, ji, k))
gtd.InsertTension(xd, ji, k,
gtd.Torque(u, ji, k) / cdpr.params.winch_params.radius_)
gtd.InsertWrench(xd, cdpr.ee_id(), ji, k, np.zeros(6))
Comment on lines +127 to +130
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cdpr.params.winch_params is accessed here, but CdprParams in cdpr_planar.py does not define winch_params. This will raise AttributeError unless it is initialized elsewhere; either add winch_params to CdprParams.__init__ or guard/remove this access.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be easy for an agent to fix. Might have been a merge conflict I made at some point.

gtd.InsertPose(xd, cdpr.ee_id(), k + 1, gtd.Pose(x, lid, k))
gtd.InsertTwist(xd, cdpr.ee_id(), k + 1, gtd.Twist(x, lid, k))
gtd.InsertTwistAccel(xd, cdpr.ee_id(), k, np.zeros(6))
# optimize
result = gtsam.LevenbergMarquardtOptimizer(fg, x).optimize()
assert abs(fg.error(result)) < 1e-20, "dynamics simulation didn't converge"
x.update(result)
return fg, x
result = gtsam.LevenbergMarquardtOptimizer(fg, xd, MyLMParams()).optimize()
assert abs(fg.error(result)) < 1e-15, "dynamics simulation didn't converge: {:.5e}".format(
abs(fg.error(result)))
xd.update(result)
return fg, xd

def step(self, verbose=False):
"""Performs one time step of the simulation, which consists of:
Expand All @@ -133,21 +149,38 @@ def step(self, verbose=False):

Returns:
gtsam.Values: The new values object containing the current state and next Pose+Twist.
"""
if verbose:
print('time step: {:4d} -- EE position: ({:.2f}, {:.2f}, {:.2f})'.format(
self.k,
*gtd.Pose(self.x, self.cdpr.ee_id(), self.k).translation()), end=' -- ')
self.update_kinematics(self.cdpr, self.fg, self.x, self.k)
if self.k == 0:
self.x.insert(0, self.dt)
u = self.controller.update(self.x, self.k)
"""
# setup
x, lid, k, dt = self.x, self.cdpr.ee_id(), self.k, self.dt

# kinematics
_, xk = self.update_kinematics(self.cdpr, x, k)
# controller
u = self.controller.update(xk, k)
# dynamics
_, xd = self.update_dynamics(self.cdpr, x, u, k, dt)

# update x
for ji in range(4):
gtd.InsertJointAngle(x, ji, k, gtd.JointAngle(xk, ji, k))
gtd.InsertJointVel(x, ji, k, gtd.JointVel(xk, ji, k))
gtd.InsertTorque(x, ji, k, gtd.Torque(u, ji, k))
gtd.InsertTwistAccel(x, lid, k, gtd.TwistAccel(xd, lid, k))
gtd.InsertPose(x, lid, k + 1, gtd.Pose(xd, lid, k + 1))
gtd.InsertTwist(x, lid, k + 1, gtd.Twist(xd, lid, k + 1))

# debug
if verbose:
print('time step: {:4d}'.format(k), end=' -- ')
print('EE position: ({:.2f}, {:.2f}, {:.2f})'.format(
*gtd.Pose(x, lid, k).translation()), end=' -- ')
print('next: ({:.2f}, {:.2f}, {:.2f})'.format(
*gtd.Pose(x, lid, k+1).translation()), end=' -- ')
print('control torques: {:.2e}, {:.2e}, {:.2e}, {:.2e}'.format(
*[gtd.Torque(u, ji, self.k) for ji in range(4)]))
self.update_dynamics(self.cdpr, self.fg, self.x, u, self.k, self.dt)
*[gtd.Torque(u, ji, k) for ji in range(4)]))

self.k += 1
return self.x
return x

def run(self, N=100, verbose=False):
"""Runs the simulation
Expand All @@ -158,8 +191,8 @@ def run(self, N=100, verbose=False):

Returns:
gtsam.Values: The values object containing all the data from the simulation.
"""
for k in range(N):
"""
for _ in range(N):
self.step(verbose=verbose)
return self.x

Expand Down
7 changes: 4 additions & 3 deletions gtdynamics/cablerobot/src/gerry00_planar_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from gtsam import Pose3, Rot3

from cdpr_planar import Cdpr
from cdpr_planar_controller import CdprControllerBase
from cdpr_planar_sim import cdpr_sim
from cdpr_controller import CdprControllerBase
from cdpr_planar_sim import CdprSimulator


class DummyController(CdprControllerBase):
Expand All @@ -43,7 +43,8 @@ def main():
gtd.InsertPose(x0, cdpr.ee_id(), 0, Pose3(Rot3(), (1.5, 0, 1.5)))
gtd.InsertTwist(x0, cdpr.ee_id(), 0, np.zeros(6))
# run simulation
result = cdpr_sim(cdpr, x0, controller, dt=dt, N=N)
sim = CdprSimulator(cdpr, x0, controller, dt=dt)
result = sim.run(N=N)
poses = [gtd.Pose(result, cdpr.ee_id(), k) for k in range(N)]

plt.figure(1)
Expand Down
Loading
Loading