๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

23. [ROS RC์นด] ์‘์šฉ ์‹ค์Šต, Object Following

๐Ÿ“—

23. [ROS RC์นด] ์‘์šฉ ์‹ค์Šต, Object Following

๋‘๋ฒˆ์งธ ์‘์šฉ ์‚ฌ๋ก€์ธ Object Following ์˜ˆ์ œ๋ฅผ ์‹œ์ž‘ํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๐ŸŽ†

๋จผ์ € ์‹คํ–‰ ๋ชจ์Šต๋ถ€ํ„ฐ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

jetson์„ ๊ตฌ๋งคํ•˜์…จ๋‹ค๋ฉด ๋ชจ๋‘๋“ค ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ด ๋…น์ƒ‰ ๋ฐ•์Šค๋ฅผ ๋”ฐ๋ผ์„œ ์กฐํ–ฅํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

  • ๋จผ์ € ์‹คํ–‰์‹œ์ผœ๋ณด๊ธฐ
$ roscore
# camera image publisher
$ rosrun csi_camera csi_pub.py
# CV Magic, Bind Ball with Pixel Value
$ rosrun donkey_cv find_ball.py 

# Blob Point to Twist
$ rosrun donkey_control chase_the_ball.py
# PWM Control node
$ rosrun donkey_control blob_chase.py

# image_view ์‹คํ–‰
$ rosrun image_view image_view image:=/blob/image_blob

๋ฌธ์ œ์—†์ด ์ž˜ ์‹คํ–‰ ๋˜์…จ๋‚˜์š”?? ์ „์ฒด์ ์ธ ๊ตฌ์กฐ๋ฅผ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

rqt_graph

  • csi_pub : ์นด๋ฉ”๋ผ ์ด๋ฏธ์ง€๋ฅผ ๊ฒŒ์†ํ•ด์„œ ์†ก์ถœํ•ด์ฃผ๋Š” ๋…ธ๋“œ์ด์ง€์š”?
  • blob_detector : ์ปดํ“จํ„ฐ ๋น„์ „์ด ์ ์šฉ๋œ ๋…ธ๋“œ๋กœ, ๋…น์ƒ‰ ๋ฌผ์ฒด๋ฅผ ์ฐพ์•„๋‚ด๊ณ , ์ง€์†์ ์œผ๋กœ ์ถ”์ ํ•˜๋Š” ๋…ธ๋“œ
  • chase_ball : ์ด์ „ ๋…ธ๋“œ์—์„œ ๋…น์ƒ‰ ๋ฌผ์ฒด์˜ ์œ„์น˜๊ฐ€ ํ”ฝ์…€ ๋‹จ์œ„๋กœ ์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด ํ”ฝ์…€์˜ ์œ„์น˜์— ๋”ฐ๋ผ์„œ ์ œ์–ด ์‹ ํ˜ธ๋ฅผ Twist ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • image_view : ์ด์ „์—๋„ ์‚ดํŽด๋ณด์•˜๋˜, ์ด๋ฏธ์ง€ ํ† ํ”ฝ์„ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋…ธ๋“œ์ž…๋‹ˆ๋‹ค.
  • blob_chase_node : ์‹ค์งˆ์ ์ธ PWM ์ œ์–ด๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๋…ธ๋“œ์ž…๋‹ˆ๋‹ค.

๐Ÿ’ก
์นด๋ฉ”๋ผ๋งŒ์œผ๋กœ๋Š” ๋ฌผ์ฒด์™€์˜ ๊ฑฐ๋ฆฌ๋ฅผ ์•Œ์•„๋‚ด๊ธฐ์— ํ•œ๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์›๊ทผ๋ฒ•์„ ์ด์šฉํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ๊ธด ํ•˜์ง€๋งŒ, ์šฐ๋ฆฌ๊ฐ€ ์“ฐ๊ณ  ์žˆ๋Š” ๊ด‘๊ฐ ๋ Œ์ฆˆ์—์„œ๋Š” ์ •ํ™•๋„๊ฐ€ ๋–จ์–ด์ง‘๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ๋”ฐ๋กœ Twist Topic์„ ๋ฐ›๋„๋ก ํ•ด์ค€ ๋ชจ์Šต์ด ๋ณด์ž…๋‹ˆ๋‹ค.

์ง€๋‚œ ํ”„๋กœ์ ํŠธ ๋ถ„์„๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ์ด๋ฒˆ์—๋„ ๋’ท๋ถ€๋ถ„์˜ node์—์„œ๋ถ€ํ„ฐ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ถ„์„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋Š” ๋‹ค์Œ์˜ ์˜คํ”ˆ์†Œ์Šค๋ฅผ ์ฐธ๊ณ ์‚ผ์•„ ์ œ์ž‘ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
ROS and OpenCv for beginners | Blob Tracking and Ball Chasing with Raspberry Pi
ROS and OpenCV can work together for accomplishing incredible tasks. In this tutorial for beginners we are going to use the Rover's camera for detecting and ...
https://www.youtube.com/watch?v=We6CQHhhOFo&list=PLuteWQUGtU9BU0sQIVqRQa24p-pSBCYNv&index=11

blob_chase.py


#!/usr/bin/env python

"""
referenced from those projects

DkLowLevelCtrl, ServoConvert part from tizianofiorenzani/ros_tutorials
url: https://github.com/tizianofiorenzani/ros_tutorials

PCA9685 part from donkeycar
url: https://github.com/autorope/donkeycar/blob/99c853b1737f12019ae598d3c7f00699d2166472/donkeycar/parts/actuator.py#L12

Listens to /dkcar/control/cmd_vel for corrective actions to the /cmd_vel coming from keyboard or joystick

"""
import rospy
import time
from geometry_msgs.msg import Twist

class PCA9685:
    """
    PWM motor controler using PCA9685 boards.
    This is used for most RC Cars
    """

    def __init__(
        self, channel, address=0x40, frequency=60, busnum=None, init_delay=0.1
    ):

        self.default_freq = 60
        self.pwm_scale = frequency / self.default_freq

        import Adafruit_PCA9685

        # Initialise the PCA9685 using the default address (0x40).
        if busnum is not None:
            from Adafruit_GPIO import I2C

            # replace the get_bus function with our own
            def get_bus():
                return busnum

            I2C.get_default_bus = get_bus
        self.pwm = Adafruit_PCA9685.PCA9685(address=address)
        self.pwm.set_pwm_freq(frequency)
        self.channel = channel
        time.sleep(init_delay)  # "Tamiya TBLE-02" makes a little leap otherwise

        self.pulse = 340
        self.prev_pulse = 340
        self.running = True

    def set_pwm(self, pulse):
        try:
            self.pwm.set_pwm(self.channel, 0, int(pulse * self.pwm_scale))
        except:
            self.pwm.set_pwm(self.channel, 0, int(pulse * self.pwm_scale))

    def run(self, pulse):
        pulse_diff = pulse - self.prev_pulse

        if abs(pulse_diff) > 40:
            if pulse_diff > 0:
                pulse += 0.7 * pulse_diff
            else:
                pulse -= 0.7 * pulse_diff

        self.set_pwm(pulse)
        self.prev_pulse = pulse

    def set_pulse(self, pulse):
        self.pulse = pulse

    def update(self):
        while self.running:
            self.set_pulse(self.pulse)

class ServoConvert:
    def __init__(self, id=1, center_value=370, range=90, direction=1):
        self.value = 0.0
        self.value_out = center_value
        self._center = center_value
        self._range = range
        self._half_range = 0.5 * range # 45
        self._dir = direction # 1 or -1 
        self.id = id

        # --- Convert its range in [-1, 1]
        self._sf = 1.0 / self._half_range # 1 / 45

    def get_value_out(self, value_in):
        # --- twist type value is in  [-1, 1]
        self.value = value_in
        self.value_out = int(self._dir * value_in * self._half_range + self._center)
        return self.value_out


def saturate(value, min, max):
    if value <= min:
        return min
    elif value >= max:
        return max
    else:
        return value


class DkLowLevelCtrl:
    def __init__(self):
        rospy.loginfo("Setting Up the Node...")

        # --- Initialize the node
        rospy.init_node("blob_chase_node")

        self._throttle = PCA9685(channel=0, busnum=1)
        rospy.loginfo("Throttle Controler Awaked!!")

        self._steering_servo = PCA9685(channel=1, busnum=1)
        rospy.loginfo("Steering Controler Awaked!!")

        self.actuators = {}
        self.actuators["throttle"] = ServoConvert(
            id=1, center_value=350, range=80
        )
        self.actuators["steering"] = ServoConvert(
            id=2, center_value=375, range=80, direction=1
        )  # -- positive left
        rospy.loginfo("> Actuators corrrectly initialized")

        # --- Create a debug publisher for resulting cmd_vel
        self.ros_pub_debug_command = rospy.Publisher(
            "/dkcar/debug/cmd_vel", Twist, queue_size=1
        )
        rospy.loginfo("> Publisher corrrectly initialized")

        # --- Create the Subscriber to Twist commands
        self.ros_sub_twist = rospy.Subscriber(
            "/cmd_vel", Twist, self.update_message_from_command
        )
        rospy.loginfo("> Subscriber corrrectly initialized")

        # --- Create the Subscriber to obstacle_avoidance commands
        self.ros_sub_twist = rospy.Subscriber(
            "/dkcar/control/cmd_vel", Twist, self.update_message_from_chase
        )
        rospy.loginfo("> Subscriber corrrectly initialized")

        self.throttle_cmd = 0.0
        self.throttle_chase = 1.0
        self.steer_cmd = 0.0
        self.steer_chase = 0.0

        self._debud_command_msg = Twist()

        # --- Get the last time e got a commands
        self._last_time_cmd_rcv = time.time()
        self._last_time_chase_rcv = time.time()
        self._timeout_ctrl = 100
        self._timeout_blob = 1

        rospy.loginfo("Initialization complete")

    def update_message_from_command(self, message):
        self._last_time_cmd_rcv = time.time()
        self.throttle_cmd = message.linear.x
        self.steer_cmd = message.angular.z

    def update_message_from_chase(self, message):
        self._last_time_chase_rcv = time.time()
        self.throttle_chase = message.linear.x
        self.steer_chase = message.angular.z
        # print(self.throttle_chase, self.steer_chase)

    def compose_command_velocity(self):
        self.throttle = saturate(self.throttle_cmd * self.throttle_chase, -1, 1)

        # -- Add steering
        self.steer = saturate(self.steer_cmd + self.steer_chase, -1, 1)

        self._debud_command_msg.linear.x = self.throttle
        self._debud_command_msg.angular.z = self.steer

        self.ros_pub_debug_command.publish(self._debud_command_msg)

        self.set_actuators_from_cmdvel(self.throttle, self.steer)

    def set_actuators_from_cmdvel(self, throttle, steering):
        """
        Get a message from cmd_vel, assuming a maximum input of 1
        """
        # -- Convert vel into servo values
        self.actuators["throttle"].get_value_out(throttle)
        self.actuators["steering"].get_value_out(steering)
        # rospy.loginfo("Got a command v = %2.1f  s = %2.1f"%(throttle, steering))

        self.set_pwm_pulse(self.actuators["throttle"].value_out, self.actuators["steering"].value_out)

        print( "throttle value : " + str(self.actuators["throttle"].value_out))
        print( "steering value : " + str(self.actuators["steering"].value_out))


    def set_pwm_pulse(self, speed_pulse, steering_pulse):
        self._throttle.run(speed_pulse)
        self._steering_servo.run(steering_pulse)


    def set_actuators_idle(self):
        # -- Convert vel into servo values
        self.throttle_cmd = 0.0
        self.steer_cmd = 0.0

    def reset_avoid(self):
        self.throttle_chase = 1.0
        self.steer_avoid = 0.0

    @property
    def is_controller_connected(self):
        # print time.time() - self._last_time_cmd_rcv
        return time.time() - self._last_time_cmd_rcv < self._timeout_ctrl

    @property
    def is_chase_connected(self):
        return time.time() - self._last_time_chase_rcv < self._timeout_blob

    def run(self):

        # --- Set the control rate
        rate = rospy.Rate(10)

        while not rospy.is_shutdown():
            self.compose_command_velocity()

            if not self.is_controller_connected:
                self.set_actuators_idle()

            if not self.is_chase_connected:
                self.reset_avoid()

            rate.sleep()


if __name__ == "__main__":
    dk_llc = DkLowLevelCtrl()
    dk_llc.run()

๐Ÿ˜ต ์ง€๊ธˆ๊ป ๋ด์™”๋˜ ์˜ˆ์ œ์— ๋น„ํ•ด ์ƒ๋‹นํžˆ ๊ธด ์ฝ”๋“œ์ธ๋ฐ์š”, ํ•˜์ง€๋งŒ ์ค‘๋ณต๋˜๋Š” ๋ถ€๋ถ„๋„ ๋งŽ์•„ ํฌ๊ฒŒ ๊ฑฑ์ •ํ•˜์ง€ ์•Š์œผ์…”๋„ ์ข‹์Šต๋‹ˆ๋‹ค. ์ฐจ๊ทผ์ฐจ๊ทผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!!

์ด ํ”„๋กœ์ ํŠธ๋Š” donkeycar์™€ youtube ํ”„๋กœ์ ํŠธ์—์„œ ํ•„์š” ๋ถ€๋ถ„๋“ค์„ ์ทจํ•ฉํ•˜์—ฌ ๋งŒ๋“ค์—ˆ๋‹ค๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  • import
import rospy
import time
from geometry_msgs.msg import Twist

  • PCA9685 Class
class PCA9685:
    """
    PWM motor controler using PCA9685 boards.
    This is used for most RC Cars
    """

    def __init__(
        self, channel, address=0x40, frequency=60, busnum=None, init_delay=0.1
    ):

        self.default_freq = 60
        self.pwm_scale = frequency / self.default_freq
		
		...

์ด์ „ ๊ฒŒ์ž„ ์ปจํŠธ๋กค๋Ÿฌ ์˜ˆ์ œ์™€ ์™„์ „ ๋™์ผํ•œ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์„ค๋ช…์€ ์ด์ „ ๊ฐ•์˜๋ฅผ ์ฐธ์กฐํ•ด์ฃผ์„ธ์š” ๐Ÿ™‚

  • ServoConvert Class
class ServoConvert:
    def __init__(self, id=1, center_value=370, range=90, direction=1):
        self.value = 0.0
        self.value_out = center_value
        self._center = center_value
        self._range = range
        self._half_range = 0.5 * range # 45
        self._dir = direction # 1 or -1 
        self.id = id

        # --- Convert its range in [-1, 1]
        self._sf = 1.0 / self._half_range # 1 / 45

    def get_value_out(self, value_in):
        # --- twist type value is in  [-1, 1]
        self.value = value_in
        self.value_out = int(self._dir * value_in * self._half_range + self._center)
        # print self.id, self.value_out
        return self.value_out

Twist ํ˜•์‹์˜ ์†๋„ ๊ฐ’์„ ๋ฐ›์•„ PWM Pulse๋กœ ๋งคํ•‘์‹œ์ผœ์ฃผ๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.

  • ํด๋ž˜์Šค ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋“ค์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
self.value = 0.0
self.value_out = center_value
self._center = center_value
self._range = range
self._half_range = 0.5 * range # 45
self._dir = direction # 1 or -1 
self.id = id

# --- Convert its range in [-1, 1]
self._sf = 1.0 / self._half_range # 1 / 45
  • self.value_out : output value
  • self._center : ํ•ด๋‹น ๋ชจํ„ฐ์˜ ์˜์ 
  • self._range : ํ•ด๋‹น ๋ชจํ„ฐ์˜ ์ตœ์†Œ ~ ์ตœ๋Œ€ ๊ฐ€๋™๋ฒ”์œ„
  • self._half_range : ๊ฐ€๋™๋ฒ”์œ„์˜ ์ ˆ๋ฐ˜์ž…๋‹ˆ๋‹ค.
  • self._dir : ์˜์ ์„ ๊ธฐ์ค€์œผ๋กœ์˜ ๋ฐฉํ–ฅ, 1 ํ˜น์€ -1 ์ด ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์˜ ์ˆ˜์‹์ด ๋ฉ”์ธ์ด๋ผ๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

-1 ~ 1 ์‚ฌ์ด์˜ value_in ๊ฐ’์ด ๋“ค์–ด์˜ค๊ฒŒ ๋˜๋ฉฐ, ์•„๋ž˜์™€ ๊ฐ™์€ ์ˆ˜์‹์„ ํ†ตํ•ด ์ตœ์ข… PWM Pulse๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

self.value_out = int(self._dir * value_in * self._half_range + self._center)

์˜ˆ๋ฅผ ๋“ค์–ด ๋ด…์‹œ๋‹ค.

  • dir = 1
  • half_range = 45 (range = 90)
  • center = 370

์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ value_in์ด -1 ~ 1 ๊นŒ์ง€ ๋ณ€ํ™”ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด

  1. value_in = -1 โ‡’ {1 * (-1) * 45} + 370 = 325
  1. value_in = 1 โ‡’ {1 * (1) * 45} + 370 = 415

value_in์€ 325 ~ 415์˜ ๊ฐ’์„ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด ๊ณ„์‚ฐ์€ ์ƒˆ๋กœ์šด Twist ๊ฐ’์ด subscribe ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

def set_actuators_from_cmdvel(self, throttle, steering):
        """
        Get a message from cmd_vel, assuming a maximum input of 1
        """
        # -- Convert vel into servo values
        self.actuators["throttle"].get_value_out(throttle)
        self.actuators["steering"].get_value_out(steering)

  • DkLowLevelCtrl Class
def run(self):

        # --- Set the control rate
        rate = rospy.Rate(10)

        while not rospy.is_shutdown():
            self.compose_command_velocity()

            # print self._last_time_cmd_rcv, self.is_controller_connected
            if not self.is_controller_connected:
                self.set_actuators_idle()

            if not self.is_chase_connected:
                self.reset_avoid()

            rate.sleep()

if __name__ == "__main__":
    dk_llc = DkLowLevelCtrl()
    dk_llc.run()

๊ฐ€์žฅ ๊ธธ๊ณ , ๋ณต์žกํ•œ class ์ธ๋ฐ์š”, ๋ณด์‹œ๋‹ค์‹œํ”ผ main์„ ์‚ดํŽด๋ณด๋ฉด, ์‹ค์ œ๋กœ๋Š” run ํ•จ์ˆ˜๊ฐ€ ๋™์ž‘๋˜๋Š” ๊ฒƒ์ด ๋ณด์ด๊ธฐ์— ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ ๋ถ„์„ํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์˜ˆ์™ธ์ฒ˜๋ฆฌ์— ํ•ด๋‹นํ•˜๋Š” ๋ถ€๋ถ„์„ ์‚ดํ”ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

self._timeout_ctrl = 100

def set_actuators_idle(self):
    # -- Convert vel into servo values
    self.throttle_cmd = 0.0
    self.steer_cmd = 0.0

def reset_avoid(self):
    self.throttle_chase = 1.0
    self.steer_avoid = 0.0

@property
def is_controller_connected(self):
    # print time.time() - self._last_time_cmd_rcv
    return time.time() - self._last_time_cmd_rcv < self._timeout_ctrl

@property
def is_chase_connected(self):
    return time.time() - self._last_time_chase_rcv < self._timeout_blob

1. ํŠน์ • ์‹œ๊ฐ„๋™์•ˆ ์™ธ๋ถ€์—์„œ cmd_vel ์ œ์–ด ์‹ ํ˜ธ๊ฐ€ ์—†๋‹ค๋ฉด, ์ œ์–ด์™€ ๊ด€๋ จ๋œ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ์ดˆ๊ธฐ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค.

2. ํŠน์ • ์‹œ๊ฐ„๋™์•ˆ ์ถ”์ ํ•ด์•ผ ํ•  ์ด๋ฏธ์ง€๊ฐ€ ๋ณด์ด์ง€ ์•Š์•˜๋‹ค๋ฉด, ์ง„ํ–‰ ๋ฐฉํ–ฅ์„ ์ •๋ฉด์œผ๋กœ ์ดˆ๊ธฐํ™”์‹œํ‚ต๋‹ˆ๋‹ค.

  • compose_command_velocity
def compose_command_velocity(self):
        self.throttle = saturate(self.throttle_cmd * self.throttle_chase, -1, 1)

        # -- Add steering
        self.steer = saturate(self.steer_cmd + self.steer_chase, -1, 1)

        self._debud_command_msg.linear.x = self.throttle
        self._debud_command_msg.angular.z = self.steer

        self.ros_pub_debug_command.publish(self._debud_command_msg)

        self.set_actuators_from_cmdvel(self.throttle, self.steer)

๐Ÿ˜Ž ํ•˜๋‚˜ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ด…์‹œ๋‹ค.

  • saturate
self.throttle = saturate(self.throttle_cmd * self.throttle_chase, -1, 1)
self.steer = saturate(self.steer_cmd + self.steer_chase, -1, 1)

def saturate(value, min, max):
    if value <= min:
        return min
    elif value >= max:
        return max
    else:
        return value

์ตœ๋Œ€, ์ตœ์†Œ๊ฐ’์„ ๋„˜์ง€ ์•Š๋„๋ก ํ•œ๊ณ„๋ฅผ ์ •ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค๋ฉด, -1.2 โ‡’ -1 / 1.3 โ‡’ 1 ์ด๋ ‡๊ฒŒ ๋ณ€ํ™˜๋˜๊ฒ ์ง€์š”

				# --- Create a debug publisher for resulting cmd_vel
        self.ros_pub_debug_command = rospy.Publisher(
            "/dkcar/debug/cmd_vel", Twist, queue_size=1
        )

				...
				self._debud_command_msg.linear.x = self.throttle
				self._debud_command_msg.angular.z = self.steer
				
				self.ros_pub_debug_command.publish(self._debud_command_msg)

๋””๋ฒ„๊น… ํ† ํ”ฝ์œผ๋กœ publish ํ•˜๋Š” ๋ชจ์Šต์ž…๋‹ˆ๋‹ค.

  • set_actuators_from_cmdvel
				self.set_actuators_from_cmdvel(self.throttle, self.steer)

    def set_actuators_from_cmdvel(self, throttle, steering):
        """
        Get a message from cmd_vel, assuming a maximum input of 1
        """
        # -- Convert vel into servo values
        self.actuators["throttle"].get_value_out(throttle)
        self.actuators["steering"].get_value_out(steering)

        self.set_pwm_pulse(self.actuators["throttle"].value_out, self.actuators["steering"].value_out)

        print( "throttle value : " + str(self.actuators["throttle"].value_out))
        print( "steering value : " + str(self.actuators["steering"].value_out))

์•„๊นŒ ์‚ดํŽด๋ณด์•˜๋˜ ์ˆ˜์‹์ธ get_value_out ๊ฐ€ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค!!

PWM pulse๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ์ตœ์ข…์ ์œผ๋กœ set_pwm_pulse๋ฅผ ํ†ตํ•ด PCA9685 ํด๋ž˜์Šค์— pulse modulation์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • set_pwm_pulse
		self._throttle = PCA9685(channel=0, busnum=1)
    rospy.loginfo("Throttle Controler Awaked!!")

    self._steering_servo = PCA9685(channel=1, busnum=1)
    rospy.loginfo("Steering Controler Awaked!!")

def set_pwm_pulse(self, speed_pulse, steering_pulse):
    self._throttle.run(speed_pulse)
    self._steering_servo.run(steering_pulse)

๊ฑฐ์˜ ๋‹ค ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ subscriber ์ƒ์„ฑ ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

				# --- Create the Subscriber to Twist commands
        self.ros_sub_twist = rospy.Subscriber(
            "/cmd_vel", Twist, self.update_message_from_command
        )
        rospy.loginfo("> Subscriber corrrectly initialized")

        # --- Create the Subscriber to obstacle_avoidance commands
        self.ros_sub_twist = rospy.Subscriber(
            "/dkcar/control/cmd_vel", Twist, self.update_message_from_chase
        )
        rospy.loginfo("> Subscriber corrrectly initialized")


		def update_message_from_command(self, message):
        self._last_time_cmd_rcv = time.time()
        self.throttle_cmd = message.linear.x
        self.steer_cmd = message.angular.z

    def update_message_from_chase(self, message):
        self._last_time_chase_rcv = time.time()
        self.throttle_chase = message.linear.x
        self.steer_chase = message.angular.z

์•ž์„œ ๋ง์”€๋“œ๋ ธ์ง€๋งŒ, ํ˜„ ํ”„๋กœ์ ํŠธ๋Š” ์ „์ง„, ํ›„์ง„ ์ œ์–ด๋Š” ์‚ฌ์šฉ์ž๋กœ ๋ถ€ํ„ฐ ๋”ฐ๋กœ ๋ฐ›๊ณ  ์žˆ์ง€์š”?

์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์• ๋กœ์‚ฌํ•ญ์ด ์žˆ์œผ์‹ค ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ, ์ดˆ์ŒํŒŒ ์„ผ์„œ๋ฅผ ํ†ตํ•ด ์ด๋ฅผ ๋ณด์™„ํ•˜๋Š” ๋˜๋‹ค๋ฅธ ๊ฐ•์˜๋ฅผ ์ฒจ๋ถ€ํ•˜๋‹ˆ, ๋‹ค์Œ ๊ฐ•์˜๊นŒ์ง€ ์™„๋ฃŒํ•˜์‹  ์ดํ›„ ๋„์ „ํ•ด ๋ณด์‹œ๊ธฐ๋ฅผ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

ROS and SONARS for OBSTACLE AVOIDANCE | Tutorial #6 | ROS and Raspberry Pi
Let's write our first OBSTACLE AVOIDANCE algorithm, using ROS Kinetic, a Raspberry Pi and our Donkey Car. Thanks to 3 cheap sonar sensors we define new nodes...
https://www.youtube.com/watch?v=JYnMRKVwBuQ&list=PLuteWQUGtU9BU0sQIVqRQa24p-pSBCYNv&index=8

๋‹ค์Œ ๊ฐ•์˜์— ์ด์–ด์„œ ๋‚˜๋จธ์ง€ ๋…ธ๋“œ๋“ค๋„ ๋ถ„์„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!!