Skip to content

Commit bb54c12

Browse files
committed
Introduce PID for position control.
1 parent 9427adf commit bb54c12

File tree

3 files changed

+112
-41
lines changed

3 files changed

+112
-41
lines changed

ign_ros2_control/src/ign_system.cpp

Lines changed: 96 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,17 @@
3030
#include <ignition/gazebo/components/JointPosition.hh>
3131
#include <ignition/gazebo/components/JointVelocity.hh>
3232
#include <ignition/gazebo/components/JointVelocityCmd.hh>
33+
#include <ignition/gazebo/components/JointAxis.hh>
34+
3335
#include <ignition/gazebo/components/LinearAcceleration.hh>
3436
#include <ignition/gazebo/components/Name.hh>
3537
#include <ignition/gazebo/components/ParentEntity.hh>
3638
#include <ignition/gazebo/components/Pose.hh>
3739
#include <ignition/gazebo/components/Sensor.hh>
3840

41+
// pid stuff
42+
#include <gz/math/PID.hh>
43+
3944
#include <ignition/transport/Node.hh>
4045

4146

@@ -67,6 +72,9 @@ struct jointData
6772
/// \brief handles to the joints from within Gazebo
6873
ignition::gazebo::Entity sim_joint;
6974

75+
/// \brief PID for position and velocity control
76+
gz::math::PID pid;
77+
7078
/// \brief Control method defined in the URDF for each joint.
7179
ign_ros2_control::IgnitionSystemInterface::ControlMethod joint_control_method;
7280
};
@@ -276,7 +284,8 @@ bool IgnitionSystem::initSim(
276284
"Joint '" << joint_name << "'is mimicking joint '" << mimicked_joint << "' with mutiplier: "
277285
<< mimic_joint.multiplier);
278286
this->dataPtr->mimic_joints_.push_back(mimic_joint);
279-
suffix = "_mimic";
287+
// TODO (livanov93): add parameter if suffix is used or not
288+
// suffix = "_mimic";
280289
}
281290

282291
RCLCPP_INFO_STREAM(this->nh_->get_logger(), "\tState:");
@@ -339,6 +348,37 @@ bool IgnitionSystem::initSim(
339348
if (!std::isnan(initial_position)) {
340349
this->dataPtr->joints_[j].joint_position_cmd = initial_position;
341350
}
351+
352+
// init position PID
353+
354+
// assuming every joint has axis
355+
const auto * jointAxis =
356+
this->dataPtr->ecm->Component<ignition::gazebo::components::JointAxis>(
357+
this->dataPtr->joints_[i].sim_joint);
358+
// PID parameters
359+
double p_gain = 100.0;
360+
double i_gain = 0.0;
361+
double d_gain = 50.0;
362+
// set integral max and min component to 10 percent of the max effort
363+
double iMax = std::isnan(jointAxis->Data().Effort()) ? 1.0 : jointAxis->Data().Effort() * 0.1;
364+
double iMin = std::isnan(jointAxis->Data().Effort()) ? 1.0 : -jointAxis->Data().Effort() * 0.1;
365+
double cmdMax = std::isnan(jointAxis->Data().Effort()) ? 1000.0 : jointAxis->Data().Effort();
366+
double cmdMin = std::isnan(jointAxis->Data().Effort()) ? -1000.0 : -jointAxis->Data().Effort();
367+
double cmdOffset = 0.0;
368+
369+
igndbg << "[JointController "<<joint_name<<"] Position based PID with Force/Torque output:" << std::endl;
370+
igndbg << "p_gain: [" << p_gain << "]" << std::endl;
371+
igndbg << "i_gain: [" << i_gain << "]" << std::endl;
372+
igndbg << "d_gain: [" << d_gain << "]" << std::endl;
373+
igndbg << "i_max: [" << iMax << "]" << std::endl;
374+
igndbg << "i_min: [" << iMin << "]" << std::endl;
375+
igndbg << "cmd_max: [" << cmdMax << "]" << std::endl;
376+
igndbg << "cmd_min: [" << cmdMin << "]" << std::endl;
377+
igndbg << "cmd_offset: [" << cmdOffset << "]" << std::endl;
378+
379+
380+
this->dataPtr->joints_[j].pid.Init(p_gain, i_gain, d_gain, iMax, iMin, cmdMax, cmdMin, cmdOffset);
381+
342382
} else if (joint_info.command_interfaces[i].name == "velocity") {
343383
RCLCPP_INFO_STREAM(this->nh_->get_logger(), "\t\t velocity");
344384
this->dataPtr->command_interfaces_.emplace_back(
@@ -580,9 +620,14 @@ IgnitionSystem::perform_command_mode_switch(
580620

581621
hardware_interface::return_type IgnitionSystem::write(
582622
const rclcpp::Time & /*time*/,
583-
const rclcpp::Duration & /*period*/)
623+
const rclcpp::Duration & period)
584624
{
585625
for (unsigned int i = 0; i < this->dataPtr->joints_.size(); ++i) {
626+
// assuming every joint has axis
627+
const auto * jointAxis =
628+
this->dataPtr->ecm->Component<ignition::gazebo::components::JointAxis>(
629+
this->dataPtr->joints_[i].sim_joint);
630+
586631
if (this->dataPtr->joints_[i].joint_control_method & VELOCITY) {
587632
if (!this->dataPtr->ecm->Component<ignition::gazebo::components::JointVelocityCmd>(
588633
this->dataPtr->joints_[i].sim_joint))
@@ -598,25 +643,28 @@ hardware_interface::return_type IgnitionSystem::write(
598643
{this->dataPtr->joints_[i].joint_velocity_cmd});
599644
}
600645
} else if (this->dataPtr->joints_[i].joint_control_method & POSITION) {
601-
// Get error in position
602-
double error;
603-
error = (this->dataPtr->joints_[i].joint_position -
604-
this->dataPtr->joints_[i].joint_position_cmd) * *this->dataPtr->update_rate;
605646

606-
// Calculate target velcity
607-
double target_vel = -this->dataPtr->position_proportional_gain_ * error;
647+
// calculate error with clamped position command
648+
double error = this->dataPtr->joints_[i].joint_position - std::clamp(this->dataPtr->joints_[i].joint_position_cmd, jointAxis->Data().Lower(), jointAxis->Data().Upper());
608649

609-
auto vel =
610-
this->dataPtr->ecm->Component<ignition::gazebo::components::JointVelocityCmd>(
611-
this->dataPtr->joints_[i].sim_joint);
650+
// error can be maximal 10 percent of the range
651+
error = copysign(1.0, error) * std::clamp(std::abs(error), 0.0, std::abs(jointAxis->Data().Upper() - jointAxis->Data().Lower())*0.1);
612652

613-
if (vel == nullptr) {
614-
this->dataPtr->ecm->CreateComponent(
615-
this->dataPtr->joints_[i].sim_joint,
616-
ignition::gazebo::components::JointVelocityCmd({target_vel}));
617-
} else if (!vel->Data().empty()) {
618-
vel->Data()[0] = target_vel;
619-
}
653+
// calculate target force/torque
654+
double target_force = this->dataPtr->joints_[i].pid.Update(error, std::chrono::duration<double>(period.to_chrono<std::chrono::nanoseconds>()));
655+
656+
auto forceCmd =
657+
this->dataPtr->ecm->Component<ignition::gazebo::components::JointForceCmd>(this->dataPtr->joints_[i].sim_joint);
658+
659+
if (forceCmd == nullptr)
660+
{
661+
this->dataPtr->ecm->CreateComponent(
662+
this->dataPtr->joints_[i].sim_joint,
663+
ignition::gazebo::components::JointForceCmd({0}));
664+
} else {
665+
*forceCmd = ignition::gazebo::components::JointForceCmd(
666+
{target_force});
667+
}
620668
} else if (this->dataPtr->joints_[i].joint_control_method & EFFORT) {
621669
if (!this->dataPtr->ecm->Component<ignition::gazebo::components::JointForceCmd>(
622670
this->dataPtr->joints_[i].sim_joint))
@@ -631,7 +679,7 @@ hardware_interface::return_type IgnitionSystem::write(
631679
*jointEffortCmd = ignition::gazebo::components::JointForceCmd(
632680
{this->dataPtr->joints_[i].joint_effort_cmd});
633681
}
634-
} else {
682+
} /*else {
635683
// Fallback case is a velocity command of zero
636684
double target_vel = 0.0;
637685
auto vel =
@@ -647,39 +695,46 @@ hardware_interface::return_type IgnitionSystem::write(
647695
} else if (!vel->Data().empty()) {
648696
vel->Data()[0] = target_vel;
649697
}
650-
}
698+
}*/
651699
}
652700

653701
// set values of all mimic joints with respect to mimicked joint
654702
for (const auto & mimic_joint : this->dataPtr->mimic_joints_) {
655703
for (const auto & mimic_interface : mimic_joint.interfaces_to_mimic) {
704+
// assuming every mimic joint has axis
705+
const auto * jointAxis =
706+
this->dataPtr->ecm->Component<ignition::gazebo::components::JointAxis>(
707+
this->dataPtr->joints_[mimic_joint.joint_index].sim_joint);
708+
656709
if (mimic_interface == "position") {
657710
// Get the joint position
658-
double position_mimicked_joint =
659-
this->dataPtr->ecm->Component<ignition::gazebo::components::JointPosition>(
660-
this->dataPtr->joints_[mimic_joint.mimicked_joint_index].sim_joint)->Data()[0];
711+
double position_mimicked_joint = this->dataPtr->joints_[mimic_joint.mimicked_joint_index].joint_position;
661712

662-
double position_mimic_joint =
663-
this->dataPtr->ecm->Component<ignition::gazebo::components::JointPosition>(
664-
this->dataPtr->joints_[mimic_joint.joint_index].sim_joint)->Data()[0];
713+
double position_mimic_joint = this->dataPtr->joints_[mimic_joint.joint_index].joint_position;
665714

666-
double position_error =
667-
position_mimic_joint - position_mimicked_joint * mimic_joint.multiplier;
715+
// calculate error with clamped position command
716+
double position_error =
717+
position_mimic_joint - std::clamp(position_mimicked_joint * mimic_joint.multiplier, jointAxis->Data().Lower(), jointAxis->Data().Upper());
668718

669-
double velocity_sp = (-1.0) * position_error * (*this->dataPtr->update_rate);
719+
// error can be maximal 10 percent of the range
720+
position_error = copysign(1.0, position_error) * std::clamp(std::abs(position_error), 0.0, std::abs(jointAxis->Data().Upper() - jointAxis->Data().Lower())*0.1);
670721

671-
auto vel =
672-
this->dataPtr->ecm->Component<ignition::gazebo::components::JointVelocityCmd>(
673-
this->dataPtr->joints_[mimic_joint.joint_index].sim_joint);
722+
// calculate target force/torque
723+
double target_force = this->dataPtr->joints_[mimic_joint.joint_index].pid.Update(position_error, std::chrono::duration<double>(period.to_chrono<std::chrono::nanoseconds>()));
674724

675-
if (vel == nullptr) {
676-
this->dataPtr->ecm->CreateComponent(
677-
this->dataPtr->joints_[mimic_joint.joint_index].sim_joint,
678-
ignition::gazebo::components::JointVelocityCmd({velocity_sp}));
679-
} else if (!vel->Data().empty()) {
680-
vel->Data()[0] = velocity_sp;
681-
}
682-
}
725+
auto forceCmd =
726+
this->dataPtr->ecm->Component<ignition::gazebo::components::JointForceCmd>(this->dataPtr->joints_[mimic_joint.joint_index].sim_joint);
727+
728+
if (forceCmd == nullptr)
729+
{
730+
this->dataPtr->ecm->CreateComponent(
731+
this->dataPtr->joints_[mimic_joint.joint_index].sim_joint,
732+
ignition::gazebo::components::JointForceCmd({0}));
733+
} else {
734+
*forceCmd = ignition::gazebo::components::JointForceCmd(
735+
{target_force});
736+
}
737+
}/*
683738
if (mimic_interface == "velocity") {
684739
// get the velocity of mimicked joint
685740
double velocity_mimicked_joint =
@@ -720,7 +775,7 @@ hardware_interface::return_type IgnitionSystem::write(
720775
{mimic_joint.multiplier *
721776
this->dataPtr->joints_[mimic_joint.mimicked_joint_index].joint_effort});
722777
}
723-
}
778+
}*/
724779
}
725780
}
726781

ign_ros2_control_demos/config/gripper_controller.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ controller_manager:
55
gripper_controller:
66
type: forward_command_controller/ForwardCommandController
77

8+
gripper_action_controller:
9+
type: position_controllers/GripperActionController
10+
811
joint_state_broadcaster:
912
type: joint_state_broadcaster/JointStateBroadcaster
1013

ign_ros2_control_demos/launch/gripper_mimic_joint_example.launch.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ def generate_launch_description():
7575
output='screen'
7676
)
7777

78+
# load action controller but don't start it
79+
load_gripper_action_controller = ExecuteProcess(
80+
cmd=['ros2', 'control', 'load_controller', '--set-state', 'configured',
81+
'gripper_action_controller'],
82+
output='screen'
83+
)
84+
7885
return LaunchDescription([
7986
# Launch gazebo environment
8087
IncludeLaunchDescription(
@@ -94,6 +101,12 @@ def generate_launch_description():
94101
on_exit=[load_gripper_controller],
95102
)
96103
),
104+
RegisterEventHandler(
105+
event_handler=OnProcessExit(
106+
target_action=load_gripper_controller,
107+
on_exit=[load_gripper_action_controller],
108+
)
109+
),
97110
node_robot_state_publisher,
98111
ignition_spawn_entity,
99112
# Launch Arguments

0 commit comments

Comments
 (0)