本文博客为转载,并对原博客部分错误进行更改 远程仓库地址:github 官方提供的ROS_arduino_bridge是利用一套PID控制两个马达,但是由于马达问题,机器人不能走直线,因此就需要对两个马达进行单独的PID控制,本文修改ROS_Arduino_bridge以实现两路PID控制

1.首先修改arduino代码

a.修改diff_controller.h文件
增加左右两个马达的PID控制变量:

/* PID Parameters */
int Kp = 20;
int Kd = 12;
int Ki = 0;
int Ko = 50;

int left_Kp=Kp;
int left_Kd=Kd;
int left_Ki=Ki;
int left_Ko=Ko;

int right_Kp=Kp;
int right_Kd=Kd;
int right_Ki=Ki;
int right_Ko=Ko;

分别定义左右两个马达的dorightPID()和doleftPID()函数

/* PID routine to compute the next motor commands */
void dorightID(SetPointInfo * p) {
long Perror;
long output;
int input;

//Perror = p->TargetTicksPerFrame - (p->Encoder - p->PrevEnc);
input = p->Encoder - p->PrevEnc;
Perror = p->TargetTicksPerFrame - input;

/*
* Avoid derivative kick and allow tuning changes,
* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-derivative-kick/
* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-tuning-changes/
*/
//output = (Kp * Perror + Kd * (Perror - p->PrevErr) + Ki * p->Ierror) / Ko;
// p->PrevErr = Perror;
output = (right_Kp * Perror - right_Kd * (input - p->PrevInput) + p->ITerm) / right_Ko;
p->PrevEnc = p->Encoder;

output += p->output;
// Accumulate Integral error *or* Limit output.
// Stop accumulating when output saturates
if (output >= MAX_PWM)
output = MAX_PWM;
else if (output <= -MAX_PWM)
output = -MAX_PWM;
else
/*
* allow turning changes, see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-tuning-changes/
*/
p->ITerm += Ki * Perror;

p->output = output;
p->PrevInput = input;
}

/* PID routine to compute the next motor commands */
void doleftPID(SetPointInfo * p) {
long Perror;
long output;
int input;

//Perror = p->TargetTicksPerFrame - (p->Encoder - p->PrevEnc);
input = p->Encoder - p->PrevEnc;
Perror =p->TargetTicksPerFrame + input;

/*
* Avoid derivative kick and allow tuning changes,
* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-derivative-kick/
* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-tuning-changes/
*/
//output = (Kp * Perror + Kd * (Perror - p->PrevErr) + Ki * p->Ierror) / Ko;
// p->PrevErr = Perror;
output = (left_Kp * Perror - left_Kd * (input - p->PrevInput) + p->ITerm) / left_Ko;
p->PrevEnc = p->Encoder;

output += p->output;
// Accumulate Integral error *or* Limit output.
// Stop accumulating when output saturates
if (output >= MAX_PWM)
output = MAX_PWM;
else if (output <= -MAX_PWM)
output = -MAX_PWM;
else
/*
* allow turning changes, see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-tuning-changes/
*/
p->ITerm += Ki * Perror;

p->output = output;
p->PrevInput = input;
}

修改updatePID()函数

void updatePID() {
/* Read the encoders */
leftPID.Encoder =readEncoder(LEFT);
rightPID.Encoder = readEncoder(RIGHT);

/* If we're not moving there is nothing more to do */
if (!moving){
/*
* Reset PIDs once, to prevent startup spikes,
* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-initialization/
* PrevInput is considered a good proxy to detect
* whether reset has already happened
*/
if (leftPID.PrevInput != 0 || rightPID.PrevInput != 0) resetPID();
return;
}

/* Compute PID update for each motor */
dorightID(&rightPID);//执行右马达PID
doleftPID(&leftPID);//执行左马达PID

/* Set the motor speeds accordingly */
setMotorSpeeds(leftPID.output, rightPID.output);

}

b.修改ROSArduinoBridge.ino文件
修改argv1 和argv2的数组长度

char argv1[32];
char argv2[32];

修改pid_args的声明为如下代码

int pid_args[8];

修改runCommand()函数,修改case UPDATE_PID,把原来的Kp,Kd,Ki,Ko赋值的代码注释掉,修改为如下的代码、

case UPDATE_PID:
while ((str = strtok_r(p, ":", &p)) != '\0') {
pid_args[i] = atoi(str);
i++;
}
// Kp = pid_args[0];
// Kd = pid_args[1];
// Ki = pid_args[2];
// Ko = pid_args[3];

left_Kp = pid_args[0];
left_Kd = pid_args[1];
left_Ki = pid_args[2];
left_Ko = pid_args[3];

right_Kp = pid_args[4];
right_Kd = pid_args[5];
right_Ki = pid_args[6];
right_Ko = pid_args[7];
Serial.println("OK");
break;

现在arduino端已经支持对两个电机使用不同的PID参数调速了

2.修改上位机ROS包的代码

a.修改my_arduino_params.yaml
在PID参数部分增加左右两个马达的PID参数

# === PID parameters
Kp: 10
Kd: 12
Ki: 0
Ko: 50
accel_limit: 1.0

left_Kp: 10
left_Kd: 12
left_Ki: 0
left_Ko: 50

right_Kp: 8
right_Kd: 12
right_Ki: 0
right_Ko: 50

b.修改arduino_driver.py文件
修改update_pid()函数

def update_pid(self, left_Kp, left_Kd, left_Ki, left_Ko, right_Kp, right_Kd, right_Ki, right_Ko):
''' Set the PID parameters on the Arduino
'''
print "Updating PID parameters"
cmd = 'u ' + str(left_Kp) + ':' + str(left_Kd) + ':' + str(left_Ki) + ':' + str(left_Ko) + ':' + str(right_Kp) + ':' + str(right_Kd) + ':' + str(right_Ki) + ':' + str(right_Ko)
self.execute_ack(cmd)

c.修改base_controller.py文件
修改def init(self, arduino, base_frame):函数,增加左右两个马达PID参数的初始化代码

def __init__(self, arduino, base_frame):
self.arduino = arduino
self.base_frame = base_frame
self.rate = float(rospy.get_param("~base_controller_rate", 10))
self.timeout = rospy.get_param("~base_controller_timeout", 1.0)
self.stopped = False

pid_params = dict()
pid_params['wheel_diameter'] = rospy.get_param("~wheel_diameter", "")
pid_params['wheel_track'] = rospy.get_param("~wheel_track", "")
pid_params['encoder_resolution'] = rospy.get_param("~encoder_resolution", "")
pid_params['gear_reduction'] = rospy.get_param("~gear_reduction", 1.0)

pid_params['left_Kp'] = rospy.get_param("~left_Kp", 20)
pid_params['left_Kd'] = rospy.get_param("~left_Kd", 12)
pid_params['left_Ki'] = rospy.get_param("~left_Ki", 0)
pid_params['left_Ko'] = rospy.get_param("~left_Ko", 50)
pid_params['right_Kp'] = rospy.get_param("~right_Kp", 20)
pid_params['right_Kd'] = rospy.get_param("~right_Kd", 12)
pid_params['right_Ki'] = rospy.get_param("~right_Ki", 0)
pid_params['right_Ko'] = rospy.get_param("~right_Ko", 50)

self.accel_limit = rospy.get_param('~accel_limit', 0.1)
self.motors_reversed = rospy.get_param("~motors_reversed", False)

# Set up PID parameters and check for missing values
self.setup_pid(pid_params)

# How many encoder ticks are there per meter?
self.ticks_per_meter = self.encoder_resolution * self.gear_reduction / (self.wheel_diameter * pi)

# What is the maximum acceleration we will tolerate when changing wheel speeds?
self.max_accel = self.accel_limit * self.ticks_per_meter / self.rate

# Track how often we get a bad encoder count (if any)
self.bad_encoder_count = 0

now = rospy.Time.now()
self.then = now # time for determining dx/dy
self.t_delta = rospy.Duration(1.0 / self.rate)
self.t_next = now + self.t_delta

# internal data
self.enc_left = None # encoder readings
self.enc_right = None
self.x = 0 # position in xy plane
self.y = 0
self.th = 0 # rotation in radians
self.v_left = 0
self.v_right = 0
self.v_des_left = 0 # cmd_vel setpoint
self.v_des_right = 0
self.last_cmd_vel = now

# subscriptions
rospy.Subscriber("cmd_vel", Twist, self.cmdVelCallback)

# Clear any old odometry info
self.arduino.reset_encoders()

# Set up the odometry broadcaster
self.odomPub = rospy.Publisher('odom', Odometry)
self.odomBroadcaster = TransformBroadcaster()

rospy.loginfo("Started base controller for a base of " + str(self.wheel_track) + "m wide with " + str(self.encoder_resolution) + " ticks per rev")
rospy.loginfo("Publishing odometry data at: " + str(self.rate) + " Hz using " + str(self.base_frame) + " as base frame")

修改def setup_pid(self, pid_params):

def setup_pid(self, pid_params):
# Check to see if any PID parameters are missing
missing_params = False
for param in pid_params:
if pid_params[param] == "":
print("*** PID Parameter " + param + " is missing. ***")
missing_params = True

if missing_params:
os._exit(1)

self.wheel_diameter = pid_params['wheel_diameter']
self.wheel_track = pid_params['wheel_track']
self.encoder_resolution = pid_params['encoder_resolution']
self.gear_reduction = pid_params['gear_reduction']

#self.Kp = pid_params['Kp']
#self.Kd = pid_params['Kd']
#self.Ki = pid_params['Ki']
#self.Ko = pid_params['Ko']

#self.arduino.update_pid(self.Kp, self.Kd, self.Ki, self.Ko)

#modify by william
self.left_Kp = pid_params['left_Kp']
self.left_Kd = pid_params['left_Kd']
self.left_Ki = pid_params['left_Ki']
self.left_Ko = pid_params['left_Ko']

self.right_Kp = pid_params['right_Kp']
self.right_Kd = pid_params['right_Kd']
self.right_Ki = pid_params['right_Ki']
self.right_Ko = pid_params['right_Ko']


self.arduino.update_pid(self.left_Kp, self.left_Kd, self.left_Ki, self.left_Ko, self.right_Kp, self.right_Kd, self.right_Ki, self.right_Ko)

修改完上述代码后ros_arduino_bridge可以分别对两个马达设置不同的PID参数进行调速。