class ChassisControlNode: def __init__(self): self.lock = Lock() self.is_stopped = True self.pub = rospy.Publisher('odom', Odometry, queue_size=10) self.chassis = Chassis() self.target_wheel_speed = [0, 0, 0, 0] self.now_wheel_speed = [0, 0, 0, 0] self.last_moves = self.chassis.get_feedback() self.now_x = 0 self.now_y = 0 self.now_theta = 0 self.in_action_service = False self._simple_move_feedback = SimpleMoveFeedback() self._simple_move_result = SimpleMoveResult() self._as = actionlib.SimpleActionServer("simple_move", SimpleMoveAction, execute_cb = self.execut_cb, auto_start = False) self._as.start() def execut_cb(self, goal): rate = rospy.Rate(RATE) self._simple_move_feedback.moved_distance.x = 0 self._simple_move_feedback.moved_distance.y = 0 self._simple_move_feedback.moved_distance.theta = 0 with self.lock: self.in_action_service = True original_moves = array(self.chassis.get_feedback(), dtype=np.float64) self.chassis.stop() target = goal.target distance = array([target.x, target.y, target.theta]) wheel_moves = MACANUM_MAT.dot(distance.T) total_distance = norm(wheel_moves) if total_distance < MAX_DELTA_VELOCITY: self._simple_move_result.success = True self._as.set_succeeded(self._simple_move_result) return moved_distance = 0 vels = wheel_moves / total_distance self.chassis.set_wheels_speed(vels * MAX_DELTA_VELOCITY * 4) real_moves = array([abs(v) for v in wheel_moves]) self.chassis.set_wheels_count(real_moves) success = True while True: if self._as.is_preempt_requested(): rospy.loginfo('%s: Preempted' % self._action_name) self._simple_move_result.success = False self._simple_move_result.moved_distance = self._simple_move_feedback.moved_distance self._as.set_preempted(self._simple_move_result) with self.lock: self.chassis.stop() self.in_action_service = False return with self.lock: now_moves = array(self.chassis.get_feedback(), dtype=np.float64) moved_distance = norm(now_moves - original_moves) now_speed = self.get_speed(moved_distance, total_distance) wheel_vels = now_speed * vels x, y, theta = tuple(lstsq(MACANUM_MAT, now_moves - original_moves)[0]) self._simple_move_feedback.moved_distance.x = x self._simple_move_feedback.moved_distance.y = y self._simple_move_feedback.moved_distance.theta = theta self._as.publish_feedback(self._simple_move_feedback) with self.lock: self.chassis.set_wheels_speed(wheel_vels * 4) if (fabs(moved_distance - total_distance) < 10): break rate.sleep() self._simple_move_result.success = success self._simple_move_result.moved_distance = self._simple_move_feedback.moved_distance if success: self._as.set_succeeded(self._simple_move_result) else: self._as.set_aborted(self._simple_move_result) with self.lock: self.in_action_service = False def get_speed(self, moved_distance, total_distance): if moved_distance > total_distance: rospy.logwarn("Moved too much") remain_distance = total_distance - moved_distance if remain_distance < moved_distance: moved_distance = remain_distance factor = (moved_distance / total_distance) / 0.2 if factor > 1: factor = 1 speed = factor * DEFAULT_VELOCITY if speed < MAX_DELTA_VELOCITY: speed = MAX_DELTA_VELOCITY return speed def vel_callback(self, vel): if abs(vel.linear.x) > MAX_VELOCITY or abs(vel.linear.y) > MAX_VELOCITY or abs(vel.angular.z) > MAX_A_VELOCITY: rospy.logwarn('vel too big!') return vel = array([vel.linear.x, vel.linear.y, vel.angular.z]) self.is_stopped = False with self.lock: self.target_wheel_speed = MACANUM_MAT.dot(vel.T) * PV_RATE def run(self): rate = rospy.Rate(RATE) while not rospy.is_shutdown(): with self.lock: if not self.in_action_service: self.set_speed() self.feedback() rate.sleep() def set_speed(self): delta_wheel_speed = array(self.target_wheel_speed) - array(self.now_wheel_speed) if self.is_stopped: self.chassis.stop() else: if norm(delta_wheel_speed) > MAX_DELTA_VELOCITY: delta_wheel_speed /= norm(delta_wheel_speed) delta_wheel_speed *= MAX_DELTA_VELOCITY self.now_wheel_speed += delta_wheel_speed if norm(self.now_wheel_speed) < 1: self.now_wheel_speed = [0] * 4 self.is_stopped = True self.chassis.stop() else: self.chassis.set_wheels_speed(self.now_wheel_speed) self.chassis.set_wheels_count([10000] * 4) def feedback(self): new_moves = self.chassis.get_feedback() delta_moves = array(new_moves) - array(self.last_moves) self.last_moves = new_moves x, y, theta = tuple(lstsq(MACANUM_MAT, delta_moves)[0]) x_odom = cos(self.now_theta) * x - sin(self.now_theta) * y y_odom = sin(self.now_theta) * x + cos(self.now_theta) * y self.now_x += x_odom self.now_y += y_odom self.now_theta += theta self.odom_post(self.now_x, self.now_y, self.now_theta) def odom_post(self, now_x, now_y, now_theta): odom_broadcaster = tf.TransformBroadcaster() current_time = rospy.Time.now() odom_quat = tf.transformations.quaternion_from_euler(0,0,now_theta) odom_broadcaster.sendTransform((now_x, now_y, 0), odom_quat, current_time, "base_link", "odom") odom = Odometry() odom.header.stamp = current_time odom.header.frame_id = "odom" odom.child_frame_id = "base_link" odom.pose.pose.position.x = now_x odom.pose.pose.position.y = now_y odom.pose.pose.orientation.x = odom_quat[0] odom.pose.pose.orientation.y = odom_quat[1] odom.pose.pose.orientation.z = odom_quat[2] odom.pose.pose.orientation.w = odom_quat[3] self.pub.publish(odom)