Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mecanum #23

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class Odometry
/// \param time Current time
/// \return true if the odometry is actually updated
bool update(
const double wheel_front_left_vel, const double wheel_rear_left_vel, const double wheel_rear_right_vel,
const double wheel_front_right_vel, const double dt);
const double wheel_front_left_vel, const double wheel_rear_left_vel,
const double wheel_rear_right_vel, const double wheel_front_right_vel, const double dt);

/// \return position (x component) [m]
double getX() const { return position_x_in_base_frame_; }
Expand All @@ -72,7 +72,8 @@ class Odometry
/// \param sum_of_robot_center_projection_on_X_Y_axis Wheels geometric param
/// (used in mecanum wheels' ik) [m]
/// \param wheels_radius Wheels radius [m]
void setWheelsParams(const double sum_of_robot_center_projection_on_X_Y_axis, const double wheels_radius);
void setWheelsParams(
const double sum_of_robot_center_projection_on_X_Y_axis, const double wheels_radius);

private:
/// Current timestamp:
Expand Down
48 changes: 32 additions & 16 deletions mecanum_drive_controller/src/mecanum_drive_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ controller_interface::CallbackReturn MecanumDriveController::on_configure(
{
params_ = param_listener_->get_params();

auto prepare_lists_with_joint_names = [&command_joints = this->command_joint_names_, &state_joints = this->state_joint_names_](const std::size_t index, const std::string & command_joint_name, const std::string & state_joint_name)
auto prepare_lists_with_joint_names =
[&command_joints = this->command_joint_names_, &state_joints = this->state_joint_names_](
const std::size_t index, const std::string & command_joint_name,
const std::string & state_joint_name)
{
command_joints[index] = command_joint_name;
if (state_joint_name.empty())
Expand All @@ -91,10 +94,18 @@ controller_interface::CallbackReturn MecanumDriveController::on_configure(
state_joint_names_.resize(4);

// The joint names are sorted according to the order documented in the header file!
prepare_lists_with_joint_names(FRONT_LEFT, params_.front_left_wheel_command_joint_name, params_.front_left_wheel_state_joint_name);
prepare_lists_with_joint_names(FRONT_RIGHT, params_.front_right_wheel_command_joint_name, params_.front_right_wheel_state_joint_name);
prepare_lists_with_joint_names(REAR_RIGHT, params_.rear_right_wheel_command_joint_name, params_.rear_right_wheel_state_joint_name);
prepare_lists_with_joint_names(REAR_LEFT, params_.rear_left_wheel_command_joint_name, params_.rear_left_wheel_state_joint_name);
prepare_lists_with_joint_names(
FRONT_LEFT, params_.front_left_wheel_command_joint_name,
params_.front_left_wheel_state_joint_name);
prepare_lists_with_joint_names(
FRONT_RIGHT, params_.front_right_wheel_command_joint_name,
params_.front_right_wheel_state_joint_name);
prepare_lists_with_joint_names(
REAR_RIGHT, params_.rear_right_wheel_command_joint_name,
params_.rear_right_wheel_state_joint_name);
prepare_lists_with_joint_names(
REAR_LEFT, params_.rear_left_wheel_command_joint_name,
params_.rear_left_wheel_state_joint_name);

// Set wheel params for the odometry computation
odometry_.setWheelsParams(
Expand Down Expand Up @@ -362,8 +373,8 @@ controller_interface::return_type MecanumDriveController::update_and_write_comma
{
// Estimate twist (using joint information) and integrate
odometry_.update(
wheel_front_left_state_vel, wheel_rear_left_state_vel, wheel_rear_right_state_vel, wheel_front_right_state_vel,
period.seconds());
wheel_front_left_state_vel, wheel_rear_left_state_vel, wheel_rear_right_state_vel,
wheel_front_right_state_vel, period.seconds());
}

// INVERSE KINEMATICS (move robot).
Expand Down Expand Up @@ -417,11 +428,12 @@ controller_interface::return_type MecanumDriveController::update_and_write_comma
params_.kinematics.sum_of_robot_center_projection_on_X_Y_axis *
velocity_in_center_frame_angular_z_);

// Set wheels velocities - The joint names are sorted accoring to the order documented in the header file!
command_interfaces_[FRONT_LEFT].set_value(wheel_front_left_vel);
command_interfaces_[FRONT_RIGHT].set_value(wheel_front_right_vel);
command_interfaces_[REAR_RIGHT].set_value(wheel_rear_right_vel);
command_interfaces_[REAR_LEFT].set_value(wheel_rear_left_vel);
// Set wheels velocities - The joint names are sorted according to the order documented in the
// header file!
command_interfaces_[FRONT_LEFT].set_value(wheel_front_left_vel);
command_interfaces_[FRONT_RIGHT].set_value(wheel_front_right_vel);
command_interfaces_[REAR_RIGHT].set_value(wheel_rear_right_vel);
command_interfaces_[REAR_LEFT].set_value(wheel_rear_left_vel);
}
else
{
Expand Down Expand Up @@ -463,10 +475,14 @@ controller_interface::return_type MecanumDriveController::update_and_write_comma
if (controller_state_publisher_->trylock())
{
controller_state_publisher_->msg_.header.stamp = get_node()->now();
controller_state_publisher_->msg_.front_left_wheel_velocity = state_interfaces_[FRONT_LEFT].get_value();
controller_state_publisher_->msg_.front_right_wheel_velocity = state_interfaces_[FRONT_RIGHT].get_value();
controller_state_publisher_->msg_.back_right_wheel_velocity = state_interfaces_[REAR_RIGHT].get_value();
controller_state_publisher_->msg_.back_left_wheel_velocity = state_interfaces_[REAR_LEFT].get_value();
controller_state_publisher_->msg_.front_left_wheel_velocity =
state_interfaces_[FRONT_LEFT].get_value();
controller_state_publisher_->msg_.front_right_wheel_velocity =
state_interfaces_[FRONT_RIGHT].get_value();
controller_state_publisher_->msg_.back_right_wheel_velocity =
state_interfaces_[REAR_RIGHT].get_value();
controller_state_publisher_->msg_.back_left_wheel_velocity =
state_interfaces_[REAR_LEFT].get_value();
controller_state_publisher_->msg_.reference_velocity.linear.x = reference_interfaces_[0];
controller_state_publisher_->msg_.reference_velocity.linear.y = reference_interfaces_[1];
controller_state_publisher_->msg_.reference_velocity.angular.z = reference_interfaces_[2];
Expand Down
4 changes: 2 additions & 2 deletions mecanum_drive_controller/src/odometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ void Odometry::init(
}

bool Odometry::update(
const double wheel_front_left_vel, const double wheel_rear_left_vel, const double wheel_rear_right_vel,
const double wheel_front_right_vel, const double dt)
const double wheel_front_left_vel, const double wheel_rear_left_vel,
const double wheel_rear_right_vel, const double wheel_front_right_vel, const double dt)
{
/// We cannot estimate the speed with very small time intervals:
// const double dt = (time - timestamp_).toSec();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ TEST(TestLoadMecanumDriveController, load_controller)
std::make_shared<rclcpp::executors::SingleThreadedExecutor>();

controller_manager::ControllerManager cm(
std::make_unique<hardware_interface::ResourceManager>(
ros2_control_test_assets::minimal_robot_urdf),
executor, "test_controller_manager");
executor, ros2_control_test_assets::minimal_robot_urdf, true, "test_controller_manager");

ASSERT_NE(
cm.load_controller(
Expand Down
10 changes: 5 additions & 5 deletions mecanum_drive_controller/test/test_mecanum_drive_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ TEST_F(
// reference_msg is published with provided time stamp when publish_commands( time_stamp)
// is called
publish_commands(controller_->get_node()->now());
ASSERT_TRUE(controller_->wait_for_commands(executor));
controller_->wait_for_commands(executor);

ASSERT_EQ(
controller_->update(controller_->get_node()->now(), rclcpp::Duration::from_seconds(0.01)),
Expand Down Expand Up @@ -263,7 +263,7 @@ TEST_F(MecanumDriveControllerTest, when_reference_msg_is_too_old_expect_unset_re
publish_commands(
controller_->get_node()->now() - controller_->ref_timeout_ -
rclcpp::Duration::from_seconds(0.1));
ASSERT_TRUE(controller_->wait_for_commands(executor));
controller_->wait_for_commands(executor);
ASSERT_EQ(old_timestamp, (*(controller_->input_ref_.readFromNonRT()))->header.stamp);
EXPECT_TRUE(std::isnan((reference)->twist.linear.x));
EXPECT_TRUE(std::isnan((reference)->twist.linear.y));
Expand Down Expand Up @@ -293,7 +293,7 @@ TEST_F(
// is called
publish_commands(rclcpp::Time(0));

ASSERT_TRUE(controller_->wait_for_commands(executor));
controller_->wait_for_commands(executor);
ASSERT_EQ(old_timestamp.sec, (*(controller_->input_ref_.readFromNonRT()))->header.stamp.sec);
EXPECT_FALSE(std::isnan((*(controller_->input_ref_.readFromNonRT()))->twist.linear.x));
EXPECT_FALSE(std::isnan((*(controller_->input_ref_.readFromNonRT()))->twist.angular.z));
Expand Down Expand Up @@ -324,7 +324,7 @@ TEST_F(MecanumDriveControllerTest, when_message_has_valid_timestamp_expect_refer
// is called
publish_commands(controller_->get_node()->now());

ASSERT_TRUE(controller_->wait_for_commands(executor));
controller_->wait_for_commands(executor);
EXPECT_FALSE(std::isnan((*(controller_->input_ref_.readFromNonRT()))->twist.linear.x));
EXPECT_FALSE(std::isnan((*(controller_->input_ref_.readFromNonRT()))->twist.angular.z));
EXPECT_EQ((*(controller_->input_ref_.readFromNonRT()))->twist.linear.x, 1.5);
Expand Down Expand Up @@ -555,7 +555,7 @@ TEST_F(
// is called
publish_commands(controller_->get_node()->now());

ASSERT_TRUE(controller_->wait_for_commands(executor));
controller_->wait_for_commands(executor);

EXPECT_FALSE(std::isnan((*(controller_->input_ref_.readFromNonRT()))->twist.linear.x));
EXPECT_FALSE(std::isnan((*(controller_->input_ref_.readFromNonRT()))->twist.linear.y));
Expand Down
51 changes: 25 additions & 26 deletions mecanum_drive_controller/test/test_mecanum_drive_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include "hardware_interface/types/hardware_interface_return_values.hpp"
#include "hardware_interface/types/hardware_interface_type_values.hpp"
#include "mecanum_drive_controller/mecanum_drive_controller.hpp"
#include "rclcpp/executor.hpp"
#include "rclcpp/executors.hpp"
#include "rclcpp/parameter_value.hpp"
#include "rclcpp/time.hpp"
#include "rclcpp/utilities.hpp"
Expand Down Expand Up @@ -85,13 +87,7 @@ class TestableMecanumDriveController : public mecanum_drive_controller::MecanumD
controller_interface::CallbackReturn on_configure(
const rclcpp_lifecycle::State & previous_state) override
{
auto ret = mecanum_drive_controller::MecanumDriveController::on_configure(previous_state);
// Only if on_configure is successful create subscription
if (ret == CallbackReturn::SUCCESS)
{
ref_subscriber_wait_set_.add_subscription(ref_subscriber_);
}
return ret;
return mecanum_drive_controller::MecanumDriveController::on_configure(previous_state);
}

controller_interface::CallbackReturn on_activate(
Expand All @@ -105,30 +101,25 @@ class TestableMecanumDriveController : public mecanum_drive_controller::MecanumD
* @brief wait_for_command blocks until a new ControllerReferenceMsg is received.
* Requires that the executor is not spinned elsewhere between the
* message publication and the call to this function.
*
* @return true if new ControllerReferenceMsg msg was received, false if timeout.
*/
bool wait_for_command(
rclcpp::Executor & executor, rclcpp::WaitSet & subscriber_wait_set,
void wait_for_command(
rclcpp::Executor & executor,
const std::chrono::milliseconds & timeout = std::chrono::milliseconds{500})
{
bool success = subscriber_wait_set.wait(timeout).kind() == rclcpp::WaitResultKind::Ready;
if (success)
auto until = get_node()->get_clock()->now() + timeout;
while (get_node()->get_clock()->now() < until)
{
executor.spin_some();
std::this_thread::sleep_for(std::chrono::microseconds(10));
}
return success;
}

bool wait_for_commands(
void wait_for_commands(
rclcpp::Executor & executor,
const std::chrono::milliseconds & timeout = std::chrono::milliseconds{500})
{
return wait_for_command(executor, ref_subscriber_wait_set_, timeout);
wait_for_command(executor, timeout);
}

private:
rclcpp::WaitSet ref_subscriber_wait_set_;
};

// We are using template class here for easier reuse of Fixture in specializations of controllers
Expand Down Expand Up @@ -197,34 +188,42 @@ class MecanumDriveControllerFixture : public ::testing::Test
void subscribe_to_controller_status_execute_update_and_get_messages(ControllerStateMsg & msg)
{
// create a new subscriber
ControllerStateMsg::SharedPtr received_msg;
rclcpp::Node test_subscription_node("test_subscription_node");
auto subs_callback = [&](const ControllerStateMsg::SharedPtr) {};
auto subs_callback = [&](const ControllerStateMsg::SharedPtr cb_msg) { received_msg = cb_msg; };
auto subscription = test_subscription_node.create_subscription<ControllerStateMsg>(
"/test_mecanum_drive_controller/controller_state", 10, subs_callback);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(test_subscription_node.get_node_base_interface());
// call update to publish the test value
ASSERT_EQ(
controller_->update(controller_->get_node()->now(), rclcpp::Duration::from_seconds(0.01)),
controller_interface::return_type::OK);
// call update to publish the test value
// since update doesn't guarantee a published message, republish until received
int max_sub_check_loop_count = 5; // max number of tries for pub/sub loop
rclcpp::WaitSet wait_set; // block used to wait on message
wait_set.add_subscription(subscription);
while (max_sub_check_loop_count--)
{
controller_->update(controller_->get_node()->now(), rclcpp::Duration::from_seconds(0.01));
controller_->update(rclcpp::Time(0, 0, RCL_ROS_TIME), rclcpp::Duration::from_seconds(0.01));
const auto timeout = std::chrono::milliseconds{5};
const auto until = test_subscription_node.get_clock()->now() + timeout;
while (!received_msg && test_subscription_node.get_clock()->now() < until)
{
executor.spin_some();
std::this_thread::sleep_for(std::chrono::microseconds(10));
}
// check if message has been received
if (wait_set.wait(std::chrono::milliseconds(2)).kind() == rclcpp::WaitResultKind::Ready)
if (received_msg.get())
{
break;
}
}
ASSERT_GE(max_sub_check_loop_count, 0) << "Test was unable to publish a message through "
"controller/broadcaster update loop";
ASSERT_TRUE(received_msg);

// take message from subscription
rclcpp::MessageInfo msg_info;
ASSERT_TRUE(subscription->take(msg, msg_info));
msg = *received_msg;
}

void publish_commands(
Expand Down
Loading