ROS学习零碎记录

  1. vscode插件安装:C/C++、CMake、IntelliCode、Markdown All in One、Msg Language Support、Python、ROS、URDF、vscode-icons(来源:古月居
  2. ros和vscode的安装:鱼香ros一键安装
    wget http://fishros.com/install -O fishros && . fishros
  3. ros快捷键Ctrl+Shift+B编译,并设置默认编译器:按下Ctrl+Shfit+P,输入指令tasks:configure task,选择catkin_make: build,将task.json里的group值改为{"kind":"build","isDefault":true}
  4. ModuleNotFoundError: No module named 'cv_bridge':sudo apt-get install ros-<对应的ros版本名称>-cv-bridge
  5. 编译完后,需在终端中重新source一下,否则运行的还是旧程序
  6. 如果编译报过错,将程序撤回到能正常编译的状态,但此时也可能会编译失败,可将build文件夹删除重新编译一次
  7. 创建工作空间和功能包
    # 创建工作空间 ros_ws为自定义工作空间名称
    mkdir -p ~/ros_ws/src
    
    # 切换到工作空间的src
    cd ~/ros_ws/src
    
    # 创建功能包 ros_pkg为自定义功能包名称 rospy和roscpp为依赖
    catkin_create_pkg ros_pkg rospy roscpp
    
    # 切换到工作空间
    cd ~/ros_ws
    
    # 编译
    catkin_make
  8. 关于 ~/.bashrc 中添加多个工作空间,可能会出现 source 的顺序错乱,工作空间被覆盖的问题
    1.问题说明:
    
    你有没有遇到这种情况?明明已经将工作空间中的 setup.bash 添加进 ~/.bashrc 了,可是终端却找不到对应的功能包,需要在终端中再 source 一下才能找到
    
    2.原因:
    
    没有按照先后创建并编译工作空间的顺序添加 setup.bash 到 ~/.bashrc
    
    3.如何避免:
    
    养成习惯,每次添加 setup.bash 到 ~/.bashrc 前,都把 build 和 devel 文件夹删了,然后重新编译一次,且在添加时只能在末尾添加,不要试图添加到以前添加的 source 命令前
    
    4.解决方法:
    
    方法1:按照 ~/.bashrc 中 source 工作空间的顺序,将对应 build 和 devel 文件夹删了并重新编译;
    
    方法2:找到 ~/.bashrc 中,最后一个被 source 的工作空间,在其 devel/_setup_util.py 中,找到CMAKE_PREFIX_PATH变量,按需将其他工作空间的 devel 添加进去即可。
  9. 功能包下,Python文件一般放自建的scripts里(其实随便命名一个文件夹也行),而Cpp文件则放在src里,头文件则放在include里
  10. 功能包下的Python文件需要给予可执行权限才能用rosrun执行
    # 命令行方式
    sudo chmod +x 你的Python文件.py
    
    # 文件夹右键属性方式
  11. Bash一行命令自动执行多输入
    # EOF是End Of File的缩写,表示自定义终止符。既然自定义,那么EOF就不是固定的,可以随意设置别名,意思是把内容当作标准输入传给程序,Linux中按Ctrl-d就代表EOF
    
    # 通常将EOF与 << 结合使用,表示后续的输入作为子命令或子Shell的输入,直到遇到EOF为止,再返回到主调Shell
    
    # 使用示例
    cat <<EOF
    <1
    <2
    <3
    <EOF
  12. 多相机同时采集和显示
    import cv2
    import numpy as np
    import time
    import multiprocessing
    import threading
    import os
    import shutil
    
        
    def cameraProcess(id, key_q:multiprocessing.Queue):
        def show(id, width, height, resize):
            nonlocal is_running
            nonlocal frame
            nonlocal timestamp
            nonlocal key_ctl
    
            file_path = os.path.dirname(__file__)
    
            if os.name == "posix":
                dir = os.path.join(file_path, f"cam/cam{id}")
            elif os.name == "nt":
                dir = os.path.join(file_path, f"cam\\cam{id}")
    
            if not os.path.exists(dir):
                os.makedirs(dir)
            else:
                shutil.rmtree(dir)
                os.makedirs(dir)
    
            while is_running:
                if frame is None:
                    continue
                img = frame.copy()
                frame = None
                raw_10 = img.view(dtype=np.int16)
                raw_sh = np.right_shift(raw_10, 2)
                raw_8 = raw_sh.astype(np.uint8)
                img = raw_8.reshape(height, width)
                img = cv2.cvtColor(img, cv2.COLOR_BAYER_BG2GRAY)
    
                if resize:
                    img_show = cv2.resize(img, (640, 480))
                    img_show = cv2.flip(img_show, -1)
                    cv2.imshow(f"Video{id}", img_show)
                else:
                    cv2.imshow(f"Video{id}", img)
    
                key = cv2.waitKey(1)
                if key == 27 or key_ctl == "exit":
                    is_running = False
                elif key_ctl == "save":
                    path = os.path.join(dir, str(timestamp) + ".png")
                    cv2.imwrite(path, img)
                key_ctl = None
                
            cv2.destroyAllWindows()
    
        def getFrame(cam):
            nonlocal is_running
            nonlocal frame
            nonlocal timestamp
    
            while is_running and cam.isOpened():
                timestamp = int(time.time() * 1e9)
                ret, frame = cam.read()
                if not ret:
                    is_running = False
            cam.release()
    
        cam = cv2.VideoCapture(id)
        width = int(cam.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cam.get(cv2.CAP_PROP_FRAME_HEIGHT))
        cam.set(cv2.CAP_PROP_CONVERT_RGB, 0)
    
        print(width, height)
    
        resize = False
        if width != 640:
            resize = True
        
        is_running = True
        frame = None
        timestamp = 0
        key_ctl = None
    
        getFrame_thread = threading.Thread(target=getFrame, args=(cam,), daemon=True)
        show_thread = threading.Thread(target=show, args=(id, width, height, resize,), daemon=True)
        
        getFrame_thread.start()
        show_thread.start()
    
        while is_running:
            time.sleep(0.01)
            try:
                key_ctl = key_q.get(block=False)
            except:
                continue
    
        getFrame_thread.join()
        show_thread.join()
    
    
    if __name__=="__main__":
        key_q1 = multiprocessing.Queue()
        key_q2 = multiprocessing.Queue()
    
        cam1 = multiprocessing.Process(target=cameraProcess, args=(0, key_q1,), daemon=True)
        cam2 = multiprocessing.Process(target=cameraProcess, args=(1, key_q2,), daemon=True)
    
        cam1.start()
        cam2.start()
    
        mat = np.zeros((320, 320))
    
        cv2.namedWindow("key_control", 1)
        cv2.resizeWindow("key_control", 320, 320)
    
        while True:
            cv2.imshow("key_control", mat)
            key = cv2.waitKey(1)
            if key == 27:
                key_q1.put("exit")
                key_q2.put("exit")
                break
            elif key == ord('s'):
                key_q1.put("save")
                key_q2.put("save")
        cv2.destroyWindow("key_control")
    
        cam1.join()
        cam2.join()
  13. 发布USB相机话题示例
    #!/usr/bin/env python3
    #coding:utf-8
     
    import rospy
    import cv2
    import numpy as np
    from sensor_msgs.msg import Image
    from cv_bridge import CvBridge
     
    def pubVideo():
        rospy.init_node('play_node',anonymous = False)
        pub = rospy.Publisher('/camera/image', Image, queue_size = 10)
        # rate = rospy.Rate(100)
        bridge = CvBridge()
        cap = cv2.VideoCapture(0)
        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        cap.set(cv2.CAP_PROP_CONVERT_RGB, 0)
        if not cap.isOpened():return -1
        while not rospy.is_shutdown():
            # rate.sleep()
            now = rospy.Time.now()
            ret, frame = cap.read()
            if ret:
                raw_10 = frame.view(dtype=np.int16)
                raw_sh = np.right_shift(raw_10, 2)
                raw_8 = raw_sh.astype(np.uint8)
                img = raw_8.reshape(height, width)
                # img2 = cv2.cvtColor(img, cv2.COLOR_BAYER_BG2RGB)
                img2 = cv2.cvtColor(img, cv2.COLOR_BAYER_BG2GRAY)
                msg = bridge.cv2_to_imgmsg(img2)
                msg.header.stamp = now
                rospy.loginfo("%.6f"%msg.header.stamp.to_sec())
                pub.publish(msg)
        cap.release()
           
    if __name__ == '__main__':
        pubVideo()
    
  14. 命令行传参发布相机或视频话题
    #!/usr/bin/env python3
    #coding:utf-8
     
    import rospy
    import cv2
    import numpy as np
    from sensor_msgs.msg import Image
    from cv_bridge import CvBridge
    import argparse
    import subprocess
     
    def pubVideo(id=0):
        rospy.init_node('camera_node',anonymous = False)
        pub = rospy.Publisher(f'/image', Image, queue_size = 10)
    
        if args.rate:
            rate = rospy.Rate(args.rate)
        bridge = CvBridge()
    
        cap = cv2.VideoCapture(id)
        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    
        if not cap.isOpened():
            return -1
    
        while not rospy.is_shutdown():
            if args.rate:rate.sleep()
            now = rospy.Time.now()
            ret, frame = cap.read()
            if ret:
                msg = bridge.cv2_to_imgmsg(frame)
                msg.header.stamp = now
                msg.width = width
                msg.height = height
                rospy.loginfo("%.6f"%msg.header.stamp.to_sec())
                pub.publish(msg)
        cap.release()
           
    if __name__ == '__main__':
        p = argparse.ArgumentParser(description="相机或视频话题发布")
        p.add_argument("-i", "--input", type=str, help="相机id或视频路经")
        p.add_argument("-s", "--start", type=int, default=0, help="开始遍历的相机id")
        p.add_argument("-r", "--rate", type=int, help="读取的速度hz")
    
        args = p.parse_args()
    
        if args.input:
            path = args.input
            try:
                path = int(path)
            except:
                pass
            pubVideo(path)
            exit()
        
        res = subprocess.run("ls /dev/video*", stdout=subprocess.PIPE, shell=True).stdout.decode().strip().split('\n')
    
        if len(res) == 1 and res[0] == '':
            print("Not find camera")
            exit()
    
        for i in range(args.start, len(res)):
            id = int(res[i][-1])
            cap = cv2.VideoCapture(id)
            if not cap.isOpened():
                cap.release()
                continue
            cap.release()
            print("cam_id:", int(res[i][-1]))
            res = pubVideo(id)
            break
  15. 订阅相机话题示例
    #!/usr/bin/env python3
    #coding:utf-8
     
    import rospy
    import cv2
    from sensor_msgs.msg import Image
    from cv_bridge import CvBridge
    
    bridge = CvBridge()
    
    def subVideo(msg):
        img = bridge.imgmsg_to_cv2(msg)
        rospy.loginfo("%.6f"%msg.header.stamp.to_sec())
        cv2.imshow("img", img)
        cv2.waitKey(1)
           
    if __name__ == '__main__':
        rospy.init_node('show_node')
        sub = rospy.Subscriber('/camera/image', Image, subVideo)
        rospy.spin()
    
  16. 相机视场角计算
    import cv2
    import numpy as np
    
    def camera_fov(K: np.ndarray, D: np.ndarray, distortion_mode: int = 0):
        """
        计算相机的视场角
        
        :param K 相机内参3*3
        :param D 相机畸变参数5*1
        :param distortion_mode 畸变模型, 0:鱼眼模型,1:径向切向模型
        :return: 相机的视场角1*3,水平, 垂直, 对角
        """
        cx = K[0, 2]
        cy = K[1, 2]
        p1 = (0, cy)
        p2 = (cx, 0)
        p3 = (0, 0)
        if distortion_mode == 0:
            D1 = D[0:4] if type(D) == np.ndarray else 0
            # 输入像素坐标,输出归一化坐标
            pds = cv2.fisheye.undistortPoints(np.array([[p1, p2, p3]]), K, D1)
            w = np.abs(pds[0][0][0])
            h = np.abs(pds[0][1][1])
            d = np.sqrt(pds[0][2][0]**2 + pds[0][2][1]**2)   
        else:
            # 输入像素坐标,输出归一化坐标
            pds = cv2.undistortPoints(np.array([p1, p2, p3]), K, D)
            w = np.abs(pds[0][0][0])
            h = np.abs(pds[1][0][1])
            d = np.sqrt(pds[2][0][0]**2 + pds[2][0][1]**2)
        ax = 2 * np.degrees(np.arctan(w))
        ay = 2 * np.degrees(np.arctan(h))
        ad = 2 * np.degrees(np.arctan(d))
        return ax, ay, ad
    
    
    if __name__ == "__main__":
    
        K = np.array([
                [1.4160544575705535e+03, 0, 3.195e+02],
                [0, 1.4160544575705535e+03, 2.395e+02],
                [0, 0, 1]
            ])
    
        D = np.array([
                [-9.4068857788802127e-02],
                [2.9141590071782497],
                [0.0],
                [0.0],
                [-1.3025885590073166e+01]
            ])
    
        print(camera_fov(K, D, 1))
  17. 多话题消息同步后发布话题,同步后的频率向订阅的话题中频率最低的看齐
    #!/usr/bin/env python3
    #coding:utf-8
    
    import rospy
    import message_filters
    from sensor_msgs.msg import Image, Imu
    
    def multi_callback(Sub_img1, Sub_img2, Sub_imu):
        pub_img1.publish(Sub_img1)
        pub_img2.publish(Sub_img2)
        pub_imu.publish(Sub_imu)
    
    if __name__ == '__main__':
        rospy.init_node('together_node', anonymous=False) #初始化节点
    
        sub_img1 = message_filters.Subscriber('/cam0/image', Image)
        sub_img2 = message_filters.Subscriber('/cam1/image', Image)
        sub_imu = message_filters.Subscriber('/imu/imu', Imu) 
    
        pub_img1 = rospy.Publisher('/cam0/image_t', Image, queue_size = 10)
        pub_img2 = rospy.Publisher('/cam1/image_t', Image, queue_size = 10)
        pub_imu = rospy.Publisher('/imu/imu_t', Imu, queue_size = 10)
     
        sync = message_filters.ApproximateTimeSynchronizer([sub_img1, sub_img2, sub_imu], 10, 1) #同步时间戳,slop为可以同步的最大延迟(以秒为单位)
        sync.registerCallback(multi_callback)   #执行反馈函数
        try:
            rospy.spin()
        except KeyboardInterrupt:
            print("over!")
    
  18. kalibr标定工具箱的安装和使用github
    1.启动ros内核
    roscore
    
    2.查看bag信息
    rosbag info cam.bag
    
    3.标定
    3.1 设置环境变量
    export KALIBR_MANUAL_FOCAL_LENGTH_INIT=1
    3.2 开始
    rosrun kalibr kalibr_calibrate_cameras --models pinhole-equi --target april_9x9.yaml --bag cam.bag --topics /image
    3.3 设置初始焦距
    3.4 写成一行
    export KALIBR_MANUAL_FOCAL_LENGTH_INIT=1;echo 400 | rosrun kalibr kalibr_calibrate_cameras --models pinhole-equi --target april_9x9.yaml --bag test.bag --topics /cam0/image_raw
    
    4.解包 图片命名十九位数
    rosrun kalibr kalibr_bagextractor --image-topics /image --imu-topics /imu --output-folder cam/ --bag cam.bag
    
    5.打包 图片命名十九位数
    rosrun kalibr kalibr_bagcreater --folder cam/. --output-bag test.bag
    5.1 打包目录示例
    -cam
    --cam0
    ---1385030208726607500.png
    ---i1385030212176607500.png
    --cam1
    ---1385030208726607500.png
    ---1385030212176607500.png
    --imu0.csv
    5.2 imu.csv示例
    timestamp,omega_x,omega_y,omega_z,alpha_x,alpha_y,alpha_z
    1385030208736607488,0.5,-0.2,-0.1,8.1,-1.9,-3.3
     ...
    1386030208736607488,0.5,-0.1,-0.1,8.1,-1.9,-3.3
    
    6.标定help
    rosrun kalibr kalibr_calibrate_cameras -h
  19. python进程绑定cpu内核的方法
    import os
    
    # 绑定倒数十个核
    cpu_ave = list(os.sched_getaffinity(os.getpid()))[::-1][:10]
    os.sched_setaffinity(os.getpid(), cpu_ave)
  20. rosbag的使用
    # 录制/topic_name1和/topic_name2的话题,打包成output.bag
    rosbag record /topic_name1 /topic_name2 -O output.bag
    
    # 以0.5倍速播放output.bag
    rosbag play output.bag -r 0.5
    
    # 查看output.bag信息
    rosbag info output.bag
  21. 关于VMware虚拟机ubuntu无网络问题
    1.先检查一下VM虚拟网络开机自启服务有没有被关闭
    
    2.如果电脑主机有网络,但虚拟机右上角有线网络图标消失了,则删除NetworkManager缓存文件,重启网络
    sudo service NetworkManager stop 
    sudo rm /var/lib/NetworkManager/NetworkManager.state 
    sudo service NetworkManager start
  22. 欧拉角转旋转矩阵与可视化(原始方法)
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.spatial.transform import Rotation
    
    # 欧拉角转旋转矩阵,外旋
    def euler2matrix(euler, seq='xyz', degrees:bool=True):
        r = Rotation.from_euler(seq, euler, degrees)
        R = r.as_matrix()
        return R
    
    # 画出转移矩阵
    def plot_transform_matrix(T, ax, scale, name):
        R = T[0:3, 0:3]
        # 绘制旋转后的坐标系
        new_origin = T[0:3, 3]
        new_x_axis = R[:, 0]
        new_y_axis = R[:, 1]
        new_z_axis = R[:, 2]
    
        ax.quiver(*new_origin, *new_x_axis, color='r', length=scale, normalize=True)
        ax.quiver(*new_origin, *new_y_axis, color='g', length=scale, normalize=True)
        ax.quiver(*new_origin, *new_z_axis, color='b', length=scale, normalize=True)
    
        # 设置图例
        ax.legend()
        ax.text(*new_origin, name)
    
    # 右手螺旋定则,从轴的正方向看向原点,逆正顺负
    a1 = np.deg2rad(-90)
    a2 = np.deg2rad(-60)
    a3 = np.deg2rad(-90)
    
    # 绕x轴旋转a1度的旋转矩阵
    Rx = np.array([[1, 0, 0],
                   [0, np.cos(a1), -np.sin(a1)],
                   [0, np.sin(a1), np.cos(a1)]])
    # 绕Y轴旋转a2度的旋转矩阵
    Ry = np.array([[np.cos(a2), 0, np.sin(a2)],
                   [0, 1, 0],
                   [-np.sin(a2), 0, np.cos(a2)]])
    # 绕Z轴旋转a3度的旋转矩阵
    Rz = np.array([[np.cos(a3), -np.sin(a3), 0],
                   [np.sin(a3), np.cos(a3), 0],
                   [0, 0, 1]])
    
    # 按XYZ顺序,绕旋转后的自身轴旋转(内旋,右乘),原始系到目标系,欧拉角
    R = Rx @ Ry @ Rz
    
    # 按XYZ顺序,绕参考系的固定轴旋转(外旋,左乘),参考系到目标系,一般以外旋为主
    R = Rz @ Ry @ Rx
    
    R = euler2matrix((a1, a2, a3), degrees=False)
    
    # 构造转移矩阵T,旋转矩阵与平移矩阵的组合体,4x4
    T = np.zeros((4, 4))
    T[0:3,0:3] = R
    T[0:3,3] = 0.01
    print(T)
    
    fig = plt.figure()
    ax = fig.add_subplot(projection='3d')
    plot_transform_matrix(np.identity(4), ax, 0.005, 'base')
    plot_transform_matrix(T, ax, 0.005, 'rot')
    
    plt.axis('equal')
    plt.show()
    
  23.  欧拉角、旋转矩阵、四元数、旋转向量的互换(Python scipy)
    import numpy as np
    from scipy.spatial.transform import Rotation
    
    '''
    一、四元数 (x, y, z, w)
    二、旋转向量 (rx, ry, rz) 弧度或角度
    三、欧拉角(内旋) 弧度或角度:
        1.绕前后轴Roll 绕左右轴Pitch 绕上下轴Yaw
        2.(yaw-z, pitch-y, roll-x) 世界坐标系 前x左y上z ZYX 常用
        3.(yaw-y, pitch-x, roll-z) 相机坐标系 前z右x下y
        4.欧拉角万向锁问题(奇异性):俯仰角为+-90°时,第一次旋转与第三次旋转将使用同一个轴,使系统丢失了一个自由度
    
    '''
    
    '''旋转矩阵'''
    
    # 旋转矩阵转旋转向量
    def matrix2rvec(R, degrees:bool=False):
        r = Rotation.from_matrix(R)
        rvec = r.as_rotvec(degrees)
        return rvec
    
    # 旋转向量转旋转矩阵
    def rvec2matrix(rvec, degrees:bool=False):
        r = Rotation.from_rotvec(rvec, degrees)
        R = r.as_matrix()
        return R
    
    # 旋转矩阵转四元数
    def matrix2quat(R):
        r = Rotation.from_matrix(R)
        quat = r.as_quat()
        return quat
    
    # 四元数转旋转矩阵
    def quat2matrix(quat):
        r = Rotation.from_quat(quat)
        matrix = r.as_matrix()
        return matrix
    
    # 旋转矩阵转欧拉角 euler顺序与seq相同
    def matrix2euler(R, seq='xyz', degrees:bool=True):
        r = Rotation.from_matrix(R)
        euler = r.as_euler(seq, degrees)
        return euler
    
    # 欧拉角转旋转矩阵 euler顺序与seq相同
    def euler2matrix(euler, seq='xyz', degrees:bool=True):
        r = Rotation.from_euler(seq, euler, degrees)
        R = r.as_matrix()
        return R
    
    '''旋转向量'''
    
    # 旋转向量转四元数
    def rvec2quat(rvec, degrees:bool=False):
        r = Rotation.from_rotvec(rvec, degrees)
        quat = r.as_quat()
        return quat
    
    # 四元数转旋转向量
    def quat2rvec(quat, degrees:bool=False):
        r = Rotation.from_quat(quat)
        rvec = r.as_rotvec(degrees)
        return rvec
    
    # 旋转向量转欧拉角 euler顺序与seq相同
    def rvec2euler(rvec, seq='xyz', degrees_in:bool=False, degrees_out:bool=True):
        r = Rotation.from_rotvec(rvec, degrees_in)
        euler = r.as_euler(seq, degrees_out)
        return euler
    
    # 欧拉角转旋转向量 euler顺序与seq相同
    def euler2rvec(euler, seq='xyz', degrees_in:bool=True, degrees_out:bool=False):
        r = Rotation.from_euler(seq, euler, degrees_in)
        rvec = r.as_rotvec(degrees_out)
        return rvec
    
    '''四元数'''
    
    # 四元数转欧拉角 euler顺序与seq相同
    def quat2euler(quat, seq='xyz', degrees:bool=True):
        r = Rotation.from_quat(quat)
        euler = r.as_euler(seq, degrees)
        return euler
    
    # 欧拉角转四元数 euler顺序与seq相同
    def euler2quat(euler, seq='xyz', degrees:bool=True):
        r = Rotation.from_euler(seq, euler, degrees)
        quat = r.as_quat()
        return quat
    
    '''原始方法 外旋 XYZ'''
    
    def isRotationMatrix(R):
        Rt = np.transpose(R)
        shouldBeIdentity = np.dot(Rt, R)
        I = np.identity(3, dtype=R.dtype)
        n = np.linalg.norm(I - shouldBeIdentity)
        return n < 1e-6
    
    def rot2euler(R):
        assert (isRotationMatrix(R))
    
        sy = np.sqrt(R[0, 0] * R[0, 0] + R[1, 0] * R[1, 0])
    
        singular = sy < 1e-6
    
        if not singular:
            x = np.arctan2(R[2, 1], R[2, 2]) * 180 / np.pi
            y = np.arctan2(-R[2, 0], sy) * 180 / np.pi
            z = np.arctan2(R[1, 0], R[0, 0]) * 180 / np.pi
        else:
            x = np.arctan2(-R[1, 2], R[1, 1]) * 180 / np.pi
            y = np.arctan2(-R[2, 0], sy) * 180 / np.pi
            z = 0
    
        return np.array([x, y, z])
  24. 旋转向量与旋转矩阵的互换(opencv)
    import cv2
    
    '''
    jacobian 雅可比矩阵
    '''
    
    rx = -0.0137838
    ry = -0.0034871
    rz = 3.13607115
    
    rvec = (rx, ry, rz)
    
    # 旋转向量转旋转矩阵
    R, jacobian = cv2.Rodrigues(rvec)
    
    # 旋转矩阵转旋转向量
    rvec, jacobian = cv2.Rodrigues(R)
  25. 雅可比矩阵的计算方法
    import sympy as sp
    
    # 定义符号变量
    x, y, k1, k2, k3, k4, k5, k6, p1, p2, r = sp.symbols('x y k1 k2 k3 k4 k5 k6 p1 p2 r')
    
    # 定义半径平方
    r_squared = x**2 + y**2
    
    kr = (1 + k1 * r_squared + k2 * r_squared**2 + k3 * r_squared**3) / (1 + k4 * r_squared + k5 * r_squared**2 + k6 * r_squared**3)
    
    # 畸变后的坐标
    x_distorted = x * kr + 2 * p1 * x * y + p2 * (r_squared + 2 * x**2)
    y_distorted = y * kr + p1 * (r_squared + 2 * y**2) + 2 * p2 * x * y
    
    # 创建函数向量
    f = sp.Matrix([x_distorted, y_distorted])
    
    # 计算雅可比矩阵
    J = f.jacobian([x, y])
    
    values = {
        x: 0.472428,  # 替换 x 的值
        y: -0.126214,  # 替换 y 的值
        k1: -0.093702,  # 替换 k1 的值
        k2: -0.008392,  # 替换 k2 的值
        k3: 0.000,  # 替换 k3 的值
        k4: 0.000,  # 替换 k4 的值
        k5: 0.000,  # 替换 k5 的值
        k6: 0.000,  # 替换 k6 的值
        p1: -0.000315,  # 替换 p1 的值
        p2: 0.000155   # 替换 p2 的值
    }
    
    print("雅可比矩阵的每个元素:")
    for i in range(J.shape[0]):
        for j in range(J.shape[1]):
            simplified_expr = sp.simplify(J[i, j].subs(x**2 + y**2, r))
            evaluated_expr = J[i, j].subs(values).evalf()
            print(f"J({i}, {j}) = {simplified_expr}\n")
            print(f"J({i}, {j}) = {evaluated_expr}\n")
    
    # 比较元素
    print("\n元素间相等性比较结果:")
    for i in range(J.shape[0]):
        for j in range(J.shape[1]):
            for k in range(J.shape[0]):
                for l in range(J.shape[1]):
                    if (i != k or j != l) and sp.simplify(J[i, j] - J[k, l]) == 0:
                        print(f"J({i}, {j}) 等于 J({k}, {l})")
    
    print("\n雅可比矩阵:")
    sp.pprint(J)
    
  26. 旋转向量转欧拉角(opencv,pnp,外旋,绕固定轴,XYZ,相机【z前x右y下】到目标)
    R = cv2.Rodrigues(rvec)[0]
    proj = np.hstack((R, tvec))
    euler = cv2.decomposeProjectionMatrix(proj)[6].reshape(1, -1)[0]
    
    # 按xyz顺序旋转的欧拉角
    pitch = euler[0] # x
    yaw = euler[1]   # y
    roll = euler[2]  # z
  27. 坐标系变换之变换矩阵T
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.spatial.transform import Rotation
    
    '''
    四元数 (x, y, z, w)
    旋转向量 (rx, ry, rz)
    '''
    
    # 旋转矩阵转四元数 
    def matrix2quat(rotation_matrix):
        r = Rotation.from_matrix(rotation_matrix)
        quat = r.as_quat()
        return quat
    
    # 四元数转旋转矩阵
    def quat2matrix(quaternion):
        r = Rotation.from_quat(quaternion)
        matrix = r.as_matrix()
        return matrix
    
    # 旋转矩阵转旋转向量
    def matrix2rotvec(rotation_matrix):
        r = Rotation.from_matrix(rotation_matrix)
        rotvec = r.as_rotvec()
        return rotvec
    
    # 旋转向量转旋转矩阵
    def rotvec2matrix(rotvec):
        r = Rotation.from_rotvec(rotvec)
        matrix = r.as_matrix()
        return matrix
    
    # 旋转向量转欧拉角
    def rotvec2euler(rotvec):
        r = Rotation.from_rotvec(rotvec)
        euler = r.as_euler("xyz", True)
        return euler
    
    def plot_transform_matrix(T, ax, scale, name):
    
        R = T[0:3, 0:3]
        # 绘制旋转后的坐标系
        new_origin = T[0:3, 3]
        new_x_axis = R[:, 0]
        new_y_axis = R[:, 1]
        new_z_axis = R[:, 2]
    
        ax.quiver(*new_origin, *new_x_axis, color='r', length=scale, normalize=True)
        ax.quiver(*new_origin, *new_y_axis, color='g', length=scale, normalize=True)
        ax.quiver(*new_origin, *new_z_axis, color='b', length=scale, normalize=True)
    
        # 设置图例
        ax.legend()
        ax.text(*new_origin, name)
    
    
    if __name__=="__main__":
    
        R1 = np.array([[1, 0, 0],  
                       [0, 1, 0],  
                       [0, 0, 1]]) 
    
        a1 = np.deg2rad(90)
        a2 = np.deg2rad(90)
        a3 = np.deg2rad(0)
    
        # 绕x轴旋转a1度的旋转矩阵
        Rx = np.array([[1, 0, 0],  
                       [0, np.cos(a1), -np.sin(a1)],  
                       [0, np.sin(a1), np.cos(a1)]]) 
    
        # 绕Y轴旋转a2度的旋转矩阵
        Ry = np.array([[np.cos(a2), 0, np.sin(a2)],  
                       [0, 1, 0],  
                       [-np.sin(a2), 0, np.cos(a2)]])  
         
        # 绕Z轴旋转a3度的旋转矩阵  
        Rz = np.array([[np.cos(a3), -np.sin(a3), 0],  
                       [np.sin(a3), np.cos(a3), 0],  
                       [0, 0, 1]])   
        
        # 绕固定轴按XYZ顺序旋转
        R = Rz @ Ry @ Rx
    
        print(matrix2rotvec(R.T)) 
       
    
        '''cam1'''
    
        # cam1:T_cam_imu imu_to_cam1 (imu, cam1为单位矩阵)
        T_cam1_imu = np.array([
            [-0.006440212456178318, -0.9999663050460452, -0.005090425923179875, 0],
            [0.103752775587519, 0.004394862232823593, -0.9945934077520526, -0.01],
            [0.9945822666936133, -0.006933538671890227, 0.1037209757960279, -0.02],
            [0, 0, 0, 1]   
        ])
    
        # cam1:T_cam1_cam0 T_cn_cnm1 cam0_to_cam1 (cam0, cam1为单位矩阵)
        T_cam1_cam0 = np.array([
            [-0.003285363915904449, 0.9999792135042117, -0.0055478773819153115, -0.01],
            [-0.09509479515523178, -0.005835183050213633, -0.9954511191279869, -0.02],
            [-0.9954628000675521, -0.002742844923650234, 0.09511198916750835, -0.03],
            [0, 0, 0, 1]   
        ])
    
        T_cam1_cam0[0:3, 0:3] = R
    
    
        '''imu'''
    
        # T = [R, t], T的逆 = [R.T, -R.T*t]
        print(T_cam1_cam0.astype(np.float16))
        print(np.linalg.inv(T_cam1_cam0).astype(np.float16))
        print(-T_cam1_cam0[0:3,0:3].T @ T_cam1_cam0[0:3, 3])
    
        # imu:T_ic cam0_to_imu (cam0, imu为单位矩阵)
        # T_imu_cam0 = T_cam1_imu.inv @ T_cam1_cam0 (顺:从左到右,右乘); T_cam02imu = T_imu2cam1.inv @ T_cam02cam1 (逆:从右到左,左乘)
        T_imu_cam0 = np.linalg.inv(T_cam1_imu).astype(np.float16) @ T_cam1_cam0
    
        # imu:T_ic cam1_to_imu (cam1, imu为单位矩阵)
        # T_imu_cam1 = T_cam1_imu.inv
        T_imu_cam1 = np.linalg.inv(T_cam1_imu).astype(np.float16)
    
    
        '''cam0'''
    
        # cam0:T_ci imu_to_cam0 (imu, cam0为单位矩阵)
        # T_cam0_imu = T_imu_cam0.inv
        T_cam0_imu = np.linalg.inv(T_imu_cam0).astype(np.float16)
        # T_cam0_imu = T_cam1_cam0.inv @ T_cam1_imu
        T_cam0_imu = np.linalg.inv(T_cam1_cam0).astype(np.float16) @ T_cam1_imu
    
        # cam0:T_cam0_cam1 T_cn_cnm1
        # T_cam0_cam1 = T_cam1_cam0.inv
        T_cam0_cam1 = np.linalg.inv(T_cam1_cam0).astype(np.float16)
        # T_cam0_cam1 = T_imu_cam0.inv @ T_imu_cam1
        T_cam0_cam1 = np.linalg.inv(T_imu_cam0).astype(np.float16) @ T_imu_cam1
    
        
        '''plot'''
    
        fig = plt.figure()
        ax = fig.add_subplot(projection='3d')
    
        # cam1
        plot_transform_matrix(np.identity(4), ax, 0.05, 'cam1')
        plot_transform_matrix(T_cam1_imu, ax, 0.05, 'imu')
        plot_transform_matrix(T_cam1_cam0, ax, 0.05, 'cam0')
        
        # imu
        plot_transform_matrix(np.identity(4), ax, 0.05, 'imu')
        plot_transform_matrix(T_imu_cam1, ax, 0.05, 'cam1')
        plot_transform_matrix(T_imu_cam0, ax, 0.05, 'cam0')
    
        # cam0
        plot_transform_matrix(np.identity(4), ax, 0.05, 'cam0')
        plot_transform_matrix(T_cam0_cam1, ax, 0.05, 'cam1')
        plot_transform_matrix(T_cam0_imu, ax, 0.05, 'imu')
    
        plt.axis('equal')
        plt.show()
  28. 发布Marker,在rviz上显示
    #!/usr/bin/env python3
    #coding:utf-8
    
    import rospy
    from visualization_msgs.msg import Marker
    
    if __name__=="__main__":
        rospy.init_node("mark_node")
        rate = rospy.Rate(1)
        pub = rospy.Publisher("marker_py", Marker, queue_size=1)
    
        shape = Marker.CUBE
    
        while not rospy.is_shutdown():
            marker = Marker()
    
            marker.header.frame_id = "map"
            marker.header.stamp = rospy.Time.now()
    
            marker.ns = "basic_shapes"
            marker.id = 0
    
            marker.type = shape
    
            marker.action = Marker.ADD
    
            marker.pose.position.x = 0
            marker.pose.position.y = 0
            marker.pose.position.z = 0
            marker.pose.orientation.x = 0.0
            marker.pose.orientation.y = 0.0
            marker.pose.orientation.z = 0.0
            marker.pose.orientation.w = 0.0
    
            marker.scale.x = 1
            marker.scale.y = 1
            marker.scale.z = 1
    
            marker.color.r = 0.0
            marker.color.g = 1.0
            marker.color.b = 0.0
            marker.color.a = 1.0
    
            marker.lifetime = rospy.Duration()
    
            pub.publish(marker)
    
            if shape == Marker.CUBE:shape = Marker.SPHERE
            elif shape == Marker.SPHERE:shape = Marker.ARROW
            elif shape == Marker.ARROW:shape = Marker.CYLINDER
            elif shape == Marker.CYLINDER:shape = Marker.CUBE
    
            rate.sleep()
    // basic_shapes.cpp
    
    #include <ros/ros.h>
    #include <visualization_msgs/Marker.h>
    
    int main( int argc, char** argv )
    {
      ros::init(argc, argv, "basic_shapes");
      ros::NodeHandle n;
      ros::Rate r(1);
      ros::Publisher marker_pub = n.advertise<visualization_msgs::Marker>("marker_cpp", 1);
    
      uint32_t shape = visualization_msgs::Marker::CUBE;
    
      while (ros::ok())
      {
        visualization_msgs::Marker marker;
    
        marker.header.frame_id = "map";
        marker.header.stamp = ros::Time::now();
    
        marker.ns = "basic_shapes";
        marker.id = 0;
    
        marker.type = shape;
    
        marker.action = visualization_msgs::Marker::ADD;
    
        marker.pose.position.x = 0;
        marker.pose.position.y = 0;
        marker.pose.position.z = 0;
        marker.pose.orientation.x = 0.0;
        marker.pose.orientation.y = 0.0;
        marker.pose.orientation.z = 0.0;
        marker.pose.orientation.w = 1.0;
    
        marker.scale.x = 1.0;
        marker.scale.y = 1.0;
        marker.scale.z = 1.0;
    
        marker.color.r = 0.0f;
        marker.color.g = 1.0f;
        marker.color.b = 0.0f;
        marker.color.a = 1.0;
    
        marker.lifetime = ros::Duration();
    
        while (marker_pub.getNumSubscribers() < 1)
        {
          if (!ros::ok())
          {
            return 0;
          }
          ROS_WARN_ONCE("Please create a subscriber to the marker");
          sleep(1);
        }
        marker_pub.publish(marker);
    
        switch (shape)
        {
        case visualization_msgs::Marker::CUBE:
          shape = visualization_msgs::Marker::SPHERE;
          break;
        case visualization_msgs::Marker::SPHERE:
          shape = visualization_msgs::Marker::ARROW;
          break;
        case visualization_msgs::Marker::ARROW:
          shape = visualization_msgs::Marker::CYLINDER;
          break;
        case visualization_msgs::Marker::CYLINDER:
          shape = visualization_msgs::Marker::CUBE;
          break;
        }
    
        r.sleep();
      }
    }
    
    
    /*
    需在CMakeLists.txt中增加一些配置
    
    find_package(catkin REQUIRED COMPONENTS
      roscpp
      visualization_msgs
    )
    
    add_executable(basic_shapes src/basic_shapes.cpp)
    target_link_libraries(basic_shapes ${catkin_LIBRARIES})
    
    */

    4a9ba7681d0a4389836e733faf75e6d2.png

  29. 用MakerArray发布多个Marker
  30. 安装foxglove远程可视化工具(ROS1&&ROS2)
    # 1.到官网下载安装包并安装
    https://foxglove.dev/download
    sudo apt install ./foxglove-studio-*.deb
    
    # 2.rosbridge_server安装
    # ROS2
    sudo apt-get install ros-ROS2版本名称-rosbridge-suite
    # ROS1
    sudo apt-get install ros-ROS1版本名称-foxglove-bridge
    
    # 3.将服务端与客户端置于同一网络下,远程调试使用网线连接提高通信效率。
    
    # 4.启动WebSocket服务器,同时会输出监听的端口号(一般为port:9090)
    # ROS2
    ros2 launch rosbridge_server rosbridge_websocket_launch.xml
    # ROS1
    roslaunch foxglove_bridge foxglove_bridge.launch
    
    # 5.打开Foxglove Studio,选择‘打开连接’,点击侧边栏‘Rosbridge’,更改对应WebSocket URL,包括两部分:服务端的本机ip地址+服务器监听的端口号。

  31. 关于安装Ubuntu时显示界面太小,有组件被遮挡的问题:按Alt+F7后移动鼠标可以把被遮挡的部分拖出来
  32. 关于NUC小电脑安装Ubuntu20.04后,无WIFI驱动的问题
    # 注意:装完系统后不要用软件更新器更新Ubuntu核心组件,否则下列方法将无效
    
    # 前期电脑没网可以暂时用数据线连接手机,通过USB网络共享给电脑提供网络
    
    # 1.用软件和更新换源,建议用中科大ustc的
    
    # 2.安装依赖
    sudo apt install git make gcc
    
    # 3.下载backport-iwlwifi并解压
    https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/backport-iwlwifi.git/snapshot/backport-iwlwifi-master.tar.gz
    
    # 4.安装backport-iwlwifi
    cd backport-iwlwifi-master
    sudo make defconfig-iwlwifi-public
    sudo make
    sudo make install
    
    # 5.下载linux-firmware
    git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux-firmware.git
    
    # 6.复制到/lib/firmware/
    cd linux-firmware
    sudo cp iwlwifi-* /lib/firmware/
    
    # 7.重启
    reboot
    
    # 以上步骤成功了也不能更新Ubuntu核心组件,不然WiFi又会消失

 33.关于Ubuntu22.04接入ch340后,无/dev/ttyUSB*的问题,参考

### 关于ROS学习的教程和资源 #### 官方文档与指南 官方文档提供了详尽的学习材料,适合不同层次的学习者。对于初学者而言,可以从安装指导开始了解如何设置环境;而对于有一定基础的人,则可以深入探索特定功能模块的应用实例[^1]。 #### 社区贡献的教学视频系列 存在一系列由社区成员制作并分享出来的教学视频,这些视频覆盖了从基础知识介绍到高级应用开发等多个方面。例如有关于基于`roslibjs`、`ros3djs`进行Web端集成展示的内容,也有利用Flask框架配合上述JavaScript库来实现在网页上加载机器人结构描述(URDF)模型的例子,还有针对桌面应用程序如Qt界面下操作ROS节点的方法讲解等[^2]。 #### 实战项目实践 通过实际项目的练习能够加深理解。比如创建一个简单的C++程序作为ROS节点,在此过程中会涉及到编写CMakeLists.txt配置文件以指定编译选项、链接必要的第三方库等工作。具体来说就是定义最小支持版本号、声明工程项目名称之后添加可执行文件项,并指明源码位置;接着调用find_package命令自动定位所需组件及其关联资源路径;最后再分别设定公共包含目录以及连接时使用的静态/动态库列表[^3]。 ```cpp cmake_minimum_required(VERSION 3.8) project(ros2_cpp) add_executable(ros2_cpp_node src/ros2_cpp_node.cpp) find_package(rclcpp REQUIRED) target_include_directories(ros2_cpp_node PRIVATE ${rclcpp_INCLUDE_DIRS}) target_link_libraries(ros2_cpp_node PRIVATE rclcpp) ``` #### 开发者论坛交流平台 加入活跃的技术讨论群组可以获得及时的帮助和支持。这里聚集了许多经验丰富的开发者愿意解答疑问并且分享个人见解和技术心得。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值