Coder Social home page Coder Social logo

Comments (11)

altineller avatar altineller commented on June 1, 2024 1

Hello fjp,

I saw your post on duckietown stackoverflow, and decided to respond here. I have implemented a circuit, that drives two motors, in a duckietown like configuration. Except I am using n20 encoder motors, and the chassis is 3d printed. (https://gitlab.com/ROSRider/rosrider)

Here is my experience: even if the PID is implemented perfectly, for a robot of that scale, and for motors of that non-linearity, it is not possible to make the robot drive straight, without external correction. Also PID makes it slower to respond, and this is a problem when turning, etc. The deadzones of the motors you are using are 30-40%, which is horrible. There are also wheel slip factors, etc. Not only that the deadzones are horrible, the motors will turn at different speeds when same voltage/current is applied. Yes, PID corrects those, but it can only correct so much.

In your PID code you also have to implement the case when the motor changes direction, like full forward to full reverse.

Best Regards, and good luck on your work.

from diffbot.

fjp avatar fjp commented on June 1, 2024

Comments from The robocademy:

I'd go for option 3 with the PID controller inside the hight level hardware interface because it's easier to adjust and also you could implement the dynamic reconfigure in order to have a flexible adjustment of these parameters.

Let's start by implementing a PID in a high-level hardware interface. We will get a lot of issues while choosing a cheap motor. These motors are not designed for accurate operation, so each will be triggered in different levels of voltage. One initial step that we can do is, apply the same voltage to both of these motors and keep changing the voltage using a standard SMPS power supply, so you can identify which starts first and which start next. If we have multiple motors, we can test in all combinations and select the best matching motors.

Also, I hope you also checked the voltage from the motor driver. Make sure, both channels giving the same values. So if the values are correct, then the suspect is the motor.

I also assume the caster wheel is fixed, so that it will not rotate when the robot starts. If it rotates, then it can change the direction too. Check the wheel alignment too. Choose very similar wheels and check the axis of rotation. If there is any change in the axis of rotation, it may affect movement.

Also, weight distribution plays a role too. The motor triggering can be delayed due to more weight distribution to one wheel.

Even though this robot is very difficult to make accurate, the good thing is, we will learn a lot of aspects of robotics and its fine-tuning in hardware and software.

from diffbot.

fjp avatar fjp commented on June 1, 2024

I'll start to implement the PID controller in the high level hardware interface and see how it works.

implement the dynamic reconfigure in order to have a flexible adjustment of these parameters.

Good idea, I add that too.

One initial step that we can do is, apply the same voltage to both of these motors and keep changing the voltage using a standard SMPS power supply, so you can identify which starts first and which start next. If we have multiple motors, we can test in all combinations and select the best matching motors.
Also, I hope you also checked the voltage from the motor driver. Make sure, both channels giving the same values. So if the values are correct, then the suspect is the motor.
Also, weight distribution plays a role too. The motor triggering can be delayed due to more weight distribution to one wheel.

Good suggestions, I didn't check the voltage levels of both channels yet. Maybe the motor driver is the main problem.

I also assume the caster wheel is fixed, so that it will not rotate when the robot starts. If it rotates, then it can change the direction too. Check the wheel alignment too. Choose very similar wheels and check the axis of rotation. If there is any change in the axis of rotation, it may affect movement.

The caster wheel of the kit that DiffBot uses can rotate freely but as you mention it can lead to errors depending on how it is initially aligned.

from diffbot.

fjp avatar fjp commented on June 1, 2024

Also, I hope you also checked the voltage from the motor driver. Make sure, both channels giving the same values. So if the values are correct, then the suspect is the motor.

I have just checked the voltage output of the motor driver. Both channels give the same voltage level when the same output value is applied.

from diffbot.

fjp avatar fjp commented on June 1, 2024

Here is an update on how this issue is going on (should be solved soon):
To control the motors I am now using two instances of a new PID class that inherits from control_toolbox::Pid which is part of ROS Control. The control_toolbox::Pid provides ROS dynamic reconfigure for the its three gains (P, I, D). With only these gains it is hard to tune the motors resulting in overshoot and instable behaviour (motor output switches between positive and negative values). Another problem is that the i_error can get really large (windup) when the setpoint isn't reached for a long time. Although there is antiwindup functionality in the control_toolbox::Pid, I have added the following check (also done by Linorobot's low level PID) to reset the errors (especially the i_error), (here in my code):

// Reset the i_error in case the p_error and the setpoint is zero
// Otherwise there will always be a constant i_error_ that won't vanish
if (0.0 == setpoint && 0.0 == error_)
{
     // reset() will reset
     // p_error_last_ = 0.0;
     // p_error_ = 0.0;
     // i_error_ = 0.0;
     // d_error_ = 0.0;
     // cmd_ = 0.0;
     reset(); // this method is inherited from control_toolbox::Pid
}

To get everything more stable I am using a feed forward gain (adding f * setpoint to the PID output) to reach the setpoint faster. This helps a lot to get the motors spinning at roughly the same time.

The only disadvantage is that the F gain value is currently hard coded. Now I would like add this gain value to the same group of dynamic reconfigure values as the ones provided by the control_toolbox::Pid which my PID class is inheriting from. Currently there is /diffbot/pid_lef and /diffbot/pid_right for the left and right motor respectively, each having parameters for p, i, d, i_min, i_max, anti-windup. I hope adding the f gain parameter is possible.

from diffbot.

fjp avatar fjp commented on June 1, 2024

I checked the voltage levels of the motor driver Grove i2c motor driver v1.3 and they look good, both motor outputs give the same voltage.
I will also test some different sets of motors next, maybe one of them has a slight defect.

And I finished editing the PID tuning video, you can see the best result at the beginning. The rest of the video shows the tedious tuning process (feel free to skip some sections). For the best found behaviour, the parameters of the high level PID are set to the following:

F = 0.8 (the feed forward term is used to reach the set point faster)
P = 0.35
I = 0.5
D = 0.01 (if this is set higher, everything becomes unstable. I think because of the encoder measurement noise)
anti-windup: [-3.5, 3.5]
control loop rate: 10 Hz
queue_size = 1 (this could be increased)

It was really hard to find some good values and even now it is not ideal because the dc motors DG01D-E are a non-linear system. Especially at low speeds it can happen that one motor starts rotating slightly before the other and then stops again after it has overcome the internal gear friction because it's spinning too fast due to the initially required "high" voltage.

Anyway, the robot drives more or less straight now when the motors operate at a steady state. Still there are some options that can probably improve the behaviour:

  • Vary the control loop rate: Linorobot uses 20Hz, 10Hz works as shown in the video but 100 Hz seem too much. It is not exactly clear how to find the limiting factor.
  • Implement a low level PID on the Teensy 3.2 or Teensy 4.0 to operate the motor driver board Grove i2c motor driver v1.3. There exists already a library for Arduino. And avoid using the current python motor_driver.py node.
  • Remove delay() after the nh.spinOnce() in Arduino/Teensy code (although this lead to errors, see the code in the link)
  • make use of odometry calibration using the gain/trim model. This would probably require implementing a new ROS-Control controller instead of the diff_drive_controller which provides only open loop control and therefore requires the speed PID controller.
    image
  • Try other controllers and use a more detailed model including the motor:

image

  • Use a LQR instead of a simple PID
  • "Go to angle" controller

Or try some other differential drive ros packages instead of ROS control, although I would prefer to keep using ROS Control.

The currently found gain parameters seem to work ok but I need to do some more testing, like trying to map a real enviornment with the RPLidar and gmapping. Depending on how this works, I will try some of the methods mentioned above. Although I would like to implement the low level PID on the Teensy anyway, just for comparison with the high level PID.

Regarding rosserial I have also another question (might be suited for another issue): I have read something about adapting the ros.h and NodeHandle for the Arduino when using rosserial in the wiki here (changing buffer and publishers/subscribers). I am wondering if this will be required for the teensy or if it could already improve the current behaviour and which values should be adapted.

Please let me know if you have some other ideas to improve the driving behaviour or find some bugs in the DiffBot code or anything else that could need adjustments. Any suggestions or discussions are more than welcome. Thank you.

from diffbot.

fjp avatar fjp commented on June 1, 2024

Maybe one approach to having a more realistic model would be doing these tests with the robot on the floor, because, then you have the load of the robot weight and the behavior would be closer to the "real behavior".

Yes this would be helpful. As in the video I did some tests on the ground but next time I will use a tape to make a straight line of 2 meter and record the final offset.

I didn't get it but, is it possible to have different PID values for the wheel? Because, as you said, if the motor is behaving differently, probably they need different parameters.

It is a dynamic error that will vary with speed. I would apply a different pid for each wheel.

Yes right, there are two PID controllers, one for each wheel. It is just not that easy to find good values. But I think doing more tests on the floor will be helpful to tune them individually.

The error is causing it that the motor reduction does not rotate symmetrically. When I have run into these errors, in almost all diffbots, I adjust to the slowest wheel. In your case the left wheel.It starts to turn at 45%, calculates the speed and you give it the value that corresponds to the right motor, maybe 37%, is an example. At low speeds it usually works quite well.

This might work as well. I previously started with a simple approach (adding an offset to the left wheel) but it seems to me that this requires a lot of code to get good behavior for different speeds and also requires different cases for accelerating and decelerating. Do you have some references or examples where this is implemented?

Seeing the platform you have in amazon. Can you turn one of the motors and connect the wheel where the encoder goes? If they rotate on the same axis,and the same speed, that way connecting a motor in an inverted way to the driver eliminates the difference in reduction.

Good point. Having the encoder directly where the wheel is attached ("after the gear reduction") would be better I guess. Unfortunately, with the motors I am using (Hobby Motor with Encoder - Metal Gear (DG01D-E)) it is not really possible to change the location of the encoder because it is directly attached to the rotor shaft of the motor. I just opened one of them:

dg01d-e-open

So far I didn't find motors with a quadrature encoder that can be installed on the bottom of the chassis and leave enough space for the battery pack (which is currently located at the bottom). And adding separate quadrature encoders will also be hard because the battery pack leaves too little space. I might need some new part placement or use a double-decker chassis.

Also, I just made a voltage sweep test (slowly increasing the voltage) with six of these motors (all that I have) and only one (the one that was installed on the right of DiffBot) starts already to spin at 2.5 V, sometimes even 2.4 V. Another one starts to rotate between 2.6 V and 3.0 V (although this one varies a lot). All others require more than 3.0 V to start rotating, as specified in the datasheet:

image

So I will start using a pair of motors that start spinning at roughly the same voltage level.

from diffbot.

fjp avatar fjp commented on June 1, 2024

The following video shows the results of driving straight on a 2m test track:

2m-straight-thumbnail (2)

Keeping in mind the used hardware, the resulting accuracy of the different test runs seems to be ok. As noted in other comments of this issue the different offsets can be caused by:

  • Orientation of the start pose
  • Inaccurate motors start to spin at different voltage levels
  • Low encoder resolution (geared output resolution at wheel)
  • Wheel slip or alignment of the left/right and caster wheel
  • Weight distribution
  • Not tuning the individual PID controllers for each motor

from diffbot.

fjp avatar fjp commented on June 1, 2024

Thank you @altineller for sharing your valuable experience. Yes, the motors are not easy to handle. I soon want try running the PID software more low level on the microcontroller (similar to Linorobot), instead of in the ROS control hardware interface, to see if this improves the behaviour. But I also think switching to other motors/encoders would be easier.

The ROSRider board you have is very interesting. What dimensions does the board have and when will it be ready? Because we are working on a 3d printed chassis as well and I might try to fit this board at the bottom of the upcoming chassis #26.

Thanks, also good luck on your projects.
Best regards,
Franz

from diffbot.

altineller avatar altineller commented on June 1, 2024

Helllo,

If you move the PID to the micro-controller, it will have some advantages, mainly around latency. I have done both ways, and although the PID running at a lower latency improves performance, it is not enough to compensate for the non-linearity of the motors. I have done first PID on the micro-controller, then decided the advantages were not so great, and decided to run ROS's PID package on the host computer instead of the micro-controller, in favor of configurability.

The problem is the non-linearity of the motors, and I am still working on this problem. I am thinking of something in form of this: besides PID have a mechanism to increase PWM, or even just send 1 to enable line, until the motor is in motion, then PID takes over.

Another solution could be mechanical. Greasing the gears with high quality teflon grease spray that they use in model cars and helicopters has worked great for me.

The ROSRider board is 76mm x 80mm. I am trying to get it manufactured in the 100s. But I can ship singular quantities, that I build at home for now.

Best Regards,
Can

from diffbot.

fjp avatar fjp commented on June 1, 2024

Sorry for the late reply @altineller. Thanks for your suggestions how to improve these prolems.

I recently changed the code and interface between high-level hardware interface and low-level micro-controller code. Now the PID is running on the low-level base controller (e.g. micro-controller):

// Compute PID output
// The value sent to the motor driver is calculated by the PID based on the error between commanded angular velocity vs measured angular velocity
// The calculated PID ouput value is capped at -/+ MAX_RPM to prevent the PID from having too much error
motor_cmd_left_ = motor_pid_left_.compute(wheel_cmd_velocity_left_, joint_state_left_.angular_velocity_);
motor_cmd_right_ = motor_pid_right_.compute(wheel_cmd_velocity_right_, joint_state_right_.angular_velocity_);

As you noted too, I can also confirm that the performance is better. At leats the driving seems to be more smooth to me.

The problem is the non-linearity of the motors, and I am still working on this problem. I am thinking of something in form of this: besides PID have a mechanism to increase PWM, or even just send 1 to enable line, until the motor is in motion, then PID takes over.

I agree with you regarding the non-linearity of the motors. Espcially when the robot starts to move or drives slow the differences of the motors are more severe. Although it seems that with the new Adafruit motor driver and its library I tested, it works much better (compared to the previous python code and grove motor driver). However, a better model could improve this further I am sure.

One more thing about configurability. I am planning to use dynamic reconfigure for the PID parameters and everytime a change happens a message could be published to the low-level base controller to update its PID values. This way we could keep the performance up, because the PID runs on the microcontroller and also have configurability in the high-level code. This could be a workaround of rosserial not supporting dynamic reconfigure. But I need to check if this can actually work.

Regarding the straight driving behavior I noticed that now my robot drives slightly to the left on every "straight line driving test". For this I am planning to use the "gain trim model", that is also used on the Duckiebots. Setting the trim correctly should help to drive straight and compensate for differences in the motors, wheels, etc...

The ROSRider board is 76mm x 80mm. I am trying to get it manufactured in the 100s. But I can ship singular quantities, that I build at home for now.

The design is now finished of the new robot platform but unfortunately there wasn't enough space for the ROSRider board at the bottom nor the back, where the breadboard is placed now. It would be really great to have a dedicated board that plugs into the GPIO header of the Raspberry Pi or Jetson Nano and replace some of the wiring or even some of the other components: Teensy mcu, motor driver, IMU, etc. Do you think such a control board could be made smaller?

from diffbot.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.