kknives / pyvesc

Creative Commons Attribution 4.0 International
0 stars 0 forks source link

connection problems #1

Open ghost opened 1 year ago

ghost commented 1 year ago

hello everyone.

My name is Yonatan. My team and I are making an AUV and we're using the VESC 75/300 in order to control our main motor. We do most of our coding in oython and we make use of ROS2 in order to manage the communication between different modules.

While workshopping in the lab, we had little to no connection problems with the VESC. For some reason, after transportting the AUV and locking it up, we cant seem to reach serial connection. Moreover, our computer doesn't even see anything connected to the port where we connect our VESC.

Our connection method:

image

We have a rules.d file that sends '/dev/jet' to '/dev/ttyACM0'

We're using a USB cable to connect the VESC to an UP board (https://up-board.org/wp-content/uploads/datasheets/UPDatasheetV8.5.pdf). While in transit, the VESC is powered and physically connected to the UP board, but the UP board itself is turned off. We started thinking this might be one of the reasons for our trouble but we're unable to confirm this yet.

Has anyone used the python PyVESC module and had a similar problem?

Thanks in advance,

Yonatan.

kknives commented 1 year ago

If you can share your rules.d file, maybe I can help.

Moreover, our computer doesn't even see anything connected to the port where we connect our VESC.

Does lsusb not show the VESC connected?

ghost commented 1 year ago

If you can share your rules.d file, maybe I can help.

Moreover, our computer doesn't even see anything connected to the port where we connect our VESC.

Does lsusb not show the VESC connected?

No we can't seem to find it with lsusb

image

kknives commented 1 year ago

The udev rules seem fine. But if the device doesn't show up on lsusb, then there's a good chance there is something wrong with the connections or the VESC is not powered.

ghost commented 1 year ago

The udev rules seem fine. But if the device doesn't show up on lsusb, then there's a good chance there is something wrong with the connections or the VESC is not powered.

I saw in your motor_examples.py file that you use stop_heartbeat() saying that not using it before we get out of scope may cause issues. I'm not making use of a heartbeat function anywhere in my code, either to stop or start the heartbeat. is there a chance my connection issues have anything to do with that?

image could you maybe expand a bit on what you meant here?

image this is how we get data from the VESC.

kknives commented 1 year ago

I'm not making use of a heartbeat function anywhere in my code, either to stop or start the heartbeat. is there a chance my connection issues have anything to do with that?

I am not familiar with the exact details of stop_heartbeat, as I did not write the code for it, but, it seems that VESC sends the last command periodically. It is done automatically, but since it spawns a separate thread, you need to shutdown that thread before you stop using the motor. You should add a call to stop_heartbeat in your code too. https://github.com/kknives/pyvesc/blob/cc0043383b7f86d28a479ceab2d4c8cca8b7daee/pyvesc/VESC/VESC.py#L77-L81

Also, could you post your code as text, so its easier for me to make corrections.

ghost commented 1 year ago
    #!/usr/bin/env python3
    import rclpy
    from rclpy.node import Node
    from lar_interfaces.msg import Joystick, JetMotorData, TigerMode, MotorGuidance
    import pyvesc
    from pyvesc import VESC
    import time
    from autopilot_pkg.lar_common import Error_code, Tiger_mode
    import os
    import subprocess
    serialport = '/dev/jet'
    class JetNode(Node):                  # MODIFY NAME !!
      def __init__(self):
        super().__init__('jet_node')  # MODIFY NAME !!

        self.vesc_is_connected = False
        while not self.vesc_is_connected:
            try:
                self.motor = VESC(serialport)
                self.get_logger().info('vesc has been connected')
                self.vesc_is_connected = True
            except Exception as e:
                # self.get_logger().info(e)
                self.get_logger().info('VESC has serial connection issue')
                pass
            finally:
                pass
        # self.motor = VESC(serialport)
        self.get_logger().info('Jet node has been started')
        # self.motor_data_valid = False
        self.wdt_jet = 0
        self.epoch_jet = int(time.time() * 1e6)
        self.motor_data_list = [None] * 19
        self.tiger_mode = 1
        self.tiger_mode_subscriber = self.create_subscription(TigerMode, 'tiger_mode', self.tiger_mode_callback, 10)
        self.jet_subscriber = self.create_subscription(Joystick, "joystick_data", self.callback_jet, 10)
        self.servo_guided_subscriber = self.create_subscription(MotorGuidance, "motor_guidance_data", self.callback_jet_guided, 0)
        self.jet_data_publisher = self.create_publisher(JetMotorData, "jet_motor_data",10)
        self.jet_pub_timer = self.create_timer(1, self.motor_data)
        # self.jet_data_timer = self.create_timer(1, self.get_motor_data)
        # self.motor.set_rpm(2000)
        # self.motor_test()
        # self.motor_data_list[0] = int(time.time()*1e6) 

      def callback_jet_guided(self, msg):
          if self.tiger_mode == Tiger_mode.Tiger_GUIDED_MISSION.value:
              self.motor.set_rpm(int(msg.jet_rpm))

      def tiger_mode_callback(self, msg):
          self.tiger_mode = msg.tiger_mode
          if self.tiger_mode == Tiger_mode.Tiger_HOLD_POSITION.value:
              self.motor.set_rpm(0)

      def motor_test(self):
          initial_speed = 2000
          with VESC(serialport, timeout=1) as motor:
              motor.set_rpm(initial_speed)

      def callback_jet(self, msg):
          if self.tiger_mode == Tiger_mode.Tiger_IDLE.value:
              desired_rpm = self.remote_value_scaling(msg.chan3)
              self.motor.set_rpm(int(desired_rpm))

      def remote_value_scaling(self, x):
          old_min = 1000
          old_max = 2000
          new_min = -20000
          new_max = 20000
          desired_speed = (((x - old_min) * (new_max - new_min)) / (old_max - old_min)) + new_min
          if desired_speed < 900 and desired_speed > -900:
              desired_speed = 0
              return desired_speed
          else:
              return desired_speed

      def motor_data(self):
          msg = JetMotorData()
          msg.timestamp = int(time.time() * 1e6)
          try:
              # msg = JetMotorData()
              # msg.avg_motor_current = self.motor.get_measurements().avg_motor_current
              # self.get_logger().info(str(msg.avg_motor_current))
              msg.rpm = self.motor.get_measurements().rpm
              msg.temp_fet = self.motor.get_measurements().temp_fet
              msg.temp_motor = self.motor.get_measurements().temp_motor
              msg.v_in = self.motor.get_measurements().v_in
              self.jet_data_publisher.publish(msg)
          except Exception as e:
              self.wdt_jet = (int(time.time()*1e6) - msg.timestamp)/1e6
              # if self.wdt_vn
              if self.wdt_jet > 5: 
                  message = 'Problem with JET, Time: ' + str(self.wdt_jet)
                  self.get_logger().info(message)
          finally:
              pass

                  # rosbag_process = subprocess.Popen('ros2 bag record /ADC_measurements /BME_Back_measurements /BA30_measurements /BME_Front_measurements /Vn200Data /joystick_data /qgc_mode /status /tiger_mode /pid_data_log /GnssVn200 /InsStatus /jet_motor_data /motor_guidance_data /parameter /status /servo_motor_data /waypoint /guidance_data_log', stdin=subprocess.PIPE, shell=True, cwd=self.dir_save_bagfile)

    def main(args=None):
        rclpy.init(args=args)                               # Starts ros2 comm
        node = JetNode()                 # MODIFY NAME !!    # Node constructor ("node name")  
        rclpy.spin(node)
        rclpy.shutdown()                                    # Ends comm

    if __name__ == '__main__':
        main()

thank you for the help!

kknives commented 1 year ago

For adding a call to stop_heartbeat, make a new method in JetNode:

def close(self):
    self.motor.stop_heartbeat()

Modify main:

  def main(args=None):
      rclpy.init(args=args)                               # Starts ros2 comm
      node = JetNode()                 # MODIFY NAME !!    # Node constructor ("node name")  
      rclpy.spin(node)
      node.close()
      rclpy.shutdown()                                    # Ends comm

also now the problem is that it seems to connect, send out a couple of messages and then stops sending. we're using ROS2 to manage the communication.

Are you getting Exception messages? As you defined them in motor_data:

def motor_data(self):
        msg = JetMotorData()
        msg.timestamp = int(time.time() * 1e6)
        try:
            # msg = JetMotorData()
            # msg.avg_motor_current = self.motor.get_measurements().avg_motor_current
            # self.get_logger().info(str(msg.avg_motor_current))
            msg.rpm = self.motor.get_measurements().rpm
            msg.temp_fet = self.motor.get_measurements().temp_fet
            msg.temp_motor = self.motor.get_measurements().temp_motor
            msg.v_in = self.motor.get_measurements().v_in
            self.jet_data_publisher.publish(msg)
        except Exception as e:
            self.wdt_jet = (int(time.time()*1e6) - msg.timestamp)/1e6
            # if self.wdt_vn
            if self.wdt_jet > 5: 
                message = 'Problem with JET, Time: ' + str(self.wdt_jet)
                self.get_logger().info(message)
        finally:
            pass
ghost commented 1 year ago

Are you getting Exception messages? As you defined them in motor_data:

No. I get a connection, a few messages (it changes between 2 and a max of 25) are sent by the VESC and received on my end, and after that it just seems to be getting out of the scope of the node. it just doesn't go into the publisher function at some point.

kknives commented 1 year ago

Can you try running the example? Make sure to change the serial port. Let me know if you get errors here too.

ghost commented 1 year ago

the example works and doesn't seem to make problems. also when I run my code from the IDE and not a ROS2 terminal command, it seems to work. only when I try and use the ROS2 terminal command ros2 run motor_pkg jet_node am I getting problems.

def motor_data(self):
        msg = JetMotorData()
        msg.timestamp = int(time.time() * 1e6)
        try:
            # msg = JetMotorData()
            # msg.avg_motor_current = self.motor.get_measurements().avg_motor_current
            # self.get_logger().info(str(msg.avg_motor_current))
            msg.rpm = self.motor.get_measurements().rpm
            msg.temp_fet = self.motor.get_measurements().temp_fet
            msg.temp_motor = self.motor.get_measurements().temp_motor
            msg.v_in = self.motor.get_measurements().v_in
            self.jet_data_publisher.publish(msg)
            self.get_logger().info('done with msg')
        except Exception as e:
            self.wdt_jet = (int(time.time()*1e6) - msg.timestamp)/1e6
            # if self.wdt_vn
            if self.wdt_jet > 5: 
                message = 'Problem with JET, Time: ' + str(self.wdt_jet)
                self.get_logger().info(message)
        finally:
            pass

I added a get_logger() function to see when the motor is still sending messages and you can see in the image that it just stops sending at a seemingly random time.

image

kknives commented 1 year ago

Try running ros2 topic echo /jet_motor_data in another terminal when you run the jet_node, does it also stop updating after sometime?

the example works and doesn't seem to make problems.

Then the problem isn't in PyVESC

ghost commented 1 year ago

Try running ros2 topic echo /jet_motor_data in another terminal when you run the jet_node, does it also stop updating after sometime?

It does. It stops echoing at the same time as it stops printing.

Then the problem isn't in PyVESC

I thought as much, I think it's either a ROS2 problem or a hardware problem but I just hoped someone else faced a similar problem.

kknives commented 1 year ago

also when I run my code from the IDE and not a ROS2 terminal command, it seems to work

Does your IDE start the node from a launch file? Also, can you try printing a message every time the motor_data function is called, something like this:

def motor_data(self):
        msg = JetMotorData()
        msg.timestamp = int(time.time() * 1e6)
        try:
            # msg = JetMotorData()
            # msg.avg_motor_current = self.motor.get_measurements().avg_motor_current
            # self.get_logger().info(str(msg.avg_motor_current))
            msg.rpm = self.motor.get_measurements().rpm
            msg.temp_fet = self.motor.get_measurements().temp_fet
            msg.temp_motor = self.motor.get_measurements().temp_motor
            msg.v_in = self.motor.get_measurements().v_in
            self.get_logger().info('Read all values')
            ...

This would narrow the problem down to the self.jet_data_publisher.publish(msg) call

ghost commented 1 year ago

I'm using Visual studio code IDE and I think it does make use of a launch file.

also printing the outputs never stops in the middle of the publisher function. at some point (5 - 15 seconds in) the communication with the VESC just seems to stop.

def motor_data(self):
        msg = JetMotorData()
        msg.timestamp = int(time.time() * 1e6)
        try:
            meas = self.motor.get_measurements()
            # msg = JetMotorData()
            # msg.avg_motor_current = self.motor.get_measurements().avg_motor_current
            # self.get_logger().info(str(msg.avg_motor_current))
            # msg.rpm = self.motor.get_measurements().rpm
            msg.rpm = meas.rpm
            self.get_logger().info(str(msg.rpm))
            # msg.temp_fet = self.motor.get_measurements().temp_fet
            msg.temp_fet = meas.temp_fet
            self.get_logger().info(str(msg.temp_fet))
            # msg.temp_motor = self.motor.get_measurements().temp_motor
            msg.temp_motor = meas.temp_motor
            self.get_logger().info(str(msg.temp_motor))
            # msg.v_in = self.motor.get_measurements().v_in
            msg.v_in = meas.v_in
            self.get_logger().info(str(msg.v_in))
            self.jet_data_publisher.publish(msg)
            self.get_logger().info('done with msg')
        except Exception as e:
            self.wdt_jet = (int(time.time()*1e6) - msg.timestamp)/1e6
            # if self.wdt_vn
            if self.wdt_jet > 5: 
                message = 'Problem with JET, Time: ' + str(self.wdt_jet)
                self.get_logger().info(message)
        finally:
            pass

image

kknives commented 1 year ago

It's strange that a connection or a hardware issue only occurs when not using VSCode.

I'm using Visual studio code IDE and I think it does make use of a launch file.

That's the only thing left to check now. When you run the node from VSCode, what command does it use?

ghost commented 1 year ago

image

when I hit play in the IDE that's the command that he uses.

what's most strange to me is that it just seems to happen at random. I've tried using different system loads (other nodes working in parallel) and it dosen't seem to matter. it always claims to make a connection, just after some times stops interacting.

kknives commented 1 year ago

The VSCode command does the same thing as ros2 run.

As for the connection issues, you can try increasing the range in the example to see what error occurs: https://github.com/kknives/pyvesc/blob/cc0043383b7f86d28a479ceab2d4c8cca8b7daee/pyvesc/examples/motor_example.py#L15

Without a good idea of what is stopping the connection, it's hard to come up with a fix 😕