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

Add support for Donkeycar #41

Open
Ezward opened this issue Jan 3, 2023 · 3 comments
Open

Add support for Donkeycar #41

Ezward opened this issue Jan 3, 2023 · 3 comments

Comments

@Ezward
Copy link

Ezward commented Jan 3, 2023

Donkeycar

I am one of the maintainers of the Donkeycar project. We have recently had a user ask about running the Donkeycar code on his Freenove 4WD Smart Car Kit for Raspberry Pi. I believe it could easily be done. Perhaps you might do this work and make Donkeycar available to your users. Here is what I suggested to him; if Freenove could do this work and test it, then you could open a pull request into donkeycar and add value for your users.

Looking at the github, it appears the Freenove 4WD Smart Car Kit for Raspberry Pi is using a PCA9685 to generate the duty cycle for 4 motors; https://github.com/Freenove/Freenove_4WD_Smart_Car_Kit_for_Raspberry_Pi/blob/master/Code/Server/Motor.py. The 4 motors, one per wheel, each use two PWM outputs from the PCA9685; one for forward duty cycle and one for reverse duty cycle.

The closest drivetrain the Donkeycar project has is DRIVE_TRAIN_TYPE == "DC_TWO_WHEEL", which uses two PWM outputs each for two motors (a left and right motor) as in a typicaly differential drive configuration. So you need to 1) create a new drivetrain type in manage.py/complete.py, 2) Add configuration for the new drivetrain to myconfig.py/cfg_complete.py and make sure it is uncommented. 3) set DRIVE_TRAIN_TYPE == <new_configuration_name> in myconfig.py. I'll detail these steps below:

  1. So you could create a new drivetrain from the code at https://github.com/autorope/donkeycar/blob/5d7fd5014e3eb0831a270b57f5b8f7783787fc47/donkeycar/templates/complete.py#L903 that creates actuator.L298N_HBridge_2pin part for each of the 4 motors. Something like this;
        elif cfg.DRIVE_TRAIN_TYPE == "DC_TWO_WHEEL_SKID":
            dt = cfg.DC_TWO_WHEEL_SKID
            left_front_motor = actuator.L298N_HBridge_2pin(
                pins.pwm_pin_by_id(dt['LEFT_FRONT_FWD_DUTY_PIN']),
                pins.pwm_pin_by_id(dt['LEFT_FRONT_BWD_DUTY_PIN']))
            left_rear_motor = actuator.L298N_HBridge_2pin(
                pins.pwm_pin_by_id(dt['LEFT_REAR_FWD_DUTY_PIN']),
                pins.pwm_pin_by_id(dt['LEFT_REAR_BWD_DUTY_PIN']))
            right_front_motor = actuator.L298N_HBridge_2pin(
                pins.pwm_pin_by_id(dt['RIGHT_FRONT_FWD_DUTY_PIN']),
                pins.pwm_pin_by_id(dt['RIGHT_FRONT_BWD_DUTY_PIN']))
            right_rear_motor = actuator.L298N_HBridge_2pin(
                pins.pwm_pin_by_id(dt['RIGHT_REAR_FWD_DUTY_PIN']),
                pins.pwm_pin_by_id(dt['RIGHT_REAR_BWD_DUTY_PIN']))

            V.add(left_front_motor, inputs=['left/throttle'])
            V.add(left_rear_motor, inputs=['left/throttle'])
            V.add(right_front_motor, inputs=['right/throttle'])
            V.add(right_rear_motor, inputs=['right/throttle'])
  1. Modify your myconfig.py file to support DRIVE_TRAIN_TYPE == "DC_TWO_WHEEL_SKID" by copying the DC_TWO_WHEEL configuration block at https://github.com/autorope/donkeycar/blob/5d7fd5014e3eb0831a270b57f5b8f7783787fc47/donkeycar/templates/cfg_complete.py#L290, like;
DC_TWO_WHEEL_SKID = {
    "LEFT_FRONT_FWD_DUTY_PIN": "PCA9685.1:40.3",  # pwm pin produces duty cycle for left front wheel forward
    "LEFT_FRONT_BWD_DUTY_PIN": "PCA9685.1:40.2",  # pwm pin produces duty cycle for left front wheel reverse
    "LEFT_REAR_FWD_DUTY_PIN": "PCA9685.1:40.1",  # pwm pin produces duty cycle for left rear wheel forward
    "LEFT_REAR_BWD_DUTY_PIN": "PCA9685.1:40.0",  # pwm pin produces duty cycle for left rear wheel reverse
    "RIGHT_FRONT_FWD_DUTY_PIN": "PCA9685.1:40.4", # pwm pin produces duty cycle for right front wheel forward
    "RIGHT_FRONT_BWD_DUTY_PIN": "PCA9685.1:40.5", # pwm pin produces duty cycle for right front wheel reverse
    "RIGHT_REAR_FWD_DUTY_PIN": "PCA9685.1:40.6", # pwm pin produces duty cycle for right rear wheel forward
    "RIGHT_REAR_BWD_DUTY_PIN": "PCA9685.1:40.7", # pwm pin produces duty cycle for right rear wheel reverse
}
  1. set DRIVE_TRAIN_TYPE == "DC_TWO_WHEEL_SKID" in your myconfig.py

NOTE myconfig.py is really just a copy of cfg_complete.py and manage.py is really just a copy of complete.py (both linked above). So if you want to make integrate the changes into the donkeycar framework, then you could change those files. The when you create a new mycar application using the donkey createcar path=~/mycar command it will create a myconfig.py and manage.py with the changes. If you do that and your testing shows that car works, please open a pull request and we can make that new drivetrain part of the regular donkeycar code.

@jjakubassa
Copy link

I'm very interested in the autonomous driving features by the Donkeycar project. It would be great if the freenove 4wd smart car would support this.

@Ezward
Copy link
Author

Ezward commented Jan 4, 2023

The code I added above will handle the drive train. We must also consider the camera. The camera is a standard Raspberry Pi camera, so the default CAMERA_TYPE = "PICAM" will work. The camera is mounted on a two axis, servo controlled mount. So we will need some code to initialize the camera position. It appears to be using channels 8 to 15 on the PCA9685 to control servos,

self.PwmServo.setServoPulse(8,1500)
I am guessing channel 8 and 9 are for the camera mount, since they are explicitly set to center position at startup.

In donkey we can use the PulseController https://github.com/autorope/donkeycar/blob/5d7fd5014e3eb0831a270b57f5b8f7783787fc47/donkeycar/parts/actuator.py#L85 with an appropriate pin specifier, like PCA9685.1:40.8 for channel 8. We would construct two instance of the PulseController, one for channel 8 and one for channel 9 and add them to the vehicle. Then use the duty_cycle() helper function to calculate the duty cycle for a given pulse length and cycle frequency https://github.com/autorope/donkeycar/blob/5d7fd5014e3eb0831a270b57f5b8f7783787fc47/donkeycar/parts/actuator.py#L55, like duty_cycle(1500, 50), and pass the result to the PulseController.set_pulse() method.

We may want to point the camera slightly down so it can see immediately in front of the car.

@jjakubassa
Copy link

Regarding the camera position: I copied https://github.com/Freenove/Freenove_4WD_Smart_Car_Kit_for_Raspberry_Pi/blob/faa05beb1a29668aacaf331609e4ce62b01d9353/Code/Server/servo.py and https://github.com/Freenove/Freenove_4WD_Smart_Car_Kit_for_Raspberry_Pi/blob/faa05beb1a29668aacaf331609e4ce62b01d9353/Code/Server/PCA9685.py into the mycar folder and used the servo class to initialize the camera position once in the drive() function in manage.py.

  import time
  from servo_freenove import Servo

  pwm=Servo()
  pwm.setServoPwm('0',90, 10)
  time.sleep(0.2)
  pwm.setServoPwm('1', 90, 10)
  time.sleep(0.2)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants