|
1 | 1 | //
|
2 |
| -// Created by yezi on 2021/12/3. |
| 2 | +// Created by qiayuan on 2022/7/29. |
3 | 3 | //
|
4 | 4 |
|
5 |
| -#include <rm_chassis_controllers/omni.h> |
6 |
| -#include <rm_common/ros_utilities.h> |
7 | 5 | #include <string>
|
| 6 | +#include <Eigen/QR> |
| 7 | + |
| 8 | +#include <rm_common/ros_utilities.h> |
8 | 9 | #include <pluginlib/class_list_macros.hpp>
|
9 | 10 |
|
| 11 | +#include "rm_chassis_controllers/omni.h" |
| 12 | + |
10 | 13 | namespace rm_chassis_controllers
|
11 | 14 | {
|
12 | 15 | bool OmniController::init(hardware_interface::RobotHW* robot_hw, ros::NodeHandle& root_nh,
|
13 | 16 | ros::NodeHandle& controller_nh)
|
14 | 17 | {
|
15 | 18 | ChassisBase::init(robot_hw, root_nh, controller_nh);
|
16 |
| - if (!controller_nh.getParam("chassis_radius", chassis_radius_)) |
| 19 | + |
| 20 | + XmlRpc::XmlRpcValue wheels; |
| 21 | + controller_nh.getParam("wheels", wheels); |
| 22 | + chassis2joints_.resize(wheels.size(), 3); |
| 23 | + |
| 24 | + size_t i = 0; |
| 25 | + for (const auto& wheel : wheels) |
17 | 26 | {
|
18 |
| - ROS_ERROR("chassis_radius is not set"); |
19 |
| - return false; |
| 27 | + ROS_ASSERT(wheel.second.hasMember("pose")); |
| 28 | + ROS_ASSERT(wheel.second["pose"].getType() == XmlRpc::XmlRpcValue::TypeArray); |
| 29 | + ROS_ASSERT(wheel.second["pose"].size() == 3); |
| 30 | + ROS_ASSERT(wheel.second.hasMember("roller_angle")); |
| 31 | + ROS_ASSERT(wheel.second.hasMember("radius")); |
| 32 | + |
| 33 | + // Ref: Modern Robotics, Chapter 13.2: Omnidirectional Wheeled Mobile Robots |
| 34 | + Eigen::MatrixXd direction(1, 2), in_wheel(2, 2), in_chassis(2, 3); |
| 35 | + double beta = (double)wheel.second["pose"][2]; |
| 36 | + double roller_angle = (double)wheel.second["roller_angle"]; |
| 37 | + direction << 1, tan(roller_angle); |
| 38 | + in_wheel << cos(beta), sin(beta), -sin(beta), cos(beta); |
| 39 | + in_chassis << -(double)wheel.second["pose"][1], 1., 0., (double)wheel.second["pose"][0], 0., 1.; |
| 40 | + Eigen::MatrixXd chassis2joint = 1. / (double)wheel.second["radius"] * direction * in_wheel * in_chassis; |
| 41 | + chassis2joints_.block<1, 3>(i, 0) = chassis2joint; |
| 42 | + |
| 43 | + ros::NodeHandle nh_wheel = ros::NodeHandle(controller_nh, "wheels/" + wheel.first); |
| 44 | + joints_.push_back(std::make_shared<effort_controllers::JointVelocityController>()); |
| 45 | + if (!joints_.back()->init(effort_joint_interface_, nh_wheel)) |
| 46 | + return false; |
| 47 | + |
| 48 | + i++; |
20 | 49 | }
|
21 |
| - ros::NodeHandle nh_lf = ros::NodeHandle(controller_nh, "left_front"); |
22 |
| - ros::NodeHandle nh_rf = ros::NodeHandle(controller_nh, "right_front"); |
23 |
| - ros::NodeHandle nh_lb = ros::NodeHandle(controller_nh, "left_back"); |
24 |
| - ros::NodeHandle nh_rb = ros::NodeHandle(controller_nh, "right_back"); |
25 |
| - if (!ctrl_lf_.init(effort_joint_interface_, nh_lf) || !ctrl_rf_.init(effort_joint_interface_, nh_rf) || |
26 |
| - !ctrl_lb_.init(effort_joint_interface_, nh_lb) || !ctrl_rb_.init(effort_joint_interface_, nh_rb)) |
27 |
| - return false; |
28 |
| - joint_handles_.push_back(ctrl_lf_.joint_); |
29 |
| - joint_handles_.push_back(ctrl_rf_.joint_); |
30 |
| - joint_handles_.push_back(ctrl_lb_.joint_); |
31 |
| - joint_handles_.push_back(ctrl_rb_.joint_); |
32 | 50 | return true;
|
33 | 51 | }
|
34 | 52 |
|
35 | 53 | void OmniController::moveJoint(const ros::Time& time, const ros::Duration& period)
|
36 | 54 | {
|
37 |
| - ctrl_rf_.setCommand(((vel_cmd_.x + vel_cmd_.y + sqrt(2) * vel_cmd_.z * chassis_radius_) / sqrt(2)) / wheel_radius_); |
38 |
| - ctrl_lf_.setCommand(((-vel_cmd_.x + vel_cmd_.y + sqrt(2) * vel_cmd_.z * chassis_radius_) / sqrt(2)) / wheel_radius_); |
39 |
| - ctrl_lb_.setCommand(((-vel_cmd_.x - vel_cmd_.y + sqrt(2) * vel_cmd_.z * chassis_radius_) / sqrt(2)) / wheel_radius_); |
40 |
| - ctrl_rb_.setCommand(((vel_cmd_.x - vel_cmd_.y + sqrt(2) * vel_cmd_.z * chassis_radius_) / sqrt(2)) / wheel_radius_); |
41 |
| - ctrl_lf_.update(time, period); |
42 |
| - ctrl_rf_.update(time, period); |
43 |
| - ctrl_lb_.update(time, period); |
44 |
| - ctrl_rb_.update(time, period); |
| 55 | + Eigen::Vector3d vel_chassis; |
| 56 | + vel_chassis << vel_cmd_.z, vel_cmd_.x, vel_cmd_.y; |
| 57 | + Eigen::VectorXd vel_joints = chassis2joints_ * vel_chassis; |
| 58 | + for (size_t i = 0; i < joints_.size(); i++) |
| 59 | + { |
| 60 | + joints_[i]->setCommand(vel_joints(i)); |
| 61 | + joints_[i]->update(time, period); |
| 62 | + } |
45 | 63 | }
|
46 | 64 |
|
47 |
| -geometry_msgs::Twist OmniController::forwardKinematics() |
| 65 | +geometry_msgs::Twist OmniController::odometry() |
48 | 66 | {
|
49 |
| - geometry_msgs::Twist vel_data; |
50 |
| - double k = wheel_radius_ / 2; |
51 |
| - double lf_velocity = ctrl_lf_.joint_.getVelocity(); |
52 |
| - double rf_velocity = ctrl_rf_.joint_.getVelocity(); |
53 |
| - double lb_velocity = ctrl_lb_.joint_.getVelocity(); |
54 |
| - double rb_velocity = ctrl_rb_.joint_.getVelocity(); |
55 |
| - vel_data.linear.x = k * (-lf_velocity + rf_velocity - lb_velocity + rb_velocity) / sqrt(2); |
56 |
| - vel_data.linear.y = k * (lf_velocity + rf_velocity - lb_velocity - rb_velocity) / sqrt(2); |
57 |
| - vel_data.angular.z = k * (lf_velocity + rf_velocity + lb_velocity + rb_velocity) / (2 * chassis_radius_); |
58 |
| - return vel_data; |
| 67 | + Eigen::VectorXd vel_joints(joints_.size()); |
| 68 | + for (size_t i = 0; i < joints_.size(); i++) |
| 69 | + vel_joints[i] = joints_[i]->joint_.getVelocity(); |
| 70 | + Eigen::Vector3d vel_chassis = |
| 71 | + (chassis2joints_.transpose() * chassis2joints_).inverse() * chassis2joints_.transpose() * vel_joints; |
| 72 | + geometry_msgs::Twist twist; |
| 73 | + twist.angular.z = vel_chassis(0); |
| 74 | + twist.linear.x = vel_chassis(1); |
| 75 | + twist.linear.y = vel_chassis(2); |
| 76 | + return twist; |
59 | 77 | }
|
| 78 | + |
60 | 79 | } // namespace rm_chassis_controllers
|
61 | 80 | PLUGINLIB_EXPORT_CLASS(rm_chassis_controllers::OmniController, controller_interface::ControllerBase)
|
0 commit comments