Skip to content

Commit 33f1eaa

Browse files
Merge remote-tracking branch 'origin/main' into rolling
2 parents efe7f89 + 9b808ee commit 33f1eaa

9 files changed

Lines changed: 125 additions & 52 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# Changelog
22

3+
## [1.2.1](https://github.com/utiasDSL/crisp_controllers/compare/v1.2.0...v1.2.1) (2025-10-21)
4+
5+
6+
### Bug Fixes
7+
8+
* Rollback jittery controller ([5d78af9](https://github.com/utiasDSL/crisp_controllers/commit/5d78af9f9f1b2b2aa3bfe089eb6b33a03ce33858))
9+
10+
## [1.2.0](https://github.com/utiasDSL/crisp_controllers/compare/v1.1.0...v1.2.0) (2025-10-18)
11+
12+
13+
### Features
14+
15+
* Fix Cartesian Controller to work with respect to the base_frame indicated in the parameters and not just world frame ([#10](https://github.com/utiasDSL/crisp_controllers/issues/10)) ([da805ab](https://github.com/utiasDSL/crisp_controllers/commit/da805ab45691a66279ca88138fc2583d5e77319b))
16+
317
## [1.1.0](https://github.com/utiasDSL/crisp_controllers/compare/v1.0.0...v1.1.0) (2025-09-08)
418

519

CMakeLists.txt

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,37 @@ endif()
1818
# Enable precompiled headers
1919
option(USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
2020

21-
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
22-
add_compile_options(-Wall -Wextra -Wpedantic)
21+
option(CHECK_TIDY "Adds clang-tidy tests" OFF)
22+
23+
option(ENABLE_NATIVE_OPTIMIZATION
24+
"Enable CPU-specific optimizations (-march=native)"
25+
OFF
26+
)
27+
28+
# Default to release build
29+
if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
30+
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
2331
endif()
2432

25-
option(CHECK_TIDY "Adds clang-tidy tests" OFF)
33+
# Compiler flags by build type
34+
add_library(project_compile_options INTERFACE)
35+
target_compile_options(project_compile_options INTERFACE
36+
# Warnings (build only)
37+
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic>
38+
39+
# Debug
40+
$<$<AND:$<CONFIG:Debug>,$<CXX_COMPILER_ID:GNU,Clang>>:-O0 -g>
41+
42+
# Release
43+
$<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:GNU,Clang>>:-O3 -DNDEBUG>
44+
)
45+
46+
# Optional CPU-specific optimizations (non-portable)
47+
if(ENABLE_NATIVE_OPTIMIZATION)
48+
target_compile_options(project_compile_options INTERFACE
49+
$<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:GNU,Clang>>:-march=native>
50+
)
51+
endif()
2652

2753
# find dependencies
2854
find_package(ament_cmake REQUIRED)
@@ -65,6 +91,11 @@ add_library(
6591
src/twist_broadcaster.cpp
6692
)
6793

94+
target_link_libraries(${PROJECT_NAME}
95+
PRIVATE
96+
project_compile_options
97+
)
98+
6899
target_include_directories(${PROJECT_NAME}
69100
PUBLIC
70101
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>

docs/getting_started.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ python -c "import crisp_gym"
245245

246246
1. This will set some environment variable pre-installation as well as checking that you defined the previous script properly.
247247

248+
You can also check that your configs are set up with:
249+
250+
```sh
251+
pixi shell crisp-check-config
252+
```
253+
248254
If the previous steps worked, then you are good to go.
249255

250256
### Teleoperation: Record data in [LeRobotFormat](https://github.com/huggingface/lerobot)
@@ -263,7 +269,7 @@ For your specific setup you need to:
263269

264270
Then, to record data use:
265271
```sh
266-
pixi run -e humble-lerobot python scripts/record_lerobot_format_leader_follower.py \
272+
pixi run -e humble-lerobot crisp-record-leader-follower \
267273
--repo-id <your_account>/<repo_name> # (1)!
268274
```
269275

@@ -366,7 +372,7 @@ a similar record script to [`scripts/record_lerobot_format_leader_follower.py`](
366372

367373
You can use LeRobot train scripts to train a policy simply by running:
368374
```sh
369-
pixi run -e lerobot python -m lerobot.scripts.train \
375+
pixi run -e lerobot python -m lerobot.scripts.lerobot-train \
370376
--dataset.repo_id=<your_account>/<repo_name> \
371377
--policy.type=diffusion \
372378
--policy.push_to_hub=false
@@ -382,7 +388,7 @@ Check [LeRobot](https://github.com/huggingface/lerobot) for more information.
382388

383389
After training with LeRobot, you can deploy the policy with:
384390
```sh
385-
pixi run -e humble-lerobot python scripts/deploy_policy.py # (1)!
391+
pixi run -e humble-lerobot crisp-deploy-policy # (1)!
386392
```
387393

388394
1. The script will interactively allow you to choose a model inside `outputs/train`. If you want to explicitly pass a path you can override it with `--path`

docs/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ _If you use this work, please cite it using the [bibtex](#citing) below._
2020

2121
Check the [controllers (CRISP controllers) :simple-github:](https://github.com/utiasDSL/crisp_controllers) , robot [demos (CRISP controllers demos) :simple-github:](https://github.com/utiasDSL/crisp_controllers_demos), a simple [python interface (CRISP_PY) :simple-github:](https://github.com/utiasDSL/crisp_py), and a [Gymnasium wrapper (CRISP_GYM) :simple-github:](https://github.com/utiasDSL/crisp_gym) for real-world experiments.
2222

23+
!!! info "Aloha gripper for Manipulators"
24+
Check out [aloha4franka](https://tum-lsy.github.io/aloha4franka/) for the gripper used in the videos.
25+
26+
2327
| <video src="media/pap_demo.mp4" playsinline muted controls loop autoplay width="800"/> | <video src="media/policy.mp4" playsinline controls="true" loop="true" autoplay="true" width="800"/> |
2428
|:--:|:--:|
2529
| Robot teleoperated using a Follower-Leader system in [CRISP_GYM :simple-github:](https://github.com/utiasDSL/crisp_gym) | Diffusion Policy trained and deployed from the same demonstrations |

docs/misc/create_own_config.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# How to create your own config
22

33
You can bring your own config to CRISP, so that you are able to create your own environments, teleoperation setup, controllers...
4+
45
1. First create a config folder and give it the following structure (you do not need to add all config folders):
56

67
```bash
@@ -30,10 +31,10 @@ export CRISP_CONFIG_PATH=/path/to/my_crisp_configs
3031
3. Check that the config works.
3132

3233
```bash
33-
pixi run python scripts/check_config.py
34+
crisp-check-config # Do this inside an environment with crisp_py or crisp_gym installed
3435
```
3536

36-
This should output your config if it can loaded properly
37+
This should output your config if it can be loaded properly
3738

3839
---
3940

include/crisp_controllers/pose_broadcaster.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ class PoseBroadcaster
6464
{"JointModelRUBX", "JointModelRUBY", "JointModelRUBZ"};
6565

6666
Eigen::VectorXd q;
67-
rclcpp::Time last_publish_time_;
67+
68+
rclcpp::Duration publish_elapsed_{0,0};
69+
rclcpp::Duration publish_interval_{0,0};
6870
};
6971

7072
} // namespace crisp_controllers

include/crisp_controllers/twist_broadcaster.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ class TwistBroadcaster
6565

6666
Eigen::VectorXd q;
6767
Eigen::VectorXd q_dot;
68-
rclcpp::Time last_publish_time_;
68+
69+
rclcpp::Duration publish_elapsed_{0,0};
70+
rclcpp::Duration publish_interval_{0,0};
6971
};
7072

7173
} // namespace crisp_controllers

src/pose_broadcaster.cpp

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ PoseBroadcaster::state_interface_configuration() const {
3535

3636
controller_interface::return_type
3737
PoseBroadcaster::update(const rclcpp::Time &time,
38-
const rclcpp::Duration & /*period*/) {
38+
const rclcpp::Duration &period) {
3939

4040
size_t num_joints = params_.joints.size();
4141
Eigen::VectorXd q_pin = Eigen::VectorXd::Zero(model_.nq);
@@ -64,28 +64,27 @@ PoseBroadcaster::update(const rclcpp::Time &time,
6464
Eigen::Quaterniond(current_pose.rotation());
6565

6666
// Decide whether to publish the pose or not
67-
bool should_publish = true;
68-
if (params_.publish_frequency > 0.0) {
69-
auto time_since_last = time - last_publish_time_;
70-
auto min_interval = rclcpp::Duration::from_seconds(1.0 / params_.publish_frequency);
71-
should_publish = time_since_last >= min_interval;
72-
}
73-
67+
publish_elapsed_ = publish_elapsed_ + period;
68+
bool should_publish = (publish_elapsed_ >= publish_interval_) ||
69+
(publish_interval_.nanoseconds() == 0);
7470
if (should_publish && realtime_pose_publisher_)
7571
{
76-
geometry_msgs::msg::PoseStamped pose_msg;
77-
pose_msg.header.stamp = time;
78-
pose_msg.header.frame_id = params_.base_frame;
79-
pose_msg.pose.position.x = current_pose.translation()[0];
80-
pose_msg.pose.position.y = current_pose.translation()[1];
81-
pose_msg.pose.position.z = current_pose.translation()[2];
82-
pose_msg.pose.orientation.x = current_quaternion.x();
83-
pose_msg.pose.orientation.y = current_quaternion.y();
84-
pose_msg.pose.orientation.z = current_quaternion.z();
85-
pose_msg.pose.orientation.w = current_quaternion.w();
86-
87-
if (realtime_pose_publisher_->try_publish(pose_msg)) {
88-
last_publish_time_ = time;
72+
if (realtime_pose_publisher_->trylock()) {
73+
auto & pose_msg = realtime_pose_publisher_->msg_;
74+
pose_msg.header.stamp = time;
75+
pose_msg.header.frame_id = params_.base_frame;
76+
pose_msg.pose.position.x = current_pose.translation()[0];
77+
pose_msg.pose.position.y = current_pose.translation()[1];
78+
pose_msg.pose.position.z = current_pose.translation()[2];
79+
pose_msg.pose.orientation.x = current_quaternion.x();
80+
pose_msg.pose.orientation.y = current_quaternion.y();
81+
pose_msg.pose.orientation.z = current_quaternion.z();
82+
pose_msg.pose.orientation.w = current_quaternion.w();
83+
realtime_pose_publisher_->unlockAndPublish();
84+
85+
publish_elapsed_ = publish_elapsed_ - publish_interval_;
86+
// clamp to publish only 1 time even if missed multiple intervals
87+
publish_elapsed_ = std::min(publish_elapsed_, publish_interval_);
8988
}
9089
}
9190

@@ -170,12 +169,19 @@ CallbackReturn PoseBroadcaster::on_configure(
170169
std::make_shared<realtime_tools::RealtimePublisher<geometry_msgs::msg::PoseStamped>>(
171170
pose_publisher_);
172171

173-
last_publish_time_ = this->get_node()->now();
172+
if (params_.publish_frequency > 0.0) {
173+
publish_interval_ = rclcpp::Duration::from_seconds(1.0 / params_.publish_frequency);
174+
} else {
175+
publish_interval_ = rclcpp::Duration(0, 0); // publish every cycle
176+
}
177+
174178
return CallbackReturn::SUCCESS;
175179
}
176180

177181
CallbackReturn PoseBroadcaster::on_activate(
178182
const rclcpp_lifecycle::State & /*previous_state*/) {
183+
// reset publish time accumulation
184+
publish_elapsed_ = rclcpp::Duration(0, 0);
179185
return CallbackReturn::SUCCESS;
180186
}
181187

src/twist_broadcaster.cpp

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ TwistBroadcaster::state_interface_configuration() const {
3636

3737
controller_interface::return_type
3838
TwistBroadcaster::update(const rclcpp::Time &time,
39-
const rclcpp::Duration & /*period*/) {
39+
const rclcpp::Duration &period) {
4040

4141
size_t num_joints = params_.joints.size();
4242
Eigen::VectorXd q_pin = Eigen::VectorXd::Zero(model_.nq);
@@ -69,27 +69,27 @@ TwistBroadcaster::update(const rclcpp::Time &time,
6969
auto current_velocity = pinocchio::getFrameVelocity(model_, data_, end_effector_frame_id);
7070

7171
// Decide whether to publish the twist or not
72-
bool should_publish = true;
73-
if (params_.publish_frequency > 0.0) {
74-
auto time_since_last = time - last_publish_time_;
75-
auto min_interval = rclcpp::Duration::from_seconds(1.0 / params_.publish_frequency);
76-
should_publish = time_since_last >= min_interval;
77-
}
72+
publish_elapsed_ = publish_elapsed_ + period;
73+
bool should_publish = (publish_elapsed_ >= publish_interval_) ||
74+
(publish_interval_.nanoseconds() == 0);
7875

7976
if (should_publish && realtime_twist_publisher_)
8077
{
81-
geometry_msgs::msg::TwistStamped twist_msg;
82-
twist_msg.header.stamp = time;
83-
twist_msg.header.frame_id = params_.end_effector_frame;
84-
twist_msg.twist.linear.x = current_velocity.linear()[0];
85-
twist_msg.twist.linear.y = current_velocity.linear()[1];
86-
twist_msg.twist.linear.z = current_velocity.linear()[2];
87-
twist_msg.twist.angular.x = current_velocity.angular()[0];
88-
twist_msg.twist.angular.y = current_velocity.angular()[1];
89-
twist_msg.twist.angular.z = current_velocity.angular()[2];
90-
91-
if (realtime_twist_publisher_->try_publish(twist_msg)) {
92-
last_publish_time_ = time;
78+
if (realtime_twist_publisher_->trylock()) {
79+
auto & twist_msg = realtime_twist_publisher_->msg_;
80+
twist_msg.header.stamp = time;
81+
twist_msg.header.frame_id = params_.end_effector_frame;
82+
twist_msg.twist.linear.x = current_velocity.linear()[0];
83+
twist_msg.twist.linear.y = current_velocity.linear()[1];
84+
twist_msg.twist.linear.z = current_velocity.linear()[2];
85+
twist_msg.twist.angular.x = current_velocity.angular()[0];
86+
twist_msg.twist.angular.y = current_velocity.angular()[1];
87+
twist_msg.twist.angular.z = current_velocity.angular()[2];
88+
realtime_twist_publisher_->unlockAndPublish();
89+
90+
publish_elapsed_ = publish_elapsed_ - publish_interval_;
91+
// clamp to publish only 1 time even if missed multiple intervals
92+
publish_elapsed_ = std::min(publish_elapsed_, publish_interval_);
9393
}
9494
}
9595

@@ -175,12 +175,19 @@ CallbackReturn TwistBroadcaster::on_configure(
175175
std::make_shared<realtime_tools::RealtimePublisher<geometry_msgs::msg::TwistStamped>>(
176176
twist_publisher_);
177177

178-
last_publish_time_ = this->get_node()->now();
178+
if (params_.publish_frequency > 0.0) {
179+
publish_interval_ = rclcpp::Duration::from_seconds(1.0 / params_.publish_frequency);
180+
} else {
181+
publish_interval_ = rclcpp::Duration(0, 0); // publish every cycle
182+
}
183+
179184
return CallbackReturn::SUCCESS;
180185
}
181186

182187
CallbackReturn TwistBroadcaster::on_activate(
183188
const rclcpp_lifecycle::State & /*previous_state*/) {
189+
// reset publish time accumulation
190+
publish_elapsed_ = rclcpp::Duration(0, 0);
184191
return CallbackReturn::SUCCESS;
185192
}
186193

0 commit comments

Comments
 (0)